Bladeren bron

Merge branch 'dev' of http://1.94.207.143:3000/xyy/xyy-m into dev

suwenjiang 2 maanden geleden
bovenliggende
commit
5e0c021fc7

+ 1 - 1
.env.development

@@ -1,6 +1,6 @@
 VITE_APP_ENV=development
 
-# VITE_APP_BASE_URL=https://www.xiaoyaotravel.com/api/
+# VITE_APP_BASE_URL=https://service.xiaoyaotravel.com/api/
 VITE_APP_BASE_URL=http://101.126.146.250:8082/
 # VITE_APP_BASE_URL=http://192.168.1.204:8082
 VITE_APP_EMOJI_API=https://v.xiaoyaotravel.com/emoji/

BIN
src/assets/img/equipment.png


BIN
src/assets/img/house/modalBg.png


BIN
src/assets/img/house/newHouse.png


+ 7 - 1
src/components/Car/Search/CarList/Item.vue

@@ -23,7 +23,13 @@
       </div>
       <div class="flex items-center space-x-10">
         <span class="text-base font-semibold text-black-6">评分</span>
-        <van-rate disabled :size="14" v-model="itemData.score" />
+        <van-rate
+          disabled
+          :size="14"
+          v-model="itemData.score"
+          disabled-color="#FD9A00"
+          color="#FD9A00"
+        />
       </div>
       <div class="flex items-center justify-between">
         <div>

+ 15 - 1
src/pages/car/search.client.vue

@@ -21,6 +21,9 @@
 <script setup>
 const route = useRoute();
 
+// const authStore = useAuthStore();
+// const { token } = storeToRefs(authStore);
+
 const selectedCar = ref({});
 const selectedDriver = ref({});
 
@@ -60,7 +63,18 @@ function handleSelectDriver(driver) {
   toBookOrder();
 }
 
