1.AI助手模版、样式修改成谢提供的对话模版(有特效)。
2.点击AI分析后,对话弹出类似商品链接引入,用户点击确认后进行分析。 3.AI分析对话新增提示词编辑,根据组件名称:模块编号进行获取提示词进行模块分析。 4.商品大盘首页字体调整,数据卡片中的字体调小一点,文字描述超长不显示,鼠标放上去显示全量。
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -142,5 +142,6 @@
|
||||
"package.json": "pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.eslintrc-auto-import.json,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore"
|
||||
},
|
||||
"terminal.integrated.scrollback": 10000,
|
||||
"nuxt.isNuxtApp": false
|
||||
"nuxt.isNuxtApp": false,
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
||||
|
||||
148
AIRBT.html
Normal file
148
AIRBT.html
Normal file
@@ -0,0 +1,148 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI 决策通</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
.ai-drawer { transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1); transform: translateX(110%); }
|
||||
.ai-drawer.active { transform: translateX(0); }
|
||||
.ai-drawer.fullscreen { width: 85vw !important; height: 80vh !important; right: 7.5vw !important; top: 10vh !important; border-radius: 1.5rem !important; transform: translateX(0); }
|
||||
.typing::after { content: '|'; animation: blink 1s infinite; margin-left: 2px; color: #3B82F6; }
|
||||
@keyframes blink { 50% { opacity: 0; } }
|
||||
.custom-scroll::-webkit-scrollbar { width: 5px; }
|
||||
.custom-scroll::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
|
||||
#overlay { display: none; position: fixed; inset: 0; background: rgba(15, 23, 42, 0.4); backdrop-filter: blur(4px); z-index: 55; }
|
||||
#overlay.active { display: block; }
|
||||
#fileInput, #imageInput { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-[#F1F5F9]">
|
||||
|
||||
<div id="overlay" onclick="closeAI()"></div>
|
||||
|
||||
<div class="fixed bottom-[85px] right-6 z-50" id="aiFab">
|
||||
<button onclick="openAI()" class="flex items-center gap-2 bg-white border border-blue-100 px-5 py-3 rounded-full shadow-2xl hover:scale-105 transition-all group">
|
||||
<div class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white shadow-lg shadow-blue-200">
|
||||
<i class="fas fa-robot text-sm"></i>
|
||||
</div>
|
||||
<span class="text-blue-600 font-bold text-sm tracking-wide">AI 决策助手</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="aiPanel" class="fixed top-0 right-0 h-full w-[420px] bg-white shadow-2xl z-[60] ai-drawer flex flex-col overflow-hidden border-l border-slate-100">
|
||||
|
||||
<div class="px-6 py-4 border-b flex items-center justify-between bg-white shrink-0">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 bg-blue-600 rounded-xl flex items-center justify-center text-white shadow-md shadow-blue-100">
|
||||
<i class="fas fa-robot text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-bold text-slate-800 text-sm">AI 决策助手</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<button onclick="toggleFullscreen()" class="w-9 h-9 rounded-lg hover:bg-slate-100 flex items-center justify-center text-slate-500">
|
||||
<i class="fas fa-expand-alt" id="expandIcon"></i>
|
||||
</button>
|
||||
<button onclick="closeAI()" class="w-9 h-9 rounded-lg hover:bg-red-50 flex items-center justify-center text-slate-400 hover:text-red-500">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chatBox" class="flex-1 overflow-y-auto p-6 space-y-6 bg-[#FBFCFE] custom-scroll">
|
||||
<div class="flex gap-4 items-start">
|
||||
<div class="w-8 h-8 bg-blue-50 text-blue-600 rounded-lg flex items-center justify-center shrink-0 mt-1 border border-blue-100">
|
||||
<i class="fas fa-robot text-xs"></i>
|
||||
</div>
|
||||
<div class="bg-white border border-slate-200 p-4 rounded-2xl rounded-tl-none shadow-sm text-sm text-slate-700">
|
||||
您好!请发送数据或输入指令。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-white border-t border-slate-100 shrink-0">
|
||||
<div class="max-w-4xl mx-auto flex flex-col">
|
||||
|
||||
<div class="flex gap-1 mb-2">
|
||||
<input type="file" id="fileInput" accept=".xls,.xlsx,.csv" onchange="handleFileUpload(event, 'excel')">
|
||||
<input type="file" id="imageInput" accept="image/*" onchange="handleFileUpload(event, 'image')">
|
||||
|
||||
<button onclick="triggerInput('image')" class="w-8 h-8 flex items-center justify-center text-slate-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-all">
|
||||
<i class="fas fa-image"></i>
|
||||
</button>
|
||||
<button onclick="triggerInput('excel')" class="w-8 h-8 flex items-center justify-center text-slate-400 hover:text-emerald-600 hover:bg-emerald-50 rounded-md transition-all">
|
||||
<i class="fas fa-file-excel"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 bg-slate-100 rounded-2xl px-5 py-1.5 border border-transparent focus-within:bg-white focus-within:border-blue-500 transition-all">
|
||||
<input id="chatInput" type="text" placeholder="输入指令..."
|
||||
class="flex-1 bg-transparent py-2.5 text-sm outline-none text-slate-700 font-medium"
|
||||
onkeypress="if(event.key==='Enter') sendMsg()">
|
||||
|
||||
<button onclick="sendMsg()" class="w-9 h-9 bg-blue-600 text-white rounded-xl flex items-center justify-center hover:bg-blue-700 shadow-lg shadow-blue-100 active:scale-95 transition-all">
|
||||
<i class="fas fa-paper-plane text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const panel = document.getElementById('aiPanel');
|
||||
const chatBox = document.getElementById('chatBox');
|
||||
const input = document.getElementById('chatInput');
|
||||
const overlay = document.getElementById('overlay');
|
||||
const fab = document.getElementById('aiFab');
|
||||
const icon = document.getElementById('expandIcon');
|
||||
|
||||
function openAI() { panel.classList.add('active'); overlay.classList.add('active'); fab.style.opacity = '0'; }
|
||||
function closeAI() { panel.classList.remove('active'); panel.classList.remove('fullscreen'); overlay.classList.remove('active'); fab.style.opacity = '1'; icon.className = 'fas fa-expand-alt'; }
|
||||
function toggleFullscreen() { panel.classList.toggle('fullscreen'); icon.className = panel.classList.contains('fullscreen') ? 'fas fa-compress-alt' : 'fas fa-expand-alt'; }
|
||||
function triggerInput(type) { document.getElementById(type === 'image' ? 'imageInput' : 'fileInput').click(); }
|
||||
|
||||
function handleFileUpload(event, type) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
const contentHtml = type === 'image' ? `<img src="${URL.createObjectURL(file)}" class="rounded-lg max-w-full">` : `<div class="flex items-center gap-3 bg-white/10 p-3 rounded-xl border border-white/20"><div class="w-8 h-8 bg-emerald-500 rounded flex items-center justify-center text-white shrink-0"><i class="fas fa-file-excel text-xs"></i></div><p class="text-xs font-bold truncate">${file.name}</p></div>`;
|
||||
const msgHtml = `<div class="flex justify-end"><div class="bg-blue-600 text-white p-3 rounded-2xl rounded-tr-none text-sm max-w-[85%] shadow-lg">${contentHtml}</div></div>`;
|
||||
chatBox.insertAdjacentHTML('beforeend', msgHtml);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
setTimeout(() => {
|
||||
const aiId = 'ai-' + Date.now();
|
||||
const aiHtml = `<div class="flex gap-4 items-start"><div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white shrink-0 mt-1 shadow-sm"><i class="fas fa-robot text-xs"></i></div><div class="bg-white border border-slate-200 p-4 rounded-2xl rounded-tl-none shadow-sm text-sm text-slate-700 flex-1"><p id="${aiId}" class="typing leading-relaxed"></p></div></div>`;
|
||||
chatBox.insertAdjacentHTML('beforeend', aiHtml);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
typeWriter(`正在分析您的文件数据...`, aiId);
|
||||
}, 800);
|
||||
}
|
||||
|
||||
function sendMsg() {
|
||||
const text = input.value.trim();
|
||||
if(!text) return;
|
||||
const userHtml = `<div class="flex justify-end"><div class="bg-blue-600 text-white p-4 rounded-2xl rounded-tr-none text-sm max-w-[80%] shadow-lg shadow-blue-100/50">${text}</div></div>`;
|
||||
chatBox.insertAdjacentHTML('beforeend', userHtml);
|
||||
input.value = '';
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
setTimeout(() => {
|
||||
const aiId = 'ai-' + Date.now();
|
||||
const aiHtml = `<div class="flex gap-4 items-start"><div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white shrink-0 mt-1 border border-blue-100"><i class="fas fa-robot text-xs"></i></div><div class="bg-white border border-slate-200 p-4 rounded-2xl rounded-tl-none shadow-sm text-sm text-slate-700 flex-1"><p id="${aiId}" class="typing leading-relaxed"></p></div></div>`;
|
||||
chatBox.insertAdjacentHTML('beforeend', aiHtml);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
typeWriter(`正在处理您的指令...`, aiId);
|
||||
}, 600);
|
||||
}
|
||||
|
||||
function typeWriter(text, elementId) {
|
||||
let i = 0;
|
||||
const el = document.getElementById(elementId);
|
||||
function type() { if (i < text.length) { el.innerHTML += text.charAt(i); i++; chatBox.scrollTop = chatBox.scrollHeight; setTimeout(type, 30); } else { el.classList.remove('typing'); } }
|
||||
type();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
50
package-lock.json
generated
50
package-lock.json
generated
@@ -35,6 +35,7 @@
|
||||
"element-plus": "2.11.1",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"highlight.js": "^11.9.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jsoneditor": "^10.1.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -7813,6 +7814,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -8987,6 +8997,15 @@
|
||||
"node": ">=12 || >=16"
|
||||
}
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.1.0.tgz",
|
||||
@@ -12286,6 +12305,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
@@ -18834,6 +18866,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
|
||||
@@ -19806,6 +19847,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"element-plus": "2.11.1",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"highlight.js": "^11.9.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jsoneditor": "^10.1.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
39
pnpm-lock.yaml
generated
39
pnpm-lock.yaml
generated
@@ -86,6 +86,9 @@ importers:
|
||||
highlight.js:
|
||||
specifier: ^11.9.0
|
||||
version: 11.10.0
|
||||
html2canvas:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
jsencrypt:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
@@ -2531,6 +2534,10 @@ packages:
|
||||
balanced-match@2.0.0:
|
||||
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
base@0.11.2:
|
||||
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2842,6 +2849,9 @@ packages:
|
||||
resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==}
|
||||
engines: {node: '>=12 || >=16'}
|
||||
|
||||
css-line-break@2.1.0:
|
||||
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
||||
|
||||
css-select@4.3.0:
|
||||
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
|
||||
|
||||
@@ -3798,6 +3808,10 @@ packages:
|
||||
html-void-elements@3.0.0:
|
||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||
|
||||
html2canvas@1.4.1:
|
||||
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
htmlparser2@3.10.1:
|
||||
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
|
||||
|
||||
@@ -5480,6 +5494,9 @@ packages:
|
||||
resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
text-segmentation@1.0.3:
|
||||
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
|
||||
|
||||
text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
|
||||
@@ -5706,6 +5723,9 @@ packages:
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
utrie@1.0.2:
|
||||
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
|
||||
|
||||
uuid@10.0.0:
|
||||
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||
hasBin: true
|
||||
@@ -8603,6 +8623,8 @@ snapshots:
|
||||
|
||||
balanced-match@2.0.0: {}
|
||||
|
||||
base64-arraybuffer@1.0.2: {}
|
||||
|
||||
base@0.11.2:
|
||||
dependencies:
|
||||
cache-base: 1.0.1
|
||||
@@ -8962,6 +8984,10 @@ snapshots:
|
||||
|
||||
css-functions-list@3.2.3: {}
|
||||
|
||||
css-line-break@2.1.0:
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
|
||||
css-select@4.3.0:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
@@ -10129,6 +10155,11 @@ snapshots:
|
||||
|
||||
html-void-elements@3.0.0: {}
|
||||
|
||||
html2canvas@1.4.1:
|
||||
dependencies:
|
||||
css-line-break: 2.1.0
|
||||
text-segmentation: 1.0.3
|
||||
|
||||
htmlparser2@3.10.1:
|
||||
dependencies:
|
||||
domelementtype: 1.3.1
|
||||
@@ -11880,6 +11911,10 @@ snapshots:
|
||||
|
||||
text-extensions@2.4.0: {}
|
||||
|
||||
text-segmentation@1.0.3:
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
|
||||
text-table@0.2.0: {}
|
||||
|
||||
through@2.3.8: {}
|
||||
@@ -12163,6 +12198,10 @@ snapshots:
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
utrie@1.0.2:
|
||||
dependencies:
|
||||
base64-arraybuffer: 1.0.2
|
||||
|
||||
uuid@10.0.0: {}
|
||||
|
||||
vanilla-picker@2.12.3:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { isDark } from '@/utils/is'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
import routerSearch from '@/components/RouterSearch/index.vue'
|
||||
import AiAssistantProvider from '@/components/AiAssistant/AiAssistantProvider.vue'
|
||||
|
||||
defineOptions({ name: 'APP' })
|
||||
|
||||
@@ -14,11 +14,11 @@ const currentSize = computed(() => appStore.getCurrentSize)
|
||||
const greyMode = computed(() => appStore.getGreyMode)
|
||||
const { wsCache } = useCache()
|
||||
|
||||
// 根据浏览器当前主题设置系统主题色
|
||||
// 默认主题为白色(浅色),无缓存时使用浅色;有缓存则沿用用户选择
|
||||
const setDefaultTheme = () => {
|
||||
let isDarkTheme = wsCache.get(CACHE_KEY.IS_DARK)
|
||||
if (isDarkTheme === null) {
|
||||
isDarkTheme = isDark()
|
||||
isDarkTheme = false
|
||||
}
|
||||
appStore.setIsDark(isDarkTheme)
|
||||
}
|
||||
@@ -26,8 +26,10 @@ setDefaultTheme()
|
||||
</script>
|
||||
<template>
|
||||
<ConfigGlobal :size="currentSize">
|
||||
<AiAssistantProvider>
|
||||
<RouterView :class="greyMode ? `${prefixCls}-grey-mode` : ''" />
|
||||
<routerSearch />
|
||||
</AiAssistantProvider>
|
||||
</ConfigGlobal>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
|
||||
@@ -13,5 +13,7 @@ const prefixCls = getPrefixCls('backtop')
|
||||
<ElBacktop
|
||||
:class="`${prefixCls}-backtop`"
|
||||
:target="`.${variables.namespace}-layout-content-scrollbar .${variables.elNamespace}-scrollbar__wrap`"
|
||||
:right="24"
|
||||
:bottom="90"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -59,6 +59,10 @@ service.interceptors.request.use(
|
||||
// 设置超时时间为 24 小时(86400000ms),确保无论多久都等待响应
|
||||
config.timeout = 24 * 60 * 60 * 1000 // 24小时 = 86400000毫秒
|
||||
}
|
||||
// AI 图片对话流式接口:不超时
|
||||
if (config.url && config.url.includes('/ydoyun/ai-chat/stream')) {
|
||||
config.timeout = 0
|
||||
}
|
||||
|
||||
// 是否需要设置 token
|
||||
let isToken = (config!.headers || {}).isToken === false
|
||||
|
||||
@@ -35,6 +35,7 @@ provide('reload', reload)
|
||||
|
||||
<template>
|
||||
<section
|
||||
id="app-main-content"
|
||||
:class="[
|
||||
'p-[var(--app-content-padding)] w-full bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
|
||||
{
|
||||
|
||||
@@ -599,6 +599,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
hidden: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'image-chat',
|
||||
component: () => import('@/views/ydoyun/aichat/index.vue'),
|
||||
name: 'AiImageChat',
|
||||
meta: {
|
||||
title: 'AI 图片对话',
|
||||
icon: 'ep:chat-dot-round',
|
||||
noCache: false
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'image/square',
|
||||
component: () => import('@/views/ai/image/square/index.vue'),
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<div class="kpi-value">{{ kpi.value }}</div>
|
||||
<div class="kpi-footer">
|
||||
<span class="kpi-trend" :class="kpi.trendClass">{{ kpi.trendText }}</span>
|
||||
<el-tooltip :content="kpi.desc" placement="top" :show-after="300" class="kpi-desc-tooltip">
|
||||
<span class="kpi-desc">{{ kpi.desc }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 迷你柱状图 -->
|
||||
<div v-if="kpi.miniBars" class="mini-bars">
|
||||
@@ -77,7 +79,7 @@ defineProps<{
|
||||
}
|
||||
|
||||
.kpi-value {
|
||||
font-size: 32px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
line-height: 1.2;
|
||||
@@ -90,8 +92,10 @@ defineProps<{
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
min-width: 0;
|
||||
|
||||
.kpi-trend {
|
||||
flex-shrink: 0;
|
||||
font-weight: 500;
|
||||
|
||||
&.trend-up {
|
||||
@@ -103,7 +107,16 @@ defineProps<{
|
||||
}
|
||||
}
|
||||
|
||||
.kpi-desc-tooltip {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.kpi-desc {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,11 +162,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { ReportApi } from '@/api/ydoyun/report/reportpage'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
|
||||
const REPORT_ID = 6
|
||||
|
||||
@@ -190,7 +191,10 @@ interface MiddleClassItem {
|
||||
defineOptions({ name: 'MiddleClassRanking' })
|
||||
|
||||
const route = useRoute()
|
||||
const { setPageLoading } = useAiAssistant()
|
||||
const loading = ref(false)
|
||||
watch(loading, (v) => setPageLoading(v), { immediate: true })
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
const dateRange = ref<[string, string] | null>([
|
||||
dayjs().subtract(6, 'day').format('YYYY-MM-DD'),
|
||||
dayjs().format('YYYY-MM-DD')
|
||||
|
||||
@@ -520,13 +520,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { Picture, Grid, List, Search } from '@element-plus/icons-vue'
|
||||
import { ReportApi } from '@/api/ydoyun/report/reportpage'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
|
||||
const REPORT_ID = 6
|
||||
|
||||
@@ -585,7 +586,10 @@ defineOptions({ name: 'ProductCardsPage' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { setPageLoading } = useAiAssistant()
|
||||
const loading = ref(false)
|
||||
watch(loading, (v) => setPageLoading(v), { immediate: true })
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
const userStore = useUserStore()
|
||||
const username = computed(() => userStore.user?.username || '')
|
||||
|
||||
|
||||
@@ -212,6 +212,11 @@
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="view-toolbar-right">
|
||||
<el-button link type="primary" size="small" @click="openPromptEdit">
|
||||
<Icon icon="ep:edit" /> 编辑提示词
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 标签分类筛选:与主页供应商表现一致 -->
|
||||
@@ -294,8 +299,15 @@
|
||||
|
||||
<!-- 底部 -->
|
||||
<div class="card-footer">
|
||||
<span class="footer-label"> </span>
|
||||
<span class="footer-link">详情 →</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="default"
|
||||
class="ai-analysis-btn"
|
||||
@click="onCardAiAnalysis($event)"
|
||||
>
|
||||
<el-icon class="btn-icon"><ChatDotRound /></el-icon>
|
||||
AI分析
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -360,17 +372,29 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<AiPromptEditDialog
|
||||
v-model="promptEditVisible"
|
||||
:module-key="SUPPLIER_PERFORMANCE_MODULE_KEY"
|
||||
module-name="供应商表现"
|
||||
:initial-prompt="promptMap[SUPPLIER_PERFORMANCE_MODULE_KEY]"
|
||||
@saved="loadPromptMap"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
import { Grid, List } from '@element-plus/icons-vue'
|
||||
import { Grid, List, ChatDotRound } from '@element-plus/icons-vue'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { ReportApi } from '@/api/ydoyun/report/reportpage'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
import { AiModulePromptApi } from '@/api/ydoyun/aiModulePrompt'
|
||||
import AiPromptEditDialog from './AiPromptEditDialog.vue'
|
||||
|
||||
interface ColumnCfg {
|
||||
title: string
|
||||
@@ -380,10 +404,41 @@ interface ColumnCfg {
|
||||
colorKey?: string
|
||||
}
|
||||
|
||||
const SUPPLIER_PERFORMANCE_COMPONENT = 'SupplierPerformance'
|
||||
const SUPPLIER_PERFORMANCE_MODULE_KEY = `${SUPPLIER_PERFORMANCE_COMPONENT}:main`
|
||||
|
||||
const REPORT_ID = 6
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
const { openWithScreenshot, setPageLoading } = useAiAssistant()
|
||||
const loading = ref(false)
|
||||
watch(loading, (v) => setPageLoading(v), { immediate: true })
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
|
||||
const promptMap = ref<Record<string, string>>({})
|
||||
const promptEditVisible = ref(false)
|
||||
|
||||
const openPromptEdit = () => {
|
||||
promptEditVisible.value = true
|
||||
}
|
||||
|
||||
const loadPromptMap = async () => {
|
||||
try {
|
||||
const map = await AiModulePromptApi.getPromptMapByComponent(SUPPLIER_PERFORMANCE_COMPONENT)
|
||||
promptMap.value = map || {}
|
||||
} catch {
|
||||
promptMap.value = {}
|
||||
}
|
||||
}
|
||||
|
||||
/** 卡片 AI 分析:截图当前卡片并打开 AI 对话框,代入模块名+提示词 */
|
||||
function onCardAiAnalysis(e: MouseEvent) {
|
||||
const card = (e.currentTarget as HTMLElement).closest('.supplier-card')
|
||||
if (card instanceof HTMLElement) {
|
||||
const prompt = promptMap.value[SUPPLIER_PERFORMANCE_MODULE_KEY]
|
||||
openWithScreenshot(card, { moduleName: '供应商表现', prompt })
|
||||
}
|
||||
}
|
||||
|
||||
/** 展示形式:卡片 | 列表 */
|
||||
const displayMode = ref<'card' | 'table'>('card')
|
||||
@@ -906,6 +961,7 @@ const handleReset = () => {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadPromptMap()
|
||||
// 等待下一个 tick,确保路由参数完全加载
|
||||
await nextTick()
|
||||
|
||||
@@ -952,6 +1008,10 @@ onMounted(async () => {
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.view-toolbar-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.view-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -1469,36 +1529,38 @@ onMounted(async () => {
|
||||
|
||||
/* 底部 */
|
||||
.card-footer {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 2px solid var(--el-border-color-lighter);
|
||||
margin-top: 20px;
|
||||
padding: 18px 8px 0;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 8px;
|
||||
justify-content: flex-end;
|
||||
min-height: 52px;
|
||||
}
|
||||
|
||||
.footer-label {
|
||||
font-size: 10px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
font-style: italic;
|
||||
.ai-analysis-btn {
|
||||
font-size: 14px !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 10px 24px !important;
|
||||
border-radius: 12px !important;
|
||||
border: none !important;
|
||||
background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%) !important;
|
||||
color: #fff !important;
|
||||
box-shadow: 0 2px 10px rgba(64, 158, 255, 0.22);
|
||||
transition: all 0.25s ease;
|
||||
|
||||
.btn-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
vertical-align: -0.12em;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
font-size: 10px;
|
||||
color: #3b82f6;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
text-decoration: underline;
|
||||
color: #2563eb;
|
||||
.ai-analysis-btn:hover {
|
||||
background: linear-gradient(135deg, var(--el-color-primary-light-3) 0%, var(--el-color-primary) 100%) !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(64, 158, 255, 0.32);
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
|
||||
@@ -168,11 +168,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { ReportApi } from '@/api/ydoyun/report/reportpage'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
|
||||
const REPORT_ID = 6
|
||||
|
||||
@@ -195,7 +196,10 @@ interface SupplierContributionData {
|
||||
defineOptions({ name: 'SupplierRanking' })
|
||||
|
||||
const route = useRoute()
|
||||
const { setPageLoading } = useAiAssistant()
|
||||
const loading = ref(false)
|
||||
watch(loading, (v) => setPageLoading(v), { immediate: true })
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
const dateRange = ref<[string, string] | null>([
|
||||
dayjs().subtract(6, 'day').format('YYYY-MM-DD'),
|
||||
dayjs().format('YYYY-MM-DD')
|
||||
|
||||
@@ -371,13 +371,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { Search, Grid, List } from '@element-plus/icons-vue'
|
||||
import { ReportApi } from '@/api/ydoyun/report/reportpage'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
|
||||
defineOptions({ name: 'CategoryCardListComponents' })
|
||||
|
||||
@@ -472,7 +473,10 @@ const getTimeRange = (range: string) => {
|
||||
return { rq: start.format('YYYY-MM-DD'), rq2: end.format('YYYY-MM-DD') }
|
||||
}
|
||||
|
||||
const { setPageLoading } = useAiAssistant()
|
||||
const loading = ref(false)
|
||||
watch(loading, (v) => setPageLoading(v), { immediate: true })
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
/** 展示形式:卡片 | 列表 */
|
||||
const displayMode = ref<'card' | 'table'>('card')
|
||||
const columns = ref<{ title: string; key: string; order?: number; labelKey?: string; colorKey?: string }[]>([])
|
||||
|
||||
@@ -434,13 +434,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
import { Picture, Grid, List, Search } from '@element-plus/icons-vue'
|
||||
import { ReportApi } from '@/api/ydoyun/report/reportpage'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
import type { ProductDetailData, ProductSizeStatus, UiStatus } from './index.vue'
|
||||
|
||||
defineOptions({ name: 'ProductDetailPage' })
|
||||
@@ -449,7 +450,10 @@ const REPORT_ID = 6
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const { setPageLoading } = useAiAssistant()
|
||||
const loading = ref(false)
|
||||
watch(loading, (v) => setPageLoading(v), { immediate: true })
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
const productList = ref<ProductDetailData[]>([])
|
||||
/** 标签分类筛选 */
|
||||
const labelFilter = ref<string>('all')
|
||||
|
||||
@@ -196,7 +196,11 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- KPI 卡片区域(加载中显示特效) -->
|
||||
<!-- KPI 卡片区域 -->
|
||||
<div class="kpi-section">
|
||||
<div class="module-header">
|
||||
<span class="module-title">KPI 指标</span>
|
||||
</div>
|
||||
<div
|
||||
class="kpi-grid"
|
||||
v-loading="loadingKpi"
|
||||
@@ -211,7 +215,9 @@
|
||||
<div v-if="kpi.value != null && kpi.value !== ''" class="kpi-value">{{ kpi.value }}</div>
|
||||
<div v-if="(formatTrendText(kpi) || (kpi.descs != null && kpi.descs !== ''))" class="kpi-footer">
|
||||
<span v-if="formatTrendText(kpi)" class="kpi-trend" :class="kpi.trend">{{ formatTrendText(kpi) }}</span>
|
||||
<span v-if="kpi.descs != null && kpi.descs !== ''" class="kpi-desc">{{ kpi.descs }}</span>
|
||||
<el-tooltip v-if="kpi.descs != null && kpi.descs !== ''" :content="kpi.descs" placement="top" :show-after="300" class="kpi-desc-tooltip">
|
||||
<span class="kpi-desc">{{ kpi.descs }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 迷你柱状图 -->
|
||||
<div v-if="kpi.miniBars" class="mini-bars">
|
||||
@@ -238,10 +244,11 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 供应商表现 (Top 10) + 中类销售排名 Top 5 -->
|
||||
<div class="middle-section">
|
||||
<!-- 供应商表现 (Top 10):KBStyle 筛选 + 紧凑表格 + 展开更多 -->
|
||||
<!-- 供应商表现 (Top 10) -->
|
||||
<el-card
|
||||
class="kb-cat-card"
|
||||
shadow="never"
|
||||
@@ -253,10 +260,12 @@
|
||||
<el-icon class="kb-title-icon"><StarFilled /></el-icon>
|
||||
<h3 class="kb-card-title">供应商表现 (Top 10)</h3>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" link size="small" @click="handleViewMoreCategories" class="more-button">
|
||||
更多 <el-icon><ArrowRight /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kb-card-content">
|
||||
<div class="filter-bar">
|
||||
<div
|
||||
@@ -489,6 +498,7 @@
|
||||
<el-icon class="category-title-icon"><StarFilled /></el-icon>
|
||||
<h3 class="category-card-title">中类销售排名 Top 5</h3>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@@ -500,6 +510,7 @@
|
||||
<el-icon><ArrowRight /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="category-pie-wrap">
|
||||
<Echart
|
||||
v-if="categoryTop6.length > 0"
|
||||
@@ -799,7 +810,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, onBeforeUnmount, onDeactivated, nextTick } from 'vue'
|
||||
import { ref, reactive, computed, watch, onMounted, onUnmounted, onBeforeUnmount, onDeactivated, nextTick } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import Echart from '@/components/Echart/src/Echart.vue'
|
||||
@@ -817,6 +828,7 @@ import { ElMessage } from 'element-plus'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { CategoryDiagnosticData } from './components/categoryCardListComponents.vue'
|
||||
import { useAiAssistant } from '@/components/AiAssistant/useAiAssistant'
|
||||
|
||||
defineOptions({ name: 'ProductDashboard' })
|
||||
|
||||
@@ -1233,6 +1245,15 @@ const loadingCategory = ref(false)
|
||||
const loadingPie = ref(false)
|
||||
/** 商品明细列表加载状态 */
|
||||
const loadingProductList = ref(false)
|
||||
|
||||
const { setPageLoading } = useAiAssistant()
|
||||
watch(
|
||||
[loadingKpi, loadingCategory, loadingPie, loadingProductList],
|
||||
([a, b, c, d]) => setPageLoading(!!(a || b || c || d)),
|
||||
{ immediate: true }
|
||||
)
|
||||
onUnmounted(() => setPageLoading(false))
|
||||
|
||||
const searchKeyword = ref('')
|
||||
const hoverDetail = ref<ProductDetailData | null>(null)
|
||||
|
||||
@@ -2861,7 +2882,7 @@ onDeactivated(() => {
|
||||
}
|
||||
|
||||
.kpi-value {
|
||||
font-size: 32px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--el-text-color-primary);
|
||||
line-height: 1.2;
|
||||
@@ -2874,8 +2895,10 @@ onDeactivated(() => {
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
min-width: 0;
|
||||
|
||||
.kpi-trend {
|
||||
flex-shrink: 0;
|
||||
font-weight: 500;
|
||||
|
||||
&.up {
|
||||
@@ -2891,7 +2914,16 @@ onDeactivated(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.kpi-desc-tooltip {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.kpi-desc {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
@@ -4197,4 +4229,5 @@ onDeactivated(() => {
|
||||
.kb22-legend-item { display: flex; align-items: center; gap: 4px; font-size: 9px; color: var(--el-text-color-placeholder); }
|
||||
.kb22-dot { width: 6px; height: 6px; border-radius: 50%; }
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user