Files
nexus/Project/fonrey/UI_DESIGN/房源详情_UI.html
2026-04-28 16:39:52 +08:00

663 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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' }
},
boxShadow: {
xs: '0 1px 2px rgba(15,23,42,0.04)'
},
fontFamily: {
sans: ['Inter', 'PingFang SC', 'Microsoft YaHei', 'sans-serif']
}
}
}
}
</script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<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);
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
[x-cloak] { display: none !important; }
.tabular-nums { font-variant-numeric: tabular-nums; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: #CBD5E1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94A3B8; }
.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); }
.section-nav-btn { color: #64748B; background: transparent; }
.section-nav-btn.active {
color: #0F766E;
background: #F0FDFA;
font-weight: 600;
}
.subtab-pill {
border: 1px solid #E2E8F0;
background: #FFFFFF;
color: #64748B;
}
.subtab-pill.active {
border-color: #0F766E;
color: #0F766E;
background: #F0FDFA;
font-weight: 600;
}
</style>
</head>
<body class="text-sm antialiased" x-data="propertyDetailPage()" x-init="init()">
<!-- Topbar -->
<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>
<!-- Sidebar -->
<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-[1600px] space-y-4">
<div class="bg-surface border border-surface rounded-lg p-4">
<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>
<div class="flex items-start justify-between gap-4">
<div>
<h1 class="text-xl font-semibold text-surface">出售 · 都市港湾055-0301</h1>
<p class="text-xs text-muted mt-1">嘉定 · 丰庄 · 109.77㎡ · 2室2厅 · 房源编号 01DS016848</p>
</div>
<div class="flex items-center gap-2 flex-wrap justify-end">
<button class="px-3 py-1.5 rounded-md border border-neutral-300 text-neutral-700 hover:bg-neutral-50" @click="openStatusModal = true">改状态</button>
<button class="px-3 py-1.5 rounded-md border border-neutral-300 text-neutral-700 hover:bg-neutral-50">编辑房源</button>
<button class="px-3 py-1.5 rounded-md border border-neutral-300 text-neutral-700 hover:bg-neutral-50">分享</button>
<button class="px-3 py-1.5 rounded-md bg-primary-600 text-white hover:bg-primary-700" @click="openFollowModal = true">写跟进</button>
</div>
</div>
</div>
<!-- 二级导航:锚点 -->
<section class="bg-surface border border-surface rounded-lg px-3 py-2 sticky top-16 z-30 shadow-xs">
<nav class="flex items-center gap-1 overflow-x-auto whitespace-nowrap" aria-label="房源详情分区导航">
<template x-for="item in navItems" :key="item.id">
<a :href="'#' + item.id"
@click.prevent="scrollToSection(item.id)"
:aria-current="activeSection === item.id ? 'true' : 'false'"
:class="activeSection === item.id ? 'section-nav-btn active' : 'section-nav-btn hover:bg-neutral-100 hover:text-neutral-800'"
class="px-3 py-1.5 text-sm rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40 transition-colors"
x-text="item.label"></a>
</template>
</nav>
</section>
<div class="grid grid-cols-12 gap-6 items-start">
<section class="col-span-8 space-y-4">
<!-- 核心信息 -->
<div id="section-core" class="section-anchor scroll-mt-24 space-y-4">
<article class="bg-surface border border-surface rounded-lg p-4">
<div class="grid grid-cols-12 gap-4">
<div class="col-span-4">
<div class="aspect-[4/3] rounded-lg bg-neutral-200 flex items-center justify-center text-neutral-500">房源主图</div>
<p class="text-xs text-muted mt-2">全部 16 张 · 封面 1 张</p>
</div>
<div class="col-span-8 space-y-3">
<div class="flex items-end justify-between">
<div>
<div class="text-3xl font-bold text-danger-600 tabular-nums">650 万</div>
<div class="text-sm text-muted tabular-nums">59215 元/㎡ · 近30天无价格变动</div>
</div>
<button class="text-sm text-primary-600 hover:underline">调价</button>
</div>
<dl class="grid grid-cols-3 gap-3 text-sm">
<div><dt class="text-xs text-muted">户型</dt><dd class="text-surface">2室2厅1卫1厨2阳</dd></div>
<div><dt class="text-xs text-muted">面积</dt><dd class="text-surface tabular-nums">109.77㎡</dd></div>
<div><dt class="text-xs text-muted">楼层</dt><dd class="text-surface">3 / 14</dd></div>
<div><dt class="text-xs text-muted">电梯</dt><dd class="text-surface">有电梯</dd></div>
<div><dt class="text-xs text-muted">装修</dt><dd class="text-surface">精装</dd></div>
<div><dt class="text-xs text-muted">朝向</dt><dd class="text-surface">南北</dd></div>
</dl>
</div>
</div>
</article>
<article class="bg-surface border border-surface rounded-lg p-4">
<div class="flex items-center justify-between mb-3">
<h2 class="text-base font-semibold">基础信息</h2>
<div class="flex items-center gap-3 text-sm">
<a href="#" class="text-primary-600 hover:underline">改等级</a>
<a href="#" class="text-primary-600 hover:underline">改属性</a>
<a href="#" class="text-primary-600 hover:underline">改用途</a>
</div>
</div>
<dl class="grid grid-cols-3 gap-y-3 gap-x-6 text-sm">
<div><dt class="text-xs text-muted">等级</dt><dd>C一般</dd></div>
<div><dt class="text-xs text-muted">属性</dt><dd>公盘</dd></div>
<div><dt class="text-xs text-muted">房屋现状</dt><dd>未知</dd></div>
<div><dt class="text-xs text-muted">看房时间</dt><dd>随时可看</dd></div>
<div><dt class="text-xs text-muted">用途</dt><dd>住宅</dd></div>
<div><dt class="text-xs text-muted">录入时间</dt><dd class="tabular-nums">2015-05-01 19:05:53</dd></div>
</dl>
</article>
</div>
<!-- 跟进日志 -->
<div id="section-follow" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4 space-y-4">
<div class="flex items-center justify-between">
<h2 class="text-base font-semibold">跟进日志</h2>
<button class="px-3 py-1.5 rounded-md bg-primary-600 text-white hover:bg-primary-700" @click="openFollowModal = true">写跟进</button>
</div>
<div class="flex flex-wrap items-center gap-2">
<template x-for="sub in followTabs" :key="sub.key">
<button class="subtab-pill px-3 py-1 rounded-full text-xs" :class="{ 'active': activeFollowTab === sub.key }" @click="activeFollowTab = sub.key" x-text="sub.label"></button>
</template>
</div>
<div class="bg-subtle border border-surface rounded-md p-3 flex flex-wrap items-center gap-3 text-xs">
<label class="flex items-center gap-1.5">开始 <input type="date" class="px-2 py-1 rounded border border-surface bg-surface"></label>
<label class="flex items-center gap-1.5">结束 <input type="date" class="px-2 py-1 rounded border border-surface bg-surface"></label>
<label class="flex items-center gap-1.5"><input type="checkbox" class="rounded border-surface"> 有录音</label>
<label class="flex items-center gap-1.5"><input type="checkbox" class="rounded border-surface"> 有附件</label>
<label class="flex items-center gap-1.5"><input type="checkbox" class="rounded border-surface"> 仅看我的</label>
<input type="text" placeholder="搜索跟进内容" class="px-2 py-1 rounded border border-surface bg-surface min-w-[180px]" />
</div>
<ol class="relative border-l-2 border-surface ml-3 pl-5 space-y-4">
<template x-for="item in filteredTimeline" :key="item.id">
<li class="relative">
<span class="absolute -left-[29px] top-1.5 w-3 h-3 rounded-full" :class="item.dot"></span>
<div class="border border-surface rounded-md p-3 bg-surface space-y-1.5">
<div class="flex items-start justify-between gap-3">
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium" :class="item.tagClass" x-text="item.tag"></span>
<button class="text-xs text-muted hover:text-neutral-700">公开</button>
</div>
<p class="text-sm" x-text="item.content"></p>
<p class="text-xs text-muted" x-text="item.meta"></p>
</div>
</li>
</template>
</ol>
</article>
</div>
<!-- 业务信息 -->
<div id="section-biz" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4">
<h2 class="text-base font-semibold mb-3">业务信息</h2>
<div class="grid grid-cols-3 gap-3">
<div class="border border-surface rounded-md p-3 bg-subtle">
<p class="text-sm font-medium">钥匙管理</p>
<p class="text-xs text-muted mt-1">当前:无钥匙</p>
<div class="mt-3 flex gap-2">
<button class="px-2 py-1 text-xs rounded border border-surface bg-surface">钥匙在他司</button>
<button class="px-2 py-1 text-xs rounded border border-surface bg-surface">新增钥匙</button>
</div>
</div>
<div class="border border-surface rounded-md p-3 bg-subtle">
<p class="text-sm font-medium">委托管理</p>
<p class="text-xs text-muted mt-1">当前:无委托</p>
<div class="mt-3 flex gap-2">
<button class="px-2 py-1 text-xs rounded border border-surface bg-surface">新增委托</button>
<button class="px-2 py-1 text-xs rounded border border-surface bg-surface">全部(0)</button>
</div>
</div>
<div class="border border-surface rounded-md p-3 bg-subtle">
<p class="text-sm font-medium">实勘管理</p>
<p class="text-xs text-muted mt-1">当前:无实勘</p>
<div class="mt-3 flex gap-2">
<button class="px-2 py-1 text-xs rounded border border-surface bg-surface">新增实勘</button>
</div>
</div>
</div>
</article>
</div>
<!-- 房源信息 -->
<div id="section-info" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4">
<h2 class="text-base font-semibold mb-3">房源信息</h2>
<div class="grid grid-cols-2 gap-3">
<div class="border border-surface rounded-md p-3">
<div class="flex justify-between"><p class="font-medium">基本信息</p><a href="#" class="text-primary-600 text-xs">编辑</a></div>
<p class="text-xs text-muted mt-2">购房付款方式、原购价、包税费、来源</p>
</div>
<div class="border border-surface rounded-md p-3">
<div class="flex justify-between"><p class="font-medium">产证信息</p><a href="#" class="text-primary-600 text-xs">编辑</a></div>
<p class="text-xs text-muted mt-2">权属方名称、证件号、房屋坐落</p>
</div>
<div class="border border-surface rounded-md p-3">
<div class="flex justify-between"><p class="font-medium">房屋介绍</p><a href="#" class="text-primary-600 text-xs">编辑</a></div>
<p class="text-xs text-muted mt-2">营销标题、核心卖点、业主心态</p>
</div>
<div class="border border-surface rounded-md p-3">
<div class="flex justify-between"><p class="font-medium">楼盘信息</p><a href="#" class="text-primary-600 text-xs">编辑</a></div>
<p class="text-xs text-muted mt-2">建成年代、物业公司、物业费、绿化率</p>
</div>
</div>
</article>
</div>
<div id="section-marketing" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4">
<h2 class="text-base font-semibold mb-3">营销信息</h2>
<div class="grid grid-cols-3 gap-3">
<div class="rounded-md p-3 bg-info-50 border border-info-600/20">
<p class="font-medium text-info-600">AI 视频</p>
<p class="text-xs text-muted mt-1">生成讲房短视频素材</p>
</div>
<div class="rounded-md p-3 bg-warning-50 border border-warning-600/20">
<p class="font-medium text-warning-600">抖音营销</p>
<p class="text-xs text-muted mt-1">一键生成发布文案</p>
</div>
<div class="rounded-md p-3 bg-success-50 border border-success-600/20">
<p class="font-medium text-success-600">小红书营销</p>
<p class="text-xs text-muted mt-1">多模板图文内容</p>
</div>
</div>
</article>
</div>
<div id="section-staff" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4">
<div class="flex items-center justify-between mb-3">
<h2 class="text-base font-semibold">相关员工</h2>
<a href="#" class="text-sm text-primary-600 hover:underline">编辑</a>
</div>
<div class="grid grid-cols-2 gap-3">
<template x-for="staff in staffList" :key="staff.role">
<div class="border border-surface rounded-md p-3 flex items-center gap-3">
<div class="w-9 h-9 rounded-full bg-primary-100 text-primary-700 flex items-center justify-center font-semibold" x-text="staff.name.slice(0,1)"></div>
<div>
<p class="text-sm font-medium" x-text="staff.name"></p>
<p class="text-xs text-muted" x-text="staff.role + ' · ' + staff.team"></p>
</div>
</div>
</template>
</div>
</article>
</div>
<div id="section-album" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4 space-y-3">
<h2 class="text-base font-semibold">相册信息</h2>
<div class="flex items-center gap-2 flex-wrap text-xs">
<span class="subtab-pill active px-3 py-1 rounded-full">全部(16)</span>
<span class="subtab-pill px-3 py-1 rounded-full">封面(1)</span>
<span class="subtab-pill px-3 py-1 rounded-full">客厅(2)</span>
<span class="subtab-pill px-3 py-1 rounded-full">卧室(4)</span>
<span class="subtab-pill px-3 py-1 rounded-full">厨房(1)</span>
<span class="subtab-pill px-3 py-1 rounded-full">卫生间(1)</span>
</div>
<div class="flex items-center gap-2 text-xs">
<button class="px-2 py-1 rounded border border-surface bg-surface">上传图片</button>
<button class="px-2 py-1 rounded border border-surface bg-surface">批量改类</button>
<button class="px-2 py-1 rounded border border-surface bg-surface">批量删除</button>
<button class="px-2 py-1 rounded border border-surface bg-surface">批量下载</button>
</div>
<div class="grid grid-cols-4 gap-3">
<template x-for="idx in 8" :key="idx">
<div class="aspect-[4/3] rounded-md border border-surface bg-neutral-100 flex items-center justify-center text-xs text-neutral-500">图片{{idx}}</div>
</template>
</div>
</article>
</div>
<div id="section-attachment" class="section-anchor scroll-mt-24">
<article class="bg-surface border border-surface rounded-lg p-4 space-y-3">
<h2 class="text-base font-semibold">附件信息</h2>
<div class="flex items-center gap-2 text-xs flex-wrap">
<span class="subtab-pill active px-3 py-1 rounded-full">全部(0)</span>
<span class="subtab-pill px-3 py-1 rounded-full">身份证(0)</span>
<span class="subtab-pill px-3 py-1 rounded-full">房产证(0)</span>
<span class="subtab-pill px-3 py-1 rounded-full">委托书(0)</span>
<span class="subtab-pill px-3 py-1 rounded-full">其他(0)</span>
</div>
<div class="flex items-center gap-2 text-xs">
<button class="px-2 py-1 rounded border border-surface bg-surface">上传附件</button>
<button class="px-2 py-1 rounded border border-surface bg-surface">批量改类</button>
<button class="px-2 py-1 rounded border border-surface bg-surface">批量下载</button>
</div>
<div class="border border-dashed border-surface rounded-lg p-10 text-center text-sm text-muted">暂无附件</div>
</article>
</div>
</section>
<aside class="col-span-4 space-y-3 sticky top-16 max-h-[calc(100vh-80px)] overflow-y-auto">
<section class="bg-surface border border-surface rounded-lg p-4">
<div class="flex items-center justify-between mb-2">
<h3 class="text-sm font-semibold">业主/联系人</h3>
<button class="text-xs text-primary-600 hover:underline">新增联系人</button>
</div>
<div class="space-y-2">
<div class="rounded-md border border-surface p-2.5 bg-subtle">
<p class="text-sm font-medium">方叔叔 <span class="text-xs text-muted">业主</span></p>
<p class="text-xs text-muted mt-1">电话135****2201</p>
<button class="mt-2 text-xs text-primary-600 hover:underline">查看号码</button>
</div>
<p class="text-xs text-warning-600">TIPS该业主名下还有 2 套房源,建议一起跟进。</p>
</div>
</section>
<section class="bg-surface border border-surface rounded-lg p-4">
<h3 class="text-sm font-semibold mb-3">房源维护完成度</h3>
<div class="flex items-center justify-between mb-2">
<span class="text-xs text-muted">总分</span>
<span class="text-base font-semibold tabular-nums">69%</span>
</div>
<div class="w-full h-2 rounded-full bg-neutral-200 overflow-hidden mb-3">
<div class="h-full bg-primary-600" style="width:69%"></div>
</div>
<div class="space-y-2 text-xs">
<template x-for="item in completeness" :key="item.name">
<div class="flex items-center justify-between">
<span class="text-muted" x-text="item.name"></span>
<span class="tabular-nums" x-text="item.score + '%' "></span>
</div>
</template>
</div>
</section>
<section class="bg-surface border border-surface rounded-lg p-4">
<h3 class="text-sm font-semibold mb-3">房源动态近30天</h3>
<div class="grid grid-cols-2 gap-2 text-xs">
<template x-for="m in metrics" :key="m.name">
<div class="rounded-md border border-surface bg-subtle p-2.5">
<p class="text-muted" x-text="m.name"></p>
<p class="text-base font-semibold mt-1 tabular-nums" x-text="m.value"></p>
</div>
</template>
</div>
</section>
</aside>
</div>
</div>
</main>
<!-- 改状态弹层 -->
<div x-show="openStatusModal" x-cloak class="fixed inset-0 z-40 flex items-center justify-center bg-black/40 p-4">
<div class="w-full max-w-md bg-surface border border-surface rounded-lg shadow-lg">
<div class="flex items-center justify-between px-4 py-3 border-b border-surface">
<h3 class="text-base font-semibold">改状态</h3>
<button @click="closeStatusModal()" class="text-muted hover:text-surface"></button>
</div>
<div class="p-4 space-y-3">
<div>
<label class="text-xs text-muted">新状态 <span class="text-danger-600">*</span></label>
<select x-model="statusForm.nextStatus" class="mt-1 w-full px-3 py-2 rounded-md border" :class="statusErrors.nextStatus ? 'border-danger-600' : 'border-surface'">
<option value="">请选择</option>
<option value="suspended">暂缓</option>
<option value="sold_elsewhere">他售</option>
<option value="sold">成交</option>
</select>
<p class="text-xs text-danger-600 mt-1" x-show="statusErrors.nextStatus">请选择新状态</p>
</div>
<div>
<label class="text-xs text-muted">更改理由 <span class="text-danger-600">*</span></label>
<textarea x-model.trim="statusForm.reason" rows="3" maxlength="50" placeholder="最多50字" class="mt-1 w-full px-3 py-2 rounded-md border" :class="statusErrors.reason ? 'border-danger-600' : 'border-surface'"></textarea>
<p class="text-xs text-danger-600 mt-1" x-show="statusErrors.reason">请填写更改理由</p>
</div>
</div>
<div class="px-4 py-3 border-t border-surface flex justify-end gap-2">
<button class="px-3 py-1.5 rounded-md border border-surface" @click="closeStatusModal()">取消</button>
<button class="px-3 py-1.5 rounded-md bg-primary-600 text-white" @click="submitStatusChange()">确定</button>
</div>
</div>
</div>
<!-- 写跟进弹层 -->
<div x-show="openFollowModal" x-cloak class="fixed inset-0 z-40 flex items-center justify-center bg-black/40 p-4">
<div class="w-full max-w-lg bg-surface border border-surface rounded-lg shadow-lg">
<div class="flex items-center justify-between px-4 py-3 border-b border-surface">
<h3 class="text-base font-semibold">写跟进</h3>
<button @click="closeFollowModal()" class="text-muted hover:text-surface"></button>
</div>
<div class="p-4 space-y-3">
<div>
<label class="text-xs text-muted">跟进目的 <span class="text-danger-600">*</span></label>
<select x-model="followForm.purpose" class="mt-1 w-full px-3 py-2 rounded-md border" :class="followErrors.purpose ? 'border-danger-600' : 'border-surface'">
<option value="">请选择</option>
<option value="manual">写入跟进</option>
<option value="sensitive">敏感信息跟进</option>
<option value="modified">修改跟进</option>
<option value="other">其他跟进</option>
</select>
<p class="text-xs text-danger-600 mt-1" x-show="followErrors.purpose">请选择跟进目的</p>
</div>
<div>
<label class="text-xs text-muted">跟进内容 <span class="text-danger-600">*</span></label>
<textarea x-model.trim="followForm.content" rows="4" maxlength="500" placeholder="最少6字最多500字" class="mt-1 w-full px-3 py-2 rounded-md border" :class="followErrors.content ? 'border-danger-600' : 'border-surface'"></textarea>
<p class="text-xs text-danger-600 mt-1" x-show="followErrors.content">跟进内容至少 6 字</p>
</div>
</div>
<div class="px-4 py-3 border-t border-surface flex justify-end gap-2">
<button class="px-3 py-1.5 rounded-md border border-surface" @click="closeFollowModal()">取消</button>
<button class="px-3 py-1.5 rounded-md bg-primary-600 text-white" @click="submitFollow()">提交</button>
</div>
</div>
</div>
<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" :class="toast.type==='success' ? 'bg-success-600' : 'bg-danger-600'" x-text="toast.message"></div>
<script>
function propertyDetailPage() {
return {
navItems: [
{ id: 'section-core', label: '核心信息' },
{ id: 'section-follow', label: '跟进日志' },
{ id: 'section-biz', label: '业务信息' },
{ id: 'section-info', label: '房源信息' },
{ id: 'section-marketing', label: '营销信息' },
{ id: 'section-staff', label: '相关员工' },
{ id: 'section-album', label: '相册信息' },
{ id: 'section-attachment', label: '附件信息' }
],
activeSection: 'section-core',
observer: null,
followTabs: [
{ key: 'all', label: '全部' },
{ key: 'manual', label: '写入跟进' },
{ key: 'sensitive', label: '敏感信息跟进' },
{ key: 'sensitive_view', label: '敏感信息查看' },
{ key: 'modified', label: '修改跟进' },
{ key: 'other', label: '其他跟进' }
],
activeFollowTab: 'all',
timeline: [
{ id: 1, type: 'manual', tag: '跟进-业主跟进', tagClass: 'bg-info-50 text-info-600', dot: 'bg-info-600', content: '业主可接受 630-640 万区间,周末可安排第二次带看。', meta: '[经纪人] 魏深 · 都市港湾店一组 · 2026-04-28 10:23:12' },
{ id: 2, type: 'modified', tag: '改价格', tagClass: 'bg-warning-50 text-warning-600', dot: 'bg-warning-600', content: '售价660 => 650备注业主接受小幅让价。', meta: '[系统] system · 2026-04-27 18:11:05' },
{ id: 3, type: 'sensitive_view', tag: '查看号码', tagClass: 'bg-danger-50 text-danger-600', dot: 'bg-danger-600', content: '经纪人查看业主隐号信息。', meta: '[经纪人] 魏深 · 都市港湾店一组 · 2026-04-26 13:04:47' },
{ id: 4, type: 'other', tag: '图片下载', tagClass: 'bg-neutral-100 text-neutral-700', dot: 'bg-neutral-400', content: '下载了房源相册8张。', meta: '[经纪人] 魏深 · 都市港湾店一组 · 2026-04-25 21:30:10' }
],
staffList: [
{ role: '首录方', name: '魏深', team: '都市港湾店一组' },
{ role: '号码方', name: '雷威', team: '都市港湾店一组' },
{ role: '出售方', name: '史彬彬', team: '都市港湾店一组' },
{ role: '实买方', name: '待分配', team: '—' }
],
completeness: [
{ name: '重点信息', score: 90 },
{ name: '附件信息', score: 40 },
{ name: '实勘', score: 0 },
{ name: 'VR', score: 0 },
{ name: '钥匙', score: 20 },
{ name: '委托', score: 10 }
],
metrics: [
{ name: '带看次数', value: 8 },
{ name: '复看次数', value: 2 },
{ name: '面访次数', value: 5 },
{ name: '收藏人数', value: 13 },
{ name: '分享次数', value: 21 },
{ name: '空看人次', value: 3 }
],
openStatusModal: false,
statusForm: { nextStatus: '', reason: '' },
statusErrors: {},
openFollowModal: false,
followForm: { purpose: '', content: '' },
followErrors: {},
toast: { show: false, message: '', type: 'success' },
get filteredTimeline() {
if (this.activeFollowTab === 'all') return this.timeline
return this.timeline.filter((x) => x.type === this.activeFollowTab)
},
scrollToSection(id) {
const el = document.getElementById(id)
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
},
init() {
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (this.openStatusModal) this.closeStatusModal()
if (this.openFollowModal) this.closeFollowModal()
}
})
const sections = Array.from(document.querySelectorAll('.section-anchor'))
this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) this.activeSection = entry.target.id
})
}, {
root: null,
rootMargin: '-140px 0px -55% 0px',
threshold: 0.01
})
sections.forEach((s) => this.observer.observe(s))
},
closeStatusModal() {
this.openStatusModal = false
this.statusErrors = {}
},
submitStatusChange() {
this.statusErrors = {}
if (!this.statusForm.nextStatus) this.statusErrors.nextStatus = true
if (!this.statusForm.reason.trim()) this.statusErrors.reason = true
if (Object.keys(this.statusErrors).length) return
this.openStatusModal = false
this.statusForm = { nextStatus: '', reason: '' }
this.notify('状态已更新(原型模拟)', 'success')
},
closeFollowModal() {
this.openFollowModal = false
this.followErrors = {}
},
submitFollow() {
this.followErrors = {}
if (!this.followForm.purpose) this.followErrors.purpose = true
if (!this.followForm.content || this.followForm.content.length < 6) this.followErrors.content = true
if (Object.keys(this.followErrors).length) return
this.openFollowModal = false
this.followForm = { purpose: '', content: '' }
this.notify('跟进已写入(原型模拟)', 'success')
},
notify(message, type = 'success') {
this.toast = { show: true, message, type }
window.setTimeout(() => { this.toast.show = false }, 1800)
}
}
}
</script>
</body>
</html>