663 lines
36 KiB
HTML
663 lines
36 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' }
|
||
},
|
||
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>
|