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
JavaScript
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