367 lines
9.6 KiB
Vue
367 lines
9.6 KiB
Vue
<script lang="ts" setup>
|
|
import { uuidv4 } from "@uniiem/uuid";
|
|
import type { IWorkflowResponse, LocalMessage, VisitorRole } from "~/types";
|
|
|
|
const gstate = useGState();
|
|
const runtimeConfig = useRuntimeConfig();
|
|
|
|
const getPopularInquiriesByRole = () => {
|
|
return {
|
|
stu: [
|
|
{
|
|
label: "物联网基础",
|
|
inquiries: [
|
|
{
|
|
question: "物联网的定义是什么",
|
|
},
|
|
{
|
|
question: "物联网与互联网的关系与区别",
|
|
},
|
|
{
|
|
question: "物联网有哪些典型应用场景",
|
|
},
|
|
{
|
|
question: "物联网系统的四层结构是什么",
|
|
},
|
|
{
|
|
question: "物联网的本质是什么",
|
|
},
|
|
{
|
|
question: "新基建和物联网的关系",
|
|
},
|
|
{
|
|
question: "物联网工程的生命周期包括哪些阶段",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "智慧工地",
|
|
inquiries: [
|
|
{
|
|
question: "智慧工地的概念与核心目标",
|
|
},
|
|
{
|
|
question: "智慧工地系统的四层架构是什么",
|
|
},
|
|
{
|
|
question: "施工场地工人健康检测系统包括哪些功能",
|
|
},
|
|
{
|
|
question: "施工场地工人健康检测系统的作用是什么",
|
|
},
|
|
{
|
|
question: "智慧工地中常用的传感器有哪些",
|
|
},
|
|
{
|
|
question: "选择传感器需要考虑哪些特性参数",
|
|
},
|
|
{
|
|
question: "传感器安装调试的主要步骤有哪些",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "智能交通",
|
|
inquiries: [
|
|
{
|
|
question: "智能交通系统的定义和主要目标是什么",
|
|
},
|
|
{
|
|
question: "智能交通系统的四层结构及其功能",
|
|
},
|
|
{
|
|
question: "我国智能交通的发展现状如何",
|
|
},
|
|
{
|
|
question: "国外智能交通发展的主要特点是什么",
|
|
},
|
|
{
|
|
question: "智能交通系统中常用的感知技术有哪些",
|
|
},
|
|
{
|
|
question: "射频识别技术在智能交通中的应用有哪些",
|
|
},
|
|
{
|
|
question: "自动识别技术包括哪些分类",
|
|
},
|
|
{
|
|
question: "交通卡口监控系统需求分析包含哪些方面",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
tea: [
|
|
{
|
|
label: "教师日常",
|
|
inquiries: [
|
|
{
|
|
question: "如何申请借用公共教室",
|
|
},
|
|
{
|
|
question: "教师每年最低教学学时要求是多少",
|
|
},
|
|
{
|
|
question: "出差北京的住宿标准是什么",
|
|
},
|
|
{
|
|
question: "副教授评审的基本条件是什么",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "教研活动",
|
|
inquiries: [
|
|
{
|
|
question: "“挑战杯”国赛一等奖的奖励政策",
|
|
},
|
|
{
|
|
question: "教学能力大赛的参赛规程是怎样的",
|
|
},
|
|
{
|
|
question: "校级精品课程项目结题的要求",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "科研活动",
|
|
inquiries: [
|
|
{
|
|
question: "横向项目合同签订的流程是怎样的",
|
|
},
|
|
{
|
|
question: "成果转化中经费是如何使用的",
|
|
},
|
|
{
|
|
question: "项目课题中团队占比的排序",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
fans: [
|
|
{
|
|
label: "校园概况",
|
|
inquiries: [
|
|
{
|
|
question: "学校的发展历程是怎样的",
|
|
},
|
|
{
|
|
question: "入校预约的流程",
|
|
},
|
|
{
|
|
question: "学校有哪些专业",
|
|
},
|
|
{
|
|
question: "学校主要的教学成果",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "招生就业",
|
|
inquiries: [
|
|
{
|
|
question: "数字媒体专业历年的招生分数",
|
|
},
|
|
{
|
|
question: "学校招收外地学生的比例",
|
|
},
|
|
{
|
|
question: "学校近三年各个专业就业的情况",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
} as Record<
|
|
VisitorRole,
|
|
{ label: string; inquiries?: Array<{ question: string }> }[]
|
|
>;
|
|
};
|
|
|
|
const scrollArea = ref<HTMLDivElement | null>(null);
|
|
const regex = /<\/think>\n\n([\s\S]*)/m;
|
|
|
|
// 常见问题
|
|
const inquiries = computed(
|
|
() => getPopularInquiriesByRole()[gstate.currentRole]
|
|
);
|
|
|
|
const responding = ref(false);
|
|
const inquiryInput = ref("");
|
|
const onInquirySubmit = async () => {
|
|
if (inquiryInput.value) {
|
|
gstate.insertOrUpdateMessage({
|
|
id: uuidv4(),
|
|
role: "user",
|
|
message: inquiryInput.value,
|
|
});
|
|
responding.value = true;
|
|
|
|
scrollLastMessageIntoView();
|
|
|
|
let botMessageId = uuidv4();
|
|
gstate.insertOrUpdateMessage({
|
|
id: botMessageId,
|
|
role: "bot",
|
|
});
|
|
|
|
try {
|
|
// const resp = await useFetch<IWorkflowResponse>(
|
|
// `${runtimeConfig.public.DifyBaseURL}/workflows/run`,
|
|
// {
|
|
// method: "post",
|
|
// headers: {
|
|
// Authorization: `Bearer ${runtimeConfig.public.DifyApiKey}`,
|
|
// },
|
|
// body: JSON.stringify({
|
|
// inputs: {
|
|
// query: inquiryInput.value,
|
|
// },
|
|
// response_mode: "blocking",
|
|
// user: "xsh",
|
|
// }),
|
|
// }
|
|
// );
|
|
// gstate.insertOrUpdateMessage({
|
|
// id: botMessageId,
|
|
// role: "bot",
|
|
// message:
|
|
// resp.data.value?.data.outputs.message.match(regex)?.[1] ||
|
|
// "网络繁忙,请稍后再试",
|
|
// });
|
|
http_stream<{
|
|
query: string
|
|
response_mode: "blocking" | "streaming"
|
|
user: string
|
|
conversation_id: string
|
|
inputs: Record<string, any>
|
|
}>(`${runtimeConfig.public.DifyBaseURL}/chat-messages`, {
|
|
query: inquiryInput.value,
|
|
response_mode: "streaming",
|
|
user: "xsh",
|
|
conversation_id: "",
|
|
inputs: {}
|
|
}, {
|
|
onStart: () => {
|
|
responding.value = true;
|
|
gstate.insertOrUpdateMessage({
|
|
id: botMessageId,
|
|
role: "bot",
|
|
message: "",
|
|
});
|
|
},
|
|
onTextChunk: (message: string) => {
|
|
gstate.insertOrUpdateMessage({
|
|
id: botMessageId,
|
|
role: "bot",
|
|
message: gstate.messages.find(m => m.id === botMessageId)?.message + message,
|
|
});
|
|
},
|
|
onComplete: (id, finished_at, messageId) => {
|
|
responding.value = false;
|
|
gstate.insertOrUpdateMessage({
|
|
id: botMessageId,
|
|
role: "bot",
|
|
message: gstate.messages.find(m => m.id === botMessageId)?.message || "网络繁忙,请稍后再试",
|
|
});
|
|
scrollLastMessageIntoView();
|
|
fetch(
|
|
`${runtimeConfig.public.DifyBaseURL}/messages/${messageId}/suggested?user=xsh`,
|
|
{
|
|
method: "GET",
|
|
headers: {
|
|
Authorization: `Bearer ${runtimeConfig.public.DifyApiKey}`,
|
|
},
|
|
}
|
|
).then(res => res.json()).then(data => {
|
|
if (data?.data?.length) {
|
|
gstate.insertOrUpdateMessage({
|
|
id: uuidv4(),
|
|
role: "suggestion",
|
|
suggestions: data.data,
|
|
});
|
|
}
|
|
})
|
|
},
|
|
})
|
|
} catch (error) {
|
|
gstate.insertOrUpdateMessage({
|
|
id: botMessageId,
|
|
role: "bot",
|
|
message: "网络繁忙,请稍后再试",
|
|
});
|
|
}
|
|
inquiryInput.value = "";
|
|
|
|
responding.value = false;
|
|
scrollLastMessageIntoView();
|
|
}
|
|
};
|
|
|
|
const scrollLastMessageIntoView = () => {
|
|
nextTick(() => {
|
|
scrollArea.value?.scrollTo({
|
|
top: scrollArea.value?.scrollHeight,
|
|
behavior: "smooth",
|
|
});
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="scrollArea" class="h-full overflow-auto overflow-x-hidden mb-20">
|
|
<div class="p-4 h-full flex flex-col gap-4">
|
|
<PopularInquiries
|
|
:inquiries-list="inquiries"
|
|
@select-inquiry="
|
|
(inquiry) => {
|
|
if (responding) return;
|
|
inquiryInput = inquiry;
|
|
onInquirySubmit();
|
|
}
|
|
"
|
|
/>
|
|
|
|
<ChatMessage
|
|
v-for="message in gstate.messages"
|
|
:name="gstate.botName"
|
|
:key="message.id"
|
|
:message="message"
|
|
@select-suggestion="
|
|
(suggestion) => {
|
|
if (responding) return;
|
|
inquiryInput = suggestion;
|
|
onInquirySubmit();
|
|
}"
|
|
/>
|
|
</div>
|
|
<div
|
|
class="fixed inset-x-0 bottom-0 p-4 bg-white/90 rounded-t-2xl shadow-2xl dark:bg-neutral-800/90 backdrop-blur-2xl"
|
|
>
|
|
<img
|
|
src="~/assets/image/pattern/ai_glow.png"
|
|
class="absolute top-0 inset-x-0 opacity-50 pointer-events-none"
|
|
/>
|
|
<div class="w-full flex justify-between items-center gap-2">
|
|
<UInput
|
|
placeholder="请输入问题"
|
|
class="flex-1"
|
|
size="xl"
|
|
v-model="inquiryInput"
|
|
/>
|
|
<UButton
|
|
color="primary"
|
|
variant="solid"
|
|
size="xl"
|
|
trailing
|
|
trailing-icon="tabler:send-2"
|
|
:loading="responding"
|
|
loading-icon="svg-spinners:tadpole"
|
|
@click="onInquirySubmit"
|
|
>提问</UButton
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|