This commit is contained in:
Your Name
2026-07-10 10:28:21 +08:00
parent fdddfdb3dd
commit 546138d60e
2 changed files with 283 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

+283
View File
@@ -60,6 +60,109 @@
document.head.appendChild(link); document.head.appendChild(link);
})(); })();
</script> </script>
<!-- Dify AI 客服机器人(全局悬浮气泡) -->
<script>
window.difyChatbotConfig = {
token: 'OcRvj5VVR76cME9J',
baseUrl: 'http://chat.zhenyangtang.com.cn:8088/',
draggable: true,
dragAxis: 'both',
inputs: {},
systemVariables: {},
userVariables: {}
}
</script>
<script
src="http://chat.zhenyangtang.com.cn:8088/embed.min.js"
id="OcRvj5VVR76cME9J"
defer
></script>
<style>
/* 气泡按钮使用自定义 AI 图标(base 为 /admin/public 资源在该路径下) */
#dify-chatbot-bubble-button {
background-color: #fff !important;
background-image: url('/admin/ai-chat-icon.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
/* 隐藏 embed.min.js 内置的笑脸/关闭 SVG 图标 */
#dify-chatbot-bubble-button svg {
display: none !important;
}
/* 聊天窗口 iframe 默认透明。小窗口布局顶栏是白色文字,
顶部 56px 垫深蓝衬托,下方近白作聊天区底色 */
#dify-chatbot-bubble-window {
background: linear-gradient(
to bottom,
#1c64f2 0,
#1c64f2 56px,
#f7f8fc 56px
) !important;
}
/* 全屏(宽屏布局)顶栏是深色文字,用整片近白底色 */
#dify-chatbot-bubble-window.dify-chatbot-fullscreen {
background: #f7f8fc !important;
}
/* 全屏态:#dify-chatbot-bubble-window 是 iframe 本身,用 !important 覆盖其内联样式 */
#dify-chatbot-bubble-window.dify-chatbot-fullscreen {
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
width: 100vw !important;
height: 100vh !important;
max-width: 100vw !important;
max-height: 100vh !important;
min-width: 0 !important;
min-height: 0 !important;
border-radius: 0 !important;
}
/* 聊天窗口控制按钮(全屏切换 / 关闭),仅窗口打开时显示,位置由脚本计算 */
#dify-fullscreen-toggle,
#dify-chat-close {
position: fixed;
display: none;
width: 32px;
height: 32px;
border: none;
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.18);
color: #fff;
font-size: 16px;
line-height: 32px;
text-align: center;
cursor: pointer;
z-index: 2147483646;
}
#dify-fullscreen-toggle:hover,
#dify-chat-close:hover {
background-color: rgba(255, 255, 255, 0.35);
}
/* 全屏时顶栏是浅色的,按钮改用实色圆形放在左上角 */
#dify-fullscreen-toggle.is-fullscreen,
#dify-chat-close.is-fullscreen {
width: 40px;
height: 40px;
border-radius: 20px;
background-color: #1c64f2;
box-shadow: rgba(0, 0, 0, 0.2) 0 4px 8px 0;
line-height: 40px;
}
#dify-fullscreen-toggle.is-fullscreen:hover,
#dify-chat-close.is-fullscreen:hover {
background-color: #1a56db;
}
/* 遮盖聊天窗口底部的 POWERED BY Dify 版权条(该区域在 iframe 内部,只能覆盖) */
#dify-brand-cover,
#dify-brand-transition-cover {
position: fixed;
display: none;
background: #f2f4f7;
z-index: 2147483641;
}
</style>
<script type="module" crossorigin src="/admin/assets/index-d3j0BX4t.js"></script> <script type="module" crossorigin src="/admin/assets/index-d3j0BX4t.js"></script>
<link rel="modulepreload" crossorigin href="/admin/assets/@babel/runtime-BanGtE2-.js"> <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/fflate-_ayOYiT1.js">
@@ -109,5 +212,185 @@
</svg> </svg>
</div> </div>
</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 = 'http://chat.zhenyangtang.com.cn:8088'
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> </script>
</body> </body>