| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- <script setup lang="ts">
- import { computed, onMounted, ref, watch } from 'vue'
- import {
- ElButton,
- ElCard,
- ElCol,
- ElContainer,
- ElIcon,
- ElInput,
- ElInputNumber,
- ElMain,
- ElMessage,
- ElOption,
- ElPagination,
- ElRow,
- ElSelect,
- ElTabPane,
- ElTabs,
- ElTag,
- } from 'element-plus'
- import {
- ArrowLeft,
- Building,
- Building2,
- Eye,
- Factory,
- Lightbulb,
- MapPin,
- Maximize,
- RotateCcw,
- Search,
- Settings,
- } from 'lucide-vue-next'
- import { clientGet } from '@/utils/request.ts'
- import { useRoute, useRouter } from 'vue-router'
- // 接口类型定义
- export interface ASimplifiedHouseInfoVo {
- id: string // 主键
- assetType: string // 资产类型(公租房/厂房/创新创业基地)
- building: string // 楼栋
- floor: string // 楼层
- houseName: string // 房间名称 (如:A栋101)
- address: string // 地址
- status: string // 状态 (空闲/已租)
- rentRange: number | null // 租金范围
- createTime: string // 创建时间
- updateTime: string // 更新时间
- area: string // 面积
- isArrears: boolean // 是否欠费
- contractExpirationDate?: string | null // 合同到期日期
- }
- export interface BaseResponse {
- code: number
- message: string
- success: boolean
- }
- export interface ASimplifiedHouseInfoVoListResponse extends BaseResponse {
- data: {
- records: ASimplifiedHouseInfoVo[]
- total: number
- size: number
- current: number
- pages: number
- }
- }
- const router = useRouter()
- const route = useRoute()
- // 响应式数据
- const activeTab = ref(route.query.currentActive as string || 'housing')
- const currentPage = ref(1)
- const pageSize = ref(12)
- const sortBy = ref('default')
- const loading = ref(false)
- const total = ref(0)
- // 筛选条件 - 核心修改:默认选中空闲状态(value: '1')
- const filters = ref({
- priceRange: {
- min: undefined as number | undefined,
- max: undefined as number | undefined,
- },
- areaRange: {
- min: undefined as number | undefined,
- max: undefined as number | undefined,
- },
- district: '',
- building: undefined as number | undefined,
- floor: undefined as number | undefined,
- houseName: undefined as string | undefined,
- status: '1' as string | undefined // 默认值:空闲(数字字符串'1')
- })
- // 房屋列表数据
- const houseList = ref<ASimplifiedHouseInfoVo[]>([])
- // 资产类型映射
- const assetTypeMap = {
- housing: '公租房',
- factory: '厂房',
- innovation: '创新创业基地',
- }
- // 排序映射
- const sortMap = {
- 'price-desc': '1', // 租金从高到低
- 'price-asc': '2', // 租金从低到高
- 'area-desc': '3', // 面积从高到低
- 'area-asc': '4', // 面积从小到大
- default: '1', // 默认排序
- }
- // 解析租金范围
- const parseRentRange = () => {
- return {
- min: filters.value.priceRange.min,
- max: filters.value.priceRange.max,
- }
- }
- // 解析面积范围
- const parseAreaRange = () => {
- return {
- min: filters.value.areaRange.min,
- max: filters.value.areaRange.max,
- }
- }
- // 获取列表数据
- const getList = async () => {
- try {
- loading.value = true
- const rentRange = parseRentRange()
- const areaRange = parseAreaRange()
- // 构建请求参数
- const params = {
- assetType: assetTypeMap[activeTab.value as keyof typeof assetTypeMap],
- pageNum: currentPage.value,
- pageSize: pageSize.value,
- sortSearch: sortMap[sortBy.value as keyof typeof sortMap],
- ...(rentRange.min && { rentRangeMin: rentRange.min }),
- ...(rentRange.max && { rentRangeMax: rentRange.max }),
- ...(areaRange.min && { areaMin: areaRange.min }),
- ...(areaRange.max && { areaMax: areaRange.max }),
- ...(filters.value.building && { building: filters.value.building?.toString() }),
- ...(filters.value.floor && { floor: filters.value.floor?.toString() }),
- ...(filters.value.houseName && { houseName: filters.value.houseName }),
- // 始终传递状态参数(默认是'1',即空闲)
- ...(filters.value.status && { status: filters.value.status })
- }
- const response = await clientGet<typeof params, ASimplifiedHouseInfoVoListResponse>(
- '/asimplifiedHouseInfo/getList',
- { params: params }
- )
- if (response.code === 200) {
- houseList.value = response.data.records
- total.value = response.data.total
- } else {
- ElMessage.error(response.message || '获取数据失败')
- houseList.value = []
- total.value = 0
- }
- } catch (error) {
- console.error('获取列表数据失败:', error)
- ElMessage.error('网络请求失败,请稍后重试')
- houseList.value = []
- total.value = 0
- } finally {
- loading.value = false
- }
- }
- // 格式化显示数据
- const formatDisplayData = (item: ASimplifiedHouseInfoVo) => {
- // 状态反向映射:后端返回的数字值转前端显示文本
- const statusDisplayMap = {
- '1': '可租',
- '2': '已租',
- '3': '到期',
- '空闲': '可租', // 兼容原有文本值
- '已租': '已租',
- '到期': '到期'
- }
- return {
- id: item.id,
- buildingNumber: item.building || '-',
- houseNameNumber: item.houseName || '-',
- priceRange: item.rentRange ? `${item.rentRange}` : '面议',
- address: item.address || '未填写',
- area: parseInt(item.area) || 0,
- status: statusDisplayMap[item.status] || item.status,
- floor: item.floor || '-',
- facilities: '配套设施',
- building: item.building || '-',
- isArrears: item.isArrears,
- contractExpirationDate: item.contractExpirationDate || null,
- }
- }
- // 格式化后的列表数据
- const formattedList = computed(() => {
- return houseList.value.map(formatDisplayData)
- })
- // 优化状态标签类型判断逻辑
- const getStatusTagType = (contractExpirationDate: string | null, status: string) => {
- // 处理空值情况
- if (!contractExpirationDate) contractExpirationDate = null
- // 可租/空闲状态
- if (status === '可租' || status === '空闲') {
- return 'success'
- }
- // 已租且有到期日期(即将到期)
- if (status === '已租' && contractExpirationDate && contractExpirationDate !== '') {
- return 'danger'
- }
- // 已租无到期日期
- if (status === '已租') {
- return 'warning'
- }
- // 到期状态
- if (status === '到期') {
- return 'danger'
- }
- return 'success'
- }
- // 获取状态对应的按钮类型
- const getStatusButtonType = (contractExpirationDate: string | null, status: string) => {
- return getStatusTagType(contractExpirationDate, status)
- }
- // 标签页切换处理
- const handleTabClick = (tab: any) => {
- if (tab.paneName === activeTab.value) {
- return
- }
- activeTab.value = tab.paneName
- currentPage.value = 1
- // 切换标签页时保留默认的空闲状态筛选
- getList()
- }
- // 手动应用筛选
- const applyFilters = () => {
- currentPage.value = 1
- getList()
- }
- // 重置筛选条件 - 核心修改:重置后仍保留空闲状态
- const resetFilters = () => {
- filters.value = {
- priceRange: {
- min: undefined,
- max: undefined,
- },
- areaRange: {
- min: undefined,
- max: undefined,
- },
- district: '',
- building: undefined,
- floor: undefined,
- houseName: undefined,
- status: '1' // 重置后依然默认选中空闲状态
- }
- }
- // 分页变化处理
- const handlePageChange = (page: number) => {
- currentPage.value = page
- getList()
- }
- // 排序变化处理
- const handleSortChange = () => {
- currentPage.value = 1
- getList()
- }
- // 状态变化自动筛选
- const handleStatusChange = () => {
- currentPage.value = 1
- getList()
- }
- const goBack = () => {
- router.back()
- }
- // 查看详情
- const viewDetails = (item: any) => {
- router.push({
- path: '/zf/fwxq',
- query: {
- houseId: item.id,
- },
- })
- }
- // 监听排序变化
- watch(sortBy, handleSortChange)
- // 监听状态变化 - 选择后自动筛选
- watch(() => filters.value.status, (newVal) => {
- if (newVal !== undefined) {
- handleStatusChange()
- }
- }, { immediate: false })
- // 组件挂载时获取数据(默认加载空闲状态数据)
- onMounted(() => {
- getList()
- })
- </script>
- <template>
- <el-container class="min-h-screen bg-gradient-to-br from-blue-50">
- <el-main class="h-full p-0">
- <el-button
- type="primary"
- @click="goBack"
- class="relative top-1 z-50 bg-gradient-to-r from-blue-500 to-indigo-600 hover:from-blue-600 hover:to-indigo-700 text-white font-semibold py-3 px-6 rounded-full shadow-lg transition-all duration-300 ease-in-out transform hover:scale-105"
- >
- <el-icon class="mr-1"><ArrowLeft /></el-icon>
- 返回
- </el-button>
- <!-- 主要内容区域 -->
- <div class="pt-5">
- <div class="w-full">
- <!-- 标签页导航 -->
- <el-tabs v-model="activeTab" class="mb-2" @tab-click="handleTabClick">
- <el-tab-pane label="公租房" name="housing">
- <template #label>
- <div class="flex items-center">
- <el-icon class="mr-2"><Building /></el-icon>
- 公租房
- </div>
- </template>
- </el-tab-pane>
- <el-tab-pane label="标准化厂房" name="factory">
- <template #label>
- <div class="flex items-center">
- <el-icon class="mr-2"><Factory /></el-icon>
- 标准化厂房
- </div>
- </template>
- </el-tab-pane>
- <el-tab-pane label="创新创业基地" name="innovation">
- <template #label>
- <div class="flex items-center">
- <el-icon class="mr-2"><Lightbulb /></el-icon>
- 创新创业基地
- </div>
- </template>
- </el-tab-pane>
- </el-tabs>
- <!-- 筛选条件区域 -->
- <el-card class="mb-4 shadow-lg rounded-2xl border-0">
- <div>
- <el-row :gutter="24">
- <el-col :xs="24" :sm="12" :md="6">
- <div class="mb-4">
- <label class="block text-sm font-medium text-gray-700 mb-2">
- 租金范围 (元/月)
- </label>
- <div class="flex items-center space-x-2">
- <el-input-number
- v-model="filters.priceRange.min"
- placeholder="最低租金"
- :min="0"
- :max="filters.priceRange.max || 999999"
- :step="50"
- class="flex-1"
- controls-position="right"
- size="default"
- />
- <span class="text-gray-500">-</span>
- <el-input-number
- v-model="filters.priceRange.max"
- placeholder="最高租金"
- :min="filters.priceRange.min || 0"
- :max="999999"
- :step="50"
- class="flex-1"
- controls-position="right"
- size="default"
- />
- </div>
- </div>
- </el-col>
- <el-col :xs="24" :sm="12" :md="6">
- <div class="mb-4">
- <label class="block text-sm font-medium text-gray-700 mb-2">
- 面积范围 (㎡)
- </label>
- <div class="flex items-center space-x-2">
- <el-input-number
- v-model="filters.areaRange.min"
- placeholder="最小面积"
- :min="0"
- :max="filters.areaRange.max || 99999"
- :step="10"
- class="flex-1"
- controls-position="right"
- size="default"
- />
- <span class="text-gray-500">-</span>
- <el-input-number
- v-model="filters.areaRange.max"
- placeholder="最大面积"
- :min="filters.areaRange.min || 0"
- :max="99999"
- :step="10"
- class="flex-1"
- controls-position="right"
- size="default"
- />
- </div>
- </div>
- </el-col>
- <el-col :xs="24" :sm="12" :md="2">
- <div class="mb-4">
- <label class="block text-sm font-medium text-gray-700 mb-2">楼栋</label>
- <el-input-number
- v-model="filters.building"
- placeholder="楼栋号"
- :min="1"
- :max="999"
- class="w-full"
- controls-position="right"
- size="default"
- />
- </div>
- </el-col>
- <el-col :xs="24" :sm="12" :md="2">
- <div class="mb-4">
- <label class="block text-sm font-medium text-gray-700 mb-2">楼层</label>
- <el-input-number
- v-model="filters.floor"
- placeholder="楼层"
- :min="1"
- :max="99"
- class="w-full"
- controls-position="right"
- size="default"
- />
- </div>
- </el-col>
- <el-col :xs="24" :sm="12" :md="2">
- <div class="mb-4">
- <label class="block text-sm font-medium text-gray-700 mb-2">房间名称</label>
- <el-input
- v-model="filters.houseName"
- placeholder="房间名称"
- class="w-full"
- size="default"
- clearable
- />
- </div>
- </el-col>
- <el-col :xs="24" :sm="12" :md="2">
- <div class="mb-4">
- <label class="block text-sm font-medium text-gray-700 mb-2">房屋状态</label>
- <!-- 核心修改:默认选中空闲,且移除清空功能(避免删除默认值) -->
- <el-select
- v-model="filters.status"
- placeholder="请选择状态"
- style="width: 100%"
- size="default"
- @change="handleStatusChange"
- >
- <el-option label="空闲" value="1"/>
- <el-option label="已租" value="2"/>
- <el-option label="到期" value="3"/>
- </el-select>
- </div>
- </el-col>
- <el-col :xs="24" :sm="12" :md="6" style="display: flex">
- <div class="mb-4 flex items-end">
- <el-button
- type="primary"
- @click="applyFilters"
- class="mr-2"
- :loading="loading"
- size="default"
- >
- <el-icon class="mr-1"><Search /></el-icon>
- 搜索
- </el-button>
- <el-button @click="resetFilters" size="default">
- <el-icon class="mr-1"><RotateCcw /></el-icon>
- 重置
- </el-button>
- </div>
- </el-col>
- </el-row>
- </div>
- </el-card>
- <!-- 列表展示区域 -->
- <div class="mb-4 flex justify-between items-center">
- <div class="text-gray-600">
- 共找到
- <span class="font-semibold text-blue-600">{{ total }}</span> 个结果
- </div>
- <el-select v-model="sortBy" placeholder="排序方式" style="width: 20%" size="default">
- <el-option label="默认排序" value="default"></el-option>
- <el-option label="租金从低到高" value="price-asc"></el-option>
- <el-option label="租金从高到低" value="price-desc"></el-option>
- <el-option label="面积从小到大" value="area-asc"></el-option>
- <el-option label="面积从大到小" value="area-desc"></el-option>
- </el-select>
- </div>
- <!-- 加载状态 -->
- <div v-if="loading" class="text-center py-16">
- <el-icon :size="64" class="text-blue-500 mb-4 animate-spin"><Settings /></el-icon>
- <h3 class="text-xl font-semibold text-gray-600">加载中...</h3>
- </div>
- <!-- 列表内容 -->
- <el-row :gutter="24" v-else>
- <el-col
- :xs="24"
- :sm="12"
- :lg="8"
- :xl="6"
- v-for="item in formattedList"
- :key="item.id"
- class="mb-6"
- >
- <el-card
- class="h-full shadow-xl hover:shadow-2xl transition-all duration-300 rounded-2xl border-0 overflow-hidden group cursor-pointer"
- >
- <div>
- <!-- 状态标签 -->
- <div class="flex items-center justify-between pt-2 gap-2 flex-wrap">
- <div class="flex items-center gap-2 flex-wrap">
- <el-tag
- :type="getStatusTagType(item.contractExpirationDate,item.status)"
- size="large"
- class="custom-large-tag"
- >
- {{ item.status }}
- </el-tag>
- <el-tag
- v-if="item.contractExpirationDate && item.contractExpirationDate !== ''"
- type="danger"
- size="large"
- class="custom-large-tag"
- >
- 合同到期日: {{ item.contractExpirationDate }}
- </el-tag>
- <el-tag
- v-if="item.isArrears"
- type="danger"
- size="large"
- class="custom-large-tag"
- >缴费提醒</el-tag>
- </div>
- <div class="text-right">
- <div class="text-2xl font-bold text-blue-600">{{ item.priceRange }}</div>
- <div class="text-sm text-gray-500">元/月</div>
- </div>
- </div>
- <div class="flex justify-between items-start">
- <h1 class="text-xl font-bold text-gray-800">
- {{ item.buildingNumber }}栋{{ item.houseNameNumber }}
- </h1>
- </div>
- <div class="space-y-3 mb-6">
- <div class="flex items-center text-gray-600">
- <el-icon class="mr-2 text-blue-500"><MapPin /></el-icon>
- <span class="text-sm">{{ item.address }}</span>
- </div>
- <div class="flex items-center text-gray-600">
- <el-icon class="mr-2 text-green-500"><Maximize /></el-icon>
- <span class="text-sm">占地面积:{{ item.area }}㎡</span>
- </div>
- <div class="flex items-center text-gray-600" v-if="item.floor && item.floor !== '-'">
- <el-icon class="mr-2 text-purple-500"><Building2 /></el-icon>
- <span class="text-sm">楼层:{{ item.floor }}</span>
- </div>
- <div class="flex items-center text-gray-600" v-if="item.facilities">
- <el-icon class="mr-2 text-orange-500"><Settings /></el-icon>
- <span class="text-sm">配套:{{ item.facilities }}</span>
- </div>
- </div>
- <div class="flex gap-2">
- <el-button
- :type="getStatusButtonType(item.contractExpirationDate,item.status)"
- size="default"
- class="w-full"
- @click="viewDetails(item)"
- >
- <el-icon class="mr-1"><Eye /></el-icon>
- 查看详情
- </el-button>
- </div>
- </div>
- </el-card>
- </el-col>
- </el-row>
- <!-- 空状态 -->
- <div v-if="!loading && formattedList.length === 0" class="text-center py-16">
- <el-icon :size="64" class="text-gray-400 mb-4"><Search /></el-icon>
- <h3 class="text-xl font-semibold text-gray-600 mb-2">暂无符合条件的结果</h3>
- <p class="text-gray-500">请尝试调整筛选条件</p>
- </div>
- <!-- 分页 -->
- <div class="flex justify-center mt-12" v-if="!loading && total > 0">
- <el-pagination
- v-model:current-page="currentPage"
- :page-size="pageSize"
- :total="total"
- layout="prev, pager, next, jumper, total"
- class="justify-center"
- @current-change="handlePageChange"
- />
- </div>
- </div>
- </div>
- </el-main>
- </el-container>
- </template>
- <style scoped>
- .el-card {
- border: none;
- transition: all 0.3s ease;
- }
- .el-card:hover {
- transform: translateY(-2px);
- }
- .el-tabs__item {
- font-weight: 600;
- }
- .el-tabs__active-bar {
- background-color: #3b82f6;
- }
- .el-tabs__item.is-active {
- color: #3b82f6;
- }
- .el-button {
- font-weight: 600;
- }
- /* 渐变背景动画 */
- @keyframes gradient {
- 0% {
- background-position: 0% 50%;
- }
- 50% {
- background-position: 100% 50%;
- }
- 100% {
- background-position: 0% 50%;
- }
- }
- .bg-gradient-to-r {
- background-size: 200% 200%;
- animation: gradient 15s ease infinite;
- }
- /* 加载动画 */
- @keyframes spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
- }
- .animate-spin {
- animation: spin 1s linear infinite;
- }
- /* 增大标签字体大小 */
- .custom-large-tag {
- font-size: 16px !important;
- padding: 10px 16px !important;
- }
- /* 响应式优化 */
- @media (max-width: 768px) {
- .custom-large-tag {
- font-size: 14px !important;
- padding: 8px 12px !important;
- }
- }
- </style>
|