|
@@ -0,0 +1,435 @@
|
|
|
+<script setup lang="jsx">
|
|
|
+import { onMounted, ref, watch } from 'vue';
|
|
|
+import { useRoute } from 'vue-router';
|
|
|
+import { BaseTable,TheChatView } from "@/components";
|
|
|
+import { useInfinite, useFetchStream, useScroll } from '@/composables';
|
|
|
+import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
|
|
|
+import { CustomModal } from "@/views/analyse/components";
|
|
|
+import { inColumns, outColumns } from '@/views/analyse/config';
|
|
|
+
|
|
|
+import { formatToData } from "@/utils/format";
|
|
|
+
|
|
|
+import { waterApi } from '@/api/water';
|
|
|
+
|
|
|
+const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
|
|
|
+const { refetch, cancelFetch } = useFetchStream("/grpc/decisionStream", { methdos: 'POST' }, false);
|
|
|
+
|
|
|
+const { onRestore } = useInfinite('/front/bigModel/warning/pageList', { type: 0, warningStatus: 0 });
|
|
|
+
|
|
|
+const route = useRoute();
|
|
|
+
|
|
|
+// 回答列表
|
|
|
+const answerResult = ref([]);
|
|
|
+// 获取最终回答流数据参数
|
|
|
+const flowParams = {
|
|
|
+ feedback: '',
|
|
|
+ category: '',
|
|
|
+ warningId: '',
|
|
|
+ simulate: '{}'
|
|
|
+};
|
|
|
+
|
|
|
+const answerLoading = ref(false);
|
|
|
+const textDataSources = ref(null);
|
|
|
+const warningActive = ref(0);
|
|
|
+
|
|
|
+// 进出水数据
|
|
|
+const jsTableData = ref([]);
|
|
|
+const csTableData = ref([]);
|
|
|
+
|
|
|
+const visible = ref(false);
|
|
|
+
|
|
|
+const modalData = ref({});
|
|
|
+
|
|
|
+// 菜单水数据类型
|
|
|
+const waterTypeValue = ref('');
|
|
|
+
|
|
|
+watch(() => waterTypeValue.value, curValue => {
|
|
|
+ onRestore({ warningStatus: warningActive.value, waterType: curValue });
|
|
|
+})
|
|
|
+
|
|
|
+const handleModelVisible = () => {
|
|
|
+ visible.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 报警详情
|
|
|
+*/
|
|
|
+const handleOpenContent = async (item) => {
|
|
|
+ const { id, category, reason: title, counts } = item;
|
|
|
+
|
|
|
+ flowParams.category = category;
|
|
|
+ flowParams.warningId = id;
|
|
|
+ flowParams.feedback = '';
|
|
|
+ flowParams.simulate = '{}';
|
|
|
+
|
|
|
+ answerLoading.value = false;
|
|
|
+
|
|
|
+ answerResult.value = [];
|
|
|
+
|
|
|
+ const { data } = await waterApi.getWaringDetails(id);
|
|
|
+ const showVal = JSON.parse(data.showVal);
|
|
|
+ const { basic, jsData, csData } = showVal;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const answer = JSON.parse(data.answer);
|
|
|
+ const reportList = [];
|
|
|
+ const alertList = [];
|
|
|
+ let simulateObj = null;
|
|
|
+
|
|
|
+ answer.map(item => {
|
|
|
+ const answerObjItem = JSON.parse(item);
|
|
|
+ switch (answerObjItem.biz) {
|
|
|
+ case "DECISION_REPORT":
|
|
|
+ reportList.push(answerObjItem.message);
|
|
|
+ break
|
|
|
+ case "DECISION_ALERT":
|
|
|
+ alertList.push(answerObjItem);
|
|
|
+ break
|
|
|
+ case "DECISION_SIMULATE":
|
|
|
+ if (warningActive.value === 1) return;
|
|
|
+ const { off, on, pred } = JSON.parse(answerObjItem.message);
|
|
|
+ simulateObj = {
|
|
|
+ biz: 'DECISION_SIMULATE',
|
|
|
+ off,
|
|
|
+ on,
|
|
|
+ pred,
|
|
|
+ isDisable: false
|
|
|
+ }
|
|
|
+ modalData.value = simulateObj;
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if (reportList.length) {
|
|
|
+ answerResult.value.push({
|
|
|
+ biz: 'DECISION_REPORT',
|
|
|
+ answer: reportList.join(""),
|
|
|
+ loading: false,
|
|
|
+ delayLoading: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (alertList.length) {
|
|
|
+ const [parseAnswer] = alertList.map(item => {
|
|
|
+ item.message = Object.keys(item.message).map(key => ({ ...item.message[key], isActive: null }));
|
|
|
+ return item;
|
|
|
+ })
|
|
|
+ answerResult.value.push({
|
|
|
+ biz: 'DECISION_ALERT',
|
|
|
+ loading: false,
|
|
|
+ delayLoading: false,
|
|
|
+ isAllSelect: false,
|
|
|
+ list: parseAnswer?.message
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (simulateObj) {
|
|
|
+ answerResult.value.push(simulateObj);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ answerResult.value.push({
|
|
|
+ biz: 'DECISION_REPORT',
|
|
|
+ answer: data.answer,
|
|
|
+ loading: false,
|
|
|
+ delayLoading: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ cancelFetch();
|
|
|
+
|
|
|
+ basic.title = title;
|
|
|
+ textDataSources.value = formatToData({
|
|
|
+ dataSource: basic,
|
|
|
+ warnKey: '报警值',
|
|
|
+ statusVal: !!warningActive.value ? '系统关闭' : basic['状态']
|
|
|
+ });
|
|
|
+
|
|
|
+ jsTableData.value = [jsData];
|
|
|
+ csTableData.value = [csData];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// 生成流数据
|
|
|
+const onRegenerate = async () => {
|
|
|
+
|
|
|
+ answerLoading.value = true;
|
|
|
+
|
|
|
+ const len = answerResult.value.length ? answerResult.value.length : 0;
|
|
|
+
|
|
|
+ const tempReport = {
|
|
|
+ biz: 'DECISION_REPORT',
|
|
|
+ answer: '',
|
|
|
+ loading: true,
|
|
|
+ delayLoading: true,
|
|
|
+ };
|
|
|
+
|
|
|
+ let tempSimulate = null;
|
|
|
+
|
|
|
+ answerLoading.value = answerResult.value[len - 1].biz !== 'DECISION_TABLE';
|
|
|
+
|
|
|
+ const feedback = flowParams.feedback
|
|
|
+
|
|
|
+ const params = {
|
|
|
+ body: JSON.stringify({ ...flowParams, feedback: JSON.stringify(feedback) }),
|
|
|
+ errorHandler: () => { },
|
|
|
+ successHandler: data => {
|
|
|
+
|
|
|
+ const item = JSON.parse(data);
|
|
|
+
|
|
|
+ answerLoading.value = false;
|
|
|
+
|
|
|
+ if (item.biz === 'DECISION_REPORT') {
|
|
|
+ tempReport.answer += item.message;
|
|
|
+ tempReport.delayLoading = false;
|
|
|
+ answerResult.value[len] = { ...tempReport };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.biz === 'DECISION_ALERT') {
|
|
|
+ const list = Object.keys(item.message).map(key => ({ ...item.message[key], isActive: null }));
|
|
|
+ answerResult.value.push({
|
|
|
+ biz: 'DECISION_ALERT',
|
|
|
+ loading: true,
|
|
|
+ delayLoading: false,
|
|
|
+ isAllSelect: false,
|
|
|
+ list
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.biz === 'DECISION_SIMULATE') {
|
|
|
+ const lastAnswerItem = answerResult.value[len - 1];
|
|
|
+ if (lastAnswerItem.biz === 'DECISION_TABLE') {
|
|
|
+ answerResult.value[len - 1] = {
|
|
|
+ ...lastAnswerItem,
|
|
|
+ content: JSON.parse(item.message).pred.join(", ")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const { off, on, pred } = JSON.parse(item.message);
|
|
|
+ tempSimulate = {
|
|
|
+ biz: 'DECISION_SIMULATE',
|
|
|
+ off,
|
|
|
+ on,
|
|
|
+ pred,
|
|
|
+ isDisable: false
|
|
|
+ }
|
|
|
+ modalData.value = tempSimulate;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ scrollToBottomIfAtBottom();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ await refetch(params);
|
|
|
+
|
|
|
+ const answerItem = answerResult.value[answerResult.value.length - 1];
|
|
|
+
|
|
|
+ if (answerItem?.biz) {
|
|
|
+ answerItem.loading = false;
|
|
|
+ answerItem.delayLoading = false;
|
|
|
+
|
|
|
+ if (answerItem.biz === 'DECISION_TABLE') {
|
|
|
+ scrollToBottom()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tempSimulate) {
|
|
|
+ answerResult.value.push(tempSimulate);
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ scrollToBottomIfAtBottom();
|
|
|
+ }, 500)
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ console.log("exist error .....", error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 回答选项点击
|
|
|
+const handlerAlertOptions = (item, val, index) => {
|
|
|
+ const { list, isAllSelect } = item;
|
|
|
+
|
|
|
+ if (isAllSelect) return;
|
|
|
+
|
|
|
+ val.isActive = index;
|
|
|
+
|
|
|
+ const isExists = list.find(({ isActive }) => isActive === null);
|
|
|
+
|
|
|
+ if (!isExists) {
|
|
|
+ item.isAllSelect = true;
|
|
|
+
|
|
|
+ const result = item.list
|
|
|
+ .map(({ id, options, isActive }) => ({ [id]: options[isActive] }))
|
|
|
+ .reduce((accumulator, currentValue) => {
|
|
|
+ Object.keys(currentValue).forEach(key => accumulator[key] = currentValue[key]);
|
|
|
+ return accumulator;
|
|
|
+ }, {});
|
|
|
+
|
|
|
+ const newResult = { ...flowParams.feedback, ...result };
|
|
|
+ flowParams.feedback = newResult;
|
|
|
+ onRegenerate();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 开始预测
|
|
|
+const handleSendSimulate = ({ simulate, table }) => {
|
|
|
+ const len = answerResult.value.length;
|
|
|
+
|
|
|
+ flowParams.simulate = simulate;
|
|
|
+ answerResult.value[len - 1].isDisable = true;
|
|
|
+
|
|
|
+ answerResult.value.push({
|
|
|
+ biz: 'DECISION_TABLE',
|
|
|
+ loading: true,
|
|
|
+ delayLoading: false,
|
|
|
+ table,
|
|
|
+ isDisable: false
|
|
|
+ })
|
|
|
+
|
|
|
+ scrollToBottom();
|
|
|
+
|
|
|
+ onRegenerate();
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ const { id,category, reason } = route.query;
|
|
|
+ console.log({ id,category, reason });
|
|
|
+ handleOpenContent({ id, category, reason });
|
|
|
+})
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <section class="flex items-start h-full" id="warning">
|
|
|
+ <TheChatView ref="scrollRef" :is-footer="false" :is-header="false">
|
|
|
+ <ChatBaseCard v-if="textDataSources">
|
|
|
+ <div class="waring-answer-wrapper">
|
|
|
+ <dl class="message-inner warning-info_medium ">
|
|
|
+ <dt class="mb-[2px] font-bold text-[#1A2029]">{{ textDataSources?.title }}</dt>
|
|
|
+ <dd v-for="item, index in textDataSources?.list" :key="index">
|
|
|
+ <span :class="['message-item', { 'text-[#F44C49]': item.isWarning }]">
|
|
|
+ <span>{{ item.label }}</span>
|
|
|
+ :
|
|
|
+ <span class="message-value" :title="item.value">{{ item.value }}</span>
|
|
|
+ </span>
|
|
|
+ </dd>
|
|
|
+ </dl>
|
|
|
+ <div class="table-inner">
|
|
|
+ <div class="warning-table mb-[8px]">
|
|
|
+ <div class="title">
|
|
|
+ <span>当前进水数据:</span>
|
|
|
+ </div>
|
|
|
+ <div class="main">
|
|
|
+ <BaseTable :columns="inColumns" :data="jsTableData"></BaseTable>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="warning-table">
|
|
|
+ <div class="title">
|
|
|
+ <span>当前出水数据:</span>
|
|
|
+ </div>
|
|
|
+ <div class="main">
|
|
|
+ <BaseTable :columns="outColumns" :data="csTableData"></BaseTable>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ChatBaseCard>
|
|
|
+
|
|
|
+ <section v-for="item, index in answerResult" :key="index">
|
|
|
+ <template v-if="item.biz === 'DECISION_REPORT'">
|
|
|
+ <ChatAnswer :loading="item.loading" :delay-loading="item.delayLoading" :toggleVisibleIcons="false"
|
|
|
+ :content="item.answer"></ChatAnswer>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-if="item.biz === 'DECISION_ALERT'">
|
|
|
+ <ChatBaseCard :loading="item.loading" :delay-loading="item.delayLoading" :toggleVisibleIcons="false">
|
|
|
+ <p class="mb-[15px] font-bold text-[#1A2029]">需要确定以下问题,完成决策方案:</p>
|
|
|
+ <ul class="radio-wrapper space-y-[14px]">
|
|
|
+ <li class="flex items-center" v-for="val, i in item.list" :key="i">
|
|
|
+ <p class="mr-[14px]">{{ val.mainContent }}</p>
|
|
|
+ <p class="radio-btn-group space-x-[14px]">
|
|
|
+ <span v-for="option, index in val.options" :class="['radio-btn', { active: val.isActive === index }]"
|
|
|
+ @click="handlerAlertOptions(item, val, index)">{{ option }}</span>
|
|
|
+ </p>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </ChatBaseCard>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-if="item.biz === 'DECISION_SIMULATE'">
|
|
|
+ <button class="
|
|
|
+ px-[30px] py-[10px] mb-[20px]
|
|
|
+ rounded-[8px]
|
|
|
+ bg-white text-[13px]
|
|
|
+ text-[#5E5E5E] hover:text-[#2454FF]" :disabled="item.isDisable" @click="handleModelVisible">
|
|
|
+ 水质预测推演
|
|
|
+ </button>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-if="item.biz === 'DECISION_TABLE'">
|
|
|
+ <ChatAnswer :loading="item.loading" :delay-loading="item.delayLoading" :toggleVisibleIcons="false"
|
|
|
+ class="reset-chart">
|
|
|
+ <div class="markdown-body text-[15px] break-all">
|
|
|
+ <strong class="block mb-[16px]">推荐指标调整:</strong>
|
|
|
+ <div class="custom-table-wrapper">
|
|
|
+ <table>
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th v-for="text in item.table.header" :key="text">{{ text }}</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody class="text-center">
|
|
|
+ <tr>
|
|
|
+ <td v-for="text in item.table.body" :key="text">{{ text }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ <strong class="block mb-[16px]">预测推演结果:</strong>
|
|
|
+ <span>以上指标达成后,预计{{ flowParams.category }}可达到:{{ item.content }}</span>
|
|
|
+ </div>
|
|
|
+ </ChatAnswer>
|
|
|
+ <button class="
|
|
|
+ px-[30px] py-[10px] mb-[20px]
|
|
|
+ rounded-[8px]
|
|
|
+ bg-white text-[13px]
|
|
|
+ text-[#5E5E5E] hover:text-[#2454FF]" :disabled="item.isDisable" @click="handleModelVisible">
|
|
|
+ 水质预测推演
|
|
|
+ </button>
|
|
|
+ </template>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <ChatAnswer :loading="answerLoading" :delay-loading="answerLoading" :toggleVisibleIcons="false"
|
|
|
+ v-show="answerLoading" loadingText="内容生成中,大概需要1分钟..."></ChatAnswer>
|
|
|
+
|
|
|
+ </TheChatView>
|
|
|
+ </section>
|
|
|
+ <CustomModal v-model:visible="visible" :current-data="modalData" @on-submit="handleSendSimulate"></CustomModal>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+#warning {
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ background: #eef6fb;
|
|
|
+}
|
|
|
+.reset-chart {
|
|
|
+ .markdown-body {
|
|
|
+ .custom-table-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ table td,
|
|
|
+ table th {
|
|
|
+ white-space: normal !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.select-card {
|
|
|
+ padding: 15px 10px 0 10px;
|
|
|
+}
|
|
|
+</style>
|