513 lines
24 KiB
HTML
513 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=1280" />
|
|
<title>Fonrey 楼盘列表 · 静态原型</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {
|
|
50: '#F0FDFA',
|
|
100: '#CCFBF1',
|
|
200: '#99F6E4',
|
|
500: '#14B8A6',
|
|
600: '#0F766E',
|
|
700: '#115E59',
|
|
800: '#134E4A'
|
|
},
|
|
neutral: {
|
|
50: '#F8FAFC',
|
|
100: '#F1F5F9',
|
|
200: '#E2E8F0',
|
|
300: '#CBD5E1',
|
|
400: '#94A3B8',
|
|
500: '#64748B',
|
|
600: '#475569',
|
|
700: '#334155',
|
|
800: '#1E293B',
|
|
900: '#0F172A'
|
|
},
|
|
success: { 50: '#F0FDF4', 600: '#16A34A' },
|
|
warning: { 50: '#FFFBEB', 600: '#D97706' },
|
|
danger: { 50: '#FEF2F2', 600: '#DC2626' },
|
|
info: { 50: '#EFF6FF', 600: '#2563EB' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style>
|
|
:root {
|
|
--bg-page: #F8FAFC;
|
|
--bg-card: #FFFFFF;
|
|
--bg-subtle: #F1F5F9;
|
|
--text-primary: #0F172A;
|
|
--text-secondary: #64748B;
|
|
--border: #E2E8F0;
|
|
}
|
|
|
|
html { scroll-behavior: smooth; }
|
|
body {
|
|
background: var(--bg-page);
|
|
color: var(--text-primary);
|
|
transition: background-color .2s ease, color .2s ease;
|
|
}
|
|
[x-cloak] { display: none !important; }
|
|
.tabular-nums { font-variant-numeric: tabular-nums; }
|
|
|
|
.bg-surface { background: var(--bg-card); }
|
|
.bg-subtle { background: var(--bg-subtle); }
|
|
.border-surface { border-color: var(--border); }
|
|
.text-surface { color: var(--text-primary); }
|
|
.text-muted { color: var(--text-secondary); }
|
|
|
|
.module-tab {
|
|
color: #64748B;
|
|
border-bottom: 2px solid transparent;
|
|
}
|
|
.module-tab.active {
|
|
color: #0F766E;
|
|
border-bottom-color: #0F766E;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.chip {
|
|
border: 1px solid #E2E8F0;
|
|
background: #FFFFFF;
|
|
color: #64748B;
|
|
}
|
|
.chip.active {
|
|
border-color: #0F766E;
|
|
color: #0F766E;
|
|
background: #F0FDFA;
|
|
font-weight: 600;
|
|
}
|
|
|
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
::-webkit-scrollbar-thumb { background: #CBD5E1; border-radius: 4px; }
|
|
::-webkit-scrollbar-thumb:hover { background: #94A3B8; }
|
|
</style>
|
|
</head>
|
|
<body class="text-sm antialiased" x-data="complexListPage()" x-init="init()">
|
|
<!-- Top Bar -->
|
|
<header class="fixed top-0 left-0 right-0 h-14 z-20 bg-primary-800 flex items-center justify-between">
|
|
<div class="flex items-center gap-2 px-4 w-60 shrink-0">
|
|
<div class="w-7 h-7 rounded-md bg-primary-500 flex items-center justify-center text-white text-sm font-semibold">F</div>
|
|
<span class="text-base font-semibold text-white">Fonrey</span>
|
|
</div>
|
|
<nav class="flex items-center gap-1 flex-1 px-2" aria-label="主导航">
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">工作台</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md bg-primary-600 text-white font-medium">房源</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">客源</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">营销</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">交易</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">数据</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">人事</a>
|
|
<a class="px-3 py-1.5 text-sm rounded-md text-primary-100 hover:bg-primary-700 hover:text-white">系统</a>
|
|
</nav>
|
|
<div class="flex items-center gap-1 px-4 shrink-0">
|
|
<button class="p-1.5 text-primary-200 hover:bg-primary-700 hover:text-white rounded-md" aria-label="消息">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022 23.848 23.848 0 0 0 5.454 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0"/></svg>
|
|
</button>
|
|
<div class="flex items-center gap-2 pl-3 ml-1 border-l border-primary-700">
|
|
<div class="w-8 h-8 rounded-full bg-primary-600 text-white flex items-center justify-center text-sm font-semibold">魏</div>
|
|
<span class="text-sm font-medium text-primary-100">魏深</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Side Bar -->
|
|
<aside class="fixed left-0 top-14 h-[calc(100vh-56px)] w-60 z-20 border-r border-surface bg-surface overflow-y-auto">
|
|
<nav class="p-3 space-y-0.5">
|
|
<div class="px-2 pt-2 pb-1 text-xs font-medium text-muted uppercase tracking-wide">房源管理</div>
|
|
<a class="flex items-center gap-2 px-2 py-1.5 rounded-md bg-primary-50 text-primary-700 font-medium">楼盘管理</a>
|
|
<a class="flex items-center gap-2 px-2 py-1.5 rounded-md text-neutral-700 hover:bg-neutral-100">房源列表</a>
|
|
<a class="flex items-center gap-2 px-2 py-1.5 rounded-md text-neutral-700 hover:bg-neutral-100">新增房源</a>
|
|
<a class="flex items-center gap-2 px-2 py-1.5 rounded-md text-neutral-700 hover:bg-neutral-100">成交房源</a>
|
|
<a class="flex items-center gap-2 px-2 py-1.5 rounded-md text-neutral-700 hover:bg-neutral-100">已删房源</a>
|
|
</nav>
|
|
</aside>
|
|
|
|
<!-- Main -->
|
|
<main class="ml-60 pt-[72px] min-h-screen px-6 py-5">
|
|
<div class="mx-auto max-w-[1680px] space-y-4">
|
|
<!-- Breadcrumb + title -->
|
|
<section class="bg-surface border border-surface rounded-lg p-4">
|
|
<div class="flex items-start justify-between gap-4">
|
|
<div>
|
|
<nav class="flex items-center gap-1 text-xs text-muted mb-2" aria-label="面包屑">
|
|
<a href="#" class="hover:text-neutral-700">房源</a>
|
|
<span>/</span>
|
|
<a href="#" class="hover:text-neutral-700">小区</a>
|
|
<span>/</span>
|
|
<span class="text-surface">楼盘管理系统-楼盘管理</span>
|
|
</nav>
|
|
<h1 class="text-xl font-semibold text-surface">楼盘管理</h1>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Module Tabs -->
|
|
<section class="bg-surface border border-surface rounded-lg px-4">
|
|
<nav class="flex items-center gap-6 overflow-x-auto" aria-label="楼盘管理模块导航">
|
|
<button class="module-tab py-3 whitespace-nowrap active">楼盘</button>
|
|
<button class="module-tab py-3 whitespace-nowrap">区域管理</button>
|
|
<button class="module-tab py-3 whitespace-nowrap">学校管理</button>
|
|
<button class="module-tab py-3 whitespace-nowrap">应用标准数据</button>
|
|
</nav>
|
|
</section>
|
|
|
|
<!-- Completeness metrics -->
|
|
<section class="bg-info-50 border border-info-600/20 rounded-lg p-3">
|
|
<div class="flex items-center justify-between gap-3 mb-2">
|
|
<h2 class="text-sm font-semibold text-info-600">数据完整度指标</h2>
|
|
<button class="text-xs px-2 py-1 rounded border border-info-600/30 text-info-600 bg-white hover:bg-info-50" @click="notify('已触发重新计算(原型)')">重新计算</button>
|
|
</div>
|
|
<div class="grid grid-cols-7 gap-2 text-xs">
|
|
<template x-for="item in metrics" :key="item.name">
|
|
<div class="bg-white rounded-md border border-info-600/10 p-2">
|
|
<p class="text-muted" x-text="item.name"></p>
|
|
<p class="font-semibold tabular-nums mt-0.5" x-text="item.value"></p>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Search + Filter -->
|
|
<section class="bg-surface border border-surface rounded-lg p-4 space-y-3">
|
|
<div class="flex items-center gap-2">
|
|
<input x-model.trim="filters.keyword" type="text" placeholder="楼盘名/别名/拼音/详细地址" class="w-[380px] px-3 py-2 rounded-md border border-surface bg-white focus:outline-none focus:ring-2 focus:ring-primary-600/40" />
|
|
<button class="px-3 py-2 rounded-md bg-primary-600 text-white hover:bg-primary-700" @click="applyFilters()">查询</button>
|
|
<button class="px-3 py-2 rounded-md border border-surface hover:bg-neutral-50" @click="resetFilters()">清除</button>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
<span class="text-xs text-muted">区域:</span>
|
|
<template x-for="item in regionOptions" :key="item">
|
|
<button class="chip px-2.5 py-1 rounded-md text-xs" :class="{ 'active': filters.region === item }" @click="filters.region = item" x-text="item"></button>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
<span class="text-xs text-muted">用途:</span>
|
|
<template x-for="item in usageOptions" :key="item">
|
|
<button class="chip px-2.5 py-1 rounded-md text-xs" :class="{ 'active': filters.usage === item }" @click="filters.usage = item" x-text="item"></button>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-7 gap-2">
|
|
<template x-for="cfg in extraFilters" :key="cfg.key">
|
|
<select x-model="filters[cfg.key]" class="px-2.5 py-2 rounded-md border border-surface bg-white text-xs focus:outline-none focus:ring-2 focus:ring-primary-600/40">
|
|
<option value="" x-text="cfg.label + ':不限'"></option>
|
|
<template x-for="opt in cfg.options" :key="opt">
|
|
<option :value="opt" x-text="opt"></option>
|
|
</template>
|
|
</select>
|
|
</template>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Action bar -->
|
|
<section class="bg-surface border border-surface rounded-lg p-3 flex items-center justify-between gap-3">
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
<button class="px-3 py-1.5 rounded-md bg-primary-600 text-white hover:bg-primary-700" @click="notify('新增楼盘(原型入口)')">+ 新增楼盘</button>
|
|
<button class="px-3 py-1.5 rounded-md border border-surface" :disabled="selectedCount===0" :class="selectedCount===0 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-50'">批量新增楼栋</button>
|
|
<button class="px-3 py-1.5 rounded-md border border-surface" :disabled="selectedCount===0" :class="selectedCount===0 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-50'">批改区域商圈</button>
|
|
<button class="px-3 py-1.5 rounded-md border border-surface" :disabled="selectedCount===0" :class="selectedCount===0 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-50'">删除</button>
|
|
<button class="px-3 py-1.5 rounded-md border border-surface" :disabled="selectedCount===0" :class="selectedCount===0 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-50'">合并楼盘</button>
|
|
</div>
|
|
|
|
<div class="text-xs text-muted">
|
|
<span x-show="selectedCount===0">未选中楼盘</span>
|
|
<span x-show="selectedCount>0" x-text="'已选 ' + selectedCount + ' 条'"></span>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Table -->
|
|
<section class="bg-surface border border-surface rounded-lg overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full text-sm">
|
|
<thead class="bg-subtle border-b border-surface">
|
|
<tr class="text-left">
|
|
<th scope="col" class="px-3 py-2 w-10">
|
|
<input type="checkbox" class="rounded border-surface" :checked="allOnPageSelected" @click.prevent="toggleSelectPage(!allOnPageSelected)" aria-label="全选当前页" />
|
|
</th>
|
|
<th scope="col" class="px-3 py-2">楼盘名称</th>
|
|
<th scope="col" class="px-3 py-2">楼盘类型</th>
|
|
<th scope="col" class="px-3 py-2">详细地址</th>
|
|
<th scope="col" class="px-3 py-2">城区商圈</th>
|
|
<th scope="col" class="px-3 py-2">当月挂牌均价(元/㎡)</th>
|
|
<th scope="col" class="px-3 py-2">楼栋数</th>
|
|
<th scope="col" class="px-3 py-2">产品数</th>
|
|
<th scope="col" class="px-3 py-2">房源数</th>
|
|
<th scope="col" class="px-3 py-2">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template x-if="paginatedRows.length === 0">
|
|
<tr>
|
|
<td colspan="10" class="px-4 py-12 text-center">
|
|
<p class="text-base font-medium text-surface">暂无匹配楼盘</p>
|
|
<p class="text-xs text-muted mt-1">请尝试调整筛选条件</p>
|
|
<button class="mt-3 px-3 py-1.5 rounded-md border border-surface hover:bg-neutral-50" @click="resetFilters()">清除筛选</button>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
|
|
<template x-for="row in paginatedRows" :key="row.id">
|
|
<tr class="border-b border-surface hover:bg-neutral-50/50">
|
|
<td class="px-3 py-2 align-top">
|
|
<input type="checkbox" class="rounded border-surface" :checked="isSelected(row.id)" @click.prevent="toggleSelect(row.id, !isSelected(row.id))" aria-label="选择楼盘" />
|
|
</td>
|
|
<td class="px-3 py-2 align-top">
|
|
<button class="text-left text-info-600 hover:underline font-medium" @click="openDetail(row)" x-text="row.name"></button>
|
|
<div class="mt-1 flex items-center gap-1 flex-wrap">
|
|
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-info-50 text-info-600">信息</span>
|
|
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-warning-50 text-warning-600">标准楼盘</span>
|
|
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-danger-50 text-danger-600">标准房号</span>
|
|
</div>
|
|
</td>
|
|
<td class="px-3 py-2 align-top" x-text="row.usage"></td>
|
|
<td class="px-3 py-2 align-top" x-text="row.address"></td>
|
|
<td class="px-3 py-2 align-top" x-text="row.region + '-' + row.biz"></td>
|
|
<td class="px-3 py-2 align-top tabular-nums" x-text="row.avgPrice"></td>
|
|
<td class="px-3 py-2 align-top tabular-nums" x-text="row.buildingCount"></td>
|
|
<td class="px-3 py-2 align-top tabular-nums" x-text="row.productCount"></td>
|
|
<td class="px-3 py-2 align-top">
|
|
<span class="text-xs text-muted">出售</span>
|
|
<span class="tabular-nums" x-text="row.saleCount"></span>
|
|
<span class="text-xs text-muted">/出租</span>
|
|
<span class="tabular-nums" x-text="row.rentCount"></span>
|
|
<span class="text-xs text-muted">/共</span>
|
|
<span class="tabular-nums" x-text="row.saleCount + row.rentCount"></span>
|
|
</td>
|
|
<td class="px-3 py-2 align-top">
|
|
<div class="flex items-center gap-2">
|
|
<button class="text-info-600 hover:underline text-xs" @click="notify('编辑楼盘(原型)')">编辑</button>
|
|
<button class="text-danger-600 hover:underline text-xs" @click="notify('删除楼盘(原型)')">删除</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Pagination -->
|
|
<section class="bg-surface border border-surface rounded-lg px-4 py-3 flex items-center justify-between">
|
|
<p class="text-xs text-muted" x-text="'共 ' + filteredRows.length + ' 条'" aria-live="polite"></p>
|
|
|
|
<div class="flex items-center gap-2 text-xs">
|
|
<button class="px-2 py-1 rounded border border-surface" :disabled="currentPage===1" :class="currentPage===1 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-50'" @click="goPrev()">上一页</button>
|
|
<span class="tabular-nums" x-text="currentPage + ' / ' + totalPages"></span>
|
|
<button class="px-2 py-1 rounded border border-surface" :disabled="currentPage===totalPages || totalPages===0" :class="(currentPage===totalPages || totalPages===0) ? 'opacity-50 cursor-not-allowed' : 'hover:bg-neutral-50'" @click="goNext()">下一页</button>
|
|
|
|
<span class="ml-2">20条/页</span>
|
|
<span class="ml-2">跳至</span>
|
|
<input type="number" min="1" :max="totalPages || 1" x-model.number="jumpPage" class="w-16 px-2 py-1 rounded border border-surface" />
|
|
<button class="px-2 py-1 rounded border border-surface hover:bg-neutral-50" @click="goJump()">确定</button>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Toast -->
|
|
<div x-show="toast.show" x-cloak class="fixed bottom-5 right-5 z-50 px-4 py-2 rounded-md text-sm text-white shadow-lg bg-success-600" x-text="toast.message"></div>
|
|
|
|
<script>
|
|
function complexListPage() {
|
|
return {
|
|
rows: [],
|
|
filters: {
|
|
keyword: '',
|
|
region: '不限',
|
|
usage: '不限',
|
|
fixedStatus: '',
|
|
completion: '',
|
|
complexType: '',
|
|
hasProperty: '',
|
|
buildingType: '',
|
|
ownership: '',
|
|
hasCoord: ''
|
|
},
|
|
regionOptions: ['不限', '静安', '闵行', '普陀', '松江', '长宁', '嘉定'],
|
|
usageOptions: ['不限', '住宅', '别墅', '商住', '商业', '写字楼', '其他'],
|
|
extraFilters: [
|
|
{ key: 'fixedStatus', label: '固定情况', options: ['已固定', '未固定'] },
|
|
{ key: 'completion', label: '完善情况', options: ['高', '中', '低'] },
|
|
{ key: 'complexType', label: '楼盘类型', options: ['标准', '非标'] },
|
|
{ key: 'hasProperty', label: '有无房源', options: ['有', '无'] },
|
|
{ key: 'buildingType', label: '楼栋类型', options: ['板楼', '塔楼', '板塔结合'] },
|
|
{ key: 'ownership', label: '权属关系', options: ['商品房', '房改房', '经济适用房'] },
|
|
{ key: 'hasCoord', label: '有无坐标', options: ['有坐标', '无坐标'] }
|
|
],
|
|
metrics: [
|
|
{ name: '楼盘关联率', value: '47.61%' },
|
|
{ name: '楼栋及单元完整率', value: '100.38%' },
|
|
{ name: '房号匹配率', value: '100%' },
|
|
{ name: '处置率', value: '12.05%' },
|
|
{ name: '入住人结构数据', value: '58 / 3000' },
|
|
{ name: '有效结构数量', value: '523 / 523' },
|
|
{ name: '房源对标', value: '1.83%' }
|
|
],
|
|
|
|
pageSize: 20,
|
|
currentPage: 1,
|
|
jumpPage: 1,
|
|
selectedIds: [],
|
|
|
|
toast: { show: false, message: '' },
|
|
|
|
init() {
|
|
this.rows = this.mockRows();
|
|
},
|
|
|
|
mockRows() {
|
|
const seeds = [
|
|
['都市港湾', '住宅', '嘉定', '丰庄', '上海 嘉定 海波路1000弄'],
|
|
['阳光威尼斯四期', '别墅', '普陀', '真光', '上海 普陀 金鼎路1600弄'],
|
|
['嘉城名都', '住宅', '嘉定', '江桥', '上海 嘉定 嘉城路188弄'],
|
|
['中海臻如府', '商住', '普陀', '真如', '上海 普陀 真如路88弄'],
|
|
['凯旋华庭', '写字楼', '长宁', '中山公园', '上海 长宁 凯旋路888号'],
|
|
['虹桥商务中心', '商业', '闵行', '虹桥', '上海 闵行 申长路699号'],
|
|
['静安云邸', '住宅', '静安', '大宁', '上海 静安 万荣路66弄'],
|
|
['松江壹号院', '别墅', '松江', '大学城', '上海 松江 文汇路188号']
|
|
];
|
|
|
|
const list = [];
|
|
for (let i = 1; i <= 46; i++) {
|
|
const s = seeds[(i - 1) % seeds.length];
|
|
list.push({
|
|
id: i,
|
|
name: `${s[0]}${i}`,
|
|
usage: s[1],
|
|
region: s[2],
|
|
biz: s[3],
|
|
address: s[4],
|
|
avgPrice: (32000 + i * 137).toFixed(2),
|
|
buildingCount: (6 + (i % 16)),
|
|
productCount: (50 + (i % 90)),
|
|
saleCount: (i % 12),
|
|
rentCount: (i % 8)
|
|
});
|
|
}
|
|
return list;
|
|
},
|
|
|
|
applyFilters() {
|
|
this.currentPage = 1;
|
|
this.jumpPage = 1;
|
|
this.selectedIds = [];
|
|
},
|
|
|
|
resetFilters() {
|
|
this.filters = {
|
|
keyword: '',
|
|
region: '不限',
|
|
usage: '不限',
|
|
fixedStatus: '',
|
|
completion: '',
|
|
complexType: '',
|
|
hasProperty: '',
|
|
buildingType: '',
|
|
ownership: '',
|
|
hasCoord: ''
|
|
};
|
|
this.currentPage = 1;
|
|
this.jumpPage = 1;
|
|
this.selectedIds = [];
|
|
},
|
|
|
|
get filteredRows() {
|
|
const keyword = (this.filters.keyword || '').toLowerCase();
|
|
return this.rows.filter((r) => {
|
|
const hitKeyword = !keyword || [r.name, r.address, `${r.region}${r.biz}`].some(v => String(v).toLowerCase().includes(keyword));
|
|
const hitRegion = this.filters.region === '不限' || r.region === this.filters.region;
|
|
const hitUsage = this.filters.usage === '不限' || r.usage === this.filters.usage;
|
|
return hitKeyword && hitRegion && hitUsage;
|
|
});
|
|
},
|
|
|
|
get totalPages() {
|
|
return Math.max(1, Math.ceil(this.filteredRows.length / this.pageSize));
|
|
},
|
|
|
|
get paginatedRows() {
|
|
const page = Math.min(this.currentPage, this.totalPages);
|
|
const start = (page - 1) * this.pageSize;
|
|
return this.filteredRows.slice(start, start + this.pageSize);
|
|
},
|
|
|
|
get selectedCount() {
|
|
return this.selectedIds.length;
|
|
},
|
|
|
|
isSelected(id) {
|
|
return this.selectedIds.includes(id);
|
|
},
|
|
|
|
toggleSelect(id, checked) {
|
|
if (checked) {
|
|
if (!this.selectedIds.includes(id)) this.selectedIds.push(id);
|
|
} else {
|
|
this.selectedIds = this.selectedIds.filter(x => x !== id);
|
|
}
|
|
},
|
|
|
|
get allOnPageSelected() {
|
|
if (this.paginatedRows.length === 0) return false;
|
|
return this.paginatedRows.every(r => this.selectedIds.includes(r.id));
|
|
},
|
|
|
|
toggleSelectPage(checked) {
|
|
const pageIds = this.paginatedRows.map(r => r.id);
|
|
if (checked) {
|
|
const set = new Set([...this.selectedIds, ...pageIds]);
|
|
this.selectedIds = [...set];
|
|
} else {
|
|
this.selectedIds = this.selectedIds.filter(id => !pageIds.includes(id));
|
|
}
|
|
},
|
|
|
|
goPrev() {
|
|
if (this.currentPage > 1) this.currentPage -= 1;
|
|
this.jumpPage = this.currentPage;
|
|
this.selectedIds = [];
|
|
},
|
|
|
|
goNext() {
|
|
if (this.currentPage < this.totalPages) this.currentPage += 1;
|
|
this.jumpPage = this.currentPage;
|
|
this.selectedIds = [];
|
|
},
|
|
|
|
goJump() {
|
|
let p = Number(this.jumpPage || 1);
|
|
if (!Number.isFinite(p)) p = 1;
|
|
p = Math.max(1, Math.min(this.totalPages, Math.floor(p)));
|
|
this.currentPage = p;
|
|
this.jumpPage = p;
|
|
this.selectedIds = [];
|
|
},
|
|
|
|
openDetail(row) {
|
|
window.location.hash = `complex/${row.id}`;
|
|
this.notify(`进入楼盘详情(原型):${row.name}`);
|
|
},
|
|
|
|
notify(message) {
|
|
this.toast = { show: true, message };
|
|
window.setTimeout(() => { this.toast.show = false; }, 1800);
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |