upload project source code
This commit is contained in:
498
后端源码/yifan.action-ai.cn/index/js/index.oTkWBrWl.js
Normal file
498
后端源码/yifan.action-ai.cn/index/js/index.oTkWBrWl.js
Normal file
@@ -0,0 +1,498 @@
|
||||
import { W as connection_default, a7 as loading_default, ab as warning_default, i as ElIcon, aa as delete_default, ac as setting_default, h as ElButton, ad as chat_dot_round_default, ae as user_default, af as arrow_down_default, ag as arrow_up_default, ah as copy_document_default, K as refresh_left_default, a0 as ElAlert, y as ElInput, ai as promotion_default, E as ElMessage, D as ElMessageBox } from "./element-plus.CkEW9frc.js";
|
||||
import { J as defineComponent, ej as MarkdownIt, ek as markdownItHighlightjs, t as onMounted, ah as onUnmounted, S as openBlock, _ as createElementBlock, a1 as createBaseVNode, $ as createVNode, a0 as withCtx, T as createBlock, o as unref, a3 as normalizeClass, aa as toDisplayString, a9 as createTextVNode, a8 as createCommentVNode, H as Fragment, ay as renderList, a_ as withKeys, aw as withModifiers, r as ref, j as computed, n as nextTick, el as HighlightJS } from "./.pnpm.BW3P1y8f.js";
|
||||
import { _ as _export_sfc } from "./_plugin-vue_export-helper.1tPrXgE0.js";
|
||||
const _hoisted_1 = { class: "chatgpt-container" };
|
||||
const _hoisted_2 = { class: "main-chat" };
|
||||
const _hoisted_3 = { class: "chat-navbar" };
|
||||
const _hoisted_4 = { class: "navbar-right" };
|
||||
const _hoisted_5 = { class: "connection-status" };
|
||||
const _hoisted_6 = { class: "status-text" };
|
||||
const _hoisted_7 = {
|
||||
key: 0,
|
||||
class: "welcome-screen"
|
||||
};
|
||||
const _hoisted_8 = { class: "welcome-content" };
|
||||
const _hoisted_9 = { class: "ai-logo" };
|
||||
const _hoisted_10 = { class: "example-prompts" };
|
||||
const _hoisted_11 = {
|
||||
key: 1,
|
||||
class: "messages-list"
|
||||
};
|
||||
const _hoisted_12 = { class: "message-avatar" };
|
||||
const _hoisted_13 = {
|
||||
key: 0,
|
||||
class: "user-avatar"
|
||||
};
|
||||
const _hoisted_14 = {
|
||||
key: 1,
|
||||
class: "ai-avatar"
|
||||
};
|
||||
const _hoisted_15 = { class: "message-content" };
|
||||
const _hoisted_16 = { class: "message-header" };
|
||||
const _hoisted_17 = { class: "sender-name" };
|
||||
const _hoisted_18 = { class: "message-body" };
|
||||
const _hoisted_19 = ["innerHTML"];
|
||||
const _hoisted_20 = {
|
||||
key: 1,
|
||||
class: "typing-indicator"
|
||||
};
|
||||
const _hoisted_21 = {
|
||||
key: 0,
|
||||
class: "message-actions"
|
||||
};
|
||||
const _hoisted_22 = {
|
||||
key: 2,
|
||||
class: "error-banner"
|
||||
};
|
||||
const _hoisted_23 = { class: "chat-input" };
|
||||
const _hoisted_24 = { class: "input-wrapper" };
|
||||
const _hoisted_25 = { class: "input-container" };
|
||||
const _sfc_main = /* @__PURE__ */ defineComponent({
|
||||
__name: "index",
|
||||
setup(__props) {
|
||||
const md = new MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
breaks: true,
|
||||
highlight(str, lang) {
|
||||
if (lang && HighlightJS.getLanguage(lang)) {
|
||||
try {
|
||||
return `<pre class="hljs"><code>${HighlightJS.highlight(str, { language: lang, ignoreIllegals: true }).value}</code></pre>`;
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return `<pre class="hljs"><code>${md.utils.escapeHtml(str)}</code></pre>`;
|
||||
}
|
||||
}).use(markdownItHighlightjs);
|
||||
const defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
|
||||
tokens[idx].attrPush(["target", "_blank"]);
|
||||
tokens[idx].attrPush(["rel", "noopener noreferrer"]);
|
||||
return defaultRender(tokens, idx, options, env, self);
|
||||
};
|
||||
const messages = ref([]);
|
||||
const inputMessage = ref("");
|
||||
const sending = ref(false);
|
||||
const isConnected = ref(false);
|
||||
const connectionStatus = ref("disconnected");
|
||||
const error = ref("");
|
||||
const messagesContainer = ref();
|
||||
let ws = null;
|
||||
const WS_URL = "undefined/api/v1/application/ai/ws";
|
||||
const connectionStatusText = computed(() => {
|
||||
switch (connectionStatus.value) {
|
||||
case "connected":
|
||||
return "已连接";
|
||||
case "connecting":
|
||||
return "连接中...";
|
||||
case "disconnected":
|
||||
return "未连接";
|
||||
default:
|
||||
return "未知状态";
|
||||
}
|
||||
});
|
||||
const connectWebSocket = () => {
|
||||
if ((ws == null ? void 0 : ws.readyState) === WebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
connectionStatus.value = "connecting";
|
||||
error.value = "";
|
||||
try {
|
||||
ws = new WebSocket(WS_URL);
|
||||
ws.onopen = () => {
|
||||
console.log("WebSocket 连接已建立");
|
||||
isConnected.value = true;
|
||||
connectionStatus.value = "connected";
|
||||
ElMessage.success("连接成功");
|
||||
};
|
||||
ws.onmessage = (event) => {
|
||||
handleWebSocketMessage({ content: event.data });
|
||||
};
|
||||
ws.onclose = (event) => {
|
||||
console.log("WebSocket 连接已关闭", event.code, event.reason);
|
||||
isConnected.value = false;
|
||||
connectionStatus.value = "disconnected";
|
||||
messages.value.forEach((message) => {
|
||||
if (message.type === "assistant" && message.loading) {
|
||||
message.loading = false;
|
||||
message.collapsed = message.content.length > 200;
|
||||
}
|
||||
});
|
||||
};
|
||||
ws.onerror = (error2) => {
|
||||
console.error("WebSocket 错误:", error2);
|
||||
isConnected.value = false;
|
||||
connectionStatus.value = "disconnected";
|
||||
ElMessage.error("连接失败,请检查服务器状态");
|
||||
messages.value.forEach((message) => {
|
||||
if (message.type === "assistant" && message.loading) {
|
||||
message.loading = false;
|
||||
message.collapsed = message.content.length > 200;
|
||||
}
|
||||
});
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("创建 WebSocket 连接失败:", err);
|
||||
connectionStatus.value = "disconnected";
|
||||
error.value = "无法创建连接";
|
||||
}
|
||||
};
|
||||
const disconnectWebSocket = () => {
|
||||
if (ws) {
|
||||
ws.close(1e3, "用户主动断开");
|
||||
ws = null;
|
||||
}
|
||||
isConnected.value = false;
|
||||
connectionStatus.value = "disconnected";
|
||||
messages.value.forEach((message) => {
|
||||
if (message.type === "assistant" && message.loading) {
|
||||
message.loading = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
const toggleConnection = () => {
|
||||
if (isConnected.value) {
|
||||
disconnectWebSocket();
|
||||
ElMessage.info("已断开连接");
|
||||
} else {
|
||||
connectWebSocket();
|
||||
}
|
||||
};
|
||||
const handleWebSocketMessage = (data) => {
|
||||
const lastMessage = messages.value[messages.value.length - 1];
|
||||
if (lastMessage && lastMessage.type === "assistant" && lastMessage.loading) {
|
||||
lastMessage.content += data.content || data.message || "";
|
||||
} else {
|
||||
addMessage("assistant", data.content || data.message || "收到回复");
|
||||
}
|
||||
scrollToBottom();
|
||||
};
|
||||
const sendMessage = async () => {
|
||||
const message = inputMessage.value.trim();
|
||||
if (!message || !isConnected.value || sending.value) {
|
||||
return;
|
||||
}
|
||||
const lastMessage = messages.value[messages.value.length - 1];
|
||||
if (lastMessage && lastMessage.type === "assistant" && lastMessage.loading) {
|
||||
lastMessage.loading = false;
|
||||
}
|
||||
addMessage("user", message);
|
||||
inputMessage.value = "";
|
||||
const loadingMessage = {
|
||||
id: generateId(),
|
||||
type: "assistant",
|
||||
content: "",
|
||||
timestamp: Date.now(),
|
||||
loading: true
|
||||
};
|
||||
messages.value.push(loadingMessage);
|
||||
sending.value = true;
|
||||
scrollToBottom();
|
||||
try {
|
||||
if ((ws == null ? void 0 : ws.readyState) === WebSocket.OPEN) {
|
||||
ws.send(message);
|
||||
} else {
|
||||
throw new Error("WebSocket 连接未建立");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("发送消息失败:", err);
|
||||
messages.value.pop();
|
||||
error.value = "发送消息失败,请检查连接状态";
|
||||
ElMessage.error("发送失败");
|
||||
} finally {
|
||||
sending.value = false;
|
||||
}
|
||||
};
|
||||
const addMessage = (type, content) => {
|
||||
const message = {
|
||||
id: generateId(),
|
||||
type,
|
||||
content,
|
||||
timestamp: Date.now(),
|
||||
// 长消息自动折叠
|
||||
collapsed: content.length > 200
|
||||
};
|
||||
messages.value.push(message);
|
||||
nextTick(() => scrollToBottom());
|
||||
};
|
||||
const clearCurrentChat = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm("确定要清空当前对话吗?此操作不可恢复。", "确认清空", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
});
|
||||
messages.value = [];
|
||||
ElMessage.success("对话已清空");
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
const setPrompt = (prompt) => {
|
||||
inputMessage.value = prompt;
|
||||
};
|
||||
const copyMessage = async (content) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(content);
|
||||
ElMessage.success("已复制到剪贴板");
|
||||
} catch {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = content;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textArea);
|
||||
ElMessage.success("已复制到剪贴板");
|
||||
}
|
||||
};
|
||||
const toggleMessageFold = (message) => {
|
||||
message.collapsed = !message.collapsed;
|
||||
};
|
||||
const scrollToBottom = () => {
|
||||
nextTick(() => {
|
||||
if (messagesContainer.value) {
|
||||
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
|
||||
}
|
||||
});
|
||||
};
|
||||
const formatMessage = (content) => {
|
||||
if (!content) return "";
|
||||
return md.render(content);
|
||||
};
|
||||
const generateId = () => {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
};
|
||||
onMounted(() => {
|
||||
connectWebSocket();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
disconnectWebSocket();
|
||||
});
|
||||
return (_ctx, _cache) => {
|
||||
const _component_el_icon = ElIcon;
|
||||
const _component_el_button = ElButton;
|
||||
const _component_el_alert = ElAlert;
|
||||
const _component_el_input = ElInput;
|
||||
return openBlock(), createElementBlock("div", _hoisted_1, [
|
||||
createBaseVNode("div", _hoisted_2, [
|
||||
createBaseVNode("div", _hoisted_3, [
|
||||
_cache[8] || (_cache[8] = createBaseVNode("div", { class: "navbar-left" }, [
|
||||
createBaseVNode("h2", null, "FA智能助手")
|
||||
], -1)),
|
||||
createBaseVNode("div", _hoisted_4, [
|
||||
createBaseVNode("div", _hoisted_5, [
|
||||
createVNode(_component_el_icon, {
|
||||
class: normalizeClass(["status-icon", connectionStatus.value])
|
||||
}, {
|
||||
default: withCtx(() => [
|
||||
connectionStatus.value === "connected" ? (openBlock(), createBlock(unref(connection_default), { key: 0 })) : connectionStatus.value === "connecting" ? (openBlock(), createBlock(unref(loading_default), { key: 1 })) : (openBlock(), createBlock(unref(warning_default), { key: 2 }))
|
||||
]),
|
||||
_: 1
|
||||
}, 8, ["class"]),
|
||||
createBaseVNode("span", _hoisted_6, toDisplayString(connectionStatusText.value), 1)
|
||||
]),
|
||||
messages.value.length > 0 ? (openBlock(), createBlock(_component_el_button, {
|
||||
key: 0,
|
||||
text: "",
|
||||
icon: unref(delete_default),
|
||||
onClick: clearCurrentChat
|
||||
}, {
|
||||
default: withCtx(() => [..._cache[7] || (_cache[7] = [
|
||||
createTextVNode(" 清空对话 ", -1)
|
||||
])]),
|
||||
_: 1
|
||||
}, 8, ["icon"])) : createCommentVNode("", true),
|
||||
createVNode(_component_el_button, {
|
||||
text: "",
|
||||
icon: unref(setting_default),
|
||||
onClick: toggleConnection
|
||||
}, {
|
||||
default: withCtx(() => [
|
||||
createTextVNode(toDisplayString(isConnected.value ? "断开连接" : "重新连接"), 1)
|
||||
]),
|
||||
_: 1
|
||||
}, 8, ["icon"])
|
||||
])
|
||||
]),
|
||||
createBaseVNode("div", {
|
||||
ref_key: "messagesContainer",
|
||||
ref: messagesContainer,
|
||||
class: "chat-messages"
|
||||
}, [
|
||||
messages.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_7, [
|
||||
createBaseVNode("div", _hoisted_8, [
|
||||
createBaseVNode("div", _hoisted_9, [
|
||||
createVNode(_component_el_icon, { size: "64" }, {
|
||||
default: withCtx(() => [
|
||||
createVNode(unref(chat_dot_round_default))
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
]),
|
||||
_cache[13] || (_cache[13] = createBaseVNode("h1", null, "FA智能助手", -1)),
|
||||
_cache[14] || (_cache[14] = createBaseVNode("p", { class: "welcome-subtitle" }, " 我是您的专属AI助手,可以帮您回答问题、处理任务和进行智能对话 ", -1)),
|
||||
createBaseVNode("div", _hoisted_10, [
|
||||
createBaseVNode("div", {
|
||||
class: "prompt-card",
|
||||
onClick: _cache[0] || (_cache[0] = ($event) => setPrompt("请介绍一下FastApiAdmin系统"))
|
||||
}, [..._cache[9] || (_cache[9] = [
|
||||
createBaseVNode("h4", null, "系统介绍", -1),
|
||||
createBaseVNode("p", null, "请介绍一下FastApiAdmin系统", -1)
|
||||
])]),
|
||||
createBaseVNode("div", {
|
||||
class: "prompt-card",
|
||||
onClick: _cache[1] || (_cache[1] = ($event) => setPrompt("如何在系统中创建新的模块?"))
|
||||
}, [..._cache[10] || (_cache[10] = [
|
||||
createBaseVNode("h4", null, "开发指导", -1),
|
||||
createBaseVNode("p", null, "如何在系统中创建新的模块?", -1)
|
||||
])]),
|
||||
createBaseVNode("div", {
|
||||
class: "prompt-card",
|
||||
onClick: _cache[2] || (_cache[2] = ($event) => setPrompt("系统的权限管理是如何工作的?"))
|
||||
}, [..._cache[11] || (_cache[11] = [
|
||||
createBaseVNode("h4", null, "权限管理", -1),
|
||||
createBaseVNode("p", null, "FA系统的权限管理是如何工作的?", -1)
|
||||
])]),
|
||||
createBaseVNode("div", {
|
||||
class: "prompt-card",
|
||||
onClick: _cache[3] || (_cache[3] = ($event) => setPrompt("如何优化FA系统的性能?"))
|
||||
}, [..._cache[12] || (_cache[12] = [
|
||||
createBaseVNode("h4", null, "性能优化", -1),
|
||||
createBaseVNode("p", null, "如何优化系统的性能?", -1)
|
||||
])])
|
||||
])
|
||||
])
|
||||
])) : (openBlock(), createElementBlock("div", _hoisted_11, [
|
||||
(openBlock(true), createElementBlock(Fragment, null, renderList(messages.value, (message) => {
|
||||
return openBlock(), createElementBlock("div", {
|
||||
key: message.id,
|
||||
class: normalizeClass(["message-group", message.type])
|
||||
}, [
|
||||
createBaseVNode("div", _hoisted_12, [
|
||||
message.type === "user" ? (openBlock(), createElementBlock("div", _hoisted_13, [
|
||||
createVNode(_component_el_icon, null, {
|
||||
default: withCtx(() => [
|
||||
createVNode(unref(user_default))
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
])) : (openBlock(), createElementBlock("div", _hoisted_14, [
|
||||
createVNode(_component_el_icon, null, {
|
||||
default: withCtx(() => [
|
||||
createVNode(unref(chat_dot_round_default))
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
]))
|
||||
]),
|
||||
createBaseVNode("div", _hoisted_15, [
|
||||
createBaseVNode("div", _hoisted_16, [
|
||||
createBaseVNode("strong", _hoisted_17, toDisplayString(message.type === "user" ? "You" : "FA助手"), 1)
|
||||
]),
|
||||
createBaseVNode("div", _hoisted_18, [
|
||||
message.content.length > 200 ? (openBlock(), createBlock(_component_el_button, {
|
||||
key: 0,
|
||||
text: "",
|
||||
size: "small",
|
||||
icon: message.collapsed ? unref(arrow_down_default) : unref(arrow_up_default),
|
||||
class: "fold-button",
|
||||
onClick: ($event) => toggleMessageFold(message)
|
||||
}, {
|
||||
default: withCtx(() => [
|
||||
createTextVNode(toDisplayString(message.collapsed ? "展开" : "收起"), 1)
|
||||
]),
|
||||
_: 2
|
||||
}, 1032, ["icon", "onClick"])) : createCommentVNode("", true),
|
||||
createBaseVNode("div", {
|
||||
class: normalizeClass(["message-text", { collapsed: message.collapsed }]),
|
||||
innerHTML: formatMessage(message.content)
|
||||
}, null, 10, _hoisted_19),
|
||||
message.type === "assistant" && message.loading && !message.content ? (openBlock(), createElementBlock("div", _hoisted_20, [..._cache[15] || (_cache[15] = [
|
||||
createBaseVNode("div", { class: "typing-dots" }, [
|
||||
createBaseVNode("span"),
|
||||
createBaseVNode("span"),
|
||||
createBaseVNode("span")
|
||||
], -1)
|
||||
])])) : createCommentVNode("", true)
|
||||
]),
|
||||
!message.loading ? (openBlock(), createElementBlock("div", _hoisted_21, [
|
||||
createVNode(_component_el_button, {
|
||||
text: "",
|
||||
size: "small",
|
||||
icon: unref(copy_document_default),
|
||||
onClick: ($event) => copyMessage(message.content)
|
||||
}, null, 8, ["icon", "onClick"]),
|
||||
message.type === "assistant" ? (openBlock(), createBlock(_component_el_button, {
|
||||
key: 0,
|
||||
text: "",
|
||||
size: "small",
|
||||
icon: unref(refresh_left_default)
|
||||
}, null, 8, ["icon"])) : createCommentVNode("", true)
|
||||
])) : createCommentVNode("", true)
|
||||
])
|
||||
], 2);
|
||||
}), 128))
|
||||
])),
|
||||
error.value ? (openBlock(), createElementBlock("div", _hoisted_22, [
|
||||
createVNode(_component_el_alert, {
|
||||
title: error.value,
|
||||
type: "error",
|
||||
closable: true,
|
||||
"show-icon": "",
|
||||
onClose: _cache[4] || (_cache[4] = ($event) => error.value = "")
|
||||
}, null, 8, ["title"])
|
||||
])) : createCommentVNode("", true)
|
||||
], 512),
|
||||
createBaseVNode("div", _hoisted_23, [
|
||||
createBaseVNode("div", _hoisted_24, [
|
||||
createBaseVNode("div", _hoisted_25, [
|
||||
createVNode(_component_el_input, {
|
||||
modelValue: inputMessage.value,
|
||||
"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => inputMessage.value = $event),
|
||||
placeholder: isConnected.value ? "向FA助手发送消息..." : "请先连接到服务器",
|
||||
disabled: !isConnected.value || sending.value,
|
||||
type: "textarea",
|
||||
rows: 1,
|
||||
autosize: { minRows: 1, maxRows: 6 },
|
||||
resize: "none",
|
||||
class: "message-input",
|
||||
onKeydown: [
|
||||
withKeys(withModifiers(sendMessage, ["exact", "prevent"]), ["enter"]),
|
||||
_cache[6] || (_cache[6] = withKeys(withModifiers(($event) => inputMessage.value += "\n", ["shift", "exact"]), ["enter"]))
|
||||
]
|
||||
}, null, 8, ["modelValue", "placeholder", "disabled", "onKeydown"]),
|
||||
createVNode(_component_el_button, {
|
||||
disabled: !inputMessage.value.trim() || !isConnected.value || sending.value,
|
||||
loading: sending.value,
|
||||
class: "send-button",
|
||||
type: "primary",
|
||||
circle: "",
|
||||
onClick: sendMessage
|
||||
}, {
|
||||
default: withCtx(() => [
|
||||
createVNode(_component_el_icon, null, {
|
||||
default: withCtx(() => [
|
||||
createVNode(unref(promotion_default))
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
]),
|
||||
_: 1
|
||||
}, 8, ["disabled", "loading"])
|
||||
]),
|
||||
_cache[16] || (_cache[16] = createBaseVNode("div", { class: "input-footer" }, [
|
||||
createBaseVNode("span", { class: "input-hint" }, "按 Enter 发送消息,Shift + Enter 换行")
|
||||
], -1))
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
}
|
||||
});
|
||||
const index = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-9253047d"]]);
|
||||
export {
|
||||
index as default
|
||||
};
|
||||
Reference in New Issue
Block a user