|
@@ -1,90 +1,119 @@
|
|
|
<script setup>
|
|
|
-import { onMounted, ref, unref } from 'vue';
|
|
|
-import { useUserStore } from '@/stores/modules/userStore';
|
|
|
+import { onMounted, ref, unref, computed } from 'vue';
|
|
|
import { useRecommend } from '@/composables/useRecommend';
|
|
|
|
|
|
import { chatApi } from '@/api/chat';
|
|
|
import { streamChatRequest } from '@/utils/streamRequest';
|
|
|
import { useChat } from '@/composables/useChat';
|
|
|
-
|
|
|
-const str = `信义污水厂采用AAO(厌氧-缺氧-好氧)与移动床生物膜反应器(MBBR)相结合的工艺流程,其设计目标是高效处理污水并达到严格的排放标准。整个工艺流程可以分为预处理、二级生化处理和深度处理三个阶段,具体如下:
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-### 一、预处理阶段
|
|
|
-1. **粗格栅间**:污水经过粗格栅去除较大悬浮物,确保后续设备的正常运行。
|
|
|
-2. **进水泵房**:污水通过泵提升至适当高度,为后续处理提供动力。
|
|
|
-3. **细格栅间**:进一步去除更小的悬浮物和纤维物质。
|
|
|
-4. **旋流沉砂池**:利用机械力控制水流流态与流速,加速沙粒沉淀,降低后续设备的负荷。
|
|
|
-5. **初次沉淀池**:污水初步沉淀,分离出一部分悬浮固体。
|
|
|
-
|
|
|
-### 二、二级生化处理阶段
|
|
|
-1. **AAO+MBBR生化池**:通过AAO技术,微生物群体在好氧和厌氧条件下协同降解有机物;同时利用MBBR技术增加生物膜面积,提高硝化和反硝化效率。
|
|
|
-2. **二沉池**:处理后的混合液进行二次沉淀,进一步去除剩余的悬浮物和部分溶解性有机物。
|
|
|
-
|
|
|
-### 三、深度处理阶段
|
|
|
-1. **活性砂滤池**:通过过滤和微生物的附着作用,去除污水中的残余有机物和微量元素。
|
|
|
-2. **反冲洗配水深度处理(磁混凝)**:使用磁性混凝剂强化沉淀过程,提高悬浮物的去除效果。
|
|
|
-3. **次氯酸钠消毒**:对处理后的水进行化学消毒,杀灭剩余的病原体,确保出水达到安全标准。
|
|
|
-4. **除磷加药间**:通过添加磷去除剂,控制出水中总磷含量,防止水体富营养化。
|
|
|
-
|
|
|
-### 四、污泥处理和排放
|
|
|
-1. **污泥脱水间**:收集的污泥经过浓缩和脱水处理,减少体积,便于后续处置或资源化利用。
|
|
|
-
|
|
|
-以上流程的每个环节都经过精心设计,确保了污水处理的高效、稳定和环保,同时满足了法规要求和用户期望。`
|
|
|
+import { getFormatYesterDay } from '@/utils/format';
|
|
|
|
|
|
const ANSWER_ID_KEY = '@@id@@';
|
|
|
|
|
|
-const userStore = useUserStore();
|
|
|
-
|
|
|
// chat 数据
|
|
|
const { chatDataSource, addChat, updateChat, clearChat, updateById } = useChat();
|
|
|
|
|
|
-// 滚动条
|
|
|
-// const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
|
|
|
-
|
|
|
const scrollRef = ref(null);
|
|
|
|
|
|
-const showRight = ref(null);
|
|
|
-
|
|
|
-const { recommendList } = useRecommend({ type: 0 });
|
|
|
+const { recommendList, reloadRecommend } = useRecommend({ type: 0 });
|
|
|
|
|
|
+// 选中的智能体
|
|
|
+let selectedOption = null;
|
|
|
const currenSessionId = ref('');
|
|
|
+const helperList = ref([]);
|
|
|
|
|
|
+const helperActiveIndex = ref(null);
|
|
|
+const isLoading = ref(false);
|
|
|
const inpValue = ref('');
|
|
|
|
|
|
-// const jumpChatView = () => {
|
|
|
-// console.log("执行了");
|
|
|
-// uni.navigateTo({ url: '/pages/chat/chatView' });
|
|
|
-// }
|
|
|
-
|
|
|
-
|
|
|
-// 点击历史记录
|
|
|
+// 前往 - 历史记录
|
|
|
const onHistory = () => {
|
|
|
- console.log("history");
|
|
|
- showRight.value.open();
|
|
|
-}
|
|
|
+ if ( isLoading.value ) {
|
|
|
+ return uni.showToast({ title: '当前有会话进行中', duration: 3000, icon: 'none' });
|
|
|
+ }
|
|
|
+ uni.navigateTo({ url: '/pages/answer/history' })
|
|
|
+};
|
|
|
|
|
|
// 新建会话
|
|
|
const onAdd = () => {
|
|
|
- console.log("add");
|
|
|
+ if ( isLoading.value ) {
|
|
|
+ return uni.showToast({ title: '当前有会话进行中', duration: 3000, icon: 'none' });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!unref(chatDataSource).length) {
|
|
|
+ return uni.showToast({ title: '已切换最新会话', duration: 3000, icon: 'none' });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空选中的智能体
|
|
|
+ helperList.value.forEach(val => val.active = false);
|
|
|
+ selectedOption = null;
|
|
|
+
|
|
|
+ // 清空输入框的值
|
|
|
+ inpValue.value = '';
|
|
|
+
|
|
|
+ // 清空会话id
|
|
|
+ currenSessionId.value = null;
|
|
|
+
|
|
|
+ clearChat();
|
|
|
}
|
|
|
|
|
|
-const closeDrawer = () => {
|
|
|
- showRight.value.close();
|
|
|
+// 任务项点击
|
|
|
+const onTaskClick = (item) => {
|
|
|
+
|
|
|
+ const index = unref(helperActiveIndex);
|
|
|
+
|
|
|
+ helperList.value.forEach(val => {
|
|
|
+ if ( val.id != item.id ) val.active = false;
|
|
|
+ })
|
|
|
+
|
|
|
+ item.active = !item.active
|
|
|
+
|
|
|
+ if ( item.active ) {
|
|
|
+ inpValue.value = item.content;
|
|
|
+ selectedOption = item;
|
|
|
+ } else {
|
|
|
+ selectedOption = null;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+// 渲染markdown数据
|
|
|
const onRegenerate = ({ showVal, question, realQuestion, tools, uploadFileList }) => {
|
|
|
|
|
|
// 参数相关先不组合 - 后续统一整理
|
|
|
// const sessionId = unref(currenSessionId);
|
|
|
-
|
|
|
+
|
|
|
+ /**
|
|
|
+ sessionId":currenSessionId.value,
|
|
|
+ "showVal":"",
|
|
|
+ "question":"",
|
|
|
+ "module":0,
|
|
|
+
|
|
|
+ "modelType":0, 大模型 LibraAI 或者 deepSeek 这里默认使用 0
|
|
|
+
|
|
|
+ "isStrong":0, 是否强, 待后续支持deepseek的时候
|
|
|
+
|
|
|
+ "tools":null, 智能体
|
|
|
+ "
|
|
|
+ "onlineSearch":false, 是否在线搜索
|
|
|
+ "prompt":null
|
|
|
+ * */
|
|
|
+
|
|
|
streamChatRequest({
|
|
|
- data: {"sessionId":currenSessionId.value,"showVal":"信义污水厂工艺流程是什么?" + new Date().getTime,"question":"信义污水厂工艺流程是什么?" + new Date().getTime,"module":0,"modelType":0,"isStrong":0,"tools":null,"onlineSearch":false,"prompt":null},
|
|
|
+ data: {
|
|
|
+ sessionId: currenSessionId.value,
|
|
|
+ showVal,
|
|
|
+ question: realQuestion || question,
|
|
|
+ module: 0,
|
|
|
+ modelType: 0,
|
|
|
+ isStrong: 0,
|
|
|
+ tools: selectedOption?.tools || null,
|
|
|
+ onlineSearch: false,
|
|
|
+ prompt: null
|
|
|
+ },
|
|
|
onProgress: (responseText) => {
|
|
|
const [ answer ] = responseText.split(ANSWER_ID_KEY);
|
|
|
-
|
|
|
+
|
|
|
updateChat({
|
|
|
+ id: '',
|
|
|
sessionId: currenSessionId.value,
|
|
|
showVal: showVal,
|
|
|
question,
|
|
@@ -93,11 +122,28 @@ const onRegenerate = ({ showVal, question, realQuestion, tools, uploadFileList }
|
|
|
delayLoading: false,
|
|
|
uploadFileList
|
|
|
})
|
|
|
+
|
|
|
+ scrollRef.value.scrollToBottomIfAtBottom();
|
|
|
+ },
|
|
|
+ onSuccess: (data) => {
|
|
|
+ const [answer, id] = data.split(ANSWER_ID_KEY);
|
|
|
|
|
|
- scrollRef.value.scrollToBottom();
|
|
|
+ updateChat({
|
|
|
+ id,
|
|
|
+ sessionId: currenSessionId.value,
|
|
|
+ showVal: showVal,
|
|
|
+ question,
|
|
|
+ answer,
|
|
|
+ loading: false,
|
|
|
+ delayLoading: false,
|
|
|
+ uploadFileList
|
|
|
+ });
|
|
|
+
|
|
|
+ setTimeout(() => scrollRef.value.scrollToBottomIfAtBottom(), 100);
|
|
|
},
|
|
|
onComplete: () => {
|
|
|
- },
|
|
|
+ isLoading.value = false;
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
|
|
@@ -108,10 +154,14 @@ const handleSubmit = async ({ showVal, question, selectedOption, realQuestion =
|
|
|
* question: 问题 - 用于传给大模型使用
|
|
|
* selectedOption: 智能体-智能差数,这个需要后续完善
|
|
|
* **/
|
|
|
+
|
|
|
+ isLoading.value = true;
|
|
|
+
|
|
|
const { data: sessionId } = await chatApi.getChatSessionTag();
|
|
|
currenSessionId.value = sessionId;
|
|
|
|
|
|
addChat({
|
|
|
+ id: '',
|
|
|
sessionId,
|
|
|
showVal,
|
|
|
question,
|
|
@@ -122,13 +172,87 @@ const handleSubmit = async ({ showVal, question, selectedOption, realQuestion =
|
|
|
uploadFileList
|
|
|
})
|
|
|
|
|
|
+ scrollRef.value.scrollToBottom();
|
|
|
+
|
|
|
onRegenerate({ showVal, question, realQuestion, tools: selectedOption?.tools || null, uploadFileList });
|
|
|
+}
|
|
|
+
|
|
|
+// 处理推荐问题
|
|
|
+const handleWelcomeRecommend = ({ question, realQuestion }) => {
|
|
|
+ handleSubmit({showVal: question, question, realQuestion});
|
|
|
+}
|
|
|
+
|
|
|
+// 重新生成问题
|
|
|
+const onChatResetStream = (item) => {
|
|
|
+ const { question, uploadFileList, showVal } = item;
|
|
|
+ handleSubmit({showVal, question, uploadFileList});
|
|
|
+}
|
|
|
+
|
|
|
+const handleChatDetail = async ({ sessionId }) => {
|
|
|
+
|
|
|
+ isLoading.value = false;
|
|
|
+
|
|
|
+ inpValue.value = '';
|
|
|
+
|
|
|
+ const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
|
|
|
+
|
|
|
+ chatDataSource.value = data.map(item => {
|
|
|
+
|
|
|
+ const uploadFileList = []
|
|
|
+
|
|
|
+ if ( item.question.includes('file:') ) {
|
|
|
+
|
|
|
+ const fileInfo = item.question.split("||");
|
|
|
+ const fileArr = fileInfo[0].split(":");
|
|
|
+ const file = fileArr[1];
|
|
|
+ const url = fileInfo[1];
|
|
|
+ const suffix = file.substring( file.lastIndexOf('.') + 1 ).toUpperCase();
|
|
|
+ const originSuffix = file.substring( file.lastIndexOf('.') );
|
|
|
+ const name = file.substring(0, file.lastIndexOf('.'))
|
|
|
+
|
|
|
+ uploadFileList.push({
|
|
|
+ name,
|
|
|
+ originSuffix,
|
|
|
+ suffix,
|
|
|
+ url
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return ({ ...item, loading: false, uploadFileList});
|
|
|
+ });
|
|
|
+
|
|
|
+ currenSessionId.value = sessionId;
|
|
|
+
|
|
|
+ setTimeout(() => scrollRef.value.scrollToBottom(), 100)
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// 获取智能体
|
|
|
+const getHelperList = async () => {
|
|
|
+ const { data } = await chatApi.getHelperList();
|
|
|
+ const result = getFormatYesterDay(data);
|
|
|
+
|
|
|
+ helperList.value = result.filter(({ tools }) => tools).map(item => ({
|
|
|
+ ...item,
|
|
|
+ title: item.tools === 'work_order' ? '运行诊断' : item.title
|
|
|
+ }));
|
|
|
|
|
|
}
|
|
|
|
|
|
+// 初始化
|
|
|
+const init = async () => {
|
|
|
+ getHelperList();
|
|
|
+ reloadRecommend();
|
|
|
+}
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
+ init();
|
|
|
})
|
|
|
|
|
|
+defineExpose({
|
|
|
+ handleChatDetail,
|
|
|
+ init
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
@@ -140,7 +264,7 @@ onMounted(() => {
|
|
|
</template>
|
|
|
|
|
|
<template #content>
|
|
|
- <view class="chat-front-card" v-if="false">
|
|
|
+ <view class="chat-front-card" :style="{ display: chatDataSource.length !== 0 ? 'none' : 'flex' }">
|
|
|
<ChatWelcome
|
|
|
title="您好,我是LibraAI专家问答"
|
|
|
card-title="您可以试着问我:"
|
|
@@ -149,12 +273,13 @@ onMounted(() => {
|
|
|
'有任何重点或需讨论的事项,随时告诉我'
|
|
|
]"
|
|
|
:card-content="recommendList"
|
|
|
+ @on-click="handleWelcomeRecommend"
|
|
|
>
|
|
|
</ChatWelcome>
|
|
|
- <ChatTaskGroup ></ChatTaskGroup>
|
|
|
+ <ChatTaskGroup @on-click="onTaskClick" :options="helperList" v-model:index="helperActiveIndex"></ChatTaskGroup>
|
|
|
</view>
|
|
|
|
|
|
- <view class="qa-container">
|
|
|
+ <view class="qa-container" v-if="chatDataSource.length">
|
|
|
<view class="qa-item" v-for="item, index in chatDataSource" :key="item.id">
|
|
|
<ChatAsk :content="item.showVal" :sessionId="item.sessionId" :uploadFileList="item.uploadFileList"></ChatAsk>
|
|
|
<ChatAnswer
|
|
@@ -164,26 +289,22 @@ onMounted(() => {
|
|
|
:delay-loading="item.delayLoading"
|
|
|
:isSatisfied="item.isSatisfied"
|
|
|
:isVisibleResetBtn="chatDataSource.length - 1 === index"
|
|
|
+ @on-click-icon="params => updateById(params)"
|
|
|
+ @on-click-reset="onChatResetStream(item)"
|
|
|
></ChatAnswer>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<template #footer>
|
|
|
- <ChatInput v-model="inpValue" @on-submit="handleSubmit"></ChatInput>
|
|
|
+ <ChatInput
|
|
|
+ v-model="inpValue"
|
|
|
+ @on-submit="handleSubmit"
|
|
|
+ v-model:loading="isLoading"
|
|
|
+ ></ChatInput>
|
|
|
</template>
|
|
|
</BasePublicLayout>
|
|
|
|
|
|
- <!--
|
|
|
- TODO: 抽屉组件
|
|
|
- 后续完善
|
|
|
- -->
|
|
|
- <!-- <uni-drawer ref="showRight" mode="right" :mask-click="false" width="640">
|
|
|
- <scroll-view style="height: 100%;" scroll-y="true">
|
|
|
- <button @click="closeDrawer" type="primary">关闭Drawer</button>
|
|
|
- <view v-for="item in 60" :key="item">可滚动内容 {{ item }}</view>
|
|
|
- </scroll-view>
|
|
|
- </uni-drawer> -->
|
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
@@ -197,7 +318,11 @@ onMounted(() => {
|
|
|
|
|
|
.qa-container {
|
|
|
width: 100vw;
|
|
|
- padding: 64rpx 0;
|
|
|
+ padding: 64rpx 0 24rpx 0;
|
|
|
+
|
|
|
+ .qa-item:not(:last-child) {
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
</style>
|