|
@@ -1,5 +1,269 @@
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { computed, onMounted, ref, watch } from 'vue'
|
|
|
|
|
+import {
|
|
|
|
|
+ ElButton,
|
|
|
|
|
+ ElCard,
|
|
|
|
|
+ ElCol,
|
|
|
|
|
+ ElContainer,
|
|
|
|
|
+ ElIcon,
|
|
|
|
|
+ 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 // 面积
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+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)
|
|
|
|
|
+const currentPage = ref(1)
|
|
|
|
|
+const pageSize = ref(12)
|
|
|
|
|
+const sortBy = ref('default')
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const total = ref(0)
|
|
|
|
|
+
|
|
|
|
|
+// 筛选条件
|
|
|
|
|
+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,
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 房屋列表数据
|
|
|
|
|
+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() }),
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: item.id,
|
|
|
|
|
+ buildingNumber: item.houseName,
|
|
|
|
|
+ priceRange: item.rentRange ? `${item.rentRange}` : '面议',
|
|
|
|
|
+ address: item.address,
|
|
|
|
|
+ area: parseInt(item.area) || 0,
|
|
|
|
|
+ status: item.status === '空闲' ? '可租' : '已租',
|
|
|
|
|
+ floor: item.floor,
|
|
|
|
|
+ facilities: '配套设施', // 接口没有这个字段,可以根据实际情况调整
|
|
|
|
|
+ building: item.building,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 格式化后的列表数据
|
|
|
|
|
+const formattedList = computed(() => {
|
|
|
|
|
+ return houseList.value.map(formatDisplayData)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 方法
|
|
|
|
|
+const handleTabClick = (tab: any) => {
|
|
|
|
|
+ if (tab.paneName === activeTab.value) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ activeTab.value = tab.paneName
|
|
|
|
|
+ currentPage.value = 1
|
|
|
|
|
+ resetFilters()
|
|
|
|
|
+ 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,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handlePageChange = (page: number) => {
|
|
|
|
|
+ currentPage.value = page
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleSortChange = () => {
|
|
|
|
|
+ currentPage.value = 1
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const goBack = () => {
|
|
|
|
|
+ router.back()
|
|
|
|
|
+}
|
|
|
|
|
+// 查看详情
|
|
|
|
|
+const viewDetails = (item: any) => {
|
|
|
|
|
+ router.push({
|
|
|
|
|
+ path: '/zf/fwxq',
|
|
|
|
|
+ query: {
|
|
|
|
|
+ houseId: item.id,
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 监听排序变化
|
|
|
|
|
+watch(sortBy, handleSortChange)
|
|
|
|
|
+
|
|
|
|
|
+// 组件挂载时获取数据
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ getList()
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
<template>
|
|
<template>
|
|
|
<el-container class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
<el-container class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ @click="goBack"
|
|
|
|
|
+ class="relative top-1 left-28 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>
|
|
|
<el-main class="h-full p-0">
|
|
<el-main class="h-full p-0">
|
|
|
<!-- 主要内容区域 -->
|
|
<!-- 主要内容区域 -->
|
|
|
<div class="py-12 px-8">
|
|
<div class="py-12 px-8">
|
|
@@ -37,84 +301,91 @@
|
|
|
<div class="p-4">
|
|
<div class="p-4">
|
|
|
<h3 class="text-lg font-semibold mb-4 text-gray-800">筛选条件</h3>
|
|
<h3 class="text-lg font-semibold mb-4 text-gray-800">筛选条件</h3>
|
|
|
<el-row :gutter="24">
|
|
<el-row :gutter="24">
|
|
|
- <el-col :xs="24" :sm="12" :md="4">
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="6">
|
|
|
<div class="mb-4">
|
|
<div class="mb-4">
|
|
|
- <label class="block text-sm font-medium text-gray-700 mb-2"
|
|
|
|
|
- >租金范围 (元/月)</label
|
|
|
|
|
- >
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="filters.priceRange"
|
|
|
|
|
- placeholder="选择租金范围"
|
|
|
|
|
- class="w-full"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option label="不限" value=""></el-option>
|
|
|
|
|
- <el-option label="1000以下" value="0-1000"></el-option>
|
|
|
|
|
- <el-option label="1000-3000" value="1000-3000"></el-option>
|
|
|
|
|
- <el-option label="3000-5000" value="3000-5000"></el-option>
|
|
|
|
|
- <el-option label="5000以上" value="5000+"></el-option>
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :xs="24" :sm="12" :md="4">
|
|
|
|
|
- <div class="mb-4">
|
|
|
|
|
- <label class="block text-sm font-medium text-gray-700 mb-2"
|
|
|
|
|
- >面积范围 (㎡)</label
|
|
|
|
|
- >
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="filters.areaRange"
|
|
|
|
|
- placeholder="选择面积范围"
|
|
|
|
|
- class="w-full"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option label="不限" value=""></el-option>
|
|
|
|
|
- <el-option label="50以下" value="0-50"></el-option>
|
|
|
|
|
- <el-option label="50-100" value="50-100"></el-option>
|
|
|
|
|
- <el-option label="100-200" value="100-200"></el-option>
|
|
|
|
|
- <el-option label="200以上" value="200+"></el-option>
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <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"
|
|
|
|
|
+ />
|
|
|
|
|
+ <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"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :xs="24" :sm="12" :md="4">
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="6">
|
|
|
<div class="mb-4">
|
|
<div class="mb-4">
|
|
|
- <label class="block text-sm font-medium text-gray-700 mb-2">区域</label>
|
|
|
|
|
- <el-select v-model="filters.district" placeholder="选择区域" class="w-full">
|
|
|
|
|
- <el-option label="不限" value=""></el-option>
|
|
|
|
|
- <el-option label="高新区" value="高新区"></el-option>
|
|
|
|
|
- <el-option label="经开区" value="经开区"></el-option>
|
|
|
|
|
- <el-option label="工业园区" value="工业园区"></el-option>
|
|
|
|
|
- <el-option label="科技园区" value="科技园区"></el-option>
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <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"
|
|
|
|
|
+ />
|
|
|
|
|
+ <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"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :xs="24" :sm="12" :md="4">
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="3">
|
|
|
<div class="mb-4">
|
|
<div class="mb-4">
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">楼栋</label>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">楼栋</label>
|
|
|
- <el-select v-model="filters.building" placeholder="选择楼栋" class="w-full">
|
|
|
|
|
- <el-option label="不限" value=""></el-option>
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="building in availableBuildings"
|
|
|
|
|
- :key="building"
|
|
|
|
|
- :label="building"
|
|
|
|
|
- :value="building"
|
|
|
|
|
- ></el-option>
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="filters.building"
|
|
|
|
|
+ placeholder="楼栋号"
|
|
|
|
|
+ :min="1"
|
|
|
|
|
+ :max="999"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
</div>
|
|
</div>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :xs="24" :sm="12" :md="4" v-if="activeTab !== 'factory'">
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="3">
|
|
|
<div class="mb-4">
|
|
<div class="mb-4">
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">楼层</label>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">楼层</label>
|
|
|
- <el-select v-model="filters.floor" placeholder="选择楼层" class="w-full">
|
|
|
|
|
- <el-option label="不限" value=""></el-option>
|
|
|
|
|
- <el-option label="1楼" value="1楼"></el-option>
|
|
|
|
|
- <el-option label="2楼" value="2楼"></el-option>
|
|
|
|
|
- <el-option label="3楼" value="3楼"></el-option>
|
|
|
|
|
- <el-option label="4楼" value="4楼"></el-option>
|
|
|
|
|
- <el-option label="5楼及以上" value="5楼+"></el-option>
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="filters.floor"
|
|
|
|
|
+ placeholder="楼层"
|
|
|
|
|
+ :min="1"
|
|
|
|
|
+ :max="99"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
</div>
|
|
</div>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :xs="24" :sm="12" :md="4">
|
|
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="6">
|
|
|
<div class="mb-4 flex items-end">
|
|
<div class="mb-4 flex items-end">
|
|
|
- <el-button type="primary" @click="applyFilters" class="mr-2">
|
|
|
|
|
|
|
+ <el-button type="primary" @click="applyFilters" class="mr-2" :loading="loading">
|
|
|
<el-icon class="mr-1"><Search /></el-icon>
|
|
<el-icon class="mr-1"><Search /></el-icon>
|
|
|
搜索
|
|
搜索
|
|
|
</el-button>
|
|
</el-button>
|
|
@@ -132,7 +403,7 @@
|
|
|
<div class="mb-6 flex justify-between items-center">
|
|
<div class="mb-6 flex justify-between items-center">
|
|
|
<div class="text-gray-600">
|
|
<div class="text-gray-600">
|
|
|
共找到
|
|
共找到
|
|
|
- <span class="font-semibold text-blue-600">{{ filteredList.length }}</span> 个结果
|
|
|
|
|
|
|
+ <span class="font-semibold text-blue-600">{{ total }}</span> 个结果
|
|
|
</div>
|
|
</div>
|
|
|
<el-select v-model="sortBy" placeholder="排序方式" class="w-48">
|
|
<el-select v-model="sortBy" placeholder="排序方式" class="w-48">
|
|
|
<el-option label="默认排序" value="default"></el-option>
|
|
<el-option label="默认排序" value="default"></el-option>
|
|
@@ -143,14 +414,20 @@
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</div>
|
|
</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">
|
|
|
|
|
|
|
+ <el-row :gutter="24" v-else>
|
|
|
<el-col
|
|
<el-col
|
|
|
:xs="24"
|
|
:xs="24"
|
|
|
:sm="12"
|
|
:sm="12"
|
|
|
:lg="8"
|
|
:lg="8"
|
|
|
:xl="6"
|
|
:xl="6"
|
|
|
- v-for="item in filteredList"
|
|
|
|
|
|
|
+ v-for="item in formattedList"
|
|
|
:key="item.id"
|
|
:key="item.id"
|
|
|
class="mb-6"
|
|
class="mb-6"
|
|
|
>
|
|
>
|
|
@@ -165,7 +442,6 @@
|
|
|
<div class="text-sm text-gray-500">元/月</div>
|
|
<div class="text-sm text-gray-500">元/月</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
<div class="space-y-3 mb-6">
|
|
<div class="space-y-3 mb-6">
|
|
|
<div class="flex items-center text-gray-600">
|
|
<div class="flex items-center text-gray-600">
|
|
|
<el-icon class="mr-2 text-blue-500"><MapPin /></el-icon>
|
|
<el-icon class="mr-2 text-blue-500"><MapPin /></el-icon>
|
|
@@ -190,9 +466,13 @@
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
<div class="flex gap-2">
|
|
<div class="flex gap-2">
|
|
|
- <el-button type="primary" size="small" class="w-full">
|
|
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ @click="viewDetails(item)"
|
|
|
|
|
+ >
|
|
|
<el-icon class="mr-1"><Eye /></el-icon>
|
|
<el-icon class="mr-1"><Eye /></el-icon>
|
|
|
查看详情
|
|
查看详情
|
|
|
</el-button>
|
|
</el-button>
|
|
@@ -203,20 +483,21 @@
|
|
|
</el-row>
|
|
</el-row>
|
|
|
|
|
|
|
|
<!-- 空状态 -->
|
|
<!-- 空状态 -->
|
|
|
- <div v-if="filteredList.length === 0" class="text-center py-16">
|
|
|
|
|
|
|
+ <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>
|
|
<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>
|
|
<h3 class="text-xl font-semibold text-gray-600 mb-2">暂无符合条件的结果</h3>
|
|
|
<p class="text-gray-500">请尝试调整筛选条件</p>
|
|
<p class="text-gray-500">请尝试调整筛选条件</p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 分页 -->
|
|
<!-- 分页 -->
|
|
|
- <div class="flex justify-center mt-12" v-if="filteredList.length > 0">
|
|
|
|
|
|
|
+ <div class="flex justify-center mt-12" v-if="!loading && total > 0">
|
|
|
<el-pagination
|
|
<el-pagination
|
|
|
v-model:current-page="currentPage"
|
|
v-model:current-page="currentPage"
|
|
|
:page-size="pageSize"
|
|
:page-size="pageSize"
|
|
|
- :total="filteredList.length"
|
|
|
|
|
- layout="prev, pager, next, jumper"
|
|
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ layout="prev, pager, next, jumper, total"
|
|
|
class="justify-center"
|
|
class="justify-center"
|
|
|
|
|
+ @current-change="handlePageChange"
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -225,416 +506,6 @@
|
|
|
</el-container>
|
|
</el-container>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
-<script setup lang="ts">
|
|
|
|
|
-import { computed, onMounted, ref } from 'vue'
|
|
|
|
|
-import {
|
|
|
|
|
- ElButton,
|
|
|
|
|
- ElCard,
|
|
|
|
|
- ElCol,
|
|
|
|
|
- ElContainer,
|
|
|
|
|
- ElIcon,
|
|
|
|
|
- ElMain,
|
|
|
|
|
- ElOption,
|
|
|
|
|
- ElPagination,
|
|
|
|
|
- ElRow,
|
|
|
|
|
- ElSelect,
|
|
|
|
|
- ElTabPane,
|
|
|
|
|
- ElTabs,
|
|
|
|
|
- ElTag,
|
|
|
|
|
-} from 'element-plus'
|
|
|
|
|
-import {
|
|
|
|
|
- Building,
|
|
|
|
|
- Building2,
|
|
|
|
|
- Eye,
|
|
|
|
|
- Factory,
|
|
|
|
|
- Lightbulb,
|
|
|
|
|
- MapPin,
|
|
|
|
|
- Maximize,
|
|
|
|
|
- RotateCcw,
|
|
|
|
|
- Search,
|
|
|
|
|
- Settings,
|
|
|
|
|
-} from 'lucide-vue-next'
|
|
|
|
|
-
|
|
|
|
|
-// 响应式数据
|
|
|
|
|
-const activeTab = ref('housing')
|
|
|
|
|
-const currentPage = ref(1)
|
|
|
|
|
-const pageSize = ref(12)
|
|
|
|
|
-const sortBy = ref('default')
|
|
|
|
|
-
|
|
|
|
|
-// 筛选条件
|
|
|
|
|
-const filters = ref({
|
|
|
|
|
- priceRange: '',
|
|
|
|
|
- areaRange: '',
|
|
|
|
|
- district: '',
|
|
|
|
|
- building: '',
|
|
|
|
|
- floor: '',
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 模拟数据
|
|
|
|
|
-const housingData = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- buildingNumber: 'A栋-101',
|
|
|
|
|
- priceRange: '1200-1500',
|
|
|
|
|
- address: '高新区科技大道123号',
|
|
|
|
|
- area: 85,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '1楼',
|
|
|
|
|
- facilities: '家具齐全',
|
|
|
|
|
- building: 'A栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- buildingNumber: 'B栋-205',
|
|
|
|
|
- priceRange: '1800-2200',
|
|
|
|
|
- address: '经开区创新路456号',
|
|
|
|
|
- area: 120,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '2楼',
|
|
|
|
|
- facilities: '精装修',
|
|
|
|
|
- building: 'B栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- buildingNumber: 'C栋-308',
|
|
|
|
|
- priceRange: '2500-3000',
|
|
|
|
|
- address: '工业园区发展大道789号',
|
|
|
|
|
- area: 150,
|
|
|
|
|
- status: '已租',
|
|
|
|
|
- floor: '3楼',
|
|
|
|
|
- facilities: '豪华装修',
|
|
|
|
|
- building: 'C栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- buildingNumber: 'D栋-102',
|
|
|
|
|
- priceRange: '900-1200',
|
|
|
|
|
- address: '科技园区智慧路321号',
|
|
|
|
|
- area: 65,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '1楼',
|
|
|
|
|
- facilities: '基础装修',
|
|
|
|
|
- building: 'D栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 5,
|
|
|
|
|
- buildingNumber: 'E栋-401',
|
|
|
|
|
- priceRange: '3200-3800',
|
|
|
|
|
- address: '高新区未来街654号',
|
|
|
|
|
- area: 180,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '4楼',
|
|
|
|
|
- facilities: '智能家居',
|
|
|
|
|
- building: 'E栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 6,
|
|
|
|
|
- buildingNumber: 'F栋-203',
|
|
|
|
|
- priceRange: '1600-2000',
|
|
|
|
|
- address: '经开区产业路987号',
|
|
|
|
|
- area: 95,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '2楼',
|
|
|
|
|
- facilities: '简装修',
|
|
|
|
|
- building: 'F栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 7,
|
|
|
|
|
- buildingNumber: 'A栋-201',
|
|
|
|
|
- priceRange: '1400-1700',
|
|
|
|
|
- address: '高新区科技大道123号',
|
|
|
|
|
- area: 90,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '2楼',
|
|
|
|
|
- facilities: '精装修',
|
|
|
|
|
- building: 'A栋',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 8,
|
|
|
|
|
- buildingNumber: 'B栋-301',
|
|
|
|
|
- priceRange: '2000-2400',
|
|
|
|
|
- address: '经开区创新路456号',
|
|
|
|
|
- area: 110,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '3楼',
|
|
|
|
|
- facilities: '豪华装修',
|
|
|
|
|
- building: 'B栋',
|
|
|
|
|
- },
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const factoryData = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- buildingNumber: '1号厂房',
|
|
|
|
|
- priceRange: '8000-12000',
|
|
|
|
|
- address: '工业园区制造大道100号',
|
|
|
|
|
- area: 2000,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- facilities: '行车、配电',
|
|
|
|
|
- building: '1号厂房',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- buildingNumber: '2号厂房',
|
|
|
|
|
- priceRange: '15000-20000',
|
|
|
|
|
- address: '经开区工业路200号',
|
|
|
|
|
- area: 3500,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- facilities: '净化车间',
|
|
|
|
|
- building: '2号厂房',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- buildingNumber: '3号厂房',
|
|
|
|
|
- priceRange: '25000-30000',
|
|
|
|
|
- address: '高新区生产基地300号',
|
|
|
|
|
- area: 5000,
|
|
|
|
|
- status: '已租',
|
|
|
|
|
- facilities: '自动化设备',
|
|
|
|
|
- building: '3号厂房',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- buildingNumber: '4号厂房',
|
|
|
|
|
- priceRange: '6000-8000',
|
|
|
|
|
- address: '科技园区制造街400号',
|
|
|
|
|
- area: 1500,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- facilities: '基础设施',
|
|
|
|
|
- building: '4号厂房',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 5,
|
|
|
|
|
- buildingNumber: '5号厂房',
|
|
|
|
|
- priceRange: '18000-25000',
|
|
|
|
|
- address: '工业园区智造路500号',
|
|
|
|
|
- area: 4200,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- facilities: '环保设备',
|
|
|
|
|
- building: '5号厂房',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 6,
|
|
|
|
|
- buildingNumber: '6号厂房',
|
|
|
|
|
- priceRange: '10000-15000',
|
|
|
|
|
- address: '经开区产业大道600号',
|
|
|
|
|
- area: 2800,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- facilities: '标准厂房',
|
|
|
|
|
- building: '6号厂房',
|
|
|
|
|
- },
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const innovationData = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- buildingNumber: '创新楼A座-501',
|
|
|
|
|
- priceRange: '3000-4000',
|
|
|
|
|
- address: '科技园区创业大道50号',
|
|
|
|
|
- area: 200,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '5楼',
|
|
|
|
|
- facilities: '会议室、茶水间',
|
|
|
|
|
- building: '创新楼A座',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- buildingNumber: '孵化楼B座-302',
|
|
|
|
|
- priceRange: '2500-3500',
|
|
|
|
|
- address: '高新区孵化路80号',
|
|
|
|
|
- area: 150,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '3楼',
|
|
|
|
|
- facilities: '开放办公区',
|
|
|
|
|
- building: '孵化楼B座',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- buildingNumber: '科技楼C座-801',
|
|
|
|
|
- priceRange: '5000-6500',
|
|
|
|
|
- address: '经开区科技街120号',
|
|
|
|
|
- area: 300,
|
|
|
|
|
- status: '已租',
|
|
|
|
|
- floor: '8楼',
|
|
|
|
|
- facilities: '实验室、路演厅',
|
|
|
|
|
- building: '科技楼C座',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- buildingNumber: '创业楼D座-201',
|
|
|
|
|
- priceRange: '2000-2800',
|
|
|
|
|
- address: '工业园区创新路90号',
|
|
|
|
|
- area: 120,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '2楼',
|
|
|
|
|
- facilities: '基础办公',
|
|
|
|
|
- building: '创业楼D座',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 5,
|
|
|
|
|
- buildingNumber: '智慧楼E座-601',
|
|
|
|
|
- priceRange: '4500-5800',
|
|
|
|
|
- address: '科技园区智慧大道160号',
|
|
|
|
|
- area: 250,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '6楼',
|
|
|
|
|
- facilities: '智能办公系统',
|
|
|
|
|
- building: '智慧楼E座',
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 6,
|
|
|
|
|
- buildingNumber: '未来楼F座-401',
|
|
|
|
|
- priceRange: '3500-4500',
|
|
|
|
|
- address: '高新区未来路200号',
|
|
|
|
|
- area: 180,
|
|
|
|
|
- status: '可租',
|
|
|
|
|
- floor: '4楼',
|
|
|
|
|
- facilities: '共享空间',
|
|
|
|
|
- building: '未来楼F座',
|
|
|
|
|
- },
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-// 计算属性
|
|
|
|
|
-const currentData = computed(() => {
|
|
|
|
|
- switch (activeTab.value) {
|
|
|
|
|
- case 'housing':
|
|
|
|
|
- return housingData.value
|
|
|
|
|
- case 'factory':
|
|
|
|
|
- return factoryData.value
|
|
|
|
|
- case 'innovation':
|
|
|
|
|
- return innovationData.value
|
|
|
|
|
- default:
|
|
|
|
|
- return []
|
|
|
|
|
- }
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const availableBuildings = computed(() => {
|
|
|
|
|
- const buildings = [...new Set(currentData.value.map((item) => item.building))]
|
|
|
|
|
- return buildings.sort()
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const filteredList = computed(() => {
|
|
|
|
|
- let result = [...currentData.value]
|
|
|
|
|
-
|
|
|
|
|
- // 实际筛选逻辑
|
|
|
|
|
- if (filters.value.priceRange) {
|
|
|
|
|
- result = result.filter((item) => {
|
|
|
|
|
- const priceRange = item.priceRange.split('-').map((price) => {
|
|
|
|
|
- const num = parseFloat(price.replace(/,/g, '').replace('+', ''))
|
|
|
|
|
- return isNaN(num) ? Infinity : num
|
|
|
|
|
- })
|
|
|
|
|
- const filterRange = filters.value.priceRange.split('-').map((price) => {
|
|
|
|
|
- const num = parseFloat(price.replace(/,/g, '').replace('+', ''))
|
|
|
|
|
- return isNaN(num) ? Infinity : num
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- if (filterRange.length === 1) {
|
|
|
|
|
- // 处理 "5000+" 这种情况
|
|
|
|
|
- return priceRange[0] >= filterRange[0]
|
|
|
|
|
- } else {
|
|
|
|
|
- // 处理 "1000-3000" 这种情况
|
|
|
|
|
- return (
|
|
|
|
|
- (priceRange[0] >= filterRange[0] && priceRange[0] <= filterRange[1]) ||
|
|
|
|
|
- (priceRange[1] >= filterRange[0] && priceRange[1] <= filterRange[1])
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (filters.value.areaRange) {
|
|
|
|
|
- result = result.filter((item) => {
|
|
|
|
|
- const areaRange = item.area
|
|
|
|
|
- const [min, max] = filters.value.areaRange.split('-').map(Number)
|
|
|
|
|
-
|
|
|
|
|
- if (max) {
|
|
|
|
|
- return areaRange >= min && areaRange <= max
|
|
|
|
|
- } else {
|
|
|
|
|
- return areaRange >= min
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (filters.value.district) {
|
|
|
|
|
- result = result.filter((item) => item.address.includes(filters.value.district))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (filters.value.building) {
|
|
|
|
|
- result = result.filter((item) => item.building === filters.value.building)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (filters.value.floor && activeTab.value !== 'factory') {
|
|
|
|
|
- result = result.filter((item) => item.floor === filters.value.floor)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 排序逻辑
|
|
|
|
|
- switch (sortBy.value) {
|
|
|
|
|
- case 'price-asc':
|
|
|
|
|
- result.sort((a, b) => {
|
|
|
|
|
- const aPrice = parseFloat(a.priceRange.split('-')[0].replace(/,/g, ''))
|
|
|
|
|
- const bPrice = parseFloat(b.priceRange.split('-')[0].replace(/,/g, ''))
|
|
|
|
|
- return aPrice - bPrice
|
|
|
|
|
- })
|
|
|
|
|
- break
|
|
|
|
|
- case 'price-desc':
|
|
|
|
|
- result.sort((a, b) => {
|
|
|
|
|
- const aPrice = parseFloat(a.priceRange.split('-')[0].replace(/,/g, ''))
|
|
|
|
|
- const bPrice = parseFloat(b.priceRange.split('-')[0].replace(/,/g, ''))
|
|
|
|
|
- return bPrice - aPrice
|
|
|
|
|
- })
|
|
|
|
|
- break
|
|
|
|
|
- case 'area-asc':
|
|
|
|
|
- result.sort((a, b) => a.area - b.area)
|
|
|
|
|
- break
|
|
|
|
|
- case 'area-desc':
|
|
|
|
|
- result.sort((a, b) => b.area - a.area)
|
|
|
|
|
- break
|
|
|
|
|
- default:
|
|
|
|
|
- // 默认排序可以按ID
|
|
|
|
|
- result.sort((a, b) => a.id - b.id)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return result
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 方法
|
|
|
|
|
-const handleTabClick = (tab: any) => {
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
- resetFilters()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const applyFilters = () => {
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
- console.log('应用筛选条件:', filters.value)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const resetFilters = () => {
|
|
|
|
|
- filters.value = {
|
|
|
|
|
- priceRange: '',
|
|
|
|
|
- areaRange: '',
|
|
|
|
|
- district: '',
|
|
|
|
|
- building: '',
|
|
|
|
|
- floor: '',
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const getIconByType = (type: string) => {
|
|
|
|
|
- switch (type) {
|
|
|
|
|
- case 'housing':
|
|
|
|
|
- return Building
|
|
|
|
|
- case 'factory':
|
|
|
|
|
- return Factory
|
|
|
|
|
- case 'innovation':
|
|
|
|
|
- return Lightbulb
|
|
|
|
|
- default:
|
|
|
|
|
- return Building
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- // 组件挂载后的初始化逻辑
|
|
|
|
|
-})
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
.el-card {
|
|
.el-card {
|
|
|
border: none;
|
|
border: none;
|
|
@@ -673,4 +544,18 @@ onMounted(() => {
|
|
|
background-size: 200% 200%;
|
|
background-size: 200% 200%;
|
|
|
animation: gradient 15s ease infinite;
|
|
animation: gradient 15s ease infinite;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 加载动画 */
|
|
|
|
|
+@keyframes spin {
|
|
|
|
|
+ from {
|
|
|
|
|
+ transform: rotate(0deg);
|
|
|
|
|
+ }
|
|
|
|
|
+ to {
|
|
|
|
|
+ transform: rotate(360deg);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.animate-spin {
|
|
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|