refactor(views): 将主页面组件拆分为独立的子组件
将原 index.vue 中的大型单文件组件按功能模块拆分为多个独立的子组件: - 提取 GlobalHeader 为独立组件 - 提取 SidebarNav 为独立组件 - 提取 DashboardView 为独立组件 - 提取 StrategyView 为独立组件 - 提取 MonitorView 为独立组件 通过组件化提高代码的可维护性和复用性,使主文件结构更清晰
This commit is contained in:
172
247_Contry/src/views/index/components/DashboardView.vue
Normal file
172
247_Contry/src/views/index/components/DashboardView.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="h-full flex flex-col gap-6 overflow-y-auto pr-2">
|
||||
<div class="grid grid-cols-4 gap-6">
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-currency-yen text-6xl text-primary"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">本周 GMV 预估</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">¥245,600</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-emerald-400 bg-emerald-400/10 px-1.5 py-0.5 rounded flex items-center gap-1"><i class="ph-bold ph-trend-up"></i> 1.2%</span>
|
||||
<span class="text-slate-500">转化率 (目标 1%)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-users-three text-6xl text-secondary"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">当前并发接待</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">842 <span class="text-sm font-normal text-slate-500">人</span></div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-secondary bg-secondary/10 px-1.5 py-0.5 rounded">AI 负载: 42%</span>
|
||||
<span class="text-slate-500">系统健康</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-robot text-6xl text-accent"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">节省人力折算</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">34.5 <span class="text-sm font-normal text-slate-500">人/天</span></div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-accent bg-accent/10 px-1.5 py-0.5 rounded">≈ 4个销售组</span>
|
||||
<span class="text-slate-500">效能提升</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-warning-octagon text-6xl text-error"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">人工干预率</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">4.8%</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-error bg-error/10 px-1.5 py-0.5 rounded">异议/退费</span>
|
||||
<span class="text-slate-500">主要原因</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 h-[420px]">
|
||||
<div class="lg:col-span-1 glass-panel rounded-2xl p-6 flex flex-col">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="font-bold text-white flex items-center gap-2"><i class="ph-fill ph-funnel text-primary"></i> 销售漏斗监控</h3>
|
||||
<button class="btn btn-xs btn-ghost text-slate-400"><i class="ph-bold ph-dots-three"></i></button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col justify-center gap-4">
|
||||
<div v-for="(item, index) in funnelData" :key="index" class="relative group cursor-default">
|
||||
<div class="flex justify-between text-xs mb-1.5">
|
||||
<span class="text-slate-300 font-medium">{{ item.stage }}</span>
|
||||
<span class="text-slate-400 font-mono">{{ item.count }} <span class="opacity-50">/</span> <span :class="item.rate < 10 ? 'text-primary' : 'text-slate-400'">{{ item.rate }}%</span></span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-800/50 rounded-full h-2.5 overflow-hidden border border-slate-700/50">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-1000 relative overflow-hidden"
|
||||
:class="index > 4 ? 'bg-gradient-to-r from-primary to-accent' : 'bg-slate-600'"
|
||||
:style="{ width: item.rate + '%' }"
|
||||
>
|
||||
<div class="absolute inset-0 bg-white/20 w-full h-full animate-[shimmer_2s_infinite] translate-x-[-100%]" v-if="index > 4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-2 glass-panel rounded-2xl p-6 flex flex-col relative overflow-hidden">
|
||||
<div class="flex justify-between items-start mb-4 z-10">
|
||||
<div>
|
||||
<h3 class="font-bold text-white flex items-center gap-2">
|
||||
<i class="ph-fill ph-waves text-blue-400"></i> 流量潮汐实时监控
|
||||
</h3>
|
||||
<p class="text-xs text-slate-500 mt-1">洋葱模型预测:当前处于爬坡期,预计 20:00 达到峰值</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="badge badge-primary badge-outline text-xs gap-1"><i class="ph-bold ph-lightning"></i> AI 响应: 1.2s</div>
|
||||
<div class="badge badge-secondary badge-outline text-xs gap-1"><i class="ph-bold ph-coin"></i> Token: $14.2/h</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex items-end justify-between gap-1 z-10 px-1 pt-4 border-b border-slate-700/50">
|
||||
<div v-for="(h, i) in trafficData" :key="i"
|
||||
class="w-full bg-blue-500/20 rounded-t-sm hover:bg-blue-500/60 transition-all duration-300 relative group"
|
||||
:style="{ height: h + '%' }">
|
||||
<div class="absolute -top-10 left-1/2 -translate-x-1/2 bg-slate-800 text-white text-[10px] px-2 py-1 rounded border border-slate-600 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-20 shadow-xl">
|
||||
QPS: {{ Math.round(h * 3.5) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between text-[10px] text-slate-600 font-mono mt-2 z-10">
|
||||
<span>14:00</span>
|
||||
<span>14:15</span>
|
||||
<span>14:30</span>
|
||||
<span>14:45</span>
|
||||
<span>15:00</span>
|
||||
</div>
|
||||
|
||||
<div class="absolute inset-0 z-0 bg-gradient-to-t from-blue-500/5 to-transparent pointer-events-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-6 flex-1 min-h-[300px]">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="font-bold text-white flex items-center gap-2"><i class="ph-fill ph-list-dashes text-slate-400"></i> 系统事件日志</h3>
|
||||
<div class="flex gap-2">
|
||||
<span class="loading loading-dots loading-xs text-slate-500"></span>
|
||||
<span class="text-xs text-slate-500 font-mono">LIVE</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-xs table-pin-rows">
|
||||
<thead>
|
||||
<tr class="text-slate-500 border-b border-slate-700/50">
|
||||
<th>TIMESTAMP</th>
|
||||
<th>EVENT TYPE</th>
|
||||
<th>CUSTOMER</th>
|
||||
<th>AI DECISION & ACTION</th>
|
||||
<th>STATUS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="font-mono">
|
||||
<tr v-for="log in eventLogs" :key="log.id" class="hover:bg-slate-800/30 transition-colors border-slate-800/30 group">
|
||||
<td class="text-slate-500">{{ log.time }}</td>
|
||||
<td>
|
||||
<span class="badge badge-ghost badge-xs gap-1 border-slate-700 text-slate-300">
|
||||
{{ log.type }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="font-medium text-slate-300">{{ log.user }}</td>
|
||||
<td class="max-w-md truncate text-slate-400 group-hover:text-slate-200 transition-colors">
|
||||
<span class="text-primary mr-2">[{{log.model}}]</span>
|
||||
{{ log.action }}
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="log.status === 'success'" class="text-emerald-400 flex items-center gap-1"><i class="ph-fill ph-check-circle"></i> OK</span>
|
||||
<span v-else-if="log.status === 'warning'" class="text-orange-400 flex items-center gap-1"><i class="ph-fill ph-warning"></i> MANUAL</span>
|
||||
<span v-else class="text-blue-400 flex items-center gap-1"><i class="ph-bold ph-spinner animate-spin"></i> RUNNING</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type FunnelItem = {
|
||||
stage: string;
|
||||
count: number;
|
||||
rate: number;
|
||||
};
|
||||
|
||||
type EventLog = {
|
||||
id: number;
|
||||
time: string;
|
||||
type: string;
|
||||
user: string;
|
||||
model: string;
|
||||
action: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
defineProps<{
|
||||
funnelData: FunnelItem[];
|
||||
trafficData: number[];
|
||||
eventLogs: EventLog[];
|
||||
}>();
|
||||
</script>
|
||||
40
247_Contry/src/views/index/components/GlobalHeader.vue
Normal file
40
247_Contry/src/views/index/components/GlobalHeader.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<header class="h-16 glass-panel border-b-0 flex items-center justify-between px-6 z-50">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center shadow-lg shadow-primary/20">
|
||||
<i class="ph-bold ph-brain text-white text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="font-bold text-lg tracking-wide leading-tight text-white">247<span class="text-primary">控制中台</span></h1>
|
||||
<div class="text-[10px] text-slate-400 font-mono tracking-wider">SALES PILOT V1.0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="flex items-center gap-3 px-4 py-1.5 rounded-full bg-slate-800/80 border border-slate-700/50 backdrop-blur-sm">
|
||||
<div class="flex flex-col items-end leading-none">
|
||||
<span class="text-[10px] text-slate-400">TRAFFIC TIDE</span>
|
||||
<span class="text-xs font-bold text-green-400">LOAD: LOW</span>
|
||||
</div>
|
||||
<span class="relative flex h-3 w-3">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="divider divider-horizontal mx-0 h-8"></div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="text-right hidden sm:block">
|
||||
<div class="text-xs font-bold text-slate-200">Admin User</div>
|
||||
<div class="text-[10px] text-slate-500">Super Admin</div>
|
||||
</div>
|
||||
<div class="avatar placeholder cursor-pointer">
|
||||
<div class="bg-gradient-to-tr from-slate-700 to-slate-600 text-white rounded-full w-9 ring ring-primary ring-offset-base-100 ring-offset-2">
|
||||
<span class="text-xs">PM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
140
247_Contry/src/views/index/components/MonitorView.vue
Normal file
140
247_Contry/src/views/index/components/MonitorView.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="h-full flex gap-6">
|
||||
<div class="w-80 glass-panel rounded-2xl flex flex-col overflow-hidden">
|
||||
<div class="p-4 border-b border-slate-700/50 bg-slate-900/30">
|
||||
<h3 class="font-bold text-white text-sm mb-3">实时活跃会话</h3>
|
||||
<div class="relative">
|
||||
<i class="ph ph-magnifying-glass absolute left-3 top-2.5 text-slate-500"></i>
|
||||
<input class="input input-sm w-full bg-slate-950 border-slate-700 pl-9 text-xs focus:border-primary" placeholder="搜索家长姓名/ID..." />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto p-2 space-y-2">
|
||||
<div v-for="user in activeUsers" :key="user.id"
|
||||
class="p-3 rounded-xl cursor-pointer transition-all border border-transparent hover:border-slate-700"
|
||||
:class="user.active ? 'bg-primary/10 border-primary/30' : 'bg-slate-800/30 hover:bg-slate-800/80'"
|
||||
>
|
||||
<div class="flex justify-between items-start mb-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full" :class="user.risk ? 'bg-red-500 animate-pulse' : 'bg-green-500'"></div>
|
||||
<span class="font-bold text-sm text-slate-200">{{ user.name }}</span>
|
||||
</div>
|
||||
<span class="text-[10px] text-slate-500">{{ user.time }}</span>
|
||||
</div>
|
||||
<div class="text-xs text-slate-400 truncate mb-2">{{ user.lastMsg }}</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<div class="badge badge-xs border-0 text-white" :class="getStageBadge(user.stage)">{{ user.stage }}</div>
|
||||
<div v-if="user.risk" class="badge badge-xs badge-error gap-1"><i class="ph-bold ph-warning"></i> 需人工</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col glass-panel rounded-2xl overflow-hidden relative">
|
||||
<div class="h-16 border-b border-slate-700/50 flex justify-between items-center px-6 bg-slate-900/50 backdrop-blur-md z-10">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold text-lg text-white">王梓涵妈妈</span>
|
||||
<div class="badge badge-primary badge-outline badge-xs">ID: 883921</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mt-0.5">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
||||
</span>
|
||||
<span class="text-xs text-slate-400">AI 托管中 | 策略模型: Claude-3 Opus</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-error btn-xs" @click="emit('toggleManual')">
|
||||
<i class="ph-bold ph-hand-palm"></i> 强制接管
|
||||
</button>
|
||||
<button class="btn btn-neutral btn-xs border-slate-600">
|
||||
<i class="ph ph-file-text"></i> 画像
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-4 sm:p-6 overflow-y-auto space-y-8 bg-slate-950/50" ref="chatBox">
|
||||
<div v-for="msg in chatHistory" :key="msg.id" class="flex gap-4 group" :class="msg.role === 'user' ? 'flex-row-reverse' : ''">
|
||||
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<div class="w-9 h-9 rounded-full border border-slate-700/50 p-1 flex items-center justify-center shadow-lg transition-transform group-hover:scale-105"
|
||||
:class="msg.role === 'ai' ? 'bg-indigo-600' : 'bg-slate-800'">
|
||||
<i v-if="msg.role === 'ai'" class="ph-bold ph-robot text-lg text-white"></i>
|
||||
<i v-else class="ph-bold ph-user text-lg text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col max-w-[85%] sm:max-w-[75%]" :class="msg.role === 'user' ? 'items-end' : 'items-start'">
|
||||
|
||||
<div class="flex items-center gap-2 mb-1.5 text-[10px] text-slate-500">
|
||||
<span class="font-bold text-slate-400">{{ msg.role === 'ai' ? 'Nexus AI Agent' : '家长' }}</span>
|
||||
<time class="opacity-50 font-mono">{{ msg.time }}</time>
|
||||
</div>
|
||||
|
||||
<div v-if="msg.role === 'ai' && msg.thought" class="mb-3 w-full animate-in fade-in slide-in-from-top-2 duration-500">
|
||||
<div class="p-3 bg-yellow-500/5 border border-yellow-700/20 rounded-lg text-xs text-yellow-500/80 font-mono shadow-sm relative overflow-hidden">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-0.5 bg-yellow-600/40"></div>
|
||||
<div class="flex items-center gap-1.5 mb-1.5 opacity-90 font-bold uppercase tracking-wider text-[9px]">
|
||||
<i class="ph-bold ph-cpu"></i> 决策链路: {{ msg.stage }}
|
||||
</div>
|
||||
<div class="leading-relaxed opacity-80 break-words whitespace-pre-wrap">> {{ msg.thought }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3.5 rounded-2xl shadow-xl text-sm leading-relaxed break-words relative group-hover:shadow-2xl transition-shadow"
|
||||
:class="msg.role === 'ai'
|
||||
? 'bg-indigo-600 text-white rounded-tl-sm'
|
||||
: 'bg-slate-800 text-slate-200 rounded-tr-sm'">
|
||||
{{ msg.content }}
|
||||
</div>
|
||||
|
||||
<div v-if="msg.role === 'ai'" class="mt-1.5 flex items-center gap-3 text-[9px] text-slate-600 font-mono opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<span class="flex items-center gap-1"><i class="ph-bold ph-lightning"></i> Claude-3 Opus</span>
|
||||
<span class="flex items-center gap-1"><i class="ph-bold ph-clock"></i> 420ms</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-slate-900 border-t border-slate-700/50">
|
||||
<div class="relative">
|
||||
<input type="text" placeholder="输入内容以进行人工干预 (AI 将暂停响应)..." class="input input-bordered w-full bg-slate-950 border-slate-700 focus:border-primary pr-12 text-sm" />
|
||||
<button class="absolute right-1 top-1 btn btn-sm btn-primary rounded-lg aspect-square p-0"><i class="ph-bold ph-paper-plane-right text-lg"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type ActiveUser = {
|
||||
id: number;
|
||||
name: string;
|
||||
time: string;
|
||||
lastMsg: string;
|
||||
stage: string;
|
||||
risk: boolean;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
type ChatMessage = {
|
||||
id: number;
|
||||
role: 'ai' | 'user';
|
||||
content: string;
|
||||
time: string;
|
||||
stage: string;
|
||||
thought: string;
|
||||
};
|
||||
|
||||
defineProps<{
|
||||
activeUsers: ActiveUser[];
|
||||
chatHistory: ChatMessage[];
|
||||
getStageBadge: (stage: string) => string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggleManual'): void;
|
||||
}>();
|
||||
</script>
|
||||
45
247_Contry/src/views/index/components/SidebarNav.vue
Normal file
45
247_Contry/src/views/index/components/SidebarNav.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<aside class="w-64 glass-panel border-t-0 border-l-0 border-b-0 flex flex-col justify-between py-6 z-40">
|
||||
<ul class="menu w-full px-3 gap-2">
|
||||
<li v-for="item in navItems" :key="item.id">
|
||||
<a
|
||||
@click="emit('select', item.id)"
|
||||
:class="{'active bg-primary text-white shadow-lg shadow-primary/30': currentView === item.id, 'text-slate-400 hover:text-slate-100 hover:bg-slate-800': currentView !== item.id}"
|
||||
class="rounded-lg font-medium transition-all py-3"
|
||||
>
|
||||
<i :class="['ph text-xl', item.icon, currentView === item.id ? 'ph-fill' : '']"></i>
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="px-4 mb-4">
|
||||
<div class="p-4 rounded-xl bg-gradient-to-br from-red-900/40 to-slate-900 border border-red-500/20 relative overflow-hidden group cursor-pointer hover:border-red-500/40 transition-all">
|
||||
<div class="absolute -right-2 -top-2 w-16 h-16 bg-red-500/10 rounded-full blur-xl group-hover:bg-red-500/20 transition-all"></div>
|
||||
<div class="flex items-center gap-2 text-red-400 mb-1">
|
||||
<i class="ph-fill ph-warning-circle animate-pulse"></i>
|
||||
<span class="text-xs font-bold uppercase tracking-wider">Intervention</span>
|
||||
</div>
|
||||
<div class="text-2xl font-bold text-white mb-1">3 <span class="text-sm font-normal text-slate-400">Waitings</span></div>
|
||||
<div class="text-[10px] text-slate-500">人工接管队列 (异议/退费)</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type NavItem = {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
};
|
||||
|
||||
defineProps<{
|
||||
navItems: NavItem[];
|
||||
currentView: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', id: string): void;
|
||||
}>();
|
||||
</script>
|
||||
128
247_Contry/src/views/index/components/StrategyView.vue
Normal file
128
247_Contry/src/views/index/components/StrategyView.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="h-full flex flex-col gap-6">
|
||||
<div class="flex justify-between items-end pb-2 border-b border-slate-800">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-white mb-1 flex items-center gap-2">
|
||||
SOP 智能编排中心
|
||||
<div class="badge badge-primary badge-outline text-xs">v2.4.0</div>
|
||||
</h2>
|
||||
<p class="text-slate-400 text-xs">配置全链路各阶段的模型参数、提示词及上下文逻辑。</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button class="btn btn-neutral btn-sm border-slate-700 text-slate-300 hover:text-white"><i class="ph ph-clock-counter-clockwise"></i> 历史版本</button>
|
||||
<button class="btn btn-primary btn-sm shadow-lg shadow-primary/20" @click="emit('saveConfig', $event)"><i class="ph ph-floppy-disk"></i> 发布策略配置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 gap-6 overflow-hidden">
|
||||
<div class="w-1/4 glass-panel rounded-2xl overflow-y-auto p-2 no-scrollbar">
|
||||
<div class="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-widest">Sales Pipeline</div>
|
||||
<ul class="steps steps-vertical w-full gap-2">
|
||||
<li
|
||||
v-for="(stage, index) in salesStages"
|
||||
:key="stage.id"
|
||||
class="step cursor-pointer transition-all duration-300"
|
||||
:class="currentStageId === stage.id ? 'step-primary' : 'step-neutral'"
|
||||
@click="emit('update:currentStageId', stage.id)"
|
||||
:data-content="index + 1"
|
||||
>
|
||||
<div class="w-full text-left ml-3 p-3 rounded-xl border transition-all duration-200 group"
|
||||
:class="currentStageId === stage.id ? 'bg-primary/10 border-primary shadow-[0_0_15px_rgba(99,102,241,0.2)]' : 'bg-slate-900/50 border-slate-700/50 hover:bg-slate-800 hover:border-slate-600'">
|
||||
<div class="font-bold text-sm text-slate-200 group-hover:text-white">{{ stage.name }}</div>
|
||||
<div class="text-[10px] text-slate-500 mt-1 line-clamp-1 group-hover:text-slate-400">{{ stage.desc }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 glass-panel rounded-2xl p-6 flex flex-col gap-6 overflow-y-auto pr-4">
|
||||
<div class="grid grid-cols-2 gap-6 p-4 bg-slate-900/50 rounded-xl border border-slate-800">
|
||||
<div class="form-control w-full">
|
||||
<label class="label pt-0"><span class="label-text font-bold text-slate-300 text-xs uppercase">Execution Model</span></label>
|
||||
<select class="select select-bordered select-sm w-full bg-slate-950 border-slate-700 text-slate-200" v-model="currentConfig.model">
|
||||
<option value="gpt-4-turbo">GPT-4 Turbo (高精度/逻辑强)</option>
|
||||
<option value="deepseek-chat">DeepSeek V3 (高性价比/中文优)</option>
|
||||
<option value="claude-3-opus">Claude 3 Opus (情感/共情强)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control w-full">
|
||||
<label class="label pt-0">
|
||||
<span class="label-text font-bold text-slate-300 text-xs uppercase">Temperature: {{ currentConfig.temp }}</span>
|
||||
<span class="label-text-alt text-[10px] text-slate-500">{{ currentConfig.temp > 0.7 ? '感性/发散' : '理性/收敛' }}</span>
|
||||
</label>
|
||||
<input type="range" min="0" max="1" step="0.1" v-model="currentConfig.temp" class="range range-xs range-primary" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col min-h-[400px]">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label class="label p-0"><span class="label-text font-bold text-slate-300 text-xs uppercase flex items-center gap-2"><i class="ph-fill ph-code"></i> System Prompt</span></label>
|
||||
<div class="flex gap-2">
|
||||
<div class="badge badge-outline badge-accent text-[10px] h-5">Jinja2 Supported</div>
|
||||
<div class="badge badge-outline text-slate-500 text-[10px] h-5">{{ currentConfig.prompt.length }} chars</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative flex-1 rounded-xl overflow-hidden border border-slate-700/50 bg-[#0d1117] group focus-within:border-primary/50 transition-colors">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-8 bg-slate-900/50 border-r border-slate-800 flex flex-col items-end pt-4 pr-2 text-slate-600 font-mono text-xs select-none leading-relaxed">
|
||||
<span v-for="n in 20" :key="n">{{n}}</span>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="currentConfig.prompt"
|
||||
class="absolute inset-0 w-full h-full bg-transparent text-emerald-400 p-4 pl-10 font-mono text-sm resize-none focus:outline-none leading-relaxed selection:bg-emerald-900/50"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapse collapse-arrow bg-slate-900/50 border border-slate-800 rounded-xl">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-sm font-bold text-slate-300 flex items-center gap-2">
|
||||
<i class="ph-fill ph-wrench text-accent"></i> Function Calling (Tools)
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="flex flex-wrap gap-4 pt-2">
|
||||
<label class="cursor-pointer flex items-center gap-2 p-2 rounded hover:bg-slate-800 border border-slate-700/50 hover:border-primary/50 transition-all">
|
||||
<input type="checkbox" class="checkbox checkbox-primary checkbox-xs" checked />
|
||||
<span class="label-text text-xs text-slate-300">CRM档案查询</span>
|
||||
</label>
|
||||
<label class="cursor-pointer flex items-center gap-2 p-2 rounded hover:bg-slate-800 border border-slate-700/50 hover:border-primary/50 transition-all">
|
||||
<input type="checkbox" class="checkbox checkbox-primary checkbox-xs" />
|
||||
<span class="label-text text-xs text-slate-300">生成个性化报告</span>
|
||||
</label>
|
||||
<label class="cursor-pointer flex items-center gap-2 p-2 rounded hover:bg-slate-800 border border-slate-700/50 hover:border-primary/50 transition-all">
|
||||
<input type="checkbox" class="checkbox checkbox-primary checkbox-xs" checked />
|
||||
<span class="label-text text-xs text-slate-300">知识库检索 (RAG)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type SalesStage = {
|
||||
id: string;
|
||||
name: string;
|
||||
desc: string;
|
||||
};
|
||||
|
||||
type Config = {
|
||||
model: string;
|
||||
temp: number;
|
||||
prompt: string;
|
||||
};
|
||||
|
||||
defineProps<{
|
||||
salesStages: SalesStage[];
|
||||
currentStageId: string;
|
||||
currentConfig: Config;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:currentStageId', id: string): void;
|
||||
(e: 'saveConfig', event: Event): void;
|
||||
}>();
|
||||
</script>
|
||||
@@ -1,469 +1,15 @@
|
||||
<template>
|
||||
<div class="h-screen flex flex-col grid-bg text-slate-100 bg-slate-900 font-sans overflow-hidden">
|
||||
<!-- 1. 顶部导航 (Global Header) -->
|
||||
<header class="h-16 glass-panel border-b-0 flex items-center justify-between px-6 z-50">
|
||||
<!-- Logo -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center shadow-lg shadow-primary/20">
|
||||
<i class="ph-bold ph-brain text-white text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="font-bold text-lg tracking-wide leading-tight text-white">247<span class="text-primary">控制中台</span></h1>
|
||||
<div class="text-[10px] text-slate-400 font-mono tracking-wider">SALES PILOT V1.0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 全局状态栏 -->
|
||||
<div class="flex items-center gap-6">
|
||||
<!-- 流量潮汐指示器 (核心业务点) -->
|
||||
<div class="flex items-center gap-3 px-4 py-1.5 rounded-full bg-slate-800/80 border border-slate-700/50 backdrop-blur-sm">
|
||||
<div class="flex flex-col items-end leading-none">
|
||||
<span class="text-[10px] text-slate-400">TRAFFIC TIDE</span>
|
||||
<span class="text-xs font-bold text-green-400">LOAD: LOW</span>
|
||||
</div>
|
||||
<span class="relative flex h-3 w-3">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="divider divider-horizontal mx-0 h-8"></div>
|
||||
|
||||
<!-- 用户头像 -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="text-right hidden sm:block">
|
||||
<div class="text-xs font-bold text-slate-200">Admin User</div>
|
||||
<div class="text-[10px] text-slate-500">Super Admin</div>
|
||||
</div>
|
||||
<div class="avatar placeholder cursor-pointer">
|
||||
<div class="bg-gradient-to-tr from-slate-700 to-slate-600 text-white rounded-full w-9 ring ring-primary ring-offset-base-100 ring-offset-2">
|
||||
<span class="text-xs">PM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<GlobalHeader />
|
||||
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<!-- 2. 侧边栏导航 (Sidebar) -->
|
||||
<aside class="w-64 glass-panel border-t-0 border-l-0 border-b-0 flex flex-col justify-between py-6 z-40">
|
||||
<ul class="menu w-full px-3 gap-2">
|
||||
<li v-for="item in navItems" :key="item.id">
|
||||
<a
|
||||
@click="currentView = item.id"
|
||||
:class="{'active bg-primary text-white shadow-lg shadow-primary/30': currentView === item.id, 'text-slate-400 hover:text-slate-100 hover:bg-slate-800': currentView !== item.id}"
|
||||
class="rounded-lg font-medium transition-all py-3"
|
||||
>
|
||||
<i :class="['ph text-xl', item.icon, currentView === item.id ? 'ph-fill' : '']"></i>
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 底部警告卡片 -->
|
||||
<div class="px-4 mb-4">
|
||||
<div class="p-4 rounded-xl bg-gradient-to-br from-red-900/40 to-slate-900 border border-red-500/20 relative overflow-hidden group cursor-pointer hover:border-red-500/40 transition-all">
|
||||
<div class="absolute -right-2 -top-2 w-16 h-16 bg-red-500/10 rounded-full blur-xl group-hover:bg-red-500/20 transition-all"></div>
|
||||
<div class="flex items-center gap-2 text-red-400 mb-1">
|
||||
<i class="ph-fill ph-warning-circle animate-pulse"></i>
|
||||
<span class="text-xs font-bold uppercase tracking-wider">Intervention</span>
|
||||
</div>
|
||||
<div class="text-2xl font-bold text-white mb-1">3 <span class="text-sm font-normal text-slate-400">Waitings</span></div>
|
||||
<div class="text-[10px] text-slate-500">人工接管队列 (异议/退费)</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<SidebarNav :nav-items="navItems" :current-view="currentView" @select="currentView = $event" />
|
||||
|
||||
<!-- 3. 主内容区域 (Main Content) -->
|
||||
<main class="flex-1 overflow-hidden relative bg-slate-950/50 p-6">
|
||||
<transition name="fade" mode="out-in">
|
||||
|
||||
<!-- View A: 全景看板 (Dashboard) -->
|
||||
<div v-if="currentView === 'dashboard'" class="h-full flex flex-col gap-6 overflow-y-auto pr-2">
|
||||
<!-- 核心指标卡片 -->
|
||||
<div class="grid grid-cols-4 gap-6">
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-currency-yen text-6xl text-primary"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">本周 GMV 预估</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">¥245,600</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-emerald-400 bg-emerald-400/10 px-1.5 py-0.5 rounded flex items-center gap-1"><i class="ph-bold ph-trend-up"></i> 1.2%</span>
|
||||
<span class="text-slate-500">转化率 (目标 1%)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-users-three text-6xl text-secondary"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">当前并发接待</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">842 <span class="text-sm font-normal text-slate-500">人</span></div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-secondary bg-secondary/10 px-1.5 py-0.5 rounded">AI 负载: 42%</span>
|
||||
<span class="text-slate-500">系统健康</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-robot text-6xl text-accent"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">节省人力折算</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">34.5 <span class="text-sm font-normal text-slate-500">人/天</span></div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-accent bg-accent/10 px-1.5 py-0.5 rounded">≈ 4个销售组</span>
|
||||
<span class="text-slate-500">效能提升</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel rounded-2xl p-5 relative overflow-hidden group">
|
||||
<div class="absolute right-0 top-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><i class="ph-fill ph-warning-octagon text-6xl text-error"></i></div>
|
||||
<div class="text-slate-400 text-xs font-bold tracking-wider uppercase mb-1">人工干预率</div>
|
||||
<div class="text-3xl font-bold text-white mb-2">4.8%</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-error bg-error/10 px-1.5 py-0.5 rounded">异议/退费</span>
|
||||
<span class="text-slate-500">主要原因</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 漏斗与流量图表 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 h-[420px]">
|
||||
<!-- 左侧:销售漏斗 -->
|
||||
<div class="lg:col-span-1 glass-panel rounded-2xl p-6 flex flex-col">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="font-bold text-white flex items-center gap-2"><i class="ph-fill ph-funnel text-primary"></i> 销售漏斗监控</h3>
|
||||
<button class="btn btn-xs btn-ghost text-slate-400"><i class="ph-bold ph-dots-three"></i></button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col justify-center gap-4">
|
||||
<div v-for="(item, index) in funnelData" :key="index" class="relative group cursor-default">
|
||||
<div class="flex justify-between text-xs mb-1.5">
|
||||
<span class="text-slate-300 font-medium">{{ item.stage }}</span>
|
||||
<span class="text-slate-400 font-mono">{{ item.count }} <span class="opacity-50">/</span> <span :class="item.rate < 10 ? 'text-primary' : 'text-slate-400'">{{ item.rate }}%</span></span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-800/50 rounded-full h-2.5 overflow-hidden border border-slate-700/50">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-1000 relative overflow-hidden"
|
||||
:class="index > 4 ? 'bg-gradient-to-r from-primary to-accent' : 'bg-slate-600'"
|
||||
:style="{ width: item.rate + '%' }"
|
||||
>
|
||||
<div class="absolute inset-0 bg-white/20 w-full h-full animate-[shimmer_2s_infinite] translate-x-[-100%]" v-if="index > 4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:流量潮汐监控 (CSS Chart) -->
|
||||
<div class="lg:col-span-2 glass-panel rounded-2xl p-6 flex flex-col relative overflow-hidden">
|
||||
<div class="flex justify-between items-start mb-4 z-10">
|
||||
<div>
|
||||
<h3 class="font-bold text-white flex items-center gap-2">
|
||||
<i class="ph-fill ph-waves text-blue-400"></i> 流量潮汐实时监控
|
||||
</h3>
|
||||
<p class="text-xs text-slate-500 mt-1">洋葱模型预测:当前处于爬坡期,预计 20:00 达到峰值</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="badge badge-primary badge-outline text-xs gap-1"><i class="ph-bold ph-lightning"></i> AI 响应: 1.2s</div>
|
||||
<div class="badge badge-secondary badge-outline text-xs gap-1"><i class="ph-bold ph-coin"></i> Token: $14.2/h</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 动态柱状图 -->
|
||||
<div class="flex-1 flex items-end justify-between gap-1 z-10 px-1 pt-4 border-b border-slate-700/50">
|
||||
<div v-for="(h, i) in trafficData" :key="i"
|
||||
class="w-full bg-blue-500/20 rounded-t-sm hover:bg-blue-500/60 transition-all duration-300 relative group"
|
||||
:style="{ height: h + '%' }">
|
||||
<!-- Hover Tooltip -->
|
||||
<div class="absolute -top-10 left-1/2 -translate-x-1/2 bg-slate-800 text-white text-[10px] px-2 py-1 rounded border border-slate-600 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-20 shadow-xl">
|
||||
QPS: {{ Math.round(h * 3.5) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部时间轴 -->
|
||||
<div class="flex justify-between text-[10px] text-slate-600 font-mono mt-2 z-10">
|
||||
<span>14:00</span>
|
||||
<span>14:15</span>
|
||||
<span>14:30</span>
|
||||
<span>14:45</span>
|
||||
<span>15:00</span>
|
||||
</div>
|
||||
|
||||
<!-- 背景装饰 -->
|
||||
<div class="absolute inset-0 z-0 bg-gradient-to-t from-blue-500/5 to-transparent pointer-events-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部日志 -->
|
||||
<div class="glass-panel rounded-2xl p-6 flex-1 min-h-[300px]">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="font-bold text-white flex items-center gap-2"><i class="ph-fill ph-list-dashes text-slate-400"></i> 系统事件日志</h3>
|
||||
<div class="flex gap-2">
|
||||
<span class="loading loading-dots loading-xs text-slate-500"></span>
|
||||
<span class="text-xs text-slate-500 font-mono">LIVE</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-xs table-pin-rows">
|
||||
<thead>
|
||||
<tr class="text-slate-500 border-b border-slate-700/50">
|
||||
<th>TIMESTAMP</th>
|
||||
<th>EVENT TYPE</th>
|
||||
<th>CUSTOMER</th>
|
||||
<th>AI DECISION & ACTION</th>
|
||||
<th>STATUS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="font-mono">
|
||||
<tr v-for="log in eventLogs" :key="log.id" class="hover:bg-slate-800/30 transition-colors border-slate-800/30 group">
|
||||
<td class="text-slate-500">{{ log.time }}</td>
|
||||
<td>
|
||||
<span class="badge badge-ghost badge-xs gap-1 border-slate-700 text-slate-300">
|
||||
{{ log.type }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="font-medium text-slate-300">{{ log.user }}</td>
|
||||
<td class="max-w-md truncate text-slate-400 group-hover:text-slate-200 transition-colors">
|
||||
<span class="text-primary mr-2">[{{log.model}}]</span>
|
||||
{{ log.action }}
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="log.status === 'success'" class="text-emerald-400 flex items-center gap-1"><i class="ph-fill ph-check-circle"></i> OK</span>
|
||||
<span v-else-if="log.status === 'warning'" class="text-orange-400 flex items-center gap-1"><i class="ph-fill ph-warning"></i> MANUAL</span>
|
||||
<span v-else class="text-blue-400 flex items-center gap-1"><i class="ph-bold ph-spinner animate-spin"></i> RUNNING</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View B: SOP 编排中心 (Strategy) -->
|
||||
<div v-else-if="currentView === 'strategy'" class="h-full flex flex-col gap-6">
|
||||
<!-- 头部工具栏 -->
|
||||
<div class="flex justify-between items-end pb-2 border-b border-slate-800">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-white mb-1 flex items-center gap-2">
|
||||
SOP 智能编排中心
|
||||
<div class="badge badge-primary badge-outline text-xs">v2.4.0</div>
|
||||
</h2>
|
||||
<p class="text-slate-400 text-xs">配置全链路各阶段的模型参数、提示词及上下文逻辑。</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button class="btn btn-neutral btn-sm border-slate-700 text-slate-300 hover:text-white"><i class="ph ph-clock-counter-clockwise"></i> 历史版本</button>
|
||||
<button class="btn btn-primary btn-sm shadow-lg shadow-primary/20" @click="saveConfig"><i class="ph ph-floppy-disk"></i> 发布策略配置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 gap-6 overflow-hidden">
|
||||
<!-- 左侧:阶段选择器 -->
|
||||
<div class="w-1/4 glass-panel rounded-2xl overflow-y-auto p-2 no-scrollbar">
|
||||
<div class="px-4 py-3 text-xs font-bold text-slate-500 uppercase tracking-widest">Sales Pipeline</div>
|
||||
<ul class="steps steps-vertical w-full gap-2">
|
||||
<li
|
||||
v-for="(stage, index) in salesStages"
|
||||
:key="stage.id"
|
||||
class="step cursor-pointer transition-all duration-300"
|
||||
:class="currentStageId === stage.id ? 'step-primary' : 'step-neutral'"
|
||||
@click="currentStageId = stage.id"
|
||||
:data-content="index + 1"
|
||||
>
|
||||
<div class="w-full text-left ml-3 p-3 rounded-xl border transition-all duration-200 group"
|
||||
:class="currentStageId === stage.id ? 'bg-primary/10 border-primary shadow-[0_0_15px_rgba(99,102,241,0.2)]' : 'bg-slate-900/50 border-slate-700/50 hover:bg-slate-800 hover:border-slate-600'">
|
||||
<div class="font-bold text-sm text-slate-200 group-hover:text-white">{{ stage.name }}</div>
|
||||
<div class="text-[10px] text-slate-500 mt-1 line-clamp-1 group-hover:text-slate-400">{{ stage.desc }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:Prompt 编辑器 -->
|
||||
<div class="flex-1 glass-panel rounded-2xl p-6 flex flex-col gap-6 overflow-y-auto pr-4">
|
||||
<!-- 模型参数配置 -->
|
||||
<div class="grid grid-cols-2 gap-6 p-4 bg-slate-900/50 rounded-xl border border-slate-800">
|
||||
<div class="form-control w-full">
|
||||
<label class="label pt-0"><span class="label-text font-bold text-slate-300 text-xs uppercase">Execution Model</span></label>
|
||||
<select class="select select-bordered select-sm w-full bg-slate-950 border-slate-700 text-slate-200" v-model="currentConfig.model">
|
||||
<option value="gpt-4-turbo">GPT-4 Turbo (高精度/逻辑强)</option>
|
||||
<option value="deepseek-chat">DeepSeek V3 (高性价比/中文优)</option>
|
||||
<option value="claude-3-opus">Claude 3 Opus (情感/共情强)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control w-full">
|
||||
<label class="label pt-0">
|
||||
<span class="label-text font-bold text-slate-300 text-xs uppercase">Temperature: {{ currentConfig.temp }}</span>
|
||||
<span class="label-text-alt text-[10px] text-slate-500">{{ currentConfig.temp > 0.7 ? '感性/发散' : '理性/收敛' }}</span>
|
||||
</label>
|
||||
<input type="range" min="0" max="1" step="0.1" v-model="currentConfig.temp" class="range range-xs range-primary" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 代码编辑器 -->
|
||||
<div class="flex-1 flex flex-col min-h-[400px]">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label class="label p-0"><span class="label-text font-bold text-slate-300 text-xs uppercase flex items-center gap-2"><i class="ph-fill ph-code"></i> System Prompt</span></label>
|
||||
<div class="flex gap-2">
|
||||
<div class="badge badge-outline badge-accent text-[10px] h-5">Jinja2 Supported</div>
|
||||
<div class="badge badge-outline text-slate-500 text-[10px] h-5">{{ currentConfig.prompt.length }} chars</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative flex-1 rounded-xl overflow-hidden border border-slate-700/50 bg-[#0d1117] group focus-within:border-primary/50 transition-colors">
|
||||
<!-- 简单的行号模拟 -->
|
||||
<div class="absolute left-0 top-0 bottom-0 w-8 bg-slate-900/50 border-r border-slate-800 flex flex-col items-end pt-4 pr-2 text-slate-600 font-mono text-xs select-none leading-relaxed">
|
||||
<span v-for="n in 20" :key="n">{{n}}</span>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="currentConfig.prompt"
|
||||
class="absolute inset-0 w-full h-full bg-transparent text-emerald-400 p-4 pl-10 font-mono text-sm resize-none focus:outline-none leading-relaxed selection:bg-emerald-900/50"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工具挂载 -->
|
||||
<div class="collapse collapse-arrow bg-slate-900/50 border border-slate-800 rounded-xl">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-sm font-bold text-slate-300 flex items-center gap-2">
|
||||
<i class="ph-fill ph-wrench text-accent"></i> Function Calling (Tools)
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="flex flex-wrap gap-4 pt-2">
|
||||
<label class="cursor-pointer flex items-center gap-2 p-2 rounded hover:bg-slate-800 border border-slate-700/50 hover:border-primary/50 transition-all">
|
||||
<input type="checkbox" class="checkbox checkbox-primary checkbox-xs" checked />
|
||||
<span class="label-text text-xs text-slate-300">CRM档案查询</span>
|
||||
</label>
|
||||
<label class="cursor-pointer flex items-center gap-2 p-2 rounded hover:bg-slate-800 border border-slate-700/50 hover:border-primary/50 transition-all">
|
||||
<input type="checkbox" class="checkbox checkbox-primary checkbox-xs" />
|
||||
<span class="label-text text-xs text-slate-300">生成个性化报告</span>
|
||||
</label>
|
||||
<label class="cursor-pointer flex items-center gap-2 p-2 rounded hover:bg-slate-800 border border-slate-700/50 hover:border-primary/50 transition-all">
|
||||
<input type="checkbox" class="checkbox checkbox-primary checkbox-xs" checked />
|
||||
<span class="label-text text-xs text-slate-300">知识库检索 (RAG)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View C: 会话监控 (Monitor) -->
|
||||
<div v-else-if="currentView === 'monitor'" class="h-full flex gap-6">
|
||||
<!-- 用户列表 -->
|
||||
<div class="w-80 glass-panel rounded-2xl flex flex-col overflow-hidden">
|
||||
<div class="p-4 border-b border-slate-700/50 bg-slate-900/30">
|
||||
<h3 class="font-bold text-white text-sm mb-3">实时活跃会话</h3>
|
||||
<div class="relative">
|
||||
<i class="ph ph-magnifying-glass absolute left-3 top-2.5 text-slate-500"></i>
|
||||
<input class="input input-sm w-full bg-slate-950 border-slate-700 pl-9 text-xs focus:border-primary" placeholder="搜索家长姓名/ID..." />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto p-2 space-y-2">
|
||||
<div v-for="user in activeUsers" :key="user.id"
|
||||
class="p-3 rounded-xl cursor-pointer transition-all border border-transparent hover:border-slate-700"
|
||||
:class="user.active ? 'bg-primary/10 border-primary/30' : 'bg-slate-800/30 hover:bg-slate-800/80'"
|
||||
>
|
||||
<div class="flex justify-between items-start mb-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full" :class="user.risk ? 'bg-red-500 animate-pulse' : 'bg-green-500'"></div>
|
||||
<span class="font-bold text-sm text-slate-200">{{ user.name }}</span>
|
||||
</div>
|
||||
<span class="text-[10px] text-slate-500">{{ user.time }}</span>
|
||||
</div>
|
||||
<div class="text-xs text-slate-400 truncate mb-2">{{ user.lastMsg }}</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<div class="badge badge-xs border-0 text-white" :class="getStageBadge(user.stage)">{{ user.stage }}</div>
|
||||
<div v-if="user.risk" class="badge badge-xs badge-error gap-1"><i class="ph-bold ph-warning"></i> 需人工</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 聊天窗口 -->
|
||||
<div class="flex-1 flex flex-col glass-panel rounded-2xl overflow-hidden relative">
|
||||
<!-- Chat Header -->
|
||||
<div class="h-16 border-b border-slate-700/50 flex justify-between items-center px-6 bg-slate-900/50 backdrop-blur-md z-10">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold text-lg text-white">王梓涵妈妈</span>
|
||||
<div class="badge badge-primary badge-outline badge-xs">ID: 883921</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mt-0.5">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
||||
</span>
|
||||
<span class="text-xs text-slate-400">AI 托管中 | 策略模型: Claude-3 Opus</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-error btn-xs" @click="toggleManual">
|
||||
<i class="ph-bold ph-hand-palm"></i> 强制接管
|
||||
</button>
|
||||
<button class="btn btn-neutral btn-xs border-slate-600">
|
||||
<i class="ph ph-file-text"></i> 画像
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat Content -->
|
||||
<div class="flex-1 p-4 sm:p-6 overflow-y-auto space-y-8 bg-slate-950/50" ref="chatBox">
|
||||
<div v-for="msg in chatHistory" :key="msg.id" class="flex gap-4 group" :class="msg.role === 'user' ? 'flex-row-reverse' : ''">
|
||||
|
||||
<!-- Avatar -->
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<div class="w-9 h-9 rounded-full border border-slate-700/50 p-1 flex items-center justify-center shadow-lg transition-transform group-hover:scale-105"
|
||||
:class="msg.role === 'ai' ? 'bg-indigo-600' : 'bg-slate-800'">
|
||||
<i v-if="msg.role === 'ai'" class="ph-bold ph-robot text-lg text-white"></i>
|
||||
<i v-else class="ph-bold ph-user text-lg text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Message Body -->
|
||||
<div class="flex flex-col max-w-[85%] sm:max-w-[75%]" :class="msg.role === 'user' ? 'items-end' : 'items-start'">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex items-center gap-2 mb-1.5 text-[10px] text-slate-500">
|
||||
<span class="font-bold text-slate-400">{{ msg.role === 'ai' ? 'Nexus AI Agent' : '家长' }}</span>
|
||||
<time class="opacity-50 font-mono">{{ msg.time }}</time>
|
||||
</div>
|
||||
|
||||
<!-- Thought Chain (AI Only) -->
|
||||
<div v-if="msg.role === 'ai' && msg.thought" class="mb-3 w-full animate-in fade-in slide-in-from-top-2 duration-500">
|
||||
<div class="p-3 bg-yellow-500/5 border border-yellow-700/20 rounded-lg text-xs text-yellow-500/80 font-mono shadow-sm relative overflow-hidden">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-0.5 bg-yellow-600/40"></div>
|
||||
<div class="flex items-center gap-1.5 mb-1.5 opacity-90 font-bold uppercase tracking-wider text-[9px]">
|
||||
<i class="ph-bold ph-cpu"></i> 决策链路: {{ msg.stage }}
|
||||
</div>
|
||||
<div class="leading-relaxed opacity-80 break-words whitespace-pre-wrap">> {{ msg.thought }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bubble -->
|
||||
<div class="p-3.5 rounded-2xl shadow-xl text-sm leading-relaxed break-words relative group-hover:shadow-2xl transition-shadow"
|
||||
:class="msg.role === 'ai'
|
||||
? 'bg-indigo-600 text-white rounded-tl-sm'
|
||||
: 'bg-slate-800 text-slate-200 rounded-tr-sm'">
|
||||
{{ msg.content }}
|
||||
</div>
|
||||
|
||||
<!-- Footer (AI Only) -->
|
||||
<div v-if="msg.role === 'ai'" class="mt-1.5 flex items-center gap-3 text-[9px] text-slate-600 font-mono opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<span class="flex items-center gap-1"><i class="ph-bold ph-lightning"></i> Claude-3 Opus</span>
|
||||
<span class="flex items-center gap-1"><i class="ph-bold ph-clock"></i> 420ms</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input Area -->
|
||||
<div class="p-4 bg-slate-900 border-t border-slate-700/50">
|
||||
<div class="relative">
|
||||
<input type="text" placeholder="输入内容以进行人工干预 (AI 将暂停响应)..." class="input input-bordered w-full bg-slate-950 border-slate-700 focus:border-primary pr-12 text-sm" />
|
||||
<button class="absolute right-1 top-1 btn btn-sm btn-primary rounded-lg aspect-square p-0"><i class="ph-bold ph-paper-plane-right text-lg"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DashboardView v-if="currentView === 'dashboard'" :funnel-data="funnelData" :traffic-data="trafficData" :event-logs="eventLogs" />
|
||||
<StrategyView v-else-if="currentView === 'strategy'" :sales-stages="salesStages" v-model:current-stage-id="currentStageId" :current-config="currentConfig" @saveConfig="saveConfig" />
|
||||
<MonitorView v-else-if="currentView === 'monitor'" :active-users="activeUsers" :chat-history="chatHistory" :get-stage-badge="getStageBadge" @toggleManual="toggleManual" />
|
||||
</transition>
|
||||
</main>
|
||||
</div>
|
||||
@@ -472,6 +18,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
|
||||
import GlobalHeader from './components/GlobalHeader.vue';
|
||||
import SidebarNav from './components/SidebarNav.vue';
|
||||
import DashboardView from './components/DashboardView.vue';
|
||||
import StrategyView from './components/StrategyView.vue';
|
||||
import MonitorView from './components/MonitorView.vue';
|
||||
|
||||
const currentView = ref('dashboard');
|
||||
|
||||
@@ -508,6 +59,15 @@ interface Config {
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
interface ChatMessage {
|
||||
id: number;
|
||||
role: 'ai' | 'user';
|
||||
content: string;
|
||||
time: string;
|
||||
stage: string;
|
||||
thought: string;
|
||||
}
|
||||
|
||||
const configs = ref<Record<string, Config>>({
|
||||
'deep_connect': {
|
||||
model: 'claude-3-opus',
|
||||
@@ -548,7 +108,7 @@ const currentConfig = computed((): Config => {
|
||||
});
|
||||
|
||||
// 3. 聊天记录 Mock
|
||||
const chatHistory = ref([
|
||||
const chatHistory = ref<ChatMessage[]>([
|
||||
{ id: 1, role: 'user', content: '孩子最近回家都不说话,一问成绩就发脾气,我真的不知道该怎么办了...', time: '10:23 AM', stage: '', thought: '' },
|
||||
{
|
||||
id: 2,
|
||||
@@ -748,4 +308,4 @@ select.select {
|
||||
color: #e2e8f0 !important;
|
||||
border-color: #334155 !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user