This commit is contained in:
2026-07-28 11:04:45 +08:00
parent e1fe818dce
commit 30cf33c546
841 changed files with 2550 additions and 2553 deletions
+230 -266
View File
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>后台管理系统</title>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>后台管理系统</title>
<style>
* {
margin: 0;
@@ -50,33 +50,33 @@
stroke-dashoffset: -120px;
}
}
</style>
<script>
// 动态加载 Chat UIKit 样式
(function() {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://web.sdk.qcloud.com/im/assets/latest/index.css';
document.head.appendChild(link);
})();
</script>
<!-- Dify AI 客服机器人(全局悬浮气泡) -->
<script>
window.difyChatbotConfig = {
token: 'OcRvj5VVR76cME9J',
baseUrl: 'https://ai.zhenyangtang.com.cn/',
draggable: true,
dragAxis: 'both',
inputs: {},
systemVariables: {},
userVariables: {}
}
</script>
<script
src="https://ai.zhenyangtang.com.cn/embed.min.js"
id="OcRvj5VVR76cME9J"
defer
></script>
</style>
<script>
// 动态加载 Chat UIKit 样式
(function() {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://web.sdk.qcloud.com/im/assets/latest/index.css';
document.head.appendChild(link);
})();
</script>
<!-- Dify AI 客服机器人(全局悬浮气泡) -->
<script>
window.difyChatbotConfig = {
token: 'OcRvj5VVR76cME9J',
baseUrl: 'https://ai.zhenyangtang.com.cn/',
draggable: true,
dragAxis: 'both',
inputs: {},
systemVariables: {},
userVariables: {}
}
</script>
<script
src="https://ai.zhenyangtang.com.cn/embed.min.js"
id="OcRvj5VVR76cME9J"
defer
></script>
<style>
/* 气泡按钮使用自定义 AI 图标(base 为 /admin/public 资源在该路径下) */
#dify-chatbot-bubble-button {
@@ -162,235 +162,199 @@
background: #f2f4f7;
z-index: 2147483641;
}
</style>
<script type="module" crossorigin src="/admin/assets/index-DiYdpaIG.js"></script>
<link rel="modulepreload" crossorigin href="/admin/assets/@babel/runtime-BanGtE2-.js">
<link rel="modulepreload" crossorigin href="/admin/assets/fflate-_ayOYiT1.js">
<link rel="modulepreload" crossorigin href="/admin/assets/jspdf-BsthCvR5.js">
<link rel="modulepreload" crossorigin href="/admin/assets/lodash-D3kF6u-c.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@vue/shared-mAAVTE9n.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@vue/reactivity-DiY1c2vO.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@vue/runtime-core-C6bnekPw.js">
<link rel="modulepreload" crossorigin href="/admin/assets/vue-router-QlpZ4wdW.js">
<link rel="modulepreload" crossorigin href="/admin/assets/pinia-B8cvXbiZ.js">
<link rel="modulepreload" crossorigin href="/admin/assets/axios-C80V62Fs.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@vue/runtime-dom-DDAG46FW.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@element-plus/icons-vue-B0jSCQ-G.js">
<link rel="modulepreload" crossorigin href="/admin/assets/lodash-es-C2A-Pj28.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@popperjs/core-C5az9QE8.js">
<link rel="modulepreload" crossorigin href="/admin/assets/dayjs-DG77mNTn.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@ctrl/tinycolor-BuyEbX8F.js">
<link rel="modulepreload" crossorigin href="/admin/assets/async-validator-CFA_igpM.js">
<link rel="modulepreload" crossorigin href="/admin/assets/normalize-wheel-es-BQoi3Ox2.js">
<link rel="modulepreload" crossorigin href="/admin/assets/element-plus-Bolc0EfP.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@vueuse/shared-Cl3lVqjz.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@vueuse/core-1S4fJlii.js">
<link rel="modulepreload" crossorigin href="/admin/assets/balanced-match-BdS7OldZ.js">
<link rel="modulepreload" crossorigin href="/admin/assets/clone-DBZ6_OiU.js">
<link rel="modulepreload" crossorigin href="/admin/assets/color-name-Dju3oUBS.js">
<link rel="modulepreload" crossorigin href="/admin/assets/color-convert-OrFsTA_V.js">
<link rel="modulepreload" crossorigin href="/admin/assets/color-string-pUQDDJII.js">
<link rel="modulepreload" crossorigin href="/admin/assets/color-B2zxaWkI.js">
<link rel="modulepreload" crossorigin href="/admin/assets/ms-CzQ2E3wO.js">
<link rel="modulepreload" crossorigin href="/admin/assets/css-color-function-DI01JAaZ.js">
<link rel="modulepreload" crossorigin href="/admin/assets/clipboard-CHgxszqY.js">
<link rel="modulepreload" crossorigin href="/admin/assets/vue-clipboard3-DByT_rEQ.js">
<link rel="modulepreload" crossorigin href="/admin/assets/tslib-BDyQ-Jie.js">
<link rel="modulepreload" crossorigin href="/admin/assets/zrender-DRNXw7y3.js">
<link rel="modulepreload" crossorigin href="/admin/assets/echarts-CNLwa9wG.js">
<link rel="modulepreload" crossorigin href="/admin/assets/highlight.js-Bxt7hFFy.js">
<link rel="modulepreload" crossorigin href="/admin/assets/@highlightjs/vue-plugin-C4onkBvW.js">
<link rel="stylesheet" crossorigin href="/admin/assets/element-plus-CL62-hT2.css">
<link rel="stylesheet" crossorigin href="/admin/assets/highlight-BfC0goYb.css">
<link rel="stylesheet" crossorigin href="/admin/assets/index-B8eb0l7L.css">
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
<script>
// Dify 聊天窗口全屏切换:#dify-chatbot-bubble-window 是 iframe 本身,
// 无法往其内部挂按钮,故在气泡旁放一个独立按钮,窗口打开时才显示
(function () {
var btn = document.createElement('button')
btn.type = 'button'
btn.id = 'dify-fullscreen-toggle'
btn.title = '全屏 / 还原'
btn.textContent = '⛶'
var transitionUntil = 0
// 全屏切换(放大和还原都算)后的过渡期:Dify 内部重排期间版权位置不定,
// 用大面积遮盖兜底,并逐帧跟踪窗口尺寸动画
function startTransition() {
transitionUntil = Date.now() + TRANSITION_MS
function loop() {
sync()
if (Date.now() < transitionUntil) requestAnimationFrame(loop)
}
loop()
}
btn.addEventListener('click', function () {
var win = document.getElementById('dify-chatbot-bubble-window')
if (!win) return
win.classList.toggle('dify-chatbot-fullscreen')
startTransition()
})
document.body.appendChild(btn)
// 自定义关闭按钮:窗口打开时气泡被隐藏,由它代替气泡的关闭功能
var closeBtn = document.createElement('button')
closeBtn.type = 'button'
closeBtn.id = 'dify-chat-close'
closeBtn.title = '关闭'
closeBtn.textContent = '✕'
closeBtn.addEventListener('click', function () {
var win = document.getElementById('dify-chatbot-bubble-window')
var difyBubble = document.getElementById('dify-chatbot-bubble-button')
if (win) win.classList.remove('dify-chatbot-fullscreen')
// 复用 embed.min.js 的开关逻辑,保证气泡图标状态同步
if (difyBubble) difyBubble.click()
sync()
})
document.body.appendChild(closeBtn)
// 隐藏 Dify 头部自带的展开按钮:该按钮仅在收到 isToggledByButton:true 时渲染
// embed.min.js 默认发送),在其后补发 false 覆盖即可
var DIFY_ORIGIN = 'https://ai.zhenyangtang.com.cn'
window.addEventListener('message', function (event) {
if (event.origin !== DIFY_ORIGIN) return
if (!event.data || event.data.type !== 'dify-chatbot-iframe-ready') return
var win = document.getElementById('dify-chatbot-bubble-window')
if (!win || event.source !== win.contentWindow) return
setTimeout(function () {
win.contentWindow.postMessage(
{
type: 'dify-chatbot-config',
payload: { isToggledByButton: false, isDraggable: false }
},
DIFY_ORIGIN
)
}, 200)
})
var cover = document.createElement('div')
cover.id = 'dify-brand-cover'
document.body.appendChild(cover)
var transitionCover = document.createElement('div')
transitionCover.id = 'dify-brand-transition-cover'
document.body.appendChild(transitionCover)
var BRAND_BAR_HEIGHT = 38
// 全屏后 Dify 内部布局重排的过渡时长,期间用大面积遮盖兜底
var TRANSITION_MS = 1500
// Dify 宽屏布局断点 40rem:窄窗口版权条在底部,宽窗口(展开/全屏)移到顶栏右侧
var WIDE_BREAKPOINT = 640
function sync() {
var win = document.getElementById('dify-chatbot-bubble-window')
var opened = win && win.style.display !== 'none'
var difyBubble = document.getElementById('dify-chatbot-bubble-button')
btn.style.display = opened ? 'block' : 'none'
closeBtn.style.display = opened ? 'block' : 'none'
if (!opened) {
cover.style.display = 'none'
transitionCover.style.display = 'none'
// 窗口关闭后恢复气泡(可拖动)
if (difyBubble) difyBubble.style.display = ''
return
}
var rect = win.getBoundingClientRect()
var fullscreen = win.classList.contains('dify-chatbot-fullscreen')
var inTransition = Date.now() < transitionUntil
cover.style.display = 'block'
// 窗口打开期间隐藏气泡(气泡可能被拖到任意位置,浮在窗口上很突兀),
// 控制按钮统一停靠在窗口顶栏
if (difyBubble) difyBubble.style.display = 'none'
btn.classList.toggle('is-fullscreen', fullscreen)
closeBtn.classList.toggle('is-fullscreen', fullscreen)
if (fullscreen) {
// 全屏:实色圆钮放左上角,避开右上角版权遮盖区和底部输入区
btn.style.top = '12px'
btn.style.left = '12px'
closeBtn.style.top = '12px'
closeBtn.style.left = '60px'
} else {
// 小窗口:白色幽灵按钮停靠在蓝色顶栏右侧,避开 iframe 内部右上角的重置按钮
btn.style.top = rect.top + 12 + 'px'
btn.style.left = rect.right - 132 + 'px'
closeBtn.style.top = rect.top + 12 + 'px'
closeBtn.style.left = rect.right - 92 + 'px'
}
if (inTransition) {
// 过渡期:版权可能出现在顶部或底部,两处都盖住并跟随窗口动画;
// 顶部遮盖的配色须与当前状态的 iframe 背景一致
cover.style.left = rect.left + 'px'
cover.style.top = rect.top + 'px'
cover.style.width = rect.width + 'px'
cover.style.height = '100px'
cover.style.borderRadius = fullscreen ? '0' : '1rem 1rem 0 0'
cover.style.background = fullscreen
? '#f7f8fc'
: 'linear-gradient(to bottom, #1c64f2 0, #1c64f2 56px, #f7f8fc 56px)'
transitionCover.style.display = 'block'
transitionCover.style.left = rect.left + 'px'
transitionCover.style.top = rect.bottom - BRAND_BAR_HEIGHT + 'px'
transitionCover.style.bottom = 'auto'
transitionCover.style.width = rect.width + 'px'
transitionCover.style.height = BRAND_BAR_HEIGHT + 'px'
transitionCover.style.background = '#f7f8fc'
return
}
transitionCover.style.display = 'none'
if (fullscreen) {
// 全屏:版权信息在顶栏右侧,遮到窗口右边缘
cover.style.left = rect.right - 390 + 'px'
cover.style.top = rect.top + 'px'
cover.style.width = '390px'
cover.style.height = '56px'
cover.style.borderRadius = '0'
cover.style.background = '#f7f8fc'
} else if (rect.width >= WIDE_BREAKPOINT) {
// 宽窗口(浏览器缩放等场景):版权信息在顶栏右侧
cover.style.left = rect.right - 390 + 'px'
cover.style.top = rect.top + 'px'
cover.style.width = '390px'
cover.style.height = '84px'
cover.style.borderRadius = '0'
cover.style.background = '#f7f8fc'
} else {
// 窄窗口布局:版权条横贯底部
cover.style.left = rect.left + 'px'
cover.style.top = rect.bottom - BRAND_BAR_HEIGHT + 'px'
cover.style.width = rect.width + 'px'
cover.style.height = BRAND_BAR_HEIGHT + 'px'
cover.style.borderRadius = '0 0 1rem 1rem'
cover.style.background = '#f2f4f7'
}
}
// 监听 iframe 的创建及 display 切换(embed.min.js 通过内联 style 开关窗口),
// 再加低频轮询兜底,跟住窗口尺寸过渡动画和视口变化
var observer = new MutationObserver(sync)
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class']
})
window.addEventListener('resize', sync)
window.addEventListener('keydown', function (event) {
if (event.key !== 'Escape') return
var win = document.getElementById('dify-chatbot-bubble-window')
if (win && win.classList.contains('dify-chatbot-fullscreen')) {
win.classList.remove('dify-chatbot-fullscreen')
startTransition()
}
})
setInterval(sync, 300)
sync()
})()
</script>
</body>
</style>
<script type="module" crossorigin src="/admin/assets/index-CJvXnF8Y.js"></script>
<link rel="modulepreload" crossorigin href="/admin/assets/.pnpm-9dA1PEvN.js">
<link rel="stylesheet" crossorigin href="/admin/assets/.pnpm-LiWq8Xu0.css">
<link rel="stylesheet" crossorigin href="/admin/assets/index-or10At3s.css">
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
<script>
// Dify 聊天窗口全屏切换:#dify-chatbot-bubble-window 是 iframe 本身,
// 无法往其内部挂按钮,故在气泡旁放一个独立按钮,窗口打开时才显示
(function () {
var btn = document.createElement('button')
btn.type = 'button'
btn.id = 'dify-fullscreen-toggle'
btn.title = '全屏 / 还原'
btn.textContent = '⛶'
var transitionUntil = 0
// 全屏切换(放大和还原都算)后的过渡期:Dify 内部重排期间版权位置不定,
// 用大面积遮盖兜底,并逐帧跟踪窗口尺寸动画
function startTransition() {
transitionUntil = Date.now() + TRANSITION_MS
function loop() {
sync()
if (Date.now() < transitionUntil) requestAnimationFrame(loop)
}
loop()
}
btn.addEventListener('click', function () {
var win = document.getElementById('dify-chatbot-bubble-window')
if (!win) return
win.classList.toggle('dify-chatbot-fullscreen')
startTransition()
})
document.body.appendChild(btn)
// 自定义关闭按钮:窗口打开时气泡被隐藏,由它代替气泡的关闭功能
var closeBtn = document.createElement('button')
closeBtn.type = 'button'
closeBtn.id = 'dify-chat-close'
closeBtn.title = '关闭'
closeBtn.textContent = '✕'
closeBtn.addEventListener('click', function () {
var win = document.getElementById('dify-chatbot-bubble-window')
var difyBubble = document.getElementById('dify-chatbot-bubble-button')
if (win) win.classList.remove('dify-chatbot-fullscreen')
// 复用 embed.min.js 的开关逻辑,保证气泡图标状态同步
if (difyBubble) difyBubble.click()
sync()
})
document.body.appendChild(closeBtn)
// 隐藏 Dify 头部自带的展开按钮:该按钮仅在收到 isToggledByButton:true 时渲染
// embed.min.js 默认发送),在其后补发 false 覆盖即可
var DIFY_ORIGIN = 'https://ai.zhenyangtang.com.cn'
window.addEventListener('message', function (event) {
if (event.origin !== DIFY_ORIGIN) return
if (!event.data || event.data.type !== 'dify-chatbot-iframe-ready') return
var win = document.getElementById('dify-chatbot-bubble-window')
if (!win || event.source !== win.contentWindow) return
setTimeout(function () {
win.contentWindow.postMessage(
{
type: 'dify-chatbot-config',
payload: { isToggledByButton: false, isDraggable: false }
},
DIFY_ORIGIN
)
}, 200)
})
var cover = document.createElement('div')
cover.id = 'dify-brand-cover'
document.body.appendChild(cover)
var transitionCover = document.createElement('div')
transitionCover.id = 'dify-brand-transition-cover'
document.body.appendChild(transitionCover)
var BRAND_BAR_HEIGHT = 38
// 全屏后 Dify 内部布局重排的过渡时长,期间用大面积遮盖兜底
var TRANSITION_MS = 1500
// Dify 宽屏布局断点 40rem:窄窗口版权条在底部,宽窗口(展开/全屏)移到顶栏右侧
var WIDE_BREAKPOINT = 640
function sync() {
var win = document.getElementById('dify-chatbot-bubble-window')
var opened = win && win.style.display !== 'none'
var difyBubble = document.getElementById('dify-chatbot-bubble-button')
btn.style.display = opened ? 'block' : 'none'
closeBtn.style.display = opened ? 'block' : 'none'
if (!opened) {
cover.style.display = 'none'
transitionCover.style.display = 'none'
// 窗口关闭后恢复气泡(可拖动)
if (difyBubble) difyBubble.style.display = ''
return
}
var rect = win.getBoundingClientRect()
var fullscreen = win.classList.contains('dify-chatbot-fullscreen')
var inTransition = Date.now() < transitionUntil
cover.style.display = 'block'
// 窗口打开期间隐藏气泡(气泡可能被拖到任意位置,浮在窗口上很突兀),
// 控制按钮统一停靠在窗口顶栏
if (difyBubble) difyBubble.style.display = 'none'
btn.classList.toggle('is-fullscreen', fullscreen)
closeBtn.classList.toggle('is-fullscreen', fullscreen)
if (fullscreen) {
// 全屏:实色圆钮放左上角,避开右上角版权遮盖区和底部输入区
btn.style.top = '12px'
btn.style.left = '12px'
closeBtn.style.top = '12px'
closeBtn.style.left = '60px'
} else {
// 小窗口:白色幽灵按钮停靠在蓝色顶栏右侧,避开 iframe 内部右上角的重置按钮
btn.style.top = rect.top + 12 + 'px'
btn.style.left = rect.right - 132 + 'px'
closeBtn.style.top = rect.top + 12 + 'px'
closeBtn.style.left = rect.right - 92 + 'px'
}
if (inTransition) {
// 过渡期:版权可能出现在顶部或底部,两处都盖住并跟随窗口动画;
// 顶部遮盖的配色须与当前状态的 iframe 背景一致
cover.style.left = rect.left + 'px'
cover.style.top = rect.top + 'px'
cover.style.width = rect.width + 'px'
cover.style.height = '100px'
cover.style.borderRadius = fullscreen ? '0' : '1rem 1rem 0 0'
cover.style.background = fullscreen
? '#f7f8fc'
: 'linear-gradient(to bottom, #1c64f2 0, #1c64f2 56px, #f7f8fc 56px)'
transitionCover.style.display = 'block'
transitionCover.style.left = rect.left + 'px'
transitionCover.style.top = rect.bottom - BRAND_BAR_HEIGHT + 'px'
transitionCover.style.bottom = 'auto'
transitionCover.style.width = rect.width + 'px'
transitionCover.style.height = BRAND_BAR_HEIGHT + 'px'
transitionCover.style.background = '#f7f8fc'
return
}
transitionCover.style.display = 'none'
if (fullscreen) {
// 全屏:版权信息在顶栏右侧,遮到窗口右边缘
cover.style.left = rect.right - 390 + 'px'
cover.style.top = rect.top + 'px'
cover.style.width = '390px'
cover.style.height = '56px'
cover.style.borderRadius = '0'
cover.style.background = '#f7f8fc'
} else if (rect.width >= WIDE_BREAKPOINT) {
// 宽窗口(浏览器缩放等场景):版权信息在顶栏右侧
cover.style.left = rect.right - 390 + 'px'
cover.style.top = rect.top + 'px'
cover.style.width = '390px'
cover.style.height = '84px'
cover.style.borderRadius = '0'
cover.style.background = '#f7f8fc'
} else {
// 窄窗口布局:版权条横贯底部
cover.style.left = rect.left + 'px'
cover.style.top = rect.bottom - BRAND_BAR_HEIGHT + 'px'
cover.style.width = rect.width + 'px'
cover.style.height = BRAND_BAR_HEIGHT + 'px'
cover.style.borderRadius = '0 0 1rem 1rem'
cover.style.background = '#f2f4f7'
}
}
// 监听 iframe 的创建及 display 切换(embed.min.js 通过内联 style 开关窗口),
// 再加低频轮询兜底,跟住窗口尺寸过渡动画和视口变化
var observer = new MutationObserver(sync)
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class']
})
window.addEventListener('resize', sync)
window.addEventListener('keydown', function (event) {
if (event.key !== 'Escape') return
var win = document.getElementById('dify-chatbot-bubble-window')
if (win && win.classList.contains('dify-chatbot-fullscreen')) {
win.classList.remove('dify-chatbot-fullscreen')
startTransition()
}
})
setInterval(sync, 300)
sync()
})()
</script>
</body>
</html>