Files
LLZQ-XCX/Prototype/pages/board-customer.html

525 lines
23 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=device-width, initial-scale=1.0">
<title>看板-客户 - 球房运营助手</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#0052d9',
'primary-light': '#ecf2fe',
success: '#00a870',
warning: '#ed7b2f',
error: '#e34d59',
'gray-1': '#f3f3f3',
'gray-2': '#eeeeee',
'gray-3': '#e7e7e7',
'gray-4': '#dcdcdc',
'gray-5': '#c5c5c5',
'gray-6': '#a6a6a6',
'gray-7': '#8b8b8b',
'gray-8': '#777777',
'gray-9': '#5e5e5e',
'gray-10': '#4b4b4b',
'gray-11': '#393939',
'gray-12': '#2c2c2c',
'gray-13': '#242424',
},
fontFamily: {
sans: ['Noto Sans SC', 'sans-serif'],
}
}
}
}
</script>
<style>
body {
font-family: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
padding-bottom: 80px;
}
.safe-area-top {
padding-top: env(safe-area-inset-top, 44px);
}
.tab-active {
color: #0052d9;
position: relative;
}
.tab-active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 3px;
background: linear-gradient(90deg, #0052d9, #5b9cf8);
border-radius: 2px;
}
.filter-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.filter-overlay.show {
display: block;
}
.filter-dropdown {
display: none;
position: fixed;
left: 0;
right: 0;
background: white;
border-radius: 0 0 16px 16px;
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
z-index: 1001;
max-height: 60vh;
overflow-y: auto;
}
.filter-dropdown.show {
display: block;
}
/* 二级筛选栏层级与动效 */
#filterBar {
overflow: hidden;
max-height: 120px;
transition: transform 220ms ease, opacity 220ms ease, max-height 220ms ease;
will-change: transform, opacity;
}
/* 仅用于“首次进入页面”的下滑出现(用 transition 触发一次) */
#filterBar.filter-bar-enter {
transform: translateY(-16px);
opacity: 0;
}
#filterBar.filter-bar-hidden {
opacity: 0;
transform: translateY(-110%);
pointer-events: none;
}
@keyframes filterBarDrop {
from { transform: translateY(-16px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
#filterBar { animation: none; transition: none; }
}
.customer-card {
transition: all 0.2s ease;
}
.customer-card:active {
transform: scale(0.98);
}
</style>
</head>
<body class="bg-gray-1 min-h-screen">
<!-- 顶部导航 -->
<div class="safe-area-top bg-white sticky top-0 z-20 shadow-sm">
<div class="h-11 flex items-center justify-center relative border-b border-gray-2">
<h1 class="text-base font-medium text-gray-13">看板</h1>
</div>
<!-- 一级 Tab -->
<div class="flex">
<a href="board-finance.html" class="flex-1 py-3 text-center text-sm font-medium text-gray-7">财务</a>
<a href="board-customer.html" class="flex-1 py-3 text-center text-sm font-medium tab-active">客户</a>
<a href="board-coach.html" class="flex-1 py-3 text-center text-sm font-medium text-gray-7">助教</a>
</div>
</div>
<!-- 筛选区域(二级) -->
<div id="filterBar" class="bg-gray-1 sticky top-[88px] z-10 px-4 py-2 border-b border-gray-2">
<div class="bg-white rounded-2xl p-1.5 flex gap-2 shadow-sm border border-gray-2">
<button onclick="toggleFilter('type')" class="flex-1 px-3 py-2 bg-gray-50 rounded-lg text-sm text-gray-10 flex items-center justify-center gap-1 border border-gray-100">
<span id="typeLabel">最应召回</span>
<svg class="w-4 h-4 text-gray-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="6 9 12 15 18 9"/>
</svg>
</button>
<button onclick="toggleFilter('project')" class="flex-1 px-3 py-2 bg-gray-50 rounded-lg text-sm text-gray-10 flex items-center justify-center gap-1 border border-gray-100">
<span id="projectLabel">不限</span>
<svg class="w-4 h-4 text-gray-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="6 9 12 15 18 9"/>
</svg>
</button>
</div>
</div>
<!-- 遮罩层 -->
<div id="filterOverlay" class="filter-overlay" onclick="closeAllFilters()"></div>
<!-- 类型筛选弹窗 -->
<div id="typeDropdown" class="filter-dropdown">
<div class="p-4 space-y-2">
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最近到店')">最近到店</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最应召回')">最应召回</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最近充值')">最近充值</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最高消费')">最高消费</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最高余额')">最高余额</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最频繁')">最频繁</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('潜力股')">潜力股</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectType('最专一')">最专一</div>
</div>
</div>
<!-- 项目筛选弹窗 -->
<div id="projectDropdown" class="filter-dropdown">
<div class="p-4 space-y-2">
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectProject('不限')">不限</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectProject('中式/追分')">中式/追分</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectProject('斯诺克')">斯诺克</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectProject('麻将')">麻将</div>
<div class="px-4 py-3 text-sm text-gray-10 hover:bg-primary/5 rounded-lg cursor-pointer" onclick="selectProject('团建')">团建</div>
</div>
</div>
<!-- 客户列表 -->
<div class="p-4 space-y-3">
<!-- 客户卡片 1 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">王先生</span>
<span class="px-1.5 py-0.5 bg-error/10 text-error text-xs rounded">VIP</span>
</div>
<p class="text-xs text-gray-6">最近15天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.92</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💖 小燕</span>
<span>💛 泡芙</span>
</div>
<span class="text-gray-6">30天到店5次</span>
</div>
</a>
<!-- 客户卡片 2 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-pink-400 to-rose-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">李女士</span>
<span class="px-1.5 py-0.5 bg-error/10 text-error text-xs rounded">VIP</span>
</div>
<p class="text-xs text-gray-6">最近20天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.88</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💖 Amy</span>
<span>💖 小燕</span>
</div>
<span class="text-gray-6">余额8000元</span>
</div>
</a>
<!-- 客户卡片 3 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-400 to-indigo-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">张先生</span>
</div>
<p class="text-xs text-gray-6">最近10天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.85</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💖 小燕</span>
</div>
<span class="text-gray-6">高频客户</span>
</div>
</a>
<!-- 客户卡片 4 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-green-400 to-emerald-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">刘先生</span>
</div>
<p class="text-xs text-gray-6">最近8天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.78</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💛 泡芙</span>
<span>💛 Amy</span>
</div>
<span class="text-gray-6">偏好斯诺克</span>
</div>
</a>
<!-- 客户卡片 5 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-purple-400 to-violet-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">陈先生</span>
</div>
<p class="text-xs text-gray-6">最近12天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.72</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💛 小燕</span>
</div>
<span class="text-gray-6">潜力客户</span>
</div>
</a>
<!-- 客户卡片 6 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-rose-400 to-pink-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">赵女士</span>
<span class="px-1.5 py-0.5 bg-error/10 text-error text-xs rounded">VIP</span>
</div>
<p class="text-xs text-gray-6">最近5天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.68</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💖 泡芙</span>
<span>💖 小燕</span>
<span>💛 Amy</span>
</div>
<span class="text-gray-6">余额12000元</span>
</div>
</a>
<!-- 客户卡片 7 -->
<a href="customer-detail.html" class="block bg-white rounded-2xl p-4 shadow-sm customer-card">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-400 to-teal-500 flex items-center justify-center">
<span class="text-white font-semibold"></span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-base font-semibold text-gray-13">周先生</span>
</div>
<p class="text-xs text-gray-6">最近7天前</p>
</div>
</div>
<div class="text-right">
<p class="text-sm text-primary font-semibold">0.65</p>
<p class="text-xs text-gray-6">召回因子</p>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-gray-6">
<span>💛 Amy</span>
</div>
<span class="text-gray-6">新客户</span>
</div>
</a>
</div>
<!-- 悬浮助手按钮 -->
<script src="../js/ai-float-btn.js"></script>
<!-- 通用底部导航 -->
<script src="../js/bottom-nav.js"></script>
<script>
let currentFilter = null;
function positionDropdown(dropdownEl) {
const bar = document.getElementById('filterBar');
if (!bar || !dropdownEl) return;
const rect = bar.getBoundingClientRect();
dropdownEl.style.top = `${rect.bottom}px`;
}
function toggleFilter(type) {
const overlay = document.getElementById('filterOverlay');
const typeDropdown = document.getElementById('typeDropdown');
const projectDropdown = document.getElementById('projectDropdown');
if (currentFilter === type) {
closeAllFilters();
return;
}
closeAllFilters();
currentFilter = type;
overlay.classList.add('show');
if (type === 'type') {
positionDropdown(typeDropdown);
typeDropdown.classList.add('show');
} else if (type === 'project') {
positionDropdown(projectDropdown);
projectDropdown.classList.add('show');
}
}
function closeAllFilters() {
currentFilter = null;
document.getElementById('filterOverlay').classList.remove('show');
document.getElementById('typeDropdown').classList.remove('show');
document.getElementById('projectDropdown').classList.remove('show');
}
function selectType(value) {
document.getElementById('typeLabel').textContent = value;
closeAllFilters();
}
function selectProject(value) {
document.getElementById('projectLabel').textContent = value;
closeAllFilters();
}
// 滚动时:下滑隐藏筛选栏,上滑显示(单次滚动手势结束后判断一次,避免抖动)
(function initFilterBarScrollBehavior() {
const bar = document.getElementById('filterBar');
if (!bar) return;
const TRIGGER_DISTANCE_DOWN = 24;
const TRIGGER_DISTANCE_UP = 12;
const JITTER_THRESHOLD = 2;
let lastY = window.scrollY || 0;
let lastDir = null; // 'down' | 'up'
let ticking = false;
let acc = 0;
let lockedDir = null; // 方向锁:同方向只触发一次,直到方向反转
// 首次进入:用 transition 做一次从上到下出现
bar.classList.add('filter-bar-enter');
window.requestAnimationFrame(() => {
bar.classList.remove('filter-bar-enter');
});
function setVisible(visible) {
if (visible) {
bar.classList.remove('filter-bar-hidden');
} else {
if (!bar.classList.contains('filter-bar-hidden')) closeAllFilters();
bar.classList.add('filter-bar-hidden');
}
}
function resetGesture(y) {
lastY = y;
lastDir = null;
acc = 0;
lockedDir = null;
}
function onScrollFrame() {
const y = window.scrollY || 0;
if (y <= 8) {
setVisible(true);
resetGesture(y);
return;
}
const delta = y - lastY;
if (Math.abs(delta) <= JITTER_THRESHOLD) {
lastY = y;
return;
}
const dir = delta > 0 ? 'down' : 'up';
if (lastDir === null) {
lastDir = dir;
} else if (dir !== lastDir) {
lastDir = dir;
lockedDir = null;
acc = 0;
}
lastY = y;
// 同方向只触发一次:锁住直到方向反转
if (lockedDir === dir) return;
acc += Math.abs(delta);
const threshold = dir === 'up' ? TRIGGER_DISTANCE_UP : TRIGGER_DISTANCE_DOWN;
if (acc < threshold) return;
setVisible(dir === 'up');
lockedDir = dir;
acc = 0;
}
window.addEventListener('scroll', () => {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(() => {
onScrollFrame();
ticking = false;
});
}, { passive: true });
})();
</script>
</body>
</html>