123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- <script setup lang="jsx">
- import { ref } from 'vue';
- import { useRouter } from 'vue-router';
- import { NTabs, NTab } from 'naive-ui';
- import { useChatStore } from '@/stores/modules/chatStore';
- import { columns } from './config/index.jsx';
- import { BaseTable, ChatWelcome, RecodeSquareCardItem, TheSubMenu, TheChatView } from "@/components";
- import { ChatBaseCard, ChatAnswer } from '@/components/Chat';
- import { CustomModal } from "./components";
- import { format } from "@/utils/format";
- import { waterApi } from '@/api/water';
- import { useInfinite, useRecommend, useFetchStream, useScroll } from '@/composables';
- const { recommendList } = useRecommend({type: 1});
- const { scrollRef, scrollToTop, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
- const { refetch, cancelFetch } = useFetchStream("/grpc/decisionStream", { methdos: 'POST' }, false);
- const { recordList, isFetching, onScrolltolower, onRestore } = useInfinite('/front/bigModel/warning/pageList', { type: 0, warningStatus: 0 });
- const router = useRouter();
- const chatStore = useChatStore();
- // 回答列表
- const answerResult = ref([]);
- // 获取最终回答流数据参数
- const flowParams = {
- feedback: '',
- category: '',
- warningId: '',
- simulate: '{}'
- };
- const answerLoading = ref(false);
- const textDataSources = ref(null);
- // 进出水数据
- const jsTableData = ref([]);
- const csTableData = ref([]);
- const visible = ref(false);
- const modalData = ref({});
- const handleModelVisible = () => {
- visible.value = true;
- }
- const resetConfiguration = () => {
- /**
- * 临时这样,后续统一处理
- * */
- textDataSources.value = '';
- answerLoading.value = false;
- answerResult.value = [];
- flowParams.feedback = '';
- flowParams.category = '';
- flowParams.warningId = '';
- flowParams.simulate = '{}';
- cancelFetch();
- }
- /**
- * 报警详情
- */
- const handleOpenContent = async ({ id, category }) => {
- if ( id == flowParams.warningId ) return;
- flowParams.category = category;
- flowParams.warningId = id;
- flowParams.feedback = '';
- flowParams.simulate = '{}';
- answerLoading.value = false;
- const { data } = await waterApi.getWaringDetails(id);
- // const res = await waterApi.getWaringForecast(id);
- const showVal = JSON.parse(data.showVal);
- const { basic, jsData, csData } = showVal;
- const answer = JSON.parse(data.answer);
-
- cancelFetch();
- answerResult.value = [];
- const reportList = [];
- const alertList = [];
- answer.map(item => {
- const answerObjItem = JSON.parse( item );
- // TODO: 后面带需求确定后完善
- if ( answerObjItem.biz === "DECISION_REPORT" ) {
- reportList.push(answerObjItem.message);
- // const answerContent = answer.map(item => {
- // const itemParse = JSON.parse(item);
- // return itemParse.message;
- // }).join("");
- // answerResult.value.push({
- // biz: 'DECISION_REPORT',
- // answer: answerContent,
- // loading: false,
- // delayLoading: false
- // })
- }
- if( answerObjItem.biz === "DECISION_ALERT" ) {
- alertList.push(item);
- // const [ parseAnswer ] = answer.map(item => {
- // const result = JSON.parse( item );
- // result.message = Object.keys(result.message).map(key => ({ ...result.message[key], isActive: null }));
- // return result;
- // })
- // answerResult.value.push({
- // biz: 'DECISION_ALERT',
- // loading: false,
- // delayLoading: false,
- // isAllSelect: false,
- // list: parseAnswer?.message
- // })
- }
- if (answerObjItem.biz === "DECISION_SIMULATE") {
-
- // const usefulkeys = ['on', 'off'];
- // const resultObj = {};
- // usefulkeys.forEach(key => {
- // const tempArr = data[key];
- // resultObj[key] = tempArr.map(item => {
- // return {
- // ...item,
- // label: SIMULATE_ENUM[item.name],
- // inpVal: Array.isArray( item.value ) ? item.value.join() : item.value,
- // errMsg: ''
- // }
- // })
- // })
- }
- })
- if ( reportList.length ) {
- const answerContent = reportList.join("");
- answerResult.value.push({
- biz: 'DECISION_REPORT',
- answer: answerContent,
- loading: false,
- delayLoading: false
- })
- }
- if ( alertList.length ) {
- const [ parseAnswer ] = alertList.map(item => {
- const result = JSON.parse( item );
- result.message = Object.keys(result.message).map(key => ({ ...result.message[key], isActive: null }));
- return result;
- })
- answerResult.value.push({
- biz: 'DECISION_ALERT',
- loading: false,
- delayLoading: false,
- isAllSelect: false,
- list: parseAnswer?.message
- })
- }
- const textWhiteList = [
- { label: '报警时间', realKey: '报警时间', value: '', isWarning: false },
- { label: '报警值', realKey: '报警值', value: 'mg/L', isWarning: true },
- { label: '管控值', realKey: '管控值', value: 'mg/L', isWarning: false },
- { label: '标准值', realKey: '标准值', value: 'mg/L', isWarning: false },
- { label: '报警级别', realKey: '告警级别', value: '', isWarning: false },
- { label: '报警次数', realKey: '报警次数', value: '', isWarning: false },
- { label: '数据来源', realKey: '数据来源', value: '', isWarning: false },
- { label: '状态', realKey: '状态', value: '', isWarning: false }
- ]
- basic['数据来源'] = '在线仪表';
- textDataSources.value = format.textSorting(basic, textWhiteList);
- jsTableData.value = [jsData];
- csTableData.value = [csData];
- scrollToTop();
- }
- const onChangeTabs = warningStatus => {
- resetConfiguration();
- onRestore({ warningStatus });
- }
- // 生成流数据
- 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
- })
- onRegenerate();
- }
- // 欢迎页提交
- const handleWelcomeRecommend = question => {
- chatStore.setChatQuestion(question);
- router.push('/answer');
- }
- </script>
- <template>
- <section class="flex items-start h-full" id="warning">
- <TheSubMenu title="水质报警" @scrollToLower="onScrolltolower" :loading="isFetching">
- <template #top>
- <div class="border-[#DAE5ED]">
- <n-tabs type="line" justify-content="space-evenly">
- <n-tab name="oasis" tab="正在报警" @click="onChangeTabs(0)"></n-tab>
- <n-tab name="thebeatles" tab="历史报警" @click="onChangeTabs(1)"></n-tab>
- </n-tabs>
- </div>
- </template>
- <div class="px-[12px] py-[14px] text-[#5e5e5e]">
- <div class="grid grid-cols-1 gap-[12px]">
- <RecodeSquareCardItem
- v-for="item in recordList"
- :key="item.id"
- :item="item"
- @on-click="handleOpenContent"
- />
- </div>
- </div>
- </TheSubMenu>
- <TheChatView ref="scrollRef" :is-footer="false">
- <ChatWelcome title="您好,我是LibraAI工艺管控助手" card-title="常见处理方案:"
- :sub-title="[
- '水质报警功能针对五大核心指标实时监测,发现异常后将推送给相关人员决策方案',
- '报警时间为每小时警报,请大家及时处理'
- ]"
- :card-content="recommendList"
- @on-click="handleWelcomeRecommend"
- v-if="!textDataSources"
- />
- <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="{'text-[#F44C49]': item.isWarning}">{{ item.label }}: {{ item.value }}</span></dd>
- </dl>
- <div class="table-inner">
- <div class="warning-table mb-[8px]">
- <div class="title">
- <span>当前进水数据:</span>
- </div>
- <div class="main">
- <BaseTable :columns="columns" :data="jsTableData"></BaseTable>
- </div>
- </div>
- <div class="warning-table">
- <div class="title">
- <span>当前出水数据:</span>
- </div>
- <div class="main">
- <BaseTable :columns="columns" :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"
- >
- <div class="markdown-body text-[15px] break-all">
- <strong class="block mb-[16px]">推荐指标调整:</strong>
- <table>
- <thead>
- <tr>
- <th v-for="text in item.table.header" :key="text">{{ text }}</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td v-for="text in item.table.body" :key="text">{{ text }}</td>
- </tr>
- </tbody>
- </table>
- <strong class="block mb-[16px]">预测推演结果:</strong>
- <span>以上指标达成后,预计三小时内总氮可以达到:{{ 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="内容生成中,大概需要50秒..."
- ></ChatAnswer>
- </TheChatView>
- </section>
- <CustomModal
- v-model:visible="visible"
- :current-data="modalData"
- @on-submit="handleSendSimulate"
- ></CustomModal>
- </template>
|