UNPKG

vue-chat-button-simple

Version:

Vue 2 & Vue 3 compatible chat button components with badge support, modal popup, fixed positioning, environment configuration, and customizable themes

857 lines 70.8 kB
import { toValue, ref, defineComponent, computed, watch, onMounted, onUnmounted, createBlock, openBlock, Teleport, createVNode, Transition, withCtx, createElementBlock, createCommentVNode, createElementVNode, withModifiers, normalizeStyle, normalizeClass, renderSlot, createTextVNode, toDisplayString, Fragment, withDirectives, vShow, unref } from "vue"; typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope; const noop = () => { }; function createFilterWrapper(filter, fn) { function wrapper(...args) { return new Promise((resolve, reject) => { Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject); }); } return wrapper; } function debounceFilter(ms, options = {}) { let timer; let maxTimer; let lastRejector = noop; const _clearTimeout = (timer2) => { clearTimeout(timer2); lastRejector(); lastRejector = noop; }; let lastInvoker; const filter = (invoke) => { const duration = toValue(ms); const maxDuration = toValue(options.maxWait); if (timer) _clearTimeout(timer); if (duration <= 0 || maxDuration !== void 0 && maxDuration <= 0) { if (maxTimer) { _clearTimeout(maxTimer); maxTimer = void 0; } return Promise.resolve(invoke()); } return new Promise((resolve, reject) => { lastRejector = options.rejectOnCancel ? reject : resolve; lastInvoker = invoke; if (maxDuration && !maxTimer) { maxTimer = setTimeout(() => { if (timer) _clearTimeout(timer); maxTimer = void 0; resolve(lastInvoker()); }, maxDuration); } timer = setTimeout(() => { if (maxTimer) _clearTimeout(maxTimer); maxTimer = void 0; resolve(invoke()); }, duration); }); }; return filter; } // @__NO_SIDE_EFFECTS__ function useDebounceFn(fn, ms = 200, options = {}) { return createFilterWrapper( debounceFilter(ms, options), fn ); } const environmentMap = { development: "https://api.dev.szad.teehmoon.com/chat-service", test: "https://api.test.szad.teehmoon.com/chat-service", production: "https://api-x.westmonth.com/chat-service", uat: "https://api.uat.teehmoon.com/chat-service" }; const detectEnvironment = () => { if (typeof window !== "undefined") { const hostname = window.location.hostname; const protocol = window.location.protocol; if (hostname === "localhost" || hostname === "127.0.0.1" || hostname.includes(".local") || hostname.includes(".dev") || protocol === "file:") { return "development"; } const urlParams = new URLSearchParams(window.location.search); if (urlParams.get("env") === "dev") { return "development"; } return "production"; } return "development"; }; const modalUrlMap = { dev: "http://localhost:5666", development: "https://im.dev.szad.teehmoon.com", production: "https://im.westmonth.com", test: "https://im.test.szad.teehmoon.com", uat: "https://im.uat.teehmoon.com" }; const wsUrlMap = { development: "wss://api.test.szad.teehmoon.com/chat-service/init", test: "wss://api.test.szad.teehmoon.com/chat-service/init", uat: "wss://api.uat.teehmoon.com/chat-service/init", production: "wss://api-x.westmonth.com/chat-service/init" }; const getEnvironmentConfig = (environment, token) => { const isProduction = environment === "production"; const baseWsUrl = wsUrlMap[environment]; const wsUrl = token ? `${baseWsUrl}?token=${token}` : baseWsUrl; const defaultConfig = { isProduction, apiBaseUrl: environmentMap[environment], timeout: 1e4, retryCount: 3, modalUrl: modalUrlMap[environment], wsUrl }; return { ...defaultConfig }; }; const clearTokenCache = () => { localStorage.removeItem("im_token"); }; const globalModalInstance = ref(null); const globalModalState = ref({ visible: false, url: "", title: "西之月客服" }); const setGlobalModalInstance = (instance) => { globalModalInstance.value = instance; }; const openGlobalModal = (url, title = "西之月客服") => { if (globalModalInstance.value) { globalModalState.value.url = url; globalModalState.value.title = title; globalModalInstance.value.open(url, title); } else { console.warn("Global modal instance not found"); } }; const hasGlobalModalInstance = () => { return globalModalInstance.value !== null; }; class WebSocketClient { constructor(url, options = {}) { this.url = url; this.socket = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectInterval = 3e3; this.reconnectTimer = null; this.heartbeatTimer = null; this.heartbeatInterval = 3e3; this.pongTimeout = 5e3; this.callbacks = { onOpen: options.onOpen || (() => { }), onMessage: options.onMessage || (() => { }), onError: options.onError || (() => { }), onClose: options.onClose || (() => { }), onStatusChange: options.onStatusChange || (() => { }), onBadgeUpdate: options.onBadgeUpdate || (() => { }) }; this.status = "disconnected"; } // 设置事件回调 on(event, callback) { if (this.callbacks[event]) { this.callbacks[event] = callback; } } connect(token) { if (this.socket && this.socket.readyState === WebSocket.OPEN) { console.log("WebSocket已经连接"); return Promise.resolve(); } return new Promise((resolve, reject) => { this.updateStatus("connecting"); this.currentToken = token; try { this.socket = new WebSocket(this.url, [token]); this.socket.onopen = (event) => { this.handleOpen(event); resolve(event); }; this.socket.onmessage = (event) => { this.handleMessage(event); }; this.socket.onerror = (error) => { this.handleError(error); reject(error); }; this.socket.onclose = (event) => { this.handleClose(event); }; } catch (error) { console.error("WebSocket连接错误:", error); this.updateStatus("disconnected"); this.callbacks.onError(error); reject(error); } }); } handleOpen(event) { console.log("WebSocket连接已建立"); this.reconnectAttempts = 0; this.updateStatus("connected"); this.callbacks.onOpen(event); this.startHeartbeat(); } handleMessage(event) { try { const data = JSON.parse(event.data); this.callbacks.onBadgeUpdate(data.data.unread_data.default.total || 0); this.callbacks.onMessage(data, event); } catch (e) { this.callbacks.onMessage(event.data, event); } } handleError(error) { this.updateStatus("disconnected"); this.callbacks.onError(error); this.stopHeartbeat(); } handleClose(event) { this.updateStatus("disconnected"); this.stopHeartbeat(); this.callbacks.onClose(event); if (event.code !== 1e3 && this.reconnectAttempts < this.maxReconnectAttempts) { this.scheduleReconnect(); } else if (this.reconnectAttempts >= this.maxReconnectAttempts) { this.callbacks.onError(new Error("WebSocket重连次数已达上限")); } } scheduleReconnect() { if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); } this.reconnectAttempts++; this.reconnectTimer = setTimeout(() => { if (this.currentToken && this.reconnectAttempts <= this.maxReconnectAttempts) { this.connect(this.currentToken).catch((error) => { console.error(`第${this.reconnectAttempts}次重连失败:`, error); }); } }, this.reconnectInterval); } startHeartbeat() { this.stopHeartbeat(); this.heartbeatTimer = setInterval(() => { if (this.socket && this.socket.readyState === WebSocket.OPEN) { this.send({ type: "ping", timestamp: Date.now() }); } else { this.stopHeartbeat(); } }, this.heartbeatInterval); } stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } } send(data) { if (this.socket && this.socket.readyState === WebSocket.OPEN) { try { const message = typeof data === "object" ? JSON.stringify(data) : data; this.socket.send(message); return true; } catch (error) { console.error("WebSocket发送失败:", error); this.callbacks.onError(error); return false; } } else { console.warn("WebSocket未连接,无法发送消息"); return false; } } disconnect() { this.reconnectAttempts = this.maxReconnectAttempts; if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } this.stopHeartbeat(); if (this.socket) { this.socket.close(1e3, "用户主动断开"); this.socket = null; } this.updateStatus("disconnected"); } updateStatus(status) { if (this.status !== status) { this.status = status; this.callbacks.onStatusChange(status); } } // 获取连接状态 getStatus() { return this.status; } // 检查是否已连接 isConnected() { return this.socket && this.socket.readyState === WebSocket.OPEN; } // 重置重连计数 resetReconnectCount() { this.reconnectAttempts = 0; } // 获取当前重连次数 getReconnectAttempts() { return this.reconnectAttempts; } // 手动重连(重置计数) reconnect() { this.resetReconnectCount(); if (this.currentToken) { return this.connect(this.currentToken); } return Promise.reject(new Error("没有可用的token")); } } const _imports_0 = "data:image/png;base64,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"; const _hoisted_1$2 = { class: "chat-modal-title center" }; const _hoisted_2$1 = { key: 0, t: "1756369066305", class: "icon", viewBox: "0 0 1024 1024", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "5356", width: "200", height: "200" }; const _hoisted_3$1 = { key: 1, t: "1756369303951", class: "icon", viewBox: "0 0 1024 1024", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "6339", width: "200", height: "200" }; const _hoisted_4$1 = { class: "chat-modal-content" }; const _hoisted_5$1 = ["src", "title"]; const _hoisted_6$1 = { class: "chat-modal-placeholder" }; const _sfc_main$2 = /* @__PURE__ */ defineComponent({ __name: "ChatModal", props: { visible: { type: Boolean, default: false }, position: { default: "center" }, width: { default: "400px" }, height: { default: "600px" }, closeOnOverlay: { type: Boolean, default: false }, closeOnEscape: { type: Boolean, default: false }, draggable: { type: Boolean, default: true }, resizable: { type: Boolean, default: true }, initialX: { default: void 0 }, initialY: { default: void 0 }, initialWidth: { default: void 0 }, initialHeight: { default: void 0 }, enableBoundary: { type: Boolean, default: true }, showFullscreen: { type: Boolean, default: false }, enableAdjustSize: { type: Boolean, default: false } }, emits: ["update:visible", "close", "open", "fullscreen", "move", "resize"], setup(__props, { expose: __expose, emit: __emit }) { const props = __props; const iframeUrl = ref(""); const modalTitle = ref("西之月客服"); const iframeKey = ref(0); const isFullscreen = ref(false); const isDragging = ref(false); const isResizing = ref(false); const hasBeenDragged = ref(false); const modalPosition = ref({ x: 0, y: 0 }); const modalSize = ref({ width: 400, height: 600 }); const dragStart = ref({ x: 0, y: 0 }); const resizeStart = ref({ x: 0, y: 0, width: 0, height: 0 }); const modalRef = ref(); const modalOverlayRef = ref(); const emit = __emit; const visible = ref(props.visible); const modalStyle = computed(() => { if (isFullscreen.value || props.position === "fullscreen") { return { width: "100vw", height: "100vh", left: "0", top: "0", right: "0", bottom: "0", transform: "none", position: "fixed", maxWidth: "none", maxHeight: "none", margin: "0", borderRadius: "0" }; } if (hasBeenDragged.value || props.position === "custom" || modalPosition.value.x !== 0 && modalPosition.value.y !== 0) { return { width: `${modalSize.value.width}px`, height: `${modalSize.value.height}px`, left: `${modalPosition.value.x}px`, top: `${modalPosition.value.y}px`, transform: "none", position: "fixed" }; } if (props.position === "center") { return { width: `${modalSize.value.width}px`, height: `${modalSize.value.height}px`, left: "50%", top: "50%", transform: "translate(-50%, -50%)", position: "fixed" }; } if (props.position === "right") { return { width: `${modalSize.value.width}px`, height: `${modalSize.value.height}px`, right: "20px", top: "50%", transform: "translateY(-50%)", position: "fixed" }; } return { width: `${modalSize.value.width}px`, height: `${modalSize.value.height}px`, left: `${modalPosition.value.x}px`, top: `${modalPosition.value.y}px`, transform: "none", position: "fixed" }; }); watch( () => props.visible, (newValue) => { visible.value = newValue; if (newValue) { initializePosition(); } } ); watch(visible, (newValue) => { emit("update:visible", newValue); if (newValue) { emit("open"); } else { emit("close"); } }); const initializePosition = () => { hasBeenDragged.value = false; if (props.initialWidth && props.initialHeight) { modalSize.value = { width: props.initialWidth, height: props.initialHeight }; } else { const width = parseInt(props.width); const height = parseInt(props.height); modalSize.value = { width, height }; } if (props.initialX !== void 0 && props.initialY !== void 0) { modalPosition.value = { x: props.initialX, y: props.initialY }; hasBeenDragged.value = true; } else if (props.position === "center") { modalPosition.value = { x: (window.innerWidth - modalSize.value.width) / 2, y: (window.innerHeight - modalSize.value.height) / 2 }; } else if (props.position === "right") { modalPosition.value = { x: window.innerWidth - modalSize.value.width - 20, y: (window.innerHeight - modalSize.value.height) / 2 }; } else { modalPosition.value = { x: 100, y: 100 }; } }; const resetPosition = () => { initializePosition(); }; const toggleFullscreen = () => { isFullscreen.value = !isFullscreen.value; emit("fullscreen", isFullscreen.value); if (isFullscreen.value) { if (modalRef.value) { const rect = modalRef.value.getBoundingClientRect(); modalPosition.value = { x: rect.left, y: rect.top }; modalSize.value = { width: rect.width, height: rect.height }; } } }; const startDrag = (event) => { var _a; if (isFullscreen.value || !props.draggable) return; event.preventDefault(); isDragging.value = true; hasBeenDragged.value = true; const clientX = "touches" in event ? event.touches[0].clientX : event.clientX; const clientY = "touches" in event ? event.touches[0].clientY : event.clientY; const rect = (_a = modalRef.value) == null ? void 0 : _a.getBoundingClientRect(); if (rect) { if (props.position === "center" || props.position === "right") { modalPosition.value = { x: rect.left, y: rect.top }; } else if (modalPosition.value.x === 0 && modalPosition.value.y === 0) { modalPosition.value = { x: rect.left, y: rect.top }; } } dragStart.value = { x: clientX - modalPosition.value.x, y: clientY - modalPosition.value.y }; document.addEventListener("mousemove", handleDrag); document.addEventListener("mouseup", stopDrag); document.addEventListener("touchmove", handleDrag); document.addEventListener("touchend", stopDrag); }; const handleDrag = (event) => { if (!isDragging.value) return; const clientX = "touches" in event ? event.touches[0].clientX : event.clientX; const clientY = "touches" in event ? event.touches[0].clientY : event.clientY; let newX = clientX - dragStart.value.x; let newY = clientY - dragStart.value.y; if (props.enableBoundary) { const minX = 0; const maxX = window.innerWidth - modalSize.value.width; const minY = 0; const maxY = window.innerHeight - modalSize.value.height; if (newX < minX) { newX = minX; } else if (newX > maxX) { newX = maxX; } if (newY < minY) { newY = minY; } else if (newY > maxY) { newY = maxY; } } modalPosition.value = { x: newX, y: newY }; emit("move", modalPosition.value); }; const stopDrag = () => { isDragging.value = false; document.removeEventListener("mousemove", handleDrag); document.removeEventListener("mouseup", stopDrag); document.removeEventListener("touchmove", handleDrag); document.removeEventListener("touchend", stopDrag); }; const startResize = (event) => { if (isFullscreen.value || !props.resizable) return; event.preventDefault(); isResizing.value = true; const clientX = "touches" in event ? event.touches[0].clientX : event.clientX; const clientY = "touches" in event ? event.touches[0].clientY : event.clientY; resizeStart.value = { x: clientX, y: clientY, width: modalSize.value.width, height: modalSize.value.height }; document.addEventListener("mousemove", handleResize); document.addEventListener("mouseup", stopResize); document.addEventListener("touchmove", handleResize); document.addEventListener("touchend", stopResize); }; const handleResize = (event) => { if (!isResizing.value) return; const clientX = "touches" in event ? event.touches[0].clientX : event.clientX; const clientY = "touches" in event ? event.touches[0].clientY : event.clientY; const deltaX = clientX - resizeStart.value.x; const deltaY = clientY - resizeStart.value.y; let newWidth = Math.max(300, resizeStart.value.width + deltaX); let newHeight = Math.max(200, resizeStart.value.height + deltaY); if (props.enableBoundary) { const maxWidth = window.innerWidth - modalPosition.value.x; const maxHeight = window.innerHeight - modalPosition.value.y; if (newWidth > maxWidth) { newWidth = maxWidth; } if (newHeight > maxHeight) { newHeight = maxHeight; } } modalSize.value = { width: newWidth, height: newHeight }; emit("resize", modalSize.value); }; const stopResize = () => { isResizing.value = false; document.removeEventListener("mousemove", handleResize); document.removeEventListener("mouseup", stopResize); document.removeEventListener("touchmove", handleResize); document.removeEventListener("touchend", stopResize); }; const closeModal = () => { visible.value = false; isFullscreen.value = false; emit("close"); }; const handleOverlayClick = () => { if (props.closeOnOverlay) { closeModal(); } }; const handleKeydown = (event) => { if (event.key === "Escape" && visible.value) { if (isFullscreen.value) { toggleFullscreen(); } else if (props.closeOnEscape) { event.preventDefault(); closeModal(); } } }; const open = async (url, title = "西之月客服") => { modalTitle.value = title; visible.value = true; if (!modalOverlayRef.value) { resetPosition(); } iframeKey.value = Date.now(); iframeUrl.value = `${url}?time=${iframeKey.value}`; }; const toggle = () => { visible.value = !visible.value; }; const setPosition = (x, y) => { modalPosition.value = { x, y }; emit("move", modalPosition.value); }; const setSize = (width, height) => { modalSize.value = { width, height }; emit("resize", modalSize.value); }; const instanceMethods = { open, close: closeModal, toggle, setPosition, setSize, toggleFullscreen, isFullscreen: () => isFullscreen.value }; __expose(instanceMethods); onMounted(() => { setGlobalModalInstance(instanceMethods); resetPosition(); document.addEventListener("keydown", handleKeydown, true); }); onUnmounted(() => { document.removeEventListener("keydown", handleKeydown, true); stopDrag(); stopResize(); }); return (_ctx, _cache) => { return openBlock(), createBlock(Teleport, { to: "body" }, [ createVNode(Transition, { name: "modal-fade" }, { default: withCtx(() => [ visible.value ? (openBlock(), createElementBlock("div", { key: 0, class: "chat-modal-overlay", ref_key: "modalOverlayRef", ref: modalOverlayRef, onClick: handleOverlayClick }, [ createElementVNode("div", { ref_key: "modalRef", ref: modalRef, class: normalizeClass(["chat-modal-container", [_ctx.position, { fullscreen: isFullscreen.value, dragging: isDragging.value }]]), style: normalizeStyle(modalStyle.value), onClick: _cache[2] || (_cache[2] = withModifiers(() => { }, ["stop"])) }, [ createElementVNode("div", { class: normalizeClass(["chat-modal-header", { draggable: !isFullscreen.value }]), onMousedown: startDrag, onTouchstart: startDrag }, [ createElementVNode("div", _hoisted_1$2, [ _cache[3] || (_cache[3] = createElementVNode("img", { src: _imports_0, alt: "logo", class: "chat-modal-logo" }, null, -1)), renderSlot(_ctx.$slots, "title", {}, () => [ createTextVNode(toDisplayString(modalTitle.value), 1) ]) ]), createElementVNode("div", { class: "chat-modal-controls", onMousedown: _cache[0] || (_cache[0] = withModifiers(() => { }, ["stop"])), onTouchstart: _cache[1] || (_cache[1] = withModifiers(() => { }, ["stop"])) }, [ _ctx.showFullscreen ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [ !isFullscreen.value ? (openBlock(), createElementBlock("button", { key: 0, class: "chat-modal-close full-screen-icon", onClick: toggleFullscreen, title: "全屏" }, [ isFullscreen.value ? (openBlock(), createElementBlock("svg", _hoisted_2$1, _cache[4] || (_cache[4] = [ createElementVNode("path", { d: "M131.413333 85.333333h142.933334a32 32 0 1 0 0-64H54.186667a32 32 0 0 0-32 32v221.013334a32 32 0 1 0 64 0V131.413333l344.32 344.32a32 32 0 0 0 45.226666-45.226666zM969.813333 717.653333a32 32 0 0 0-32 32v142.506667l-344.32-344.32a32 32 0 0 0-45.226666 45.226667l344.32 345.6h-142.933334a32 32 0 0 0 0 64h220.16a32 32 0 0 0 32-32v-221.013334a32 32 0 0 0-32-32zM999.253333 42.666667a32 32 0 0 0-29.44-19.626667h-220.16a32 32 0 1 0 0 64h142.506667l-344.32 344.32a32 32 0 1 0 45.226667 45.226667L938.666667 131.413333v142.933334a32 32 0 1 0 64 0V54.186667a32 32 0 0 0-3.413334-11.52zM430.506667 548.266667L85.333333 892.586667v-142.933334a32 32 0 1 0-64 0v220.16a32 32 0 0 0 32 32h221.013334a32 32 0 1 0 0-64H131.413333l344.32-344.32a32 32 0 0 0-45.226666-45.226666z", fill: "", "p-id": "5357" }, null, -1) ]))) : (openBlock(), createElementBlock("svg", _hoisted_3$1, _cache[5] || (_cache[5] = [ createElementVNode("path", { d: "M999.908535 950.445791l-224.725864-224.789851h212.184216a36.473161 36.473161 0 1 0 0-73.138287H650.214103v334.849216a36.473161 36.473161 0 1 0 73.138286 0v-209.880648l224.725864 224.725864a36.601137 36.601137 0 0 0 51.766294-51.830282zM300.583659 36.697119v214.423796L74.706011 25.243267a36.601137 36.601137 0 0 0-51.766294 51.766294L247.729569 301.735425H36.569161a36.473161 36.473161 0 1 0 0 73.138286H373.785933V36.633131a36.473161 36.473161 0 1 0-73.138286 0zM998.756751 28.570643a36.601137 36.601137 0 0 0-51.766294 0l-223.57408 223.638068V36.633131a36.473161 36.473161 0 1 0-73.138286 0v338.24058h337.088796a36.473161 36.473161 0 1 0 0-73.138286h-209.880648l221.270512-221.3345a36.601137 36.601137 0 0 0 0-51.766294zM0.096 689.11879a36.473161 36.473161 0 0 0 36.537149 36.53715h213.336L21.851921 953.837156a36.601137 36.601137 0 0 0 51.830282 51.830281l226.965444-227.09342v208.792852a36.473161 36.473161 0 1 0 73.138286 0v-334.849216H36.633149a36.473161 36.473161 0 0 0-36.537149 36.601137z", fill: "#374866", "p-id": "6340" }, null, -1) ]))) ])) : createCommentVNode("", true) ], 64)) : createCommentVNode("", true), createElementVNode("button", { class: "chat-modal-close", onClick: closeModal }, _cache[6] || (_cache[6] = [ createElementVNode("svg", { t: "1755847270165", class: "icon", viewBox: "0 0 1024 1024", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "5200", width: "200", height: "200" }, [ createElementVNode("path", { d: "M213.333333 469.333333m21.333334 0l554.666666 0q21.333333 0 21.333334 21.333334l0 42.666666q0 21.333333-21.333334 21.333334l-554.666666 0q-21.333333 0-21.333334-21.333334l0-42.666666q0-21.333333 21.333334-21.333334Z", fill: "#4A4A4A", "p-id": "5201" }) ], -1) ])), createElementVNode("button", { class: "chat-modal-close", onClick: closeModal }, _cache[7] || (_cache[7] = [ createElementVNode("svg", { t: "1756369708796", class: "icon", viewBox: "0 0 1024 1024", fill: "red", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "7309", width: "200", height: "200" }, [ createElementVNode("path", { d: "M571.01312 523.776l311.3472-311.35232c15.7184-15.71328 15.7184-41.6256 0-57.344l-1.69472-1.69984c-15.7184-15.71328-41.6256-15.71328-57.34912 0l-311.3472 311.77728-311.35232-311.77728c-15.7184-15.71328-41.63072-15.71328-57.344 0l-1.69984 1.69984a40.0128 40.0128 0 0 0 0 57.344L452.92544 523.776l-311.35232 311.35744c-15.71328 15.71328-15.71328 41.63072 0 57.33888l1.69984 1.69984c15.71328 15.7184 41.6256 15.7184 57.344 0l311.35232-311.35232 311.3472 311.35232c15.72352 15.7184 41.63072 15.7184 57.34912 0l1.69472-1.69984c15.7184-15.70816 15.7184-41.6256 0-57.33888l-311.3472-311.35744z", "p-id": "7310" }) ], -1) ])) ], 32) ], 34), createElementVNode("div", _hoisted_4$1, [ withDirectives((openBlock(), createElementBlock("iframe", { key: iframeKey.value, src: iframeUrl.value, title: modalTitle.value, class: "chat-modal-iframe", frameborder: "0", allowfullscreen: "" }, null, 8, _hoisted_5$1)), [ [vShow, iframeUrl.value] ]), withDirectives(createElementVNode("div", _hoisted_6$1, [ renderSlot(_ctx.$slots, "content", {}, () => [ _cache[8] || (_cache[8] = createElementVNode("p", null, "请设置 iframe URL", -1)) ]) ], 512), [ [vShow, !iframeUrl.value] ]) ]), !isFullscreen.value && _ctx.position !== "fullscreen" && _ctx.enableAdjustSize ? (openBlock(), createElementBlock("div", { key: 0, class: "chat-modal-resize-handle", onMousedown: startResize, onTouchstart: startResize }, null, 32)) : createCommentVNode("", true) ], 6) ], 512)) : createCommentVNode("", true) ]), _: 3 }) ]); }; } }); const _hoisted_1$1 = { class: "error-modal-title" }; const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "ErrorModal", props: { visible: { type: Boolean, default: false }, message: { default: "操作失败" }, autoClose: { type: Boolean, default: true }, autoCloseDelay: { default: 3e3 }, onClose: { type: Function, default: () => { } }, onRetry: { type: Function, default: () => { } } }, emits: ["close", "retry"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const autoCloseTimer = ref(null); const handleClose = () => { emit("close"); props.onClose(); }; const handleOverlayClick = () => { handleClose(); }; const startAutoCloseTimer = () => { if (props.autoClose && props.autoCloseDelay > 0) { if (autoCloseTimer.value) { clearTimeout(autoCloseTimer.value); } autoCloseTimer.value = setTimeout(() => { handleClose(); }, props.autoCloseDelay); } }; const clearAutoCloseTimer = () => { if (autoCloseTimer.value) { clearTimeout(autoCloseTimer.value); autoCloseTimer.value = null; } }; watch(() => props.visible, (newVisible) => { if (newVisible) { document.body.style.overflow = "hidden"; startAutoCloseTimer(); } else { document.body.style.overflow = ""; clearAutoCloseTimer(); } }); onUnmounted(() => { clearAutoCloseTimer(); }); return (_ctx, _cache) => { return openBlock(), createBlock(Teleport, { to: "body" }, [ createVNode(Transition