更新文件以保持同步
This commit is contained in:
21
TouchVueThree/.editorconfig
Normal file
21
TouchVueThree/.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
# EditorConfig 帮助不同编辑器和 IDE 维护一致的编码风格
|
||||
# https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{json,yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.vue]
|
||||
indent_size = 2
|
||||
19
TouchVueThree/.eslintignore
Normal file
19
TouchVueThree/.eslintignore
Normal file
@@ -0,0 +1,19 @@
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Build outputs
|
||||
*.min.js
|
||||
*.min.css
|
||||
|
||||
# Config files
|
||||
*.config.js
|
||||
*.config.cjs
|
||||
*.config.mjs
|
||||
|
||||
# Static assets
|
||||
public
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
24
TouchVueThree/.eslintrc.cjs
Normal file
24
TouchVueThree/.eslintrc.cjs
Normal file
@@ -0,0 +1,24 @@
|
||||
/* eslint-env node */
|
||||
require('eslint-plugin-vue')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-unused-vars': 'warn',
|
||||
'no-unused-vars': 'warn',
|
||||
},
|
||||
}
|
||||
33
TouchVueThree/.gitignore
vendored
Normal file
33
TouchVueThree/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.production.local
|
||||
.env.development.local
|
||||
|
||||
# Build outputs
|
||||
*.tsbuildinfo
|
||||
4
TouchVueThree/.npmrc
Normal file
4
TouchVueThree/.npmrc
Normal file
@@ -0,0 +1,4 @@
|
||||
# npm 配置
|
||||
registry=https://registry.npmmirror.com
|
||||
save-exact=false
|
||||
engine-strict=false
|
||||
30
TouchVueThree/.prettierignore
Normal file
30
TouchVueThree/.prettierignore
Normal file
@@ -0,0 +1,30 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Editor
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Cache
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.production.local
|
||||
|
||||
# Other
|
||||
*.min.js
|
||||
*.min.css
|
||||
10
TouchVueThree/.prettierrc
Normal file
10
TouchVueThree/.prettierrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
8
TouchVueThree/.vscode/extensions.json
vendored
Normal file
8
TouchVueThree/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
27
TouchVueThree/CHANGELOG.md
Normal file
27
TouchVueThree/CHANGELOG.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# 更新日志
|
||||
|
||||
所有重要的项目变更都会记录在此文件中。
|
||||
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [1.0.0] - 2024-01-XX
|
||||
|
||||
### 新增
|
||||
- 初始化项目
|
||||
- 集成 Vite + Vue 3 + Tailwind CSS
|
||||
- 创建基础项目结构
|
||||
- 添加聊天模块(ChatList, ChatWindow, ChatDetail)
|
||||
- 添加账号管理页面(Accounts)
|
||||
- 添加数据看板页面(Analytics)
|
||||
- 创建全局导航栏组件(Sidebar)
|
||||
- 创建 AI 算力展示组件(AICreditPill)
|
||||
- 配置 ESLint 和 Prettier
|
||||
- 添加环境变量支持
|
||||
|
||||
### 配置
|
||||
- Vite 构建配置
|
||||
- Tailwind CSS 主题配置
|
||||
- PostCSS 配置
|
||||
- 路径别名配置(@ → src)
|
||||
- 编辑器配置文件
|
||||
21
TouchVueThree/LICENSE
Normal file
21
TouchVueThree/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 触客宝 Pro
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
122
TouchVueThree/README.md
Normal file
122
TouchVueThree/README.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# 触客宝 Pro - Vue 3 版本
|
||||
|
||||
基于 Vite + Vue 3 + Tailwind CSS 构建的现代化工作台应用。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **Vue 3** - 渐进式 JavaScript 框架
|
||||
- **Vite** - 下一代前端构建工具
|
||||
- **Tailwind CSS** - 实用优先的 CSS 框架
|
||||
- **ESLint** - 代码质量检查
|
||||
- **Prettier** - 代码格式化
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
TouchVueThree/
|
||||
├── src/
|
||||
│ ├── assets/ # 静态资源
|
||||
│ │ └── style.css # 全局样式(Tailwind 指令)
|
||||
│ ├── components/ # 公共组件
|
||||
│ │ ├── Sidebar.vue # 全局左侧导航栏
|
||||
│ │ └── AICreditPill.vue # AI算力展示胶囊
|
||||
│ ├── views/ # 页面视图
|
||||
│ │ ├── Chat/ # 聊天模块
|
||||
│ │ ├── Accounts.vue # 账号管理
|
||||
│ │ └── Analytics.vue # 数据看板
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 应用入口
|
||||
├── public/ # 公共静态资源
|
||||
├── index.html # HTML 入口
|
||||
└── vite.config.js # Vite 配置
|
||||
```
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install
|
||||
# 或
|
||||
pnpm install
|
||||
# 或
|
||||
yarn install
|
||||
```
|
||||
|
||||
### 启动开发服务器
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# 或
|
||||
pnpm dev
|
||||
# 或
|
||||
yarn dev
|
||||
```
|
||||
|
||||
开发服务器将在 `http://localhost:5173` 启动。
|
||||
|
||||
### 构建生产版本
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
# 或
|
||||
pnpm build
|
||||
# 或
|
||||
yarn build
|
||||
```
|
||||
|
||||
构建产物将输出到 `dist/` 目录。
|
||||
|
||||
### 预览生产构建
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
# 或
|
||||
pnpm preview
|
||||
# 或
|
||||
yarn preview
|
||||
```
|
||||
|
||||
### 代码检查
|
||||
|
||||
```bash
|
||||
# ESLint 检查并自动修复
|
||||
npm run lint
|
||||
|
||||
# Prettier 格式化代码
|
||||
npm run format
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
复制 `.env.example` 为 `.env` 并根据需要配置环境变量。
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
## 功能模块
|
||||
|
||||
### 1. 聊天模块 (Chat)
|
||||
- **ChatList** - 消息列表
|
||||
- **ChatWindow** - 聊天窗口
|
||||
- **ChatDetail** - CRM 资料详情
|
||||
|
||||
### 2. 账号管理 (Accounts)
|
||||
- 微信账号管理
|
||||
- 账号状态监控
|
||||
|
||||
### 3. 数据看板 (Analytics)
|
||||
- 业务数据统计
|
||||
- 可视化图表展示
|
||||
|
||||
## 开发规范
|
||||
|
||||
- 使用 Vue 3 Composition API
|
||||
- 组件采用 `<script setup>` 语法
|
||||
- 样式使用 Tailwind CSS 工具类
|
||||
- 代码提交前运行 lint 和 format
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
21
TouchVueThree/env.d.ts
vendored
Normal file
21
TouchVueThree/env.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_APP_TITLE: string
|
||||
readonly VITE_APP_VERSION: string
|
||||
readonly VITE_API_BASE_URL: string
|
||||
readonly VITE_API_TIMEOUT: string
|
||||
readonly VITE_ENABLE_DEBUG: string
|
||||
readonly VITE_ENABLE_MOCK: string
|
||||
// 更多环境变量...
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
11
TouchVueThree/env.example
Normal file
11
TouchVueThree/env.example
Normal file
@@ -0,0 +1,11 @@
|
||||
# 应用配置
|
||||
VITE_APP_TITLE=触客宝 Pro
|
||||
VITE_APP_VERSION=1.0.0
|
||||
|
||||
# API 配置
|
||||
VITE_API_BASE_URL=http://localhost:3000/api
|
||||
VITE_API_TIMEOUT=10000
|
||||
|
||||
# 功能开关
|
||||
VITE_ENABLE_DEBUG=false
|
||||
VITE_ENABLE_MOCK=false
|
||||
25
TouchVueThree/index.html
Normal file
25
TouchVueThree/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>触客宝 Pro - 全局工作台</title>
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@500;600;700&family=Inter:wght@400;500&display=swap"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
22
TouchVueThree/jsconfig.json
Normal file
22
TouchVueThree/jsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": false,
|
||||
"strict": false,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"types": ["vite/client"],
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*.js", "src/**/*.vue", "env.d.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
27
TouchVueThree/package.json
Normal file
27
TouchVueThree/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "touchvue-three",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.4.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.20.1",
|
||||
"postcss": "^8.4.35",
|
||||
"prettier": "^3.2.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"vite": "^5.1.4"
|
||||
}
|
||||
}
|
||||
2146
TouchVueThree/pnpm-lock.yaml
generated
Normal file
2146
TouchVueThree/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
TouchVueThree/postcss.config.js
Normal file
6
TouchVueThree/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
3
TouchVueThree/public/.gitkeep
Normal file
3
TouchVueThree/public/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
# Public 目录
|
||||
# 此目录用于存放静态资源文件,如 favicon.ico、图片等
|
||||
# 目录中的文件会直接被复制到构建输出的根目录
|
||||
86
TouchVueThree/src/App.vue
Normal file
86
TouchVueThree/src/App.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex h-screen w-screen bg-[#F8FAFC] text-slate-700 overflow-hidden font-sans"
|
||||
>
|
||||
<!-- 全局侧边栏 -->
|
||||
<Sidebar
|
||||
:current-tab="currentTab"
|
||||
@change-tab="handleTabChange"
|
||||
@logo-click="handleLogoClick"
|
||||
@add-account="handleAddAccount"
|
||||
@open-settings="handleOpenSettings"
|
||||
@select-account="handleSelectAccount"
|
||||
/>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<main class="flex-1 relative overflow-hidden">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<component :is="activeComponent" />
|
||||
</Transition>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import Sidebar from './components/Sidebar.vue'
|
||||
import ChatIndex from './views/Chat/Index.vue'
|
||||
import Contacts from './views/Contacts.vue'
|
||||
import Analytics from './views/Analytics.vue'
|
||||
|
||||
const currentTab = ref('chat')
|
||||
|
||||
const activeComponent = computed(() => {
|
||||
switch (currentTab.value) {
|
||||
case 'chat':
|
||||
return ChatIndex
|
||||
case 'contacts':
|
||||
return Contacts // 通讯录页面
|
||||
case 'analytics':
|
||||
return Analytics
|
||||
default:
|
||||
return ChatIndex
|
||||
}
|
||||
})
|
||||
|
||||
// 处理标签切换
|
||||
const handleTabChange = (tab) => {
|
||||
currentTab.value = tab
|
||||
}
|
||||
|
||||
// 处理 Logo 点击
|
||||
const handleLogoClick = () => {
|
||||
// 可以跳转到首页或执行其他操作
|
||||
currentTab.value = 'chat'
|
||||
}
|
||||
|
||||
// 处理添加账号
|
||||
const handleAddAccount = () => {
|
||||
// TODO: 打开添加账号对话框或跳转到账号管理页面
|
||||
console.log('添加账号')
|
||||
currentTab.value = 'contacts'
|
||||
}
|
||||
|
||||
// 处理打开设置
|
||||
const handleOpenSettings = () => {
|
||||
// TODO: 打开设置对话框或跳转到设置页面
|
||||
console.log('打开设置')
|
||||
}
|
||||
|
||||
// 处理选择账号
|
||||
const handleSelectAccount = (account) => {
|
||||
// TODO: 切换到选中的账号
|
||||
console.log('选择账号:', account)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
28
TouchVueThree/src/assets/style.css
Normal file
28
TouchVueThree/src/assets/style.css
Normal file
@@ -0,0 +1,28 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* 自定义样式 */
|
||||
body {
|
||||
font-family: "Plus Jakarta Sans", "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面切换动画 */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
65
TouchVueThree/src/components/AICreditPill.vue
Normal file
65
TouchVueThree/src/components/AICreditPill.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="group relative">
|
||||
<!-- 胶囊主体 -->
|
||||
<div
|
||||
class="flex items-center gap-3 bg-gradient-to-r from-indigo-500 to-purple-600 text-white pl-4 pr-1 py-1 rounded-full shadow-md cursor-pointer hover:shadow-lg hover:-translate-y-0.5 transition-all"
|
||||
>
|
||||
<div class="flex flex-col leading-none items-end">
|
||||
<span class="text-[9px] font-bold opacity-80 uppercase tracking-wide"
|
||||
>AI 算力</span
|
||||
>
|
||||
<span class="text-sm font-bold font-mono">{{ animatedBalance }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="w-8 h-8 bg-white text-indigo-600 rounded-full flex items-center justify-center shadow-inner"
|
||||
>
|
||||
<i
|
||||
class="fa-solid fa-bolt text-sm"
|
||||
:class="{ 'animate-pulse': isAnimating }"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 悬浮详情 (Dropdown) -->
|
||||
<div
|
||||
class="absolute top-full right-0 mt-2 w-64 bg-white rounded-xl shadow-xl border border-gray-100 p-4 hidden group-hover:block z-50"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-xs font-bold text-gray-700">算力钱包</span>
|
||||
<span
|
||||
class="text-[10px] bg-green-100 text-green-700 px-2 py-0.5 rounded-full"
|
||||
>状态正常</span
|
||||
>
|
||||
</div>
|
||||
<div class="h-2 bg-gray-100 rounded-full mb-2 overflow-hidden">
|
||||
<div class="h-full bg-indigo-500 w-3/4 rounded-full"></div>
|
||||
</div>
|
||||
<div class="text-xs text-gray-400 mb-3">本月额度剩余 75%</div>
|
||||
<button
|
||||
class="w-full py-1.5 bg-slate-800 text-white text-xs rounded-lg hover:bg-slate-700 transition"
|
||||
>
|
||||
立即充值
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
balance: { type: Number, default: 0 },
|
||||
})
|
||||
|
||||
const isAnimating = ref(false)
|
||||
|
||||
const animatedBalance = computed(() => props.balance.toLocaleString())
|
||||
|
||||
// 暴露给父组件调用的方法:播放扣费动画
|
||||
const playDeductAnimation = () => {
|
||||
isAnimating.value = true
|
||||
setTimeout(() => (isAnimating.value = false), 500)
|
||||
}
|
||||
|
||||
defineExpose({ playDeductAnimation })
|
||||
</script>
|
||||
183
TouchVueThree/src/components/Sidebar.vue
Normal file
183
TouchVueThree/src/components/Sidebar.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-16 bg-slate-900 flex flex-col items-center py-6 flex-shrink-0 z-20"
|
||||
>
|
||||
<!-- Logo -->
|
||||
<div
|
||||
class="w-10 h-10 bg-indigo-500 rounded-lg flex items-center justify-center text-white font-bold text-xl mb-8 shadow-lg cursor-pointer hover:scale-105 transition-transform"
|
||||
@click="$emit('logo-click')"
|
||||
>
|
||||
<i class="fa-solid fa-robot"></i>
|
||||
</div>
|
||||
|
||||
<!-- 功能模块 -->
|
||||
<div class="space-y-6 flex flex-col items-center w-full">
|
||||
<!-- 聚合聊天 -->
|
||||
<div
|
||||
@click="$emit('change-tab', 'chat')"
|
||||
class="group relative cursor-pointer"
|
||||
:class="
|
||||
currentTab === 'chat'
|
||||
? 'text-indigo-400'
|
||||
: 'text-gray-500 hover:text-gray-300'
|
||||
"
|
||||
>
|
||||
<i class="fa-solid fa-comments text-2xl transition-colors"></i>
|
||||
<span
|
||||
class="absolute left-14 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap z-50 pointer-events-none"
|
||||
>
|
||||
聚合聊天
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 通讯录 -->
|
||||
<div
|
||||
@click="$emit('change-tab', 'contacts')"
|
||||
class="group relative cursor-pointer transition-colors"
|
||||
:class="
|
||||
currentTab === 'contacts'
|
||||
? 'text-indigo-400'
|
||||
: 'text-gray-500 hover:text-gray-300'
|
||||
"
|
||||
>
|
||||
<i class="fa-solid fa-address-book text-xl"></i>
|
||||
<span
|
||||
class="absolute left-14 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap z-50 pointer-events-none"
|
||||
>
|
||||
通讯录
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 数据看板 -->
|
||||
<div
|
||||
@click="$emit('change-tab', 'analytics')"
|
||||
class="group relative cursor-pointer transition-colors"
|
||||
:class="
|
||||
currentTab === 'analytics'
|
||||
? 'text-indigo-400'
|
||||
: 'text-gray-500 hover:text-gray-300'
|
||||
"
|
||||
>
|
||||
<i class="fa-solid fa-chart-pie text-xl"></i>
|
||||
<span
|
||||
class="absolute left-14 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap z-50 pointer-events-none"
|
||||
>
|
||||
数据看板
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<div class="mt-8 mb-4 w-8 border-b border-gray-700"></div>
|
||||
|
||||
<!-- 多账号头像 (模拟微信多开) -->
|
||||
<div
|
||||
class="space-y-4 flex flex-col items-center overflow-y-auto no-scrollbar flex-1 min-h-0"
|
||||
>
|
||||
<div
|
||||
v-for="account in accounts"
|
||||
:key="account.id"
|
||||
@click="selectAccount(account.id)"
|
||||
class="relative cursor-pointer group"
|
||||
>
|
||||
<img
|
||||
:src="account.avatar"
|
||||
:class="[
|
||||
'w-10 h-10 rounded-full border-2 object-cover transition-all',
|
||||
account.active
|
||||
? 'border-green-500 opacity-100'
|
||||
: 'border-transparent hover:border-gray-500 opacity-60 hover:opacity-100',
|
||||
]"
|
||||
:alt="account.name"
|
||||
/>
|
||||
<!-- 未读消息红点 -->
|
||||
<div
|
||||
v-if="account.hasNotification"
|
||||
class="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border-2 border-slate-900"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 添加账号按钮 -->
|
||||
<div
|
||||
@click="$emit('add-account')"
|
||||
class="w-10 h-10 rounded-full bg-gray-800 flex items-center justify-center text-gray-400 hover:text-white cursor-pointer hover:bg-gray-700 transition"
|
||||
>
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部设置 -->
|
||||
<div class="mt-auto pb-4">
|
||||
<div
|
||||
@click="$emit('open-settings')"
|
||||
class="w-10 h-10 rounded-full bg-slate-800 flex items-center justify-center text-gray-400 hover:text-white cursor-pointer transition-colors"
|
||||
>
|
||||
<i class="fa-solid fa-gear"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
currentTab: {
|
||||
type: String,
|
||||
default: 'chat',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'change-tab',
|
||||
'logo-click',
|
||||
'add-account',
|
||||
'open-settings',
|
||||
'select-account',
|
||||
])
|
||||
|
||||
// 账号列表数据
|
||||
const accounts = ref([
|
||||
{
|
||||
id: 'wx1',
|
||||
name: '账号1',
|
||||
avatar: 'https://i.pravatar.cc/150?u=wx1',
|
||||
active: true,
|
||||
hasNotification: true,
|
||||
},
|
||||
{
|
||||
id: 'wx2',
|
||||
name: '账号2',
|
||||
avatar: 'https://i.pravatar.cc/150?u=wx2',
|
||||
active: false,
|
||||
hasNotification: false,
|
||||
},
|
||||
])
|
||||
|
||||
// 选择账号
|
||||
const selectAccount = (accountId) => {
|
||||
// 更新激活状态
|
||||
accounts.value.forEach((account) => {
|
||||
account.active = account.id === accountId
|
||||
})
|
||||
// 触发事件
|
||||
const account = accounts.value.find((acc) => acc.id === accountId)
|
||||
if (account) {
|
||||
// 可以在这里清除通知
|
||||
account.hasNotification = false
|
||||
// 触发选择账号事件
|
||||
emit('select-account', account)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义滚动条隐藏 */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
</style>
|
||||
6
TouchVueThree/src/main.js
Normal file
6
TouchVueThree/src/main.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './assets/style.css'
|
||||
|
||||
const app = createApp(App)
|
||||
app.mount('#app')
|
||||
35
TouchVueThree/src/views/Accounts.vue
Normal file
35
TouchVueThree/src/views/Accounts.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="p-8 h-full overflow-y-auto custom-scroll">
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<div class="flex justify-between items-center mb-8">
|
||||
<h2 class="text-2xl font-bold text-slate-800">聚合账号管理</h2>
|
||||
<button
|
||||
class="bg-primary text-white px-6 py-2.5 rounded-xl font-bold shadow-lg shadow-indigo-200 flex items-center gap-2"
|
||||
>
|
||||
<i class="fa-solid fa-qrcode"></i> 扫码接入
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div
|
||||
class="bg-white rounded-2xl p-6 border border-gray-100 shadow-sm hover:shadow-md transition-all relative overflow-hidden group"
|
||||
>
|
||||
<span
|
||||
class="absolute top-3 right-3 bg-green-100 text-green-700 text-xs px-2 py-1 rounded-lg font-bold"
|
||||
>在线</span
|
||||
>
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=wx1"
|
||||
class="w-16 h-16 rounded-2xl border-2 border-gray-100"
|
||||
/>
|
||||
<div>
|
||||
<h3 class="font-bold text-lg text-slate-800">商务-大刘</h3>
|
||||
<p class="text-xs text-gray-400">ID: business_01</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
42
TouchVueThree/src/views/Analytics.vue
Normal file
42
TouchVueThree/src/views/Analytics.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="p-8 h-full overflow-y-auto custom-scroll">
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<h2 class="text-2xl font-bold text-slate-800 mb-6">AI 效能看板</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<div
|
||||
class="bg-gradient-to-br from-[#4F46E5] to-[#7C3AED] rounded-2xl p-6 text-white shadow-lg relative overflow-hidden"
|
||||
>
|
||||
<div class="relative z-10">
|
||||
<div class="text-indigo-100 text-sm font-medium mb-1">
|
||||
AI 算力余额
|
||||
</div>
|
||||
<div class="text-4xl font-bold font-mono tracking-tight mb-4">
|
||||
12,450
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl p-6 border border-gray-100 shadow-sm">
|
||||
<h3 class="font-bold text-slate-800 mb-6">近7天接待趋势</h3>
|
||||
<div class="h-64 flex items-end justify-between gap-2 px-2">
|
||||
<div
|
||||
v-for="h in [20, 30, 45, 25, 60, 40, 15]"
|
||||
:key="h"
|
||||
class="w-full flex flex-col gap-1 items-center group"
|
||||
>
|
||||
<div
|
||||
:style="{ height: h + 'px' }"
|
||||
class="w-4 bg-purple-400 rounded-t-sm opacity-80 group-hover:opacity-100 transition-all"
|
||||
></div>
|
||||
<div
|
||||
:style="{ height: h / 2 + 'px' }"
|
||||
class="w-4 bg-indigo-500 rounded-b-sm opacity-80 group-hover:opacity-100 transition-all"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
95
TouchVueThree/src/views/Chat/ChatDetail.vue
Normal file
95
TouchVueThree/src/views/Chat/ChatDetail.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white border-l border-gray-100 flex flex-col flex-shrink-0 transition-all duration-300 overflow-hidden"
|
||||
:style="{ width: visible ? '300px' : '0', opacity: visible ? '1' : '0', pointerEvents: visible ? 'auto' : 'none' }"
|
||||
>
|
||||
<!-- Tab 切换 -->
|
||||
<div class="flex p-1 bg-gray-50 m-4 rounded-lg border border-gray-100">
|
||||
<button
|
||||
@click="tab = 'profile'"
|
||||
class="flex-1 py-1.5 text-xs font-bold rounded-md transition"
|
||||
:class="
|
||||
tab === 'profile'
|
||||
? 'bg-white text-indigo-600 shadow-sm'
|
||||
: 'text-gray-500'
|
||||
"
|
||||
>
|
||||
资料
|
||||
</button>
|
||||
<button
|
||||
@click="tab = 'scripts'"
|
||||
class="flex-1 py-1.5 text-xs font-bold rounded-md transition"
|
||||
:class="
|
||||
tab === 'scripts'
|
||||
? 'bg-white text-indigo-600 shadow-sm'
|
||||
: 'text-gray-500'
|
||||
"
|
||||
>
|
||||
话术
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-5 pb-5 custom-scroll">
|
||||
<!-- 资料内容 -->
|
||||
<div v-if="tab === 'profile'" class="space-y-6">
|
||||
<div class="text-center">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=8"
|
||||
class="w-20 h-20 rounded-full border-4 border-white shadow-lg mx-auto"
|
||||
/>
|
||||
<h3 class="mt-2 font-bold text-slate-800">大野</h3>
|
||||
<p class="text-xs text-gray-400">ID: ys666wow</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="text-xs font-bold text-gray-400 uppercase mb-2">标签</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span
|
||||
class="px-2 py-1 bg-red-50 text-red-600 text-xs rounded border border-red-100"
|
||||
>高意向</span
|
||||
>
|
||||
<span
|
||||
class="px-2 py-1 bg-blue-50 text-blue-600 text-xs rounded border border-blue-100"
|
||||
>游戏玩家</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="bg-yellow-50 p-3 rounded-lg border border-yellow-100 text-xs text-yellow-700"
|
||||
>
|
||||
<strong>跟进提醒:</strong><br />明天上午10点确认订单情况。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 话术内容 -->
|
||||
<div v-else class="space-y-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索话术..."
|
||||
class="w-full bg-gray-50 text-xs py-2 px-3 rounded-lg border border-gray-200 mb-2"
|
||||
/>
|
||||
<div
|
||||
v-for="i in 3"
|
||||
:key="i"
|
||||
class="p-3 bg-white border border-gray-100 rounded-xl hover:border-indigo-200 cursor-pointer group"
|
||||
>
|
||||
<h5
|
||||
class="text-xs font-bold text-slate-700 mb-1 group-hover:text-indigo-600"
|
||||
>
|
||||
通用开场 {{ i }}
|
||||
</h5>
|
||||
<p class="text-xs text-gray-500 truncate">
|
||||
您好,我是您的专属顾问,很高兴...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const props = defineProps({ visible: Boolean, chat: Object })
|
||||
const tab = ref('profile')
|
||||
</script>
|
||||
112
TouchVueThree/src/views/Chat/ChatList.vue
Normal file
112
TouchVueThree/src/views/Chat/ChatList.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="w-80 border-r border-gray-100 flex flex-col bg-white">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="p-4 border-b border-gray-50">
|
||||
<h2 class="font-bold text-lg text-slate-800 mb-3">消息 (12)</h2>
|
||||
<div class="relative">
|
||||
<i
|
||||
class="fa-solid fa-magnifying-glass absolute left-3 top-3 text-gray-400 text-xs"
|
||||
></i>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索联系人..."
|
||||
class="w-full bg-gray-50 text-sm pl-9 pr-4 py-2 rounded-lg border-transparent focus:bg-white focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 transition-all outline-none"
|
||||
/>
|
||||
</div>
|
||||
<!-- 快捷标签 -->
|
||||
<div class="flex gap-2 mt-3 overflow-x-auto no-scrollbar pb-1">
|
||||
<span
|
||||
class="px-3 py-1 bg-indigo-50 text-indigo-600 text-xs font-bold rounded-md cursor-pointer whitespace-nowrap"
|
||||
>全部</span
|
||||
>
|
||||
<span
|
||||
class="px-3 py-1 bg-white border border-gray-200 text-gray-500 hover:bg-gray-50 text-xs rounded-md cursor-pointer whitespace-nowrap"
|
||||
>未读</span
|
||||
>
|
||||
<span
|
||||
class="px-3 py-1 bg-purple-50 text-purple-600 border border-purple-100 text-xs rounded-md cursor-pointer whitespace-nowrap flex items-center gap-1"
|
||||
>
|
||||
<i class="fa-solid fa-robot"></i> AI托管
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<div class="flex-1 overflow-y-auto custom-scroll">
|
||||
<div
|
||||
v-for="chat in chats"
|
||||
:key="chat.id"
|
||||
@click="$emit('select', chat.id)"
|
||||
class="p-3 mx-2 my-1 rounded-xl cursor-pointer transition-all border flex gap-3 items-center"
|
||||
:class="
|
||||
activeId === chat.id
|
||||
? 'bg-indigo-50 border-indigo-100'
|
||||
: 'hover:bg-gray-50 border-transparent'
|
||||
"
|
||||
>
|
||||
<div class="relative">
|
||||
<img :src="chat.avatar" class="w-11 h-11 rounded-lg object-cover" />
|
||||
<div
|
||||
v-if="chat.unread"
|
||||
class="absolute -top-1.5 -right-1.5 bg-red-500 text-white text-[10px] h-4 min-w-[16px] px-1 flex items-center justify-center rounded-full border border-white"
|
||||
>
|
||||
{{ chat.unread }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-baseline mb-1">
|
||||
<h3 class="font-bold text-sm text-slate-700 truncate">
|
||||
{{ chat.name }}
|
||||
</h3>
|
||||
<span class="text-[10px] text-gray-400">{{ chat.time }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span
|
||||
v-if="chat.isAi"
|
||||
class="bg-purple-100 text-purple-600 text-[9px] px-1 rounded font-bold"
|
||||
>AI</span
|
||||
>
|
||||
<p class="text-xs text-gray-400 truncate">{{ chat.lastMsg }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps(['activeId'])
|
||||
defineEmits(['select'])
|
||||
|
||||
const chats = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '大野(朋友圈业务)',
|
||||
avatar: 'https://i.pravatar.cc/150?u=8',
|
||||
time: '11:45',
|
||||
lastMsg: '已为您生成3条回复建议...',
|
||||
unread: 0,
|
||||
isAi: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '京东内购福利群',
|
||||
avatar: 'https://i.pravatar.cc/150?u=group',
|
||||
time: '10:20',
|
||||
lastMsg: '张三: 谁有券?',
|
||||
unread: 12,
|
||||
isAi: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'VIP客户-王总',
|
||||
avatar: 'https://i.pravatar.cc/150?u=ceo',
|
||||
time: '昨天',
|
||||
lastMsg: '合同已确认',
|
||||
unread: 0,
|
||||
isAi: false,
|
||||
},
|
||||
])
|
||||
</script>
|
||||
275
TouchVueThree/src/views/Chat/ChatWindow.vue
Normal file
275
TouchVueThree/src/views/Chat/ChatWindow.vue
Normal file
@@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<div class="flex-1 flex flex-col min-w-0 bg-[#F8FAFC] relative">
|
||||
<!-- Header (关键修改:功能按钮 + 算力展示) -->
|
||||
<header
|
||||
class="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6 shadow-sm z-10"
|
||||
>
|
||||
<!-- Left: Chat Info -->
|
||||
<div class="flex items-center gap-4">
|
||||
<h2 class="font-bold text-lg text-gray-800">大野(朋友圈业务)</h2>
|
||||
<span
|
||||
class="px-2 py-0.5 bg-green-50 text-green-600 text-xs rounded border border-green-200 flex items-center gap-1"
|
||||
>
|
||||
<span
|
||||
class="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse"
|
||||
></span>
|
||||
微信在线
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Right: Functions & AI Credits -->
|
||||
<div class="flex items-center gap-6">
|
||||
<!-- Functional Buttons (Clean Style) -->
|
||||
<div class="flex items-center gap-2 border-r border-gray-200 pr-6">
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 rounded transition"
|
||||
title="发朋友圈"
|
||||
@click="$emit('post-moments')"
|
||||
>
|
||||
<i class="fa-brands fa-weixin"></i>
|
||||
<span class="hidden xl:inline">发朋友圈</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 rounded transition"
|
||||
title="同步消息"
|
||||
@click="$emit('sync-messages')"
|
||||
>
|
||||
<i class="fa-solid fa-arrows-rotate"></i>
|
||||
<span class="hidden xl:inline">同步</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 rounded transition"
|
||||
title="创建任务"
|
||||
@click="$emit('create-task')"
|
||||
>
|
||||
<i class="fa-regular fa-calendar-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 算力胶囊 -->
|
||||
<AICreditPill ref="creditPill" :balance="balance" />
|
||||
|
||||
<!-- Toggle Sidebar Info -->
|
||||
<button
|
||||
@click="$emit('toggle-detail')"
|
||||
class="text-gray-400 hover:text-indigo-600 transition ml-2"
|
||||
>
|
||||
<i class="fa-solid fa-circle-info text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 消息流 -->
|
||||
<div
|
||||
class="flex-1 overflow-y-auto p-6 space-y-6 custom-scroll"
|
||||
ref="scrollContainer"
|
||||
>
|
||||
<div class="text-center">
|
||||
<span
|
||||
class="text-[10px] text-gray-400 bg-gray-100 px-3 py-1 rounded-full"
|
||||
>今天 10:23</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div
|
||||
v-for="(msg, i) in messages"
|
||||
:key="i"
|
||||
class="flex gap-3 items-end group"
|
||||
:class="{ 'flex-row-reverse': msg.isMe }"
|
||||
>
|
||||
<img
|
||||
:src="
|
||||
msg.isMe
|
||||
? 'https://i.pravatar.cc/150?u=wx1'
|
||||
: 'https://i.pravatar.cc/150?u=8'
|
||||
"
|
||||
class="w-8 h-8 rounded-lg shadow-sm"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="flex flex-col gap-1 max-w-[70%]"
|
||||
:class="{ 'items-end': msg.isMe }"
|
||||
>
|
||||
<span class="text-[10px] text-gray-400 mx-1">{{
|
||||
msg.isMe ? '我' : '大野'
|
||||
}}</span>
|
||||
|
||||
<!-- AI 标识 -->
|
||||
<div v-if="msg.isAi" class="flex items-center gap-1 mb-0.5">
|
||||
<span
|
||||
class="text-[9px] bg-purple-50 text-purple-600 border border-purple-100 px-1 rounded"
|
||||
>AI 生成</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-3 rounded-2xl text-sm leading-relaxed shadow-sm relative"
|
||||
:class="
|
||||
msg.isMe
|
||||
? 'bg-indigo-600 text-white rounded-br-none'
|
||||
: 'bg-white text-slate-700 border border-gray-100 rounded-bl-none'
|
||||
"
|
||||
>
|
||||
{{ msg.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 建议卡片 (Interactive) -->
|
||||
<div v-if="showAiCard" class="flex justify-end pr-12 animate-fade-in-up">
|
||||
<div
|
||||
class="bg-gradient-to-br from-purple-50 to-white border border-purple-100 rounded-xl p-4 shadow-lg max-w-md relative overflow-hidden"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<div
|
||||
class="flex items-center gap-2 text-purple-600 font-bold text-xs"
|
||||
>
|
||||
<i class="fa-solid fa-sparkles"></i> AI 建议回复
|
||||
</div>
|
||||
<span
|
||||
class="text-[10px] text-orange-500 bg-orange-50 px-2 py-0.5 rounded border border-orange-100"
|
||||
>预估消耗 5 算力</span
|
||||
>
|
||||
</div>
|
||||
<p
|
||||
class="text-sm text-slate-600 mb-3 bg-white/50 p-2 rounded border border-purple-50 border-dashed"
|
||||
>
|
||||
“亲,没问题的!既然是老客户,这次直接帮您申请赠送幻化服务。现在下单的话,我这就去安排打手准备。”
|
||||
</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
@click="showAiCard = false"
|
||||
class="px-3 py-1.5 text-xs text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
忽略
|
||||
</button>
|
||||
<button
|
||||
@click="useAiSuggestion"
|
||||
class="px-4 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white text-xs rounded-lg shadow-sm transition flex items-center gap-2"
|
||||
>
|
||||
<i class="fa-solid fa-paper-plane"></i> 发送
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<div class="p-4 bg-white border-t border-gray-100 z-10">
|
||||
<div class="flex justify-between items-center mb-2 px-1">
|
||||
<div class="flex gap-4 text-gray-400">
|
||||
<i
|
||||
class="fa-regular fa-face-smile hover:text-indigo-600 cursor-pointer"
|
||||
></i>
|
||||
<i
|
||||
class="fa-solid fa-folder-open hover:text-indigo-600 cursor-pointer"
|
||||
></i>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-2 cursor-pointer"
|
||||
@click="aiMode = !aiMode"
|
||||
>
|
||||
<span class="text-xs font-bold text-slate-500">AI 辅助</span>
|
||||
<div
|
||||
class="w-8 h-4 bg-gray-200 rounded-full relative transition-colors"
|
||||
:class="{ 'bg-indigo-600': aiMode }"
|
||||
>
|
||||
<div
|
||||
class="w-3 h-3 bg-white rounded-full absolute top-0.5 transition-all shadow-sm"
|
||||
:class="aiMode ? 'right-0.5' : 'left-0.5'"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<textarea
|
||||
v-model="inputMsg"
|
||||
class="w-full h-20 bg-gray-50 border border-gray-200 rounded-xl p-3 text-sm focus:bg-white focus:border-indigo-500 focus:outline-none resize-none transition"
|
||||
placeholder="输入消息..."
|
||||
></textarea>
|
||||
<button
|
||||
@click="sendMsg"
|
||||
class="absolute bottom-3 right-3 bg-slate-900 text-white px-4 py-1.5 rounded-lg text-xs font-bold hover:bg-slate-700 transition"
|
||||
>
|
||||
发送
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from 'vue'
|
||||
import AICreditPill from '../../components/AICreditPill.vue'
|
||||
|
||||
defineEmits([
|
||||
'toggle-detail',
|
||||
'post-moments',
|
||||
'sync-messages',
|
||||
'create-task',
|
||||
])
|
||||
|
||||
const creditPill = ref(null)
|
||||
const scrollContainer = ref(null)
|
||||
const balance = ref(12450)
|
||||
const showAiCard = ref(true)
|
||||
const aiMode = ref(true)
|
||||
const inputMsg = ref('')
|
||||
|
||||
const messages = ref([
|
||||
{
|
||||
text: '老板,上次说的那个魔兽代练套餐,如果是老客户的话,还能再送一次幻化吗?',
|
||||
isMe: false,
|
||||
isAi: false,
|
||||
},
|
||||
])
|
||||
|
||||
const scrollToBottom = () => {
|
||||
nextTick(() => {
|
||||
if (scrollContainer.value) {
|
||||
scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const useAiSuggestion = () => {
|
||||
// 1. 扣费动画
|
||||
creditPill.value?.playDeductAnimation()
|
||||
balance.value -= 5
|
||||
|
||||
// 2. 发送消息
|
||||
messages.value.push({
|
||||
text: '亲,没问题的!既然是老客户,这次直接帮您申请赠送幻化服务。现在下单的话,我这就去安排打手准备。',
|
||||
isMe: true,
|
||||
isAi: true,
|
||||
})
|
||||
|
||||
// 3. 隐藏卡片
|
||||
showAiCard.value = false
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
const sendMsg = () => {
|
||||
if (!inputMsg.value.trim()) return
|
||||
messages.value.push({ text: inputMsg.value, isMe: true, isAi: false })
|
||||
inputMsg.value = ''
|
||||
scrollToBottom()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.animate-fade-in-up {
|
||||
animation: fadeInUp 0.4s ease-out;
|
||||
}
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
59
TouchVueThree/src/views/Chat/Index.vue
Normal file
59
TouchVueThree/src/views/Chat/Index.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="flex h-full w-full">
|
||||
<ChatList @select="selectChat" :activeId="activeChatId" />
|
||||
<ChatWindow
|
||||
:chat="activeChat"
|
||||
:credit-balance="creditBalance"
|
||||
@deduct-credit="handleCreditDeduction"
|
||||
@toggle-detail="showDetail = !showDetail"
|
||||
/>
|
||||
<ChatDetail :visible="showDetail" :chat="activeChat" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import ChatList from './ChatList.vue'
|
||||
import ChatWindow from './ChatWindow.vue'
|
||||
import ChatDetail from './ChatDetail.vue'
|
||||
|
||||
const showDetail = ref(false)
|
||||
const activeChatId = ref(1)
|
||||
const creditBalance = ref(12450)
|
||||
|
||||
// 模拟数据
|
||||
const chats = [
|
||||
{
|
||||
id: 1,
|
||||
name: '大野(朋友圈业务)',
|
||||
avatar: 'https://i.pravatar.cc/150?u=8',
|
||||
time: '11:45',
|
||||
msg: '已为您生成 3 条回复建议...',
|
||||
isAi: true,
|
||||
unread: 0,
|
||||
type: 'user',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '京东内购福利群',
|
||||
avatar: 'https://i.pravatar.cc/150?u=group',
|
||||
time: '10:20',
|
||||
msg: '张三: [图片] 谁有这个券?',
|
||||
isAi: false,
|
||||
unread: 99,
|
||||
type: 'group',
|
||||
},
|
||||
]
|
||||
|
||||
const activeChat = computed(() =>
|
||||
chats.find((c) => c.id === activeChatId.value)
|
||||
)
|
||||
|
||||
const selectChat = (id) => {
|
||||
activeChatId.value = id
|
||||
}
|
||||
|
||||
const handleCreditDeduction = (amount) => {
|
||||
creditBalance.value -= amount
|
||||
}
|
||||
</script>
|
||||
306
TouchVueThree/src/views/Contacts.vue
Normal file
306
TouchVueThree/src/views/Contacts.vue
Normal file
@@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div class="h-full overflow-y-auto custom-scroll bg-[#F8FAFC]">
|
||||
<div class="p-8 max-w-7xl mx-auto">
|
||||
<!-- 头部 -->
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-slate-800 mb-1">通讯录管理</h1>
|
||||
<p class="text-sm text-gray-500">管理您的所有联系人</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="relative">
|
||||
<i
|
||||
class="fa-solid fa-magnifying-glass absolute left-3 top-3 text-gray-400 text-xs"
|
||||
></i>
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="搜索联系人..."
|
||||
class="bg-white border border-gray-200 text-sm pl-9 pr-4 py-2 rounded-lg focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 transition-all outline-none w-64"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors flex items-center gap-2 text-sm font-medium"
|
||||
>
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
添加联系人
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||
<div
|
||||
class="bg-white rounded-xl p-4 border border-gray-100 shadow-sm hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">总联系人</p>
|
||||
<p class="text-2xl font-bold text-slate-800">{{ contacts.length }}</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<i class="fa-solid fa-address-book text-indigo-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white rounded-xl p-4 border border-gray-100 shadow-sm hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">最近联系</p>
|
||||
<p class="text-2xl font-bold text-slate-800">
|
||||
{{ recentContactsCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<i class="fa-solid fa-clock text-green-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bg-white rounded-xl p-4 border border-gray-100 shadow-sm hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">未读消息</p>
|
||||
<p class="text-2xl font-bold text-slate-800">{{ unreadCount }}</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-12 h-12 bg-red-100 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<i class="fa-solid fa-envelope text-red-600 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 联系人列表 -->
|
||||
<div class="bg-white rounded-xl border border-gray-100 shadow-sm overflow-hidden">
|
||||
<div class="p-4 border-b border-gray-100 flex items-center gap-2">
|
||||
<span
|
||||
v-for="tag in tags"
|
||||
:key="tag.id"
|
||||
@click="selectedTag = tag.id"
|
||||
:class="[
|
||||
'px-3 py-1.5 text-xs font-medium rounded-lg cursor-pointer transition-colors',
|
||||
selectedTag === tag.id
|
||||
? 'bg-indigo-100 text-indigo-700'
|
||||
: 'bg-gray-50 text-gray-600 hover:bg-gray-100',
|
||||
]"
|
||||
>
|
||||
{{ tag.name }}
|
||||
<span
|
||||
v-if="tag.count"
|
||||
class="ml-1 text-xs"
|
||||
:class="selectedTag === tag.id ? 'text-indigo-500' : 'text-gray-400'"
|
||||
>
|
||||
({{ tag.count }})
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div
|
||||
v-for="contact in filteredContacts"
|
||||
:key="contact.id"
|
||||
class="p-4 border border-gray-100 rounded-xl flex items-center gap-4 hover:shadow-md hover:border-indigo-200 transition-all bg-white group cursor-pointer"
|
||||
@click="openChat(contact)"
|
||||
>
|
||||
<div class="relative">
|
||||
<img
|
||||
:src="contact.avatar"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-white group-hover:ring-indigo-100 transition-all"
|
||||
:alt="contact.name"
|
||||
/>
|
||||
<div
|
||||
v-if="contact.isOnline"
|
||||
class="absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 bg-green-500 rounded-full border-2 border-white"
|
||||
></div>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<div class="font-semibold text-slate-800 truncate">
|
||||
{{ contact.name }}
|
||||
</div>
|
||||
<span
|
||||
v-if="contact.hasUnread"
|
||||
class="w-2 h-2 bg-red-500 rounded-full flex-shrink-0"
|
||||
></span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 truncate">
|
||||
{{ contact.lastContact || '暂无联系记录' }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-400 mt-0.5">
|
||||
{{ contact.phone || '未绑定手机号' }}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click.stop="startChat(contact)"
|
||||
class="opacity-0 group-hover:opacity-100 text-indigo-600 hover:bg-indigo-50 p-2 rounded-lg transition-all"
|
||||
title="发起聊天"
|
||||
>
|
||||
<i class="fa-regular fa-comment-dots"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div
|
||||
v-if="filteredContacts.length === 0"
|
||||
class="text-center py-12 text-gray-400"
|
||||
>
|
||||
<i class="fa-solid fa-address-book text-4xl mb-3 opacity-50"></i>
|
||||
<p class="text-sm">暂无联系人</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const searchQuery = ref('')
|
||||
const selectedTag = ref('all')
|
||||
|
||||
// 标签列表
|
||||
const tags = ref([
|
||||
{ id: 'all', name: '全部', count: 0 },
|
||||
{ id: 'recent', name: '最近联系', count: 0 },
|
||||
{ id: 'unread', name: '未读消息', count: 0 },
|
||||
])
|
||||
|
||||
// 联系人数据
|
||||
const contacts = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '客户 1',
|
||||
avatar: 'https://i.pravatar.cc/150?u=11',
|
||||
phone: '138****8888',
|
||||
lastContact: '最后联系:3天前',
|
||||
isOnline: true,
|
||||
hasUnread: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '客户 2',
|
||||
avatar: 'https://i.pravatar.cc/150?u=12',
|
||||
phone: '139****9999',
|
||||
lastContact: '最后联系:5天前',
|
||||
isOnline: false,
|
||||
hasUnread: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '客户 3',
|
||||
avatar: 'https://i.pravatar.cc/150?u=13',
|
||||
phone: '137****7777',
|
||||
lastContact: '最后联系:1周前',
|
||||
isOnline: true,
|
||||
hasUnread: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '客户 4',
|
||||
avatar: 'https://i.pravatar.cc/150?u=14',
|
||||
phone: '136****6666',
|
||||
lastContact: '最后联系:2周前',
|
||||
isOnline: false,
|
||||
hasUnread: false,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '客户 5',
|
||||
avatar: 'https://i.pravatar.cc/150?u=15',
|
||||
phone: '135****5555',
|
||||
lastContact: '最后联系:1个月前',
|
||||
isOnline: false,
|
||||
hasUnread: false,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '客户 6',
|
||||
avatar: 'https://i.pravatar.cc/150?u=16',
|
||||
phone: '134****4444',
|
||||
lastContact: '最后联系:今天',
|
||||
isOnline: true,
|
||||
hasUnread: false,
|
||||
},
|
||||
])
|
||||
|
||||
// 计算属性
|
||||
const recentContactsCount = computed(() => {
|
||||
return contacts.value.filter((c) => c.lastContact.includes('天')).length
|
||||
})
|
||||
|
||||
const unreadCount = computed(() => {
|
||||
return contacts.value.filter((c) => c.hasUnread).length
|
||||
})
|
||||
|
||||
// 更新标签计数
|
||||
tags.value[0].count = contacts.value.length
|
||||
tags.value[1].count = recentContactsCount.value
|
||||
tags.value[2].count = unreadCount.value
|
||||
|
||||
// 过滤联系人
|
||||
const filteredContacts = computed(() => {
|
||||
let result = contacts.value
|
||||
|
||||
// 按标签过滤
|
||||
if (selectedTag.value === 'recent') {
|
||||
result = result.filter((c) => c.lastContact.includes('天'))
|
||||
} else if (selectedTag.value === 'unread') {
|
||||
result = result.filter((c) => c.hasUnread)
|
||||
}
|
||||
|
||||
// 按搜索关键词过滤
|
||||
if (searchQuery.value.trim()) {
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
result = result.filter(
|
||||
(c) =>
|
||||
c.name.toLowerCase().includes(query) ||
|
||||
c.phone?.includes(query) ||
|
||||
''
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// 打开聊天
|
||||
const openChat = (contact) => {
|
||||
// TODO: 跳转到聊天页面并打开该联系人
|
||||
console.log('打开聊天:', contact)
|
||||
}
|
||||
|
||||
// 发起聊天
|
||||
const startChat = (contact) => {
|
||||
// TODO: 发起与联系人的聊天
|
||||
console.log('发起聊天:', contact)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义滚动条样式 */
|
||||
.custom-scroll::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
.custom-scroll::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.custom-scroll::-webkit-scrollbar-thumb {
|
||||
background: #e2e8f0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.custom-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: #cbd5e1;
|
||||
}
|
||||
</style>
|
||||
18
TouchVueThree/tailwind.config.js
Normal file
18
TouchVueThree/tailwind.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#4F46E5', // Indigo 600
|
||||
primaryLight: '#EEF2FF',
|
||||
accent: '#8B5CF6', // Violet 500
|
||||
accentLight: '#F3E8FF',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['"Plus Jakarta Sans"', 'Inter', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
38
TouchVueThree/vite.config.js
Normal file
38
TouchVueThree/vite.config.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
open: true,
|
||||
port: 5173,
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
assetsDir: 'assets',
|
||||
chunkSizeWarningLimit: 2000,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
vendor: ['vue'],
|
||||
},
|
||||
chunkFileNames: 'assets/js/[name]-[hash].js',
|
||||
entryFileNames: 'assets/js/[name]-[hash].js',
|
||||
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
|
||||
},
|
||||
},
|
||||
minify: 'esbuild',
|
||||
sourcemap: false,
|
||||
},
|
||||
})
|
||||
803
TouchVueThree/静态/index.html
Normal file
803
TouchVueThree/静态/index.html
Normal file
@@ -0,0 +1,803 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>触客宝 Pro - AI聚合工作台</title>
|
||||
<!-- Tailwind CSS (样式库) -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- FontAwesome (图标库) -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
<!-- Google Fonts -->
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
/* 自定义细滚动条 */
|
||||
.custom-scroll::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
.custom-scroll::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.custom-scroll::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.custom-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
/* 动画过渡类 */
|
||||
.transition-width {
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* 消息气泡样式 */
|
||||
.msg-bubble {
|
||||
max-width: 80%;
|
||||
position: relative;
|
||||
}
|
||||
.msg-bubble::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: '#4F46E5', // 品牌色
|
||||
brandHover: '#4338ca',
|
||||
ai: '#8B5CF6', // AI功能色
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body
|
||||
class="bg-gray-100 h-screen w-screen overflow-hidden flex text-slate-700"
|
||||
>
|
||||
<!-- 1. 左侧:账号与全局导航 (Account Sidebar) -->
|
||||
<div
|
||||
class="w-[70px] bg-slate-900 flex flex-col items-center py-6 flex-shrink-0 z-30 shadow-xl"
|
||||
>
|
||||
<!-- Brand Logo -->
|
||||
<div
|
||||
class="w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-xl flex items-center justify-center text-white text-lg shadow-lg mb-8 cursor-pointer hover:scale-105 transition"
|
||||
>
|
||||
<i class="fa-solid fa-bolt"></i>
|
||||
</div>
|
||||
|
||||
<!-- Main Nav -->
|
||||
<nav class="space-y-6 w-full flex flex-col items-center">
|
||||
<div
|
||||
class="nav-item active text-indigo-400 border-l-4 border-indigo-400 w-full flex justify-center cursor-pointer"
|
||||
>
|
||||
<i class="fa-solid fa-comments text-xl"></i>
|
||||
</div>
|
||||
<div
|
||||
class="nav-item text-gray-500 hover:text-gray-300 w-full flex justify-center cursor-pointer transition"
|
||||
>
|
||||
<i class="fa-solid fa-address-book text-xl"></i>
|
||||
</div>
|
||||
<div
|
||||
class="nav-item text-gray-500 hover:text-gray-300 w-full flex justify-center cursor-pointer transition"
|
||||
>
|
||||
<i class="fa-solid fa-chart-line text-xl"></i>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="w-8 border-b border-gray-700 my-6"></div>
|
||||
|
||||
<!-- WeChat Accounts -->
|
||||
<div
|
||||
class="flex-1 flex flex-col items-center gap-4 overflow-y-auto no-scrollbar w-full px-2"
|
||||
>
|
||||
<!-- Account 1 (Active) -->
|
||||
<div
|
||||
class="relative group cursor-pointer"
|
||||
onclick="switchAccount(this)"
|
||||
>
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=wx1"
|
||||
class="w-10 h-10 rounded-lg border-2 border-green-500 transition hover:opacity-90"
|
||||
/>
|
||||
<div
|
||||
class="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border-2 border-slate-900"
|
||||
></div>
|
||||
<!-- Tooltip -->
|
||||
<div
|
||||
class="absolute left-14 top-1 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap z-50 pointer-events-none"
|
||||
>
|
||||
商务号01 (在线)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account 2 -->
|
||||
<div
|
||||
class="relative group cursor-pointer opacity-60 hover:opacity-100 transition"
|
||||
onclick="switchAccount(this)"
|
||||
>
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=wx2"
|
||||
class="w-10 h-10 rounded-lg border-2 border-transparent group-hover:border-gray-500"
|
||||
/>
|
||||
<div
|
||||
class="absolute left-14 top-1 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap z-50 pointer-events-none"
|
||||
>
|
||||
销售小王
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add New -->
|
||||
<button
|
||||
class="w-10 h-10 rounded-lg border border-dashed border-gray-600 text-gray-500 hover:text-white hover:border-gray-400 flex items-center justify-center transition"
|
||||
>
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 text-gray-500 hover:text-white cursor-pointer">
|
||||
<i class="fa-solid fa-gear text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 中间:聊天列表 (Chat List) -->
|
||||
<div
|
||||
class="w-80 bg-white border-r border-gray-200 flex flex-col flex-shrink-0 z-20"
|
||||
>
|
||||
<!-- Search & Filter -->
|
||||
<div class="p-4 pb-2">
|
||||
<h2 class="font-bold text-xl text-gray-800 mb-4 px-1">消息</h2>
|
||||
<div class="relative group">
|
||||
<i
|
||||
class="fa-solid fa-magnifying-glass absolute left-3 top-3 text-gray-400 group-focus-within:text-brand transition"
|
||||
></i>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索联系人、群聊"
|
||||
class="w-full bg-gray-100 border-none rounded-lg pl-10 pr-4 py-2.5 text-sm focus:ring-2 focus:ring-brand/50 transition"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Quick Filters -->
|
||||
<div
|
||||
class="flex gap-2 mt-4 pb-2 border-b border-gray-100 overflow-x-auto no-scrollbar"
|
||||
>
|
||||
<button
|
||||
class="px-3 py-1 bg-brand/10 text-brand text-xs font-semibold rounded-full whitespace-nowrap"
|
||||
>
|
||||
全部
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-1 text-gray-500 hover:bg-gray-100 text-xs font-medium rounded-full whitespace-nowrap"
|
||||
>
|
||||
未读 (12)
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-1 text-purple-600 bg-purple-50 hover:bg-purple-100 text-xs font-medium rounded-full whitespace-nowrap flex items-center gap-1"
|
||||
>
|
||||
<i class="fa-solid fa-robot"></i> AI托管
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat Items List -->
|
||||
<div class="flex-1 overflow-y-auto custom-scroll" id="chatListContainer">
|
||||
<!-- JS will populate interactive items here, putting static ones for now -->
|
||||
|
||||
<!-- Item 1: AI Active -->
|
||||
<div
|
||||
class="chat-item p-3 mx-2 rounded-lg cursor-pointer bg-blue-50/50 border border-blue-100 transition relative"
|
||||
onclick="selectChat(this, '大野(朋友圈业务)', true)"
|
||||
>
|
||||
<div class="flex gap-3">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=8"
|
||||
class="w-11 h-11 rounded-lg object-cover"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-baseline mb-0.5">
|
||||
<h3 class="font-semibold text-gray-900 truncate text-sm">
|
||||
大野(朋友圈业务)
|
||||
</h3>
|
||||
<span class="text-[10px] text-gray-400">11:45</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span
|
||||
class="text-[10px] px-1 rounded bg-purple-100 text-purple-600 font-bold border border-purple-200"
|
||||
>AI</span
|
||||
>
|
||||
<p class="text-xs text-gray-500 truncate">
|
||||
已帮您生成3条回复建议...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Item 2: Unread -->
|
||||
<div
|
||||
class="chat-item p-3 mx-2 rounded-lg cursor-pointer hover:bg-gray-50 transition border-b border-transparent hover:border-gray-100"
|
||||
onclick="selectChat(this, '京东内购群', false)"
|
||||
>
|
||||
<div class="flex gap-3">
|
||||
<div class="relative">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=group"
|
||||
class="w-11 h-11 rounded-lg object-cover grayscale"
|
||||
/>
|
||||
<div
|
||||
class="absolute -top-1 -right-1 bg-red-500 text-white text-[10px] w-4 h-4 flex items-center justify-center rounded-full border border-white"
|
||||
>
|
||||
5
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-baseline mb-0.5">
|
||||
<h3 class="font-medium text-gray-700 truncate text-sm">
|
||||
京东内购群
|
||||
</h3>
|
||||
<span class="text-[10px] text-gray-400">10:20</span>
|
||||
</div>
|
||||
<p class="text-xs text-gray-400 truncate">
|
||||
张三: 谁有新的优惠券链接?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Item 3 -->
|
||||
<div
|
||||
class="chat-item p-3 mx-2 rounded-lg cursor-pointer hover:bg-gray-50 transition"
|
||||
onclick="selectChat(this, 'VIP客户-李总', false)"
|
||||
>
|
||||
<div class="flex gap-3">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=ceo"
|
||||
class="w-11 h-11 rounded-lg object-cover"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-baseline mb-0.5">
|
||||
<h3 class="font-medium text-gray-700 truncate text-sm">
|
||||
VIP客户-李总
|
||||
</h3>
|
||||
<span class="text-[10px] text-gray-400">昨天</span>
|
||||
</div>
|
||||
<p class="text-xs text-gray-400 truncate">
|
||||
好的,合同我让财务看一下。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. 右侧:主聊天区域 (Main Chat) -->
|
||||
<div class="flex-1 flex flex-col bg-slate-50 min-w-0 relative">
|
||||
<!-- Header (关键修改:功能按钮 + 算力展示) -->
|
||||
<header
|
||||
class="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6 shadow-sm z-10"
|
||||
>
|
||||
<!-- Left: Chat Info -->
|
||||
<div class="flex items-center gap-4">
|
||||
<h2 id="chatTitle" class="font-bold text-lg text-gray-800">
|
||||
大野(朋友圈业务)
|
||||
</h2>
|
||||
<span
|
||||
class="px-2 py-0.5 bg-green-50 text-green-600 text-xs rounded border border-green-200 flex items-center gap-1"
|
||||
>
|
||||
<span
|
||||
class="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse"
|
||||
></span>
|
||||
微信在线
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Right: Functions & AI Credits -->
|
||||
<div class="flex items-center gap-6">
|
||||
<!-- Functional Buttons (Clean Style) -->
|
||||
<div class="flex items-center gap-2 border-r border-gray-200 pr-6">
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-brand hover:bg-indigo-50 rounded transition"
|
||||
title="发朋友圈"
|
||||
>
|
||||
<i class="fa-brands fa-weixin"></i>
|
||||
<span class="hidden xl:inline">发朋友圈</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-brand hover:bg-indigo-50 rounded transition"
|
||||
title="同步消息"
|
||||
>
|
||||
<i class="fa-solid fa-arrows-rotate"></i>
|
||||
<span class="hidden xl:inline">同步</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-brand hover:bg-indigo-50 rounded transition"
|
||||
title="创建任务"
|
||||
>
|
||||
<i class="fa-regular fa-calendar-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- AI Compute Balance (算力值) - Highlighted -->
|
||||
<div
|
||||
class="group relative flex items-center gap-3 bg-gradient-to-r from-amber-50 to-orange-50 border border-orange-200 px-3 py-1.5 rounded-lg cursor-pointer hover:shadow-md transition"
|
||||
>
|
||||
<div class="flex flex-col items-end leading-none">
|
||||
<span
|
||||
class="text-[10px] text-orange-400 font-semibold uppercase tracking-wider"
|
||||
>AI 算力</span
|
||||
>
|
||||
<span
|
||||
class="text-sm font-bold text-gray-800 font-mono"
|
||||
id="aiBalance"
|
||||
>12,450</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="w-8 h-8 bg-gradient-to-br from-orange-400 to-amber-500 rounded-full flex items-center justify-center text-white shadow-sm group-hover:scale-110 transition"
|
||||
>
|
||||
<i class="fa-solid fa-bolt"></i>
|
||||
</div>
|
||||
<!-- Dropdown for Recharge -->
|
||||
<div
|
||||
class="absolute top-full right-0 mt-2 w-48 bg-white rounded-lg shadow-xl border border-gray-100 p-3 hidden group-hover:block animate-fade-in z-50"
|
||||
>
|
||||
<div class="text-xs text-gray-500 mb-2">预计可用: 3天</div>
|
||||
<button
|
||||
class="w-full bg-orange-500 text-white text-xs font-bold py-2 rounded hover:bg-orange-600 transition"
|
||||
>
|
||||
立即充值
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Sidebar Info -->
|
||||
<button
|
||||
onclick="toggleRightPanel()"
|
||||
class="text-gray-400 hover:text-brand transition ml-2"
|
||||
>
|
||||
<i class="fa-solid fa-circle-info text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Message Stream -->
|
||||
<div
|
||||
class="flex-1 overflow-y-auto p-6 space-y-6 custom-scroll"
|
||||
id="messageContainer"
|
||||
>
|
||||
<div class="text-center text-xs text-gray-400 my-4">今天 10:23</div>
|
||||
|
||||
<!-- Customer Message -->
|
||||
<div class="flex gap-4 items-end">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=8"
|
||||
class="w-8 h-8 rounded-full shadow-sm mb-1"
|
||||
/>
|
||||
<div class="flex flex-col gap-1 max-w-[70%]">
|
||||
<span class="text-xs text-gray-400 ml-1">大野</span>
|
||||
<div
|
||||
class="bg-white border border-gray-200 px-4 py-3 rounded-2xl rounded-bl-none shadow-sm text-sm text-gray-700 leading-relaxed"
|
||||
>
|
||||
老板,上次说的那个魔兽代练套餐,如果是老客户的话,还能再送一次幻化吗?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI Copilot Suggestion (Intervention) -->
|
||||
<div
|
||||
class="flex justify-end pr-12 animate-fade-in-up"
|
||||
id="aiSuggestionBox"
|
||||
>
|
||||
<div
|
||||
class="bg-purple-50 border border-purple-200 rounded-xl p-4 max-w-md shadow-sm relative"
|
||||
>
|
||||
<div
|
||||
class="absolute -top-3 -left-3 w-6 h-6 bg-purple-600 rounded-full flex items-center justify-center text-white text-xs border-2 border-white shadow-sm"
|
||||
>
|
||||
<i class="fa-solid fa-wand-magic-sparkles"></i>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="text-xs font-bold text-purple-700"
|
||||
>AI 建议回复 (消耗 5 算力)</span
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
onclick="useSuggestion()"
|
||||
class="text-xs bg-white hover:bg-purple-600 hover:text-white text-purple-600 px-3 py-1 rounded border border-purple-200 transition font-medium"
|
||||
>
|
||||
一键发送
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 italic">
|
||||
"亲,没问题的!既然是老客户,这次直接帮您申请赠送幻化服务。现在下单的话,我这就去安排打手准备。"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- My Message (Human) -->
|
||||
<!-- Populated via JS -->
|
||||
</div>
|
||||
|
||||
<!-- Input Area -->
|
||||
<footer class="bg-white p-4 border-t border-gray-200 z-20">
|
||||
<!-- Toolbar -->
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex gap-4 text-gray-400">
|
||||
<i
|
||||
class="fa-regular fa-face-smile text-lg hover:text-brand cursor-pointer transition"
|
||||
></i>
|
||||
<i
|
||||
class="fa-regular fa-image text-lg hover:text-brand cursor-pointer transition"
|
||||
></i>
|
||||
<i
|
||||
class="fa-solid fa-folder-open text-lg hover:text-brand cursor-pointer transition"
|
||||
title="话术库"
|
||||
></i>
|
||||
</div>
|
||||
|
||||
<!-- AI Switch -->
|
||||
<div
|
||||
class="flex items-center gap-2 bg-gray-100 rounded-full px-3 py-1 cursor-pointer"
|
||||
onclick="toggleAI(this)"
|
||||
>
|
||||
<span class="text-xs font-medium text-gray-500 select-none"
|
||||
>AI 辅助</span
|
||||
>
|
||||
<div
|
||||
class="w-8 h-4 bg-brand rounded-full relative transition-colors"
|
||||
id="aiToggleBg"
|
||||
>
|
||||
<div
|
||||
class="w-3 h-3 bg-white rounded-full absolute top-0.5 right-0.5 shadow-sm transition-all"
|
||||
id="aiToggleDot"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Textarea -->
|
||||
<div
|
||||
class="relative bg-gray-50 rounded-xl border border-gray-200 focus-within:border-brand focus-within:ring-1 focus-within:ring-brand transition"
|
||||
>
|
||||
<textarea
|
||||
id="msgInput"
|
||||
class="w-full bg-transparent h-24 p-3 text-sm resize-none focus:outline-none custom-scroll"
|
||||
placeholder="输入消息... (Shift+Enter 换行)"
|
||||
></textarea>
|
||||
<button
|
||||
onclick="sendMessage()"
|
||||
class="absolute bottom-3 right-3 bg-brand hover:bg-brandHover text-white px-4 py-1.5 rounded-lg text-sm font-medium transition shadow-md flex items-center gap-2"
|
||||
>
|
||||
发送 <i class="fa-regular fa-paper-plane text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- 4. 右侧:CRM/详情面板 (Right Panel) -->
|
||||
<div
|
||||
id="rightPanel"
|
||||
class="w-72 bg-white border-l border-gray-200 flex flex-col flex-shrink-0 transition-width overflow-hidden"
|
||||
>
|
||||
<!-- Tabs -->
|
||||
<div class="flex border-b border-gray-200">
|
||||
<button
|
||||
onclick="switchTab('profile')"
|
||||
id="tab-profile"
|
||||
class="flex-1 py-3 text-sm font-medium text-brand border-b-2 border-brand bg-indigo-50/30 transition"
|
||||
>
|
||||
客户资料
|
||||
</button>
|
||||
<button
|
||||
onclick="switchTab('scripts')"
|
||||
id="tab-scripts"
|
||||
class="flex-1 py-3 text-sm font-medium text-gray-500 hover:text-gray-700 transition"
|
||||
>
|
||||
话术/快捷
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="flex-1 overflow-y-auto p-5 custom-scroll relative">
|
||||
<!-- Tab: Profile -->
|
||||
<div id="content-profile" class="space-y-6 fade-in">
|
||||
<div class="text-center">
|
||||
<div class="relative inline-block">
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?u=8"
|
||||
class="w-20 h-20 rounded-full border-4 border-white shadow-lg object-cover"
|
||||
/>
|
||||
<div
|
||||
class="absolute bottom-0 right-0 bg-white p-1 rounded-full shadow-md cursor-pointer text-gray-500 hover:text-brand"
|
||||
>
|
||||
<i class="fa-solid fa-pen-to-square text-xs"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="mt-3 font-bold text-gray-800 text-lg">大野</h3>
|
||||
<p class="text-xs text-gray-400">ID: ys666wow</p>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div>
|
||||
<h4
|
||||
class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-2"
|
||||
>
|
||||
用户标签
|
||||
</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span
|
||||
class="tag px-2 py-1 bg-red-50 text-red-600 text-xs rounded border border-red-100"
|
||||
>高意向</span
|
||||
>
|
||||
<span
|
||||
class="tag px-2 py-1 bg-blue-50 text-blue-600 text-xs rounded border border-blue-100"
|
||||
>魔兽玩家</span
|
||||
>
|
||||
<button
|
||||
class="px-2 py-1 border border-dashed border-gray-300 text-gray-400 text-xs rounded hover:border-brand hover:text-brand transition"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info Grid -->
|
||||
<div class="space-y-4">
|
||||
<div class="group">
|
||||
<label class="text-xs text-gray-400 block mb-1">备注名</label>
|
||||
<input
|
||||
type="text"
|
||||
value="大野(朋友圈业务)"
|
||||
class="w-full text-sm border-b border-gray-200 bg-transparent py-1 focus:border-brand focus:outline-none transition"
|
||||
/>
|
||||
</div>
|
||||
<div class="group">
|
||||
<label class="text-xs text-gray-400 block mb-1">手机号</label>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600 font-mono">138****8888</span>
|
||||
<i
|
||||
class="fa-regular fa-copy text-gray-300 hover:text-brand cursor-pointer"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group">
|
||||
<label class="text-xs text-gray-400 block mb-1">跟进记录</label>
|
||||
<div
|
||||
class="bg-yellow-50 border border-yellow-100 p-2 rounded text-xs text-yellow-800 leading-snug"
|
||||
>
|
||||
上次提到需要发票,记得确认公司抬头。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab: Scripts (Hidden by default) -->
|
||||
<div id="content-scripts" class="hidden space-y-4 fade-in">
|
||||
<div class="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索话术..."
|
||||
class="w-full bg-gray-100 text-xs p-2 rounded border-none focus:ring-1 focus:ring-brand"
|
||||
/>
|
||||
<i
|
||||
class="fa-solid fa-magnifying-glass absolute right-2 top-2.5 text-gray-400 text-xs"
|
||||
></i>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
class="p-3 bg-gray-50 hover:bg-indigo-50 border border-gray-100 hover:border-indigo-100 rounded cursor-pointer group transition"
|
||||
onclick="insertScript(this)"
|
||||
>
|
||||
<h5
|
||||
class="text-xs font-bold text-gray-700 mb-1 group-hover:text-brand"
|
||||
>
|
||||
开场白 - 通用
|
||||
</h5>
|
||||
<p class="text-xs text-gray-500 line-clamp-2">
|
||||
您好,很高兴为您服务!请问有什么可以帮您的吗?我们目前正在进行...
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-gray-50 hover:bg-indigo-50 border border-gray-100 hover:border-indigo-100 rounded cursor-pointer group transition"
|
||||
onclick="insertScript(this)"
|
||||
>
|
||||
<h5
|
||||
class="text-xs font-bold text-gray-700 mb-1 group-hover:text-brand"
|
||||
>
|
||||
价格异议处理
|
||||
</h5>
|
||||
<p class="text-xs text-gray-500 line-clamp-2">
|
||||
亲,这个价格已经是我们的底价了,包含了一年的售后服务,性价比非常高...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JS Logic -->
|
||||
<script>
|
||||
// --- 1. 右侧栏折叠逻辑 ---
|
||||
let isPanelOpen = true
|
||||
function toggleRightPanel() {
|
||||
const panel = document.getElementById('rightPanel')
|
||||
if (isPanelOpen) {
|
||||
panel.style.width = '0px'
|
||||
panel.classList.add('opacity-0')
|
||||
} else {
|
||||
panel.style.width = '18rem' // 72 tailwind spacing
|
||||
panel.classList.remove('opacity-0')
|
||||
}
|
||||
isPanelOpen = !isPanelOpen
|
||||
}
|
||||
|
||||
// --- 2. 账号切换逻辑 ---
|
||||
function switchAccount(el) {
|
||||
// 简单的视觉切换,实际应用会加载数据
|
||||
document.querySelectorAll('.border-green-500').forEach((img) => {
|
||||
img.classList.remove('border-green-500')
|
||||
img.classList.add('border-transparent')
|
||||
img.parentElement.classList.add('opacity-60')
|
||||
})
|
||||
const img = el.querySelector('img')
|
||||
img.classList.remove('border-transparent')
|
||||
img.classList.add('border-green-500')
|
||||
el.classList.remove('opacity-60')
|
||||
}
|
||||
|
||||
// --- 3. 聊天列表选中逻辑 ---
|
||||
function selectChat(el, title, isAI) {
|
||||
// 清除旧选中状态
|
||||
document.querySelectorAll('.chat-item').forEach((item) => {
|
||||
item.classList.remove('bg-blue-50/50', 'border-blue-100')
|
||||
item.classList.add('border-transparent')
|
||||
})
|
||||
// 添加新选中状态
|
||||
el.classList.add('bg-blue-50/50', 'border-blue-100')
|
||||
el.classList.remove('border-transparent')
|
||||
|
||||
// 更新顶部标题
|
||||
document.getElementById('chatTitle').innerText = title
|
||||
}
|
||||
|
||||
// --- 4. Tab 切换逻辑 ---
|
||||
function switchTab(tabName) {
|
||||
// Reset Tabs
|
||||
document.getElementById('tab-profile').className =
|
||||
'flex-1 py-3 text-sm font-medium text-gray-500 hover:text-gray-700 transition cursor-pointer'
|
||||
document.getElementById('tab-scripts').className =
|
||||
'flex-1 py-3 text-sm font-medium text-gray-500 hover:text-gray-700 transition cursor-pointer'
|
||||
|
||||
// Set Active Tab
|
||||
document.getElementById(`tab-${tabName}`).className =
|
||||
'flex-1 py-3 text-sm font-medium text-brand border-b-2 border-brand bg-indigo-50/30 transition cursor-pointer'
|
||||
|
||||
// Toggle Content
|
||||
document.getElementById('content-profile').classList.add('hidden')
|
||||
document.getElementById('content-scripts').classList.add('hidden')
|
||||
document.getElementById(`content-${tabName}`).classList.remove('hidden')
|
||||
}
|
||||
|
||||
// --- 5. AI 开关动画 ---
|
||||
let aiEnabled = true
|
||||
function toggleAI(btn) {
|
||||
aiEnabled = !aiEnabled
|
||||
const bg = document.getElementById('aiToggleBg')
|
||||
const dot = document.getElementById('aiToggleDot')
|
||||
|
||||
if (aiEnabled) {
|
||||
bg.classList.replace('bg-gray-300', 'bg-brand')
|
||||
dot.classList.replace('right-5', 'right-0.5') // Move right
|
||||
dot.style.transform = 'translateX(0)'
|
||||
} else {
|
||||
bg.classList.replace('bg-brand', 'bg-gray-300')
|
||||
// Tailwind doesn't have right-auto easily, using transform for smoother toggle
|
||||
dot.style.transform = 'translateX(-16px)'
|
||||
}
|
||||
}
|
||||
|
||||
// --- 6. 发送消息模拟 & 算力扣除 ---
|
||||
function sendMessage() {
|
||||
const input = document.getElementById('msgInput')
|
||||
const text = input.value.trim()
|
||||
if (!text) return
|
||||
|
||||
const container = document.getElementById('messageContainer')
|
||||
|
||||
// 构建HTML字符串
|
||||
const html = `
|
||||
<div class="flex gap-4 items-end justify-end animate-fade-in-up">
|
||||
<div class="flex flex-col gap-1 items-end max-w-[70%]">
|
||||
<div class="bg-brand text-white px-4 py-2.5 rounded-2xl rounded-br-none shadow-md text-sm leading-relaxed">
|
||||
${text}
|
||||
</div>
|
||||
</div>
|
||||
<img src="https://i.pravatar.cc/150?u=wx1" class="w-8 h-8 rounded-full shadow-sm mb-1">
|
||||
</div>
|
||||
`
|
||||
|
||||
container.insertAdjacentHTML('beforeend', html)
|
||||
input.value = ''
|
||||
container.scrollTop = container.scrollHeight
|
||||
}
|
||||
|
||||
// --- 7. 使用AI建议 ---
|
||||
function useSuggestion() {
|
||||
const suggestion =
|
||||
'亲,没问题的!既然是老客户,这次直接帮您申请赠送幻化服务。现在下单的话,我这就去安排打手准备。'
|
||||
|
||||
// 扣除算力动画
|
||||
const balanceEl = document.getElementById('aiBalance')
|
||||
let current = parseInt(balanceEl.innerText.replace(',', ''))
|
||||
balanceEl.innerText = (current - 5).toLocaleString()
|
||||
balanceEl.classList.add('text-red-500') // Flash red
|
||||
setTimeout(() => balanceEl.classList.remove('text-red-500'), 500)
|
||||
|
||||
// 隐藏建议框
|
||||
document.getElementById('aiSuggestionBox').style.display = 'none'
|
||||
|
||||
// 发送消息
|
||||
const container = document.getElementById('messageContainer')
|
||||
const html = `
|
||||
<div class="flex gap-4 items-end justify-end animate-fade-in-up">
|
||||
<div class="flex flex-col gap-1 items-end max-w-[70%]">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="text-[10px] bg-purple-100 text-purple-600 px-1 rounded border border-purple-200 flex items-center gap-1">
|
||||
<i class="fa-solid fa-bolt"></i> AI生成
|
||||
</span>
|
||||
</div>
|
||||
<div class="bg-brand text-white px-4 py-3 rounded-2xl rounded-br-none shadow-md text-sm leading-relaxed">
|
||||
${suggestion}
|
||||
</div>
|
||||
</div>
|
||||
<img src="https://i.pravatar.cc/150?u=wx1" class="w-8 h-8 rounded-full shadow-sm mb-1">
|
||||
</div>
|
||||
`
|
||||
container.insertAdjacentHTML('beforeend', html)
|
||||
container.scrollTop = container.scrollHeight
|
||||
}
|
||||
|
||||
// --- 8. 插入话术 ---
|
||||
function insertScript(el) {
|
||||
const text = el.querySelector('p').innerText
|
||||
document.getElementById('msgInput').value = text
|
||||
}
|
||||
|
||||
// Add custom animation styles
|
||||
const style = document.createElement('style')
|
||||
style.innerHTML = `
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.animate-fade-in-up { animation: fadeInUp 0.3s ease-out forwards; }
|
||||
.animate-fade-in { animation: fadeInUp 0.2s ease-out forwards; }
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user