-function toBookOrder() {
+async function toBookOrder() {
+  // if (!token.value) {
+  //   await navigateTo({
+  //     path: "/login",
+  //     replace: true,
+  //     query: {
+  //       redirect: route.fullPath,
+  //     },
+  //   });
+
+  //   return;
+  // }
   navigateTo({
     path: "/car/submit-order",
     query: {

+ 15 - 0
src/pages/car/submit-order.client.vue

@@ -113,6 +113,9 @@
 <script setup>
 const route = useRoute();
 
+const authStore = useAuthStore();
+const { token } = storeToRefs(authStore);
+
 const useUserInfo = useUserInfoStore();
 const { userInfo } = storeToRefs(useUserInfo);
 
@@ -171,6 +174,18 @@ function onSubmit(val) {
  * handleSubmit()
  */
 async function handleSubmit() {
+  if (!token.value) {
+    navigateTo({
+      path: "/login",
+      query: {
+        redirect: route.path,
+        ...route.query,
+      },
+      replace: true,
+    });
+    return;
+  }
+
   if (!formData.connectPhone) {
     showToast("请填写手机号");
     return;

+ 221 - 0
src/pages/house/[type].vue

@@ -0,0 +1,221 @@
+<template>
+    <div v-if="showPage" class="pb-20 bg-[#fff]">
+        <div class="sticky top-50 z-50 pt-15 bg-[#fff]">
+            <Search @search="search" />
+            <div v-if="listType == OLD_HOUSE" class="flex items-center justify-center mt-10">
+                <div @click="isSaleByLandlord = 0"
+                    :class="isSaleByLandlord === 0 ? 'bg-[#FD9A00] text-[#fff]' : 'text-[#666]'"
+                    class="w-[82px] font-bold h-[30px] rounded-xl flex items-center justify-center text-[14px]">
+                    全部楼盘
+                </div>
+                <div @click="isSaleByLandlord = 1"
+                    :class="isSaleByLandlord === 1 ? 'bg-[#FD9A00] text-[#fff]' : 'text-[#666]'"
+                    class="w-[82px] font-bold h-[30px] rounded-xl flex items-center justify-center text-[14px] ml-30">
+                    房东自售
+                </div>
+            </div>
+            <Filters @change="filterChange" :listType="listType" class="mt-10" />
+        </div>
+        <van-list :immediate-check="false" :finished="finished" loading-text="加载中..."
+            error-text="获取失败" @load="loadMore" v-model:loading="loading">
+            <div class="p-[10px]">
+                <NuxtLink :to="`${listType==NEW_HOUSE?'/house/newHouse/':listType==OLD_HOUSE?'/house/oldHouse/':'/house/rentHouse/'}${item.id}`" v-for="item in dataList" :key="item.id"
+                    class="flex items-center mt-15 border-b border-[#dedede] pb-15">
+                    <div class="w-[126px] h-[126px] rounded shrink-0 overflow-hidden">
+                        <img class="object-cover w-full h-full" :src="item.coverUrl" alt="">
+                    </div>
+                    <div class="flex flex-col justify-between h-[126px] ml-8" style="width:calc(100% - 136px)">
+                        <div class="text-[#333] text-[16px] truncate font-bold w-full">
+                            {{ item.houseTitle }}
+                        </div>
+                        <template v-if="listType == NEW_HOUSE || listType == OLD_HOUSE">
+                            <div class="scrollbar w-full truncate text-[12px] text-[#fff]" style="overflow-x: auto">
+                                <div class="flex items-center">
+                                    <div v-if="item.isOnSaleDictMap?.name"
+                                        class="mr-10 rounded-full border border-[#FC9900] pl-10 pr-10 text-[#FC9900]">
+                                        {{ item.isOnSaleDictMap?.name }}
+                                    </div>
+                                    <div v-if="item.houseTypeDictMap?.name"
+                                        class="mr-10 rounded-full border border-[#FC9900] pl-10 pr-10 text-[#FC9900]">
+                                        {{ item.houseTypeDictMap?.name }}
+                                    </div>
+                                    <template v-if="Array.isArray(item.tags)">
+                                        <div v-for="(tag, tagIndex) in item.tags" :key="tagIndex"
+                                            class="mr-10 rounded-full border border-[#FC9900] pl-10 pr-10 text-[#FC9900]">
+                                            {{ tag }}
+                                        </div>
+                                    </template>
+                                </div>
+                            </div>
+                            <div class="w-full truncate text-[14px] text-[#444]">
+                                <span class="mr-10">{{ item.officeTypeDictMap?.name }}</span>
+                                <span class="mr-10">{{ item.orientationDictMap?.name }}</span>
+                                <!-- <span class="mr-10">{{dayjs(item.yearOfBuild).format('YYYY-MM-DD')}}建造</span> -->
+                                <span class="mr-10">{{ item.currentFloor }}楼(共{{ item.totalFloor }}层)</span>
+                            </div>
+                            <div class="w-full truncate text-[14px] text-[#444]">
+                                <span class="mr-20">{{ item.address }}</span>
+                                <span class="mr-20">{{ item.communityName }}</span>
+                            </div>
+                            <div class="flex items-center text-[14px]">
+                                <span v-if="listType == NEW_HOUSE" class="text-[#FF1A1A] font-bold">
+                                    {{ item.currency}}{{item.averagePrice}}/m²
+                                </span>
+                                <span v-if="listType == OLD_HOUSE" class="text-[#FF1A1A] font-bold">
+                                    {{ item.currency}}{{item.totalPrice}}
+                                </span>
+                                <span v-if="listType == OLD_HOUSE" class="ml-20">
+                                    {{ item.currency}}{{item.averagePrice}}/m²
+                                </span>
+                            </div>
+                        </template>
+                        <template v-if="listType == RENT_HOUSE">
+                            <div class="w-full truncate text=[#444] text-[14px]">
+                                <span class="mr-20">{{ item.area }}m²</span>
+                                <span>{{ item?.officeTypeDictMap?.name }}</span>
+                            </div>
+                            <div class="w-full truncate text=[#444] text-[14px]">
+                                <span class="mr-20">{{ item?.rentTypeDictMap?.name }}</span>
+                                <span>{{ item?.floorTypeDictMap?.name }}{{ item.currentFloor }}楼 (共{{ item.totalFloor }}层)</span>
+                            </div>
+                            <div class="w-full truncate text=[#444] text-[14px]">
+                                <span class="mr-20">{{ item?.region }}</span>
+                                <span>{{ item?.communityName }}</span>
+                            </div>
+                            <div class="flex items-center text-[14px]">
+                                <span class="text-[#FF1A1A] font-bold">
+                                    {{ item.currency }}{{item.rentPrice }}/月
+                                </span>
+                            </div>
+                        </template>
+                    </div>
+                </NuxtLink>
+            </div>
+            <div v-if="finished" class="py-20 text-center text-[#999] text-[15px]">
+                没有更多了~
+            </div>
+        </van-list>
+    </div>
+    <div v-else class="text-center text-[#999] mt-100">
+        页面不存在
+    </div>
+</template>
+
+<script setup>
+import Filters from './components/filters.vue';
+import Search from './components/Search.vue';
+
+const NEW_HOUSE = 'newHouse';//新房
+const RENT_HOUSE = 'rentHouse';//租房
+const OLD_HOUSE = 'oldHouse';//二手房
+
+const listType = useRouteParam('type')
+const showPage = computed(() => listType.value == NEW_HOUSE || listType.value == RENT_HOUSE || listType.value == OLD_HOUSE)
+const route = useRoute()
+
+const dayjs = useDayjs()
+const pageNum = ref(1)
+const pageSize = ref(5)
+const params = ref('')
+const dataList = ref([])
+const loading = ref(true)
+const finished = ref(false)
+const searchString = ref('')
+
+// 是否房东直售0否1是
+const isSaleByLandlord = ref(0)
+watch(isSaleByLandlord, () => {
+    if(listType.value == OLD_HOUSE){
+        pageNum.value = 1
+        getList()
+    }
+    
+})
+async function getList() {
+
+    if (!showPage.value) return
+
+    let param = `pageNum=${pageNum.value}&pageSize=${pageSize.value}${params.value}&searchString=${searchString.value}`
+
+    if(listType.value == OLD_HOUSE){
+        param+=`&houseSourceType=1&isSaleByLandlord=${isSaleByLandlord.value}`
+    }
+
+    if(listType.value == NEW_HOUSE){
+        param+=`&houseSourceType=0`
+    }
+
+    let url = '/website/house/sale/viewHouseListAfterFilter'
+    if (listType.value == RENT_HOUSE) {
+        url = '/website/tourism/tourismHouseRent/tourHouseRentInfoList'
+    }
+
+    showLoadingToast({
+        message: '加载中...',
+        forbidClick: true,
+        duration: 0,
+    })
+
+    const { data } = await request(`${url}?${param}`).finally(() => closeToast())
+
+    try {
+        data.dataList.map((item, index) => {
+            data.dataList[index].tags = item.tag.split('&')
+        })
+    } catch (error) { }
+
+    if (pageNum.value == 1) {
+        dataList.value = data.dataList
+    } else {
+        dataList.value = dataList.value.concat(data.dataList)
+    }
+
+    loading.value = false
+    if (dataList.value.length >= data.totalCount) {
+        finished.value = true;
+    } else {
+        finished.value = false;
+    }
+}
+function loadMore() {
+    pageNum.value++;
+    getList()
+}
+
+// 搜索
+function search(keyword) {
+    if (!searchString.value && !keyword) return
+    searchString.value = keyword
+    pageNum.value = 1
+    getList()
+}
+
+// 筛选
+function filterChange(param) {
+    params.value = param
+    pageNum.value = 1
+    getList()
+}
+onMounted(() => {
+    let title = '逍遥游-房屋租售'
+    if(listType.value == NEW_HOUSE) title = '逍遥游-新房'
+    if(listType.value == OLD_HOUSE) title = '逍遥游-二手房'
+    if(listType.value == RENT_HOUSE) title = '逍遥游-租房'
+    document.title=title
+})
+</script>
+
+<style scoped>
+.scrollbar::-webkit-scrollbar {
+  width: 0px;
+  height: 0px;
+  background-color: #ccc;
+}
+
+/* 滚动条滑块样式 */
+.scrollbar::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+  background-color: #555;
+}
+</style>

+ 20 - 0
src/pages/house/components/Search.vue

@@ -0,0 +1,20 @@
+<template>
+    <div class="flex justify-center ">
+        <div class="w-[328px] h-[30px] bg-[#F8F8F8] rounded-full flex items-center justify-between">
+            <input v-model="searchValue" type="text" class="text-[12px] ml-20 bg-[#F8F8F8] flex-1" placeholder="请输入小区名称、地址">
+            <div @click.stop="search" class="h-[27px] w-[50px] mr-2 rounded-full bg-[#FD9A00] text-[#fff] text-[14px] flex items-center justify-center shrink-0">
+                搜索
+            </div>
+        </div>
+    </div>
+</template>
+<script setup>
+    const emit = defineEmits(['search'])
+    const searchValue = ref('')
+    function search(){
+        emit('search',searchValue.value)
+    }
+</script>
+<style scoped>
+
+</style>

+ 507 - 0
src/pages/house/components/filters.vue

@@ -0,0 +1,507 @@
+<template>
+    <div>
+        <van-dropdown-menu ref="dropDownMenuRef">
+            <van-dropdown-item title="地域">
+                <van-picker v-model="selectedValues" @confirm="onConfirmArea" :columns="areaList"
+                    :columns-field-names="customFieldName" />
+            </van-dropdown-item>
+            <van-dropdown-item v-if="props.listType!='rentHouse'" @change="doEmit()" :title="price_select == 0 ? '价格' : priceList[price_select].text"
+                v-model="price_select" :options="priceList" />
+            <van-dropdown-item title="户型">
+                <div class="text-[#333] text-[12px] p-15">
+                    <div class="text-[14px] text-[#444]">厅室</div>
+                    <div class="flex items-center justify-between mt-10">
+                        <div @click="officeTypeId = item.itemId, doEmit()" class="bg-[#e4e4e4] px-8 py-4 rounded"
+                            :class="[officeTypeId == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in officeType" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                    <div class="text-[14px] text-[#444] mt-10">朝向</div>
+                    <div class="flex items-center mt-10">
+                        <div @click="orientation_select = item.itemId, doEmit()"
+                            class="bg-[#e4e4e4] mr-20 px-8 py-4 rounded"
+                            :class="[orientation_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in orientation" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                    <div class="text-[14px] text-[#444] mt-10">面积</div>
+                    <div class="flex items-center flex-wrap">
+                        <div @click="area_select = item.itemId, doEmit()"
+                            class="bg-[#e4e4e4] mr-15 mt-10 px-8 py-4 rounded"
+                            :class="[area_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in areas" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                </div>
+            </van-dropdown-item>
+            <van-dropdown-item title="更多">
+                <div class="text-[#333] text-[12px] p-15">
+                    <div class="text-[14px] text-[#444]">楼层</div>
+                    <div v-if="props.listType == 'newHouse' || props.listType=='oldHouse'" class="flex items-center mt-10">
+                        <div @click="floor_select = item.itemId, doEmit()" class="bg-[#e4e4e4] mr-20 px-8 py-4 rounded"
+                            :class="[floor_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in floorList" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                    <div v-if="props.listType == 'rentHouse'" class="flex items-center mt-10">
+                        <div @click="floorType_select = item.itemId, doEmit()" class="bg-[#e4e4e4] mr-20 px-8 py-4 rounded"
+                            :class="[floorType_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in houseFloorType" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                    <div class="text-[14px] text-[#444] mt-10">装修</div>
+                    <div class="flex items-center mt-10">
+                        <div @click="decoration_select = item.itemId, doEmit()"
+                            class="bg-[#e4e4e4] mr-20 px-8 py-4 rounded"
+                            :class="[decoration_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in decoration" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                    <div class="text-[14px] text-[#444] mt-10">房龄</div>
+                    <div class="flex items-center justify-between flex-wrap">
+                        <div @click="houseAge_select = item.itemId, doEmit()"
+                            class="bg-[#e4e4e4] mt-10 px-8 py-4 rounded"
+                            :class="[houseAge_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                            v-for="(item, index) in houseAgeList" :key="index">
+                            {{ item.itemName }}
+                        </div>
+                    </div>
+                    <template v-if="props.listType == 'newHouse' || props.listType == 'oldHouse'">
+                        <div class="text-[14px] text-[#444] mt-10">类型</div>
+                        <div class="flex items-center justify-between flex-wrap">
+                            <div @click="houseTypeId = item.itemId, doEmit()" class="bg-[#e4e4e4] mt-10 px-8 py-4 rounded"
+                                :class="[houseTypeId == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                                v-for="(item, index) in houseType" :key="index">
+                                {{ item.itemName }}
+                            </div>
+                        </div>
+                    </template>
+                    <template v-if="props.listType == 'rentHouse'">
+                        <div class="text-[14px] text-[#444] mt-10">类型</div>
+                        <div class="flex items-center justify-between flex-wrap">
+                            <div @click="rentType_select = item.itemId, doEmit()" class="bg-[#e4e4e4] mt-10 px-8 py-4 rounded"
+                                :class="[rentType_select == item.itemId ? 'text-[#ff9300] font-bold' : '']"
+                                v-for="(item, index) in houseRentType" :key="index">
+                                {{ item.itemName }}
+                            </div>
+                        </div>
+                    </template>
+
+                </div>
+            </van-dropdown-item>
+            <van-dropdown-item
+                v-if="props.listType!='rentHouse'"
+                :title="sortRule_select == 0 ? '排序' : sortRuleList.filter(item => item.value == sortRule_select)[0].text"
+                v-model="sortRule_select" :options="sortRuleList" @change="doEmit()" />
+        </van-dropdown-menu>
+    </div>
+</template>
+<script setup>
+import { ref } from 'vue'
+
+const emit = defineEmits(['change'])
+const props = defineProps({
+    listType: { type: String, default: '' }
+})
+
+const dropDownMenuRef = ref(null)
+
+const sortRule_select = ref(0)
+const sortRuleList = [
+    {
+        text: '默认排序', value: 0
+    },
+    {
+        text: '单价由低到高', value: 1
+    },
+    {
+        text: '单价由高到低', value: 2
+    },
+    {
+        text: '开盘时间升序', value: 3
+    },
+    {
+        text: '开盘时间倒序', value: 4
+    },
+]
+
+const areaList = ref([])
+const selectedValues = ref([])//联机组件选中值
+const customFieldName = {
+    text: 'levelName',
+    value: 'id',
+};
+// 房价
+const price_select = ref(0)
+const priceList = ref([
+    {
+        text: '全部',
+        value: 0,
+    },
+    {
+        text: '30万以下',
+        value: 1,
+        totalPriceEnd: 300000
+    },
+    {
+        text: '30万-50万',
+        value: 2,
+        totalPriceStart: 300000,
+        totalPriceEnd: 500000
+    },
+    {
+        text: '50万-80万',
+        value: 3,
+        totalPriceStart: 500000,
+        totalPriceEnd: 800000
+    },
+    {
+        text: '80万-100万',
+        value: 4,
+        totalPriceStart: 800000,
+        totalPriceEnd: 1000000
+    },
+    {
+        text: '100万-120万',
+        value: 5,
+        totalPriceStart: 1000000,
+        totalPriceEnd: 1200000
+    },
+    {
+        text: '120万-150万',
+        value: 6,
+        totalPriceStart: 1200000,
+        totalPriceEnd: 1500000
+    },
+    {
+        text: '200万以上',
+        value: 7,
+        totalPriceStart: 2000000
+    }
+])
+//厅室
+const officeTypeId = ref(-1)
+const officeType = ref([])
+
+//朝向
+const orientation_select = ref(-1) //
+const orientation = ref([])
+
+// 楼层(买房)
+const floor_select = ref(0)
+const floorList = ref([
+    {
+        itemName: '不限',
+        itemId: 0
+    },
+    {
+        itemName: '低层',
+        itemId: 1,
+        currentFloorStart: 1,
+        currentFloorEnd: 10
+    },
+    {
+        itemName: '中层',
+        itemId: 2,
+        currentFloorStart: 10,
+        currentFloorEnd: 20
+    },
+    {
+        itemName: '高层',
+        itemId: 3,
+        currentFloorStart: 20
+    }
+])
+//装修类型
+const decoration_select = ref(-1)
+const decoration = ref([])
+
+// 房龄
+const houseAge_select = ref(0)
+const houseAgeList = ref([
+    {
+        itemName: '不限',
+        itemId: 0
+    },
+    {
+        itemName: '2年以下',
+        itemId: 1,
+        houseAgeStart: 1,
+        houseAgeEnd: 2
+    },
+    {
+        itemName: '2年-5年',
+        itemId: 2,
+        houseAgeStart: 2,
+        houseAgeEnd: 5
+    },
+    {
+        itemName: '5年-10年',
+        itemId: 3,
+        houseAgeStart: 5,
+        houseAgeEnd: 10
+    },
+    {
+        itemName: '10年以上',
+        itemId: 4,
+        houseAgeStart: 10
+    }
+])
+
+//房屋类型
+const houseTypeId = ref(-1)
+const houseType = ref([])
+
+// 面积
+const area_select = ref(0)
+const areas = ref([
+    {
+        itemName: '不限',
+        itemId: 0,
+    },
+    {
+        itemName: '60m²以下',
+        itemId: 1,
+        areaEnd: 60
+    },
+    {
+        itemName: '60m²-80m²',
+        itemId: 2,
+        areaStart: 60,
+        areaEnd: 80
+    },
+    {
+        itemName: '80m²-100m²',
+        itemId: 3,
+        areaStart: 80,
+        areaEnd: 100
+    },
+    {
+        itemName: '100m²-120m²',
+        itemId: 4,
+        areaStart: 100,
+        areaEnd: 120
+    },
+    {
+        itemName: '120m²-150m²',
+        itemId: 5,
+        areaStart: 120,
+        areaEnd: 150
+    },
+    {
+        itemName: '150m²-200m²',
+        itemId: 6,
+        areaStart: 150,
+        areaEnd: 200
+    },
+    {
+        itemName: '200m²以上',
+        itemId: 7,
+        areaStart: 200
+    }
+])
+
+// 租房类型(合租整租等)
+const rentType_select = ref(-1)
+const houseRentType = ref([])
+
+// 租房楼层
+const floorType_select = ref(-1)
+const houseFloorType = ref([])
+
+const allCities = ref([])
+// 获取过滤条件
+async function getFilters() {
+    let url = `/website/house/sale/viewHouseFilterLists`
+    if (props.listType == 'rentHouse') {
+        url = '/website/tourism/tourismHouseRent/viewHouseFilterLists'
+    }
+    const {
+        data: { elseMap: eMap, houseMenuTree }
+    } = await request(url)
+
+    //   elseMap.value = eMap
+    const {
+        OfficeType,//厅室
+        Decoration,//装修
+        HouseType,//房型
+        Orientation,//朝向
+        HouseRentType,
+        HouseFloorType
+    } = eMap
+    if (Array.isArray(Orientation)) {
+        Orientation.unshift({ itemName: '不限', itemId: -1 })
+        orientation.value = Orientation
+    }
+    if (Array.isArray(OfficeType)) {
+        OfficeType.unshift({ itemName: '不限', itemId: -1 })
+        officeType.value = OfficeType
+    }
+    if (Array.isArray(Decoration)) {
+        Decoration.unshift({ itemName: '不限', itemId: -1 })
+        decoration.value = Decoration
+    }
+    if (Array.isArray(HouseType)) {
+        HouseType.unshift({ itemName: '不限', itemId: -1 })
+        houseType.value = HouseType
+    }
+    if (Array.isArray(HouseRentType)) {
+        HouseRentType.unshift({ itemName: '不限', itemId: -1 })
+        houseRentType.value = HouseRentType
+    }
+    if (Array.isArray(HouseFloorType)) {
+        HouseFloorType.unshift({ itemName: '不限', itemId: -1 })
+        houseFloorType.value = HouseFloorType
+    }
+    let cities = []
+    const list = houseMenuTree.map((item) => {
+        item.children.unshift({ levelName: '全部', id: 0, children: [] })
+        
+        item.children.map((country) => {
+            country.children.unshift({ levelName: '全部', id: 0 })
+            country.areaId = item.id
+            country.areaName = item.levelName
+            
+            country.children.map((city) => {
+                delete city.children
+                city.countryId = country.id
+                city.countryName = country.levelName
+                city.areaId = item.id
+                city.areaName = item.levelName
+                cities.push(city)
+            })
+        })
+        
+        return item
+    })
+    list.unshift({
+        levelName: '全部', id: 0,
+        children: [{ levelName: '全部', id: 0, children: [{ levelName: '全部', id: 0, }] }]
+    })
+    areaList.value = list
+    allCities.value = cities
+    doEmit()
+}
+const areaIds = ref('')
+function onConfirmArea(value) {
+    console.log('所有城市:',allCities.value)
+    const { selectedOptions,selectedValues } = value
+    console.log('value:',value)
+    if (!Array.isArray(selectedOptions)) return
+    const [areaId,countryId,cityId] = selectedValues
+    let ids = []
+    if(!countryId){
+        let citieIds = allCities.value.filter(item=>item.areaId==areaId && item.id)
+        console.log('citieIds:',citieIds)
+        citieIds.map(item=>ids.push(item.id))
+        
+    }else if(!cityId){
+        let citieIds = allCities.value.filter(item=>item.countryId==countryId && item.id)
+        console.log('citieIds:',citieIds)
+        citieIds.map(item=>ids.push(item.id))
+    }else{
+        selectedOptions.map(item => {
+            if (item.id) ids.push(item.id)
+        })
+    }
+    
+    areaIds.value = ids.join(',')
+
+    dropDownMenuRef.value && dropDownMenuRef.value.close()
+
+    doEmit()
+}
+
+getFilters()
+
+function doEmit() {
+
+    let str = ``
+    if (areaIds.value) str += `&areaIds=${areaIds.value}`
+
+    // 价格
+    if (price_select.value) {
+        const price = priceList.value.filter(item => item.value == price_select.value)[0] || {}
+        if (price.totalPriceStart) {
+            str += `&totalPriceStart=${price.totalPriceStart}`
+        }
+        if (price.totalPriceEnd) {
+            str += `&totalPriceEnd=${price.totalPriceEnd}`
+        }
+    }
+
+    // 厅室
+    if (officeTypeId.value > -1) {
+        str += `&officeType=${officeTypeId.value}`
+    }
+
+    // 类型(别墅、公寓等)
+    if (houseTypeId.value > -1) {
+        str += `&houseType=${houseTypeId.value}`
+    }
+
+    // 租房类型(整租合租等)
+    if(rentType_select.value > -1){
+        str+=`&rentType=${rentType_select.value}`
+    }
+
+    // 面积
+    if (area_select.value) {
+        const area = areas.value.filter(item => item.itemId == area_select.value)[0] || {}
+        if (area.areaStart) {
+            str += `&areaStart=${area.areaStart}`
+        }
+        if (area.areaEnd) {
+            str += `&areaEnd=${area.areaEnd}`
+        }
+    }
+    // 朝向
+    if (orientation_select.value > -1) {
+        str += `&orientation=${orientation_select.value}`
+    }
+
+    // 房龄
+    if (houseAge_select.value) {
+        const houseAge = houseAgeList.value.filter(item => item.itemId == houseAge_select.value)[0] || {}
+        if (houseAge.houseAgeStart) {
+            str += `&houseAgeStart=${houseAge.houseAgeStart}`
+        }
+        if (houseAge.houseAgeEnd) {
+            str += `&houseAgeEnd=${houseAge.houseAgeEnd}`
+        }
+    }
+
+    // 楼层
+    if (floor_select.value) {
+        const floor = floorList.value.filter(item => item.itemId == floor_select.value)[0] || {}
+        if (floor.currentFloorStart) {
+            str += `&currentFloorStart=${floor.currentFloorStart}`
+        }
+        if (floor.currentFloorEnd) {
+            str += `&currentFloorEnd=${floor.currentFloorEnd}`
+        }
+    }
+
+    // 楼层(租房)
+    if(floorType_select.value > -1){
+        str += `&floorType=${floorType_select.value}`
+    }
+
+    // 装修
+    if (decoration_select.value > -1) {
+        str += `&decoration=${decoration_select.value}`
+    }
+
+    // 排序
+    if (sortRule_select.value) {
+        str += `&sort=${sortRule_select.value}`
+    }
+    emit('change', str)
+}
+</script>

+ 90 - 0
src/pages/house/index.vue

@@ -0,0 +1,90 @@
+<template>
+    <div class="bg-[#F8F8F8] pt-15" style="min-height: calc(100vh - 70px);">
+        
+        <div class="sticky top-50 z-50 flex items-center justify-between text-[12px] px-[15px]">
+            <NuxtLink to="/house/newHouse" class="w-[80px] h-[42px] flex items-center justify-center bg-[#fff] rounded">
+                <img class="w-[30px] h-[30px]" :src="newHouseIcon" alt="">
+                <span class="text-[#707070] font-bold ">新房</span>
+            </NuxtLink>
+            <NuxtLink to="/house/oldHouse" class="w-[80px] h-[42px] flex items-center justify-center bg-[#fff] rounded">
+                <img class="w-[30px] h-[30px]" :src="newHouseIcon" alt="">
+                <span class="text-[#707070] font-bold ">二手房</span>
+            </NuxtLink>
+            <div @click="showModal=true" class="w-[80px] h-[42px] flex items-center justify-center bg-[#fff] rounded">
+                <img class="w-[30px] h-[30px]" :src="newHouseIcon" alt="">
+                <span class="text-[#707070] font-bold ">卖房</span>
+            </div>
+            <NuxtLink to="/house/rentHouse" class="w-[80px] h-[42px] flex items-center justify-center bg-[#fff] rounded">
+                <img class="w-[30px] h-[30px]" :src="newHouseIcon" alt="">
+                <span class="text-[#707070] font-bold ">租房</span>
+            </NuxtLink>
+        </div>
+        <div class="text-[#292929] p-15 text-[16px]">
+            新开楼盘
+        </div>
+        <div class="px-15 flex flex-wrap justify-between justify-bewtween">
+            <NuxtLink :to="`/house/newHouse/${item.id}`" v-for="item in dataList" :key="item.id" class="w-[48%] mb-15">
+                <div class="w-full aspect-[1/1] rounded overflow-hidden bg-[#f5f5f5]">
+                    <img :src="item.coverUrl" class="w-full h-full object-cover" alt="" />
+                </div>
+                <div class="w-full truncate text-[#444] font-bold text-[14px] mt-[12px]">
+                    {{ item.houseTitle }}
+                </div>
+                <div class="flex items-center font-bold">
+                    <span class="text-[#FF1A1A] text-[14px]">{{item.currency}}{{ item.averagePrice }}</span>
+                    <span class="text-[#666] text-[12px]">/m²</span>
+                </div>
+            </NuxtLink>
+        </div>
+        <div v-if="showModal"  class="fixed top-0 left-0 w-full h-full flex items-center justify-center " style="background:rgba(0,0,0,0.5);z-index:1000">
+            <div class="animation-in">
+                <div class="w-[246px] h-[246px] rounded-[10px] flex flex-col items-center justify-center" :style="{background:`url(${modalBgIcon})`,backgroundSize:'100% 100%'}">
+                    <div class="w-[157px] h-[157px] bg-[#fff]">
+
+                    </div>
+                    <div class="text-[#242424] text-[12px] font-bold mt-15">
+                        扫码联系我们专业卖房顾问!!!
+                    </div>
+                </div>
+                <div class="w-[50px] h-[50px] rounded-full overflow-hidden flex items-center justify-center" style="background:rgba(0,0,0,0.6);margin:0 auto;margin-top:30px;">
+                    <van-icon @click="showModal=false" name="cross" color="#fff" size="30" />
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import newHouseIcon from '~/assets/img/house/newHouse.png';
+import modalBgIcon from '~/assets/img/house/modalBg.png';
+
+
+const { data: dataList, status } = useMyFetch(`/website/house/sale/viewNewHouseList`)
+
+const showModal = ref(false)
+const pageNum = ref(1)
+const pageSize = ref(10)
+const params = ref('')
+
+async function getList(){
+    const p= `?pageNum=${pageNum.value}&pageSize=${pageSize.value}${params.value}`
+    
+}
+function filterChange(param){
+    console.log('filter change:',param)
+}
+</script>
+
+<style scoped>
+.animation-in{
+    animation: show .5s ease-in-out;
+}
+@keyframes show{
+    0%{
+        transform: scale(0.5);
+    }
+    100%{
+        transform: scale(1);
+    }
+}
+</style>

+ 199 - 0
src/pages/house/newHouse/[id].vue

@@ -0,0 +1,199 @@
+<template>
+    <div v-if="status=='success'" class="bg-gray-50 pb-50">
+        <div class="p-10 bg-[#fff]">
+            <div class="relative w-full aspect-[4/3] rounded-xl overflow-hidden bg-[#f5f5f5]">
+                <Swiper :modules="[SwiperAutoplay, SwiperPagination]" :slides-per-view="1" :loop="true" @swiper="onSwiper" @slideChange="onSlideChange">
+                    <SwiperSlide v-for="(img, index) in detailData?.housingAtlasLst" :key="index">
+                        <img :src="img" class="w-full h-full object-cover" />
+                    </SwiperSlide>
+                </Swiper>
+                <div v-if="detailData?.housingAtlasLst.length" class="absolute rounded-full top-3 left-3 bg-black/50 text-white px-4 py-2 text-sm z-10">
+                    {{ currentIndex }}/{{ detailData?.housingAtlasLst.length }}
+                </div>
+            </div>
+            
+            <div class="text-[#434343] text-[18px] font-bold mb-3">{{ detailData?.houseTitle }}</div>
+
+            <!-- 标题下方的标签 -->
+            <div class="flex flex-wrap text-[12px] mt-[14px]">
+
+                <div v-for="(item,index) in tags" :key="index" class="px-6 py-2 border border-[#666] text-[#666] rounded-full mr-8">{{ item }}</div>
+                
+            </div>
+            <div class=" mt-[14px] text-[14px] font-bold text-[#FF1E1E]">
+                {{ detailData?.tourHouseSaleInfoVo?.currency }}{{ detailData?.tourHouseSaleInfoVo?.averagePrice }}/㎡
+            </div>
+            <div class="mt-[14px] text-[#444] text-[12px]">
+                <van-row>
+                    <van-col span="12">
+                        <div class="flex items-center">
+                            <div class="w-[50px] font-bold">交房</div>
+                            <div>{{ dayjs(detailData?.tourHouseSaleInfoVo?.handingTime).format('YYYY-MM-DD') }}</div>
+                        </div>
+                    </van-col>
+                    <van-col span="12">
+                        <div class="flex items-center">
+                            <div class="w-[50px] font-bold">开盘</div>
+                            <div>{{ dayjs(detailData?.tourHouseSaleInfoVo?.openingTime).format('YYYY-MM-DD') }}</div>
+                        </div>
+                    </van-col>
+                </van-row>
+                <van-row>
+                    <van-col span="24">
+                        <div class="flex items-center mt-[14px]">
+                            <div class="w-[50px] font-bold">户型</div>
+                            <div>
+                                {{ detailData?.tourHouseSaleInfoVo?.officeTypeDictMap?.name }}
+                                {{ detailData?.tourHouseSaleInfoVo?.houseTypeDictMap?.name }}
+                            </div>
+                        </div>
+                    </van-col>
+                </van-row>
+                <van-row>
+                    <van-col span="24">
+                        <div class="flex items-center mt-[14px]">
+                            <div class="w-[50px] font-bold">地址</div>
+                            <div>{{ detailData?.tourHouseSaleInfoVo?.address }}</div>
+                        </div>
+                    </van-col>
+                </van-row>
+            </div>
+
+        </div>
+
+        <div class="p-10 bg-[#fff] mt-[14px]">
+            <div class="mt-[8px]">开发商专属顾问</div>
+            <div class="mt-[8px]">
+                <div class="w-full overflow-x-auto scrollbar">
+                    <div class="flex flex-nowrap">
+                        <div v-for="item in detailData?.houseAdvisorList" :key="item"
+                            class="w-[155px] flex items-center overflow-hidden shrink-0 h-[95px] border border-[#D4D4D4] rounded-[10px] mr-10">
+                            <div class="w-[56px] h-[56px] rounded-full overflow-hidden shrink-0 mx-5">
+                                <img :src="item.avatarUrl" class="object-cover w-full h-full rounded-full" alt="经纪人头像" />
+                            </div>
+                            <div class="flex flex-col justify-center h-full overflow-y-auto text-[10px] text-[#333]">
+                                <div>{{ item.name }}</div>
+                                <div>行业经验:{{ item.experienceAge }}年</div>
+                                <div>成功交房:{{ item.successfulDeliveryCount }}套</div>
+                                <div class="w-[54px] h-[18px] bg-[#FD9A00] flex items-center justify-center text-[#fff] rounded-full">
+                                    
+                                    <a :href="`tel:${item.mobile}`">咨询</a>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="mt-[8px]">户型</div>
+            <div class="mt-[8px]">
+                <div class="w-full overflow-x-auto scrollbar">
+                    <div class="flex flex-nowrap ">
+                        <div v-for="(item,index) in detailData?.housingTypeAtlas" :key="index"
+                            class="w-[145px] overflow-hidden mr-10 shrink-0">
+                            <div class="w-full aspect-[1/1] border">
+                                <img :src="item.fileUrl" class="object-cover w-full h-full" alt="" />
+                            </div>
+                            <div class="flex items-center justify-between text-[12px] mt-[10px]">
+                                <div class="text-[#FF1E1E] ">{{ item.currency }}{{ item.price }}起</div>
+                                <div>面积约{{ item.area }}m²</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="p-10 bg-[#fff] mt-[14px]">
+            <van-tabs v-model:active="active" title-active-color="#FD9A00" color="#FD9A00">
+                <van-tab title="周边配套">
+                    <div class="min-h-[200px] pt-[10px]">
+                        <div>{{ detailData?.tourHouseSaleInfoVo?.surroundingSupport }}</div>
+                        <div v-for="(item,index) in detailData?.surroundingAtlasList" :key="index" class="w-full aspect-[1.3/1] mt-10">
+                            <img  class="w-full h-full object-cover" :src="item" alt="" />
+                        </div>
+                    </div>
+                </van-tab>
+                <van-tab title="小区介绍">
+                    <div class="min-h-[200px] pt-[10px]">
+                        <div>{{ detailData?.tourHouseSaleInfoVo?.plotIntroduction }}</div>
+                        <div v-for="(item,index) in detailData?.communityIntroductionAtlas" :key="index" class="w-full aspect-[1.3/1] mt-10">
+                            <img  class="w-full h-full object-cover" :src="item" alt="" />
+                        </div>
+                    </div>
+                </van-tab>
+            </van-tabs>
+        </div>
+
+        <!-- 底部按钮 -->
+        <div class="fixed bottom-0 left-0 h-[44px] right-0 bg-white border-t flex justify-end items-center">
+            <button class="bg-[#FD9A00] h-[34px] text-white px-10 rounded-lg mr-10">
+                <a :href="`tel:${detailData?.tourHouseSaleInfoVo?.mobile}`">电话咨询</a>
+            </button>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { Swiper, SwiperSlide } from 'swiper/vue'
+import { Autoplay, Pagination } from 'swiper/modules'
+import 'swiper/css'
+import 'swiper/css/pagination'
+
+const id = useRouteParam('id')
+const dayjs = useDayjs()
+// 房屋详情
+const { data: detailData, status } = useMyFetch(`/website/house/sale/viewHousingDetailsById?id=${id.value}`)
+
+const tags = computed(()=>detailData.value?.tourHouseSaleInfoVo?.tag.split('&'))
+console.log('tags:',tags.value)
+console.log('detailData:', detailData)
+
+// 轮播图片 - 使用相同的图片模拟设计图效果
+const images = Array(4).fill('https://images.unsplash.com/photo-1545324418-cc1a3fa10c00?w=500')
+
+const SwiperAutoplay = Autoplay
+const SwiperPagination = Pagination
+// 当前轮播图索引
+const currentIndex = ref(1)
+
+// Swiper 实例
+let swiperInstance = null
+
+// Swiper 初始化回调
+const onSwiper = (swiper) => {
+    swiperInstance = swiper
+}
+
+// 轮播图切换回调
+const onSlideChange = () => {
+    if (swiperInstance) {
+        // realIndex 是当前实际的索引(考虑了 loop 模式)
+        currentIndex.value = swiperInstance.realIndex + 1
+    }
+}
+const active = ref(0);
+async function getData() {
+   await request(`/website/house/sale/viewHousingDetailsById?id=${id.value}`)
+}
+onMounted(() => {
+    getData()
+})
+</script>
+
+<style scoped>
+.swiper {
+    @apply w-full h-full;
+}
+
+.swiper-pagination-bullet {
+    @apply bg-white opacity-60;
+}
+
+.swiper-pagination-bullet-active {
+    @apply bg-white opacity-100;
+}
+
+/* 可选:添加过渡动画 */
+.absolute {
+    transition: all 0.3s ease;
+}
+</style>

+ 208 - 0
src/pages/house/oldHouse/[id].vue

@@ -0,0 +1,208 @@
+<template>
+    <div class="bg-gray-50 pb-50">
+        <div class="p-10 bg-[#fff]">
+            <div class="relative w-full aspect-[4/3] rounded-xl overflow-hidden bg-[#dedede]">
+                <Swiper :modules="[SwiperAutoplay, SwiperPagination]" :slides-per-view="1" :loop="true"
+                    @swiper="onSwiper" @slideChange="onSlideChange">
+                    <SwiperSlide v-for="(img, index) in detailData?.housingAtlasLst" :key="index">
+                        <img :src="img" class="w-full h-full object-cover" />
+                    </SwiperSlide>
+                </Swiper>
+                <div v-if="detailData?.housingAtlasLst.length"
+                    class="absolute rounded-full top-3 left-3 bg-black/50 text-white px-4 py-2 text-sm z-10">
+                    {{ currentIndex }}/{{ detailData?.housingAtlasLst.length }}
+                </div>
+            </div>
+            <div class="text-[#434343] text-[18px] font-bold mt-[14px]">{{ detailData?.tourHouseSaleInfoVo?.houseTitle }}
+            </div>
+
+            <div class="flex flex-wrap text-[12px] mt-[14px]">
+                <div v-for="(item, index) in tags" :key="index"
+                    class="px-6 py-2 border border-[#666] text-[#666] rounded-full mr-8">{{ item }}</div>
+            </div>
+            <div class="flex items-baseline mt-[14px]">
+                <span class="text-[14px] font-bold text-[#FF1E1E]">{{ detailData?.tourHouseSaleInfoVo?.currency }}{{
+                    detailData?.tourHouseSaleInfoVo?.totalPrice }}</span>
+                <span class="text-[12px] ml-2 text-[#999]">{{ detailData?.tourHouseSaleInfoVo?.currency }}{{
+                    detailData?.tourHouseSaleInfoVo?.averagePrice }}/㎡</span>
+            </div>
+            <div class="text-[12px] mt-[18px]">
+                <van-row>
+                    <van-col span="8">
+                        <div class="text-[#242424] font-bold">{{
+                            detailData?.tourHouseSaleInfoVo?.officeTypeDictMap?.name }}</div>
+                        <div class="text-[#666]">{{ detailData?.tourHouseSaleInfoVo?.currentFloor
+                            }}层(共{{ detailData?.tourHouseSaleInfoVo?.totalFloor }}层)</div>
+                    </van-col>
+                    <van-col span="8">
+                        <div class="text-[#242424] font-bold">{{ detailData?.tourHouseSaleInfoVo?.area }}m²</div>
+                        <div class="text-[#666]">{{ detailData?.tourHouseSaleInfoVo?.decorationDictMap?.name }}</div>
+                    </van-col>
+                    <van-col span="8">
+                        <div class="text-[#242424] font-bold">{{
+                            detailData?.tourHouseSaleInfoVo?.orientationDictMap?.name }}</div>
+                        <div class="text-[#666]">
+                            {{ dayjs(detailData?.tourHouseSaleInfoVo?.yearOfFinish).format('YYYY-MM-DD') }}竣工/{{
+                                detailData?.tourHouseSaleInfoVo?.houseTypeDictMap?.name }}</div>
+                    </van-col>
+                </van-row>
+                <van-row class="mt-[14px]">
+                    <van-col span="8">
+                        <div class="text-[#242424] font-bold">{{ detailData?.tourHouseSaleInfoVo?.communityName }}</div>
+                        <div class="text-[#666]">所属小区</div>
+                    </van-col>
+                    <van-col span="8">
+                        <div class="text-[#242424] font-bold">{{ detailData?.tourHouseSaleInfoVo?.region }}</div>
+                        <div class="text-[#666]">所属区域</div>
+                    </van-col>
+                </van-row>
+            </div>
+        </div>
+
+        <div class="p-10 bg-[#fff] mt-[14px]">
+            <div class="mt-[8px]">经纪人</div>
+            <div class="mt-[8px] border border-[#D4D4D4] rounded-[10px] h-[109px] flex items-center justify-between">
+                <div class="flex items-center ml-[14px]">
+                    <div class="w-[56px] h-[56px] rounded-full overflow-hidden shrink-0">
+                        <img :src="detailData?.houseAdvisorList[0]?.avatarUrl"
+                            class="object-cover w-full h-full rounded-full" alt="经纪人头像" />
+                    </div>
+                    <div class="flex flex-col justify-between text-[12px] text-[#333] ml-[5px]">
+                        <div>{{ detailData?.houseAdvisorList[0]?.name }}</div>
+                        <div>行业经验:{{ detailData?.houseAdvisorList[0]?.experienceAge }}年</div>
+                        <div>成功交房:{{ detailData?.houseAdvisorList[0]?.successfulDeliveryCount }}套</div>
+                    </div>
+                </div>
+                <div
+                    class="relative w-[35%] h-[80px] border-l-[1px] border-[#D4d4d4] flex flex-col shrink-0 justify-center items-center">
+                    <img :src="detailData?.tourHouseSaleInfoVo.code" class="aspect-[1/1] object-cover w-[50%]" />
+                    <span class=" text-[#333] text-[10px] bottom-[0%]">扫码联系</span>
+                </div>
+            </div>
+            <div class="mt-[14px] px-4">
+                <div class="flex text-[#666] text-[14px] ">
+                    <div class="w-[80px] font-bold shrink-0">产权性质</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.propertyRightNature }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">产权年限</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.termOfOwnership }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">唯一住房</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.isSoleDwelling ? '是' : '否' }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">发布公司</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.publishingCompany }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">营业执照</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.businessLicense }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">发布日期</div>
+                    <span
+                        class="flex-1">{{ dayjs(detailData?.tourHouseSaleInfoVo?.releaseDate).format('YYYY-MM-DD') }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">物业类型</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.propertyType }}</span>
+                </div>
+                <div class="flex text-[#666] text-[14px] mt-[14px]">
+                    <div class="w-[80px] font-bold shrink-0">房本年限</div>
+                    <span class="flex-1">{{ detailData?.tourHouseSaleInfoVo?.housePrincipalLife }}</span>
+                </div>
+            </div>
+        </div>
+        <div class="p-10 bg-[#fff] mt-[14px]">
+            <van-tabs v-model:active="active" title-active-color="#FD9A00" color="#FD9A00">
+                <van-tab title="周边配套">
+                    <div class="min-h-[200px] pt-[10px]">
+                        <div>
+                            {{ detailData?.tourHouseSaleInfoVo?.surroundingSupport }}
+                        </div>
+                        <div v-for="(item,index) in detailData?.surroundingAtlasList" :key="index" class="w-full aspect-[1.3/1] mt-10">
+                            <img  class="w-full h-full object-cover" :src="item" alt="" />
+                        </div>
+                    </div>
+                </van-tab>
+                <van-tab title="小区介绍">
+                    <div class="min-h-[200px] pt-[10px]">
+                        <div>{{ detailData?.tourHouseSaleInfoVo?.plotIntroduction }}</div>
+                        <div v-for="(item,index) in detailData?.communityIntroductionAtlas" :key="index" class="w-full aspect-[1.3/1] mt-10">
+                            <img  class="w-full h-full object-cover" :src="item" alt="" />
+                        </div>
+                    </div>
+                </van-tab>
+            </van-tabs>
+        </div>
+        
+        <div class="fixed bottom-0 left-0 h-[44px] right-0 bg-white border-t flex justify-end items-center">
+            <button class="bg-[#FD9A00] h-[34px] text-white px-10 rounded-lg mr-10">
+                <a :href="`tel:${detailData?.tourHouseSaleInfoVo?.mobile}`">电话咨询</a>
+            </button>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { Swiper, SwiperSlide } from 'swiper/vue'
+import { Autoplay, Pagination } from 'swiper/modules'
+import 'swiper/css'
+import 'swiper/css/pagination'
+
+// 房屋详情
+const id = useRouteParam('id')
+const dayjs = useDayjs()
+// 房屋详情
+const { data: detailData, status } = useMyFetch(`/website/house/sale/viewHousingDetailsById?id=${id.value}`)
+
+const tags = computed(() => detailData.value?.tourHouseSaleInfoVo?.tag.split('&'))
+console.log('二手房详情:', detailData.value)
+
+// 轮播图片 - 使用相同的图片模拟设计图效果
+const images = Array(4).fill('https://images.unsplash.com/photo-1545324418-cc1a3fa10c00?w=500')
+
+const SwiperAutoplay = Autoplay
+const SwiperPagination = Pagination
+// 当前轮播图索引
+const currentIndex = ref(1)
+
+// Swiper 实例
+let swiperInstance = null
+
+// Swiper 初始化回调
+const onSwiper = (swiper) => {
+    swiperInstance = swiper
+}
+
+// 轮播图切换回调
+const onSlideChange = () => {
+    if (swiperInstance) {
+        // realIndex 是当前实际的索引(考虑了 loop 模式)
+        currentIndex.value = swiperInstance.realIndex + 1
+    }
+}
+const active = ref(0);
+
+</script>
+
+<style scoped>
+.swiper {
+    @apply w-full h-full;
+}
+
+.swiper-pagination-bullet {
+    @apply bg-white opacity-60;
+}
+
+.swiper-pagination-bullet-active {
+    @apply bg-white opacity-100;
+}
+
+/* 可选:添加过渡动画 */
+.absolute {
+    transition: all 0.3s ease;
+}
+</style>

+ 250 - 0
src/pages/house/rentHouse/[id].vue

@@ -0,0 +1,250 @@
+<template>
+    <div class="bg-gray-50 pb-50">
+        <div class="p-10 bg-[#fff]">
+            <div class="relative w-full aspect-[4/3] rounded-xl overflow-hidden bg-[#dedede]">
+                <Swiper :modules="[SwiperAutoplay, SwiperPagination]" :slides-per-view="1" :loop="true" @swiper="onSwiper" @slideChange="onSlideChange">
+                    <SwiperSlide v-for="(img, index) in dataDetail?.tourHouseEquipment?.houseDetailConvertImage" :key="index">
+                        <img :src="img" class="w-full h-full object-cover" />
+                    </SwiperSlide>
+                </Swiper>
+                <div v-if="dataDetail?.tourHouseEquipment?.houseDetailConvertImage?.length" class="absolute rounded-full top-3 left-3 bg-black/50 text-white px-4 py-2 text-sm z-10">
+                    {{ currentIndex }}/{{ dataDetail?.tourHouseEquipment?.houseDetailConvertImage?.length }}
+                </div>
+            </div>
+            <div class="text-[#434343] text-[18px] font-bold mt-[14px]">{{ dataDetail?.houseTitle }}</div>
+
+            <div class="flex flex-wrap items-center mt-[14px] text-[#333] text-[12px]">
+                <div>{{dataDetail?.rentTypeDictMap?.name}}</div>
+                <div class="ml-[12px]">{{ dataDetail?.officeTypeDictMap?.name }}</div>
+                <div class="ml-[12px]">{{ dataDetail?.area }}m²</div>
+                <div class="ml-[12px]">{{ dataDetail?.decorationDictMap?.name }}</div>
+            </div>
+            <div class="flex flex-wrap items-center mt-[14px] text-[#333] text-[12px]">
+                <div class="">{{ dataDetail?.communityName }}</div>
+                <div class="ml-[12px]">{{ dataDetail?.orientationDictMap?.name }}</div>
+                <div class="ml-[12px]">{{ dataDetail?.floorTypeDictMap?.name }} &nbsp; {{ dataDetail?.currentFloor }}层/{{dataDetail?.totalFloor}}层</div>
+            </div>
+            <div class="flex flex-wrap items-center mt-[14px] text-[#333] text-[12px]">
+                <div>{{ dataDetail?.address }}</div>
+            </div>
+        </div>
+
+        <div class="p-10 bg-[#fff] mt-[14px]">
+            <div class="mt-[8px]">经纪人</div>
+            <div class="mt-[8px] border border-[#D4D4D4] rounded-[10px] h-[109px] flex items-center justify-between">
+                <div class="flex items-center ml-[14px]">
+                    <div class="w-[56px] h-[56px] rounded-full overflow-hidden shrink-0">
+                        <img :src="dataDetail?.tourHouseAdvisorInfo?.avatarUrl" class="object-cover w-full h-full rounded-full" alt="经纪人头像" />
+                    </div>
+                    <div class="flex flex-col justify-between text-[12px] text-[#333] ml-[5px]">
+                        <div>{{ dataDetail?.tourHouseAdvisorInfo?.name }}</div>
+                        <div>{{ dataDetail?.tourHouseAdvisorInfo?.companyName }}</div>
+                        <div>行业经验:{{dataDetail?.tourHouseAdvisorInfo?.experienceAge}}年</div>
+                    </div>
+                </div>
+                <div
+                    class="relative w-[35%] h-[80px] border-l-[1px] border-[#D4d4d4] flex flex-col shrink-0 justify-center items-center">
+                    <img :src="dataDetail?.tourHouseAdvisorInfo?.covertCode" class="aspect-[1/1] object-cover w-[50%]" />
+                    <span class=" text-[#333] text-[12px] bottom-[0%]">扫码联系</span>
+                </div>
+            </div>
+            
+        </div>
+        <div class="p-10 bg-[#fff] mt-[14px] text-[#333] text-[14px]">
+            <van-row>
+                <van-col span="5">
+                    <div class="text-[#242424] font-bold h-full flex flex-col justify-center items-center">
+                        <img :src="equipmentIcon" class="w-[26px] h-[26px]" />
+                        <div>
+                            设施
+                        </div>
+                    </div>
+                </van-col>
+                <van-col span="6">
+                    <div class="">
+                        <div class="flex items-center">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.icebox" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.icebox?'':'text-[#999]']">冰箱</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.broadband" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.icebox?'':'text-[#999]']">宽带</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.gasStove" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.icebox?'':'text-[#999]']">燃气灶</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.bed" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.icebox?'':'text-[#999]']">床</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.balcony" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.icebox?'':'text-[#999]']">阳台</span>
+                        </div>
+                    </div>
+                </van-col>
+                <van-col span="6">
+                    <div class="">
+                        <div class="flex items-center">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.washer" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.washer?'':'text-[#999]']">洗衣机</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.sofa" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.sofa?'':'text-[#999]']">沙发</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.airConditioner" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.airConditioner?'':'text-[#999]']">空调</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.restRoom" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.restRoom?'':'text-[#999]']">卫生间</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.heating" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.heating?'':'text-[#999]']">暖气</span>
+                        </div>
+                    </div>
+                </van-col>
+                <van-col span="6">
+                    <div class="">
+                        <div class="flex items-center">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.calorifier" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.calorifier?'':'text-[#999]']">热水器</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.rangeHood" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.rangeHood?'':'text-[#999]']">油烟机</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.wardrobe" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.wardrobe?'':'text-[#999]']">衣柜</span>
+                        </div>
+                        <div class="flex items-center mt-8">
+                            <van-icon v-if="dataDetail?.tourHouseEquipment?.smartDoorLock" name="success" size="20px" />
+                            <van-icon v-else name="cross" size="20px" color="#999" />
+                            <span :class="[dataDetail?.tourHouseEquipment?.smartDoorLock?'':'text-[#999]']">智能门锁</span>
+                        </div>
+                    </div>
+                </van-col>
+            </van-row>
+        </div>
+        <div class="p-10 bg-[#fff] mt-[14px]">
+            <van-tabs v-model:active="active" title-active-color="#FD9A00" color="#FD9A00">
+                <van-tab title="周边配套">
+                    <div class="min-h-[200px] pt-[10px]">
+                        <div>
+                            {{ dataDetail?.tourHouseEquipment?.surroundingSupport }}
+                        </div>
+                        <div v-for="(item,index) in dataDetail?.tourHouseEquipment?.surroundingSupportConvertImage" :key="index" class="w-full aspect-[1.3/1] mt-10">
+                            <img  class="w-full h-full object-cover" :src="item" alt="" />
+                        </div>
+                    </div>
+                </van-tab>
+                <van-tab title="小区介绍">
+                    <div class="min-h-[200px] pt-[10px]">
+                        <div>
+                            {{ dataDetail?.tourHouseEquipment?.plotIntroduction }}
+                        </div>
+                        <div v-for="(item,index) in dataDetail?.tourHouseEquipment?.plotIntroductionConvertImage" :key="index" class="w-full aspect-[1.3/1] mt-10">
+                            <img  class="w-full h-full object-cover" :src="item" alt="" />
+                        </div>
+                    </div>
+                </van-tab>
+            </van-tabs>
+        </div>
+
+        
+        <div class="fixed bottom-0 left-0 right-0 h-[55px] bg-white border-t flex items-center justify-end">
+            <div class="flex items-end">
+                <div class="text-[#FF1717] font-bold text-[14px]">
+                    {{ dataDetail?.currency }}{{ dataDetail?.rentPrice }}/月
+                </div>
+                <div class="text-[12px] text-[#333] mx-[10px]">{{dataDetail.area}}m²</div>
+            </div>
+            <button class=" bg-[#FF8C29] text-white h-[36px] rounded-[10px] text-[12px] flex items-center justify-center px-[20px]">
+                <a :href="`tel:${dataDetail?.tourHouseAdvisorInfo?.mobile}`">电话咨询</a>
+                
+            </button>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { Swiper, SwiperSlide } from 'swiper/vue'
+import { Autoplay, Pagination } from 'swiper/modules'
+import 'swiper/css'
+import 'swiper/css/pagination'
+import equipmentIcon from '~/assets/img/equipment.png'
+
+const id = useRouteParam('id')
+// 房屋详情
+const { data: dataDetail, status } = useMyFetch(`/website/tourism/tourismHouseRent/tourHouseRentInfoById?id=${id.value}`)
+
+console.log('dataDetail:', dataDetail.value)
+
+// 轮播图片 - 使用相同的图片模拟设计图效果
+const images = Array(4).fill('https://images.unsplash.com/photo-1545324418-cc1a3fa10c00?w=500')
+
+const SwiperAutoplay = Autoplay
+const SwiperPagination = Pagination
+// 当前轮播图索引
+const currentIndex = ref(1)
+
+// Swiper 实例
+let swiperInstance = null
+
+// Swiper 初始化回调
+const onSwiper = (swiper) => {
+    swiperInstance = swiper
+}
+
+// 轮播图切换回调
+const onSlideChange = () => {
+    if (swiperInstance) {
+        // realIndex 是当前实际的索引(考虑了 loop 模式)
+        currentIndex.value = swiperInstance.realIndex + 1 || 1
+    }
+}
+const active = ref(0);
+async function getData() {
+   await request(`/website/tourism/tourismHouseRent/tourHouseRentInfoById?id=${id.value}`)
+}
+onMounted(() => {
+    getData()
+})
+</script>
+
+<style scoped>
+.swiper {
+    @apply w-full h-full;
+}
+
+.swiper-pagination-bullet {
+    @apply bg-white opacity-60;
+}
+
+.swiper-pagination-bullet-active {
+    @apply bg-white opacity-100;
+}
+
+/* 可选:添加过渡动画 */
+.absolute {
+    transition: all 0.3s ease;
+}
+</style>

+ 1 - 0
src/pages/profile/car-orders.vue

@@ -37,6 +37,7 @@
             </div>
             <span>{{ item.startPlaceIdDictMap?.name ?? "" }}</span>
             <span>{{ item.categoryTypeDictMap?.name ?? "" }}</span>
+            <span v-if="item.driveMark || item.categoryMark">已打分</span>
           </div>
           <div>
             <span class="text-[#FF1717] font-semibold"

+ 60 - 29
src/pages/travel-notes/index.vue

@@ -18,29 +18,30 @@
         ></van-dropdown-item>
       </van-dropdown-menu>
     </div>
-    <div class="px-10 pt-20">
-      <van-list
-        v-if="dataList.length"
-        v-model:loading="loading"
-        :immediate-check="false"
-        :finished="finished"
-        loading-text="加载中..."
-        error-text="获取失败"
-        @load="loadMore"
-      >
+    <div  class="px-10 pt-20">
+      <div v-if="recomendList.length" class="scrollbar w-full" style="overflow-x: auto">
+        <div class="flex ">
+          <NuxtLink :to="'/t/'+item.id" v-for="(item,index) in recomendList" :key="index" class="w-[110px] border rounded overflow-hidden shrink-0 mr-[12px]">
+            <div class="w-full h-[76px] bg-[#dedede]">
+              <img v-if="Array.isArray(item?.tourismUrlsAfterConvert)" :src="item?.tourismUrlsAfterConvert[0]" class="w-full h-full object-cover" alt="">
+            </div>
+            <div class="text-clip overflow-hidden text-[13px] text-[13px] px-5 h-[40px] w-full overflow-ellipsis"
+              style="line-height:22px">
+              {{ item.projectTitle }}
+            </div>
+            <div class="flex justify-end text-[#FF476A] text-[14px] font-semibold pr-10">
+              {{ item.priceUnit }}{{ item.price }}
+            </div>
+          </NuxtLink>
+        </div>
+      </div>
+      <van-list v-if="dataList.length" v-model:loading="loading" :immediate-check="false" :finished="finished"
+        loading-text="加载中..." error-text="获取失败" @load="loadMore">
         <template v-for="itemData in dataList">
-          <NuxtLink
-            class="group flex relative cursor-pointer bg-white pb-10 mb-20"
-            :to="`/yj/${itemData.id}`"
-          >
+          <NuxtLink class="group flex relative cursor-pointer bg-white pb-10 mt-20" :to="`/yj/${itemData.id}`">
             <div class="aspect-[120/80] h-80 shrink-0 rounded overflow-hidden bg-[#ddd]">
-              <img
-                v-if="Array.isArray(itemData?.tourismUrlsAfterConvert)"
-                :src="itemData?.tourismUrlsAfterConvert[0]"
-                class="w-full h-full rounded object-cover"
-                alt=""
-                srcset=""
-              />
+              <img v-if="Array.isArray(itemData?.tourismUrlsAfterConvert)" :src="itemData?.tourismUrlsAfterConvert[0]"
+                class="w-full h-full rounded object-cover" alt="" srcset="" />
             </div>
             <div
               class="h-80 pl-[8px] flex flex-col text-[#FD9A00] justify-between w-[calc(100%-120px)]"
@@ -94,6 +95,15 @@
 
 <script setup>
 const router = useRoute()
+
+// 推荐项目
+const { data } = await useMyFetch(
+  `website/tourism/project/list?isHotspot=1&pageNum=1&pageSize=10`
+)
+
+const recomendList = ref([])
+
+
 const AREA_TEXT = '地域'
 
 const areaId = useRouteParam('area')
@@ -113,11 +123,7 @@ const activeIndex = ref('')
 const dropDownMenuRef = ref(null)
 // 获取筛选列表
 async function getFilters() {
-  const { data } = await request(
-    `/website/tourism/projectTravelNotes/travelNotesDirectoryList`
-  ).finally(() => {
-    closeToast()
-  })
+  const { data } = await request(`/website/tourism/projectTravelNotes/travelNotesDirectoryList`).finally(() => { closeToast() })
 
   if (!Array.isArray(data)) return getList()
   const routerAreaId = null
@@ -198,9 +204,7 @@ async function getList() {
     param.travelWriteType = travelWriteType.value
   }
   loading.value = true
-  const { data } = await request(`/website/tourism/projectTravelNotes/travelNotesPageList`, {
-    query: param
-  }).finally(() => closeToast())
+  const { data } = await request(`/website/tourism/projectTravelNotes/travelNotesPageList`, { query: param }).finally(() => closeToast())
 
   dataList.value = dataList.value.concat(data.dataList)
   loading.value = false
@@ -229,6 +233,7 @@ function handleFilterClick(item) {
   pageNum.value = 1
   dataList.value = []
   getList()
+  getRecommend()
   dropDownMenuRef.value && dropDownMenuRef.value.close()
   document.title = `${item.id ? '游记-' + item.areaName + '-' + item.text : '旅游笔记'}`
 }
@@ -250,8 +255,24 @@ function convertTag(str = '') {
   return str.split('&')
 }
 
+async function getRecommend(){
+  const param={
+    isHotspot:1,
+    pageNum:1,
+    pageSize:10
+  }
+  if (curFilter.value.areaId) param.areaId = curFilter.value.areaId
+
+  if (curFilter.value.id) {
+    const countryId = curFilter.value.id.split(',')
+    if (!countryId[1]) param.countryId = curFilter.value.id
+  }
+  const {data:{dataList}} = await request('website/tourism/project/list',{ query: param })
+  recomendList.value = dataList || []
+}
 onMounted(async () => {
   getFilters()
+  getRecommend()
 })
 
 useSeoMeta({
@@ -267,6 +288,7 @@ useSeoMeta({
   /* 选中项文本颜色 */
   --van-tree-select-item-active-color: red;
 }
+
 .scrollbar::-webkit-scrollbar {
   width: 0px;
   height: 0px;
@@ -279,4 +301,13 @@ useSeoMeta({
   -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
   background-color: #555;
 }
+
+.overflow-ellipsis {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
+  -webkit-box-orient: vertical;
+}
 </style>