123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- <script setup lang="jsx">
- import { ref, unref, watch } from 'vue';
- import { NTabs, NTab } from 'naive-ui';
- import { BaseCard, BaseTable, ChatWelcome, SvgIcon, RecodeSquareCardItem, TheSubMenu, TheChatView } from "@/components";
- import { ChatAsk, ChatBaseCard, ChatAnswer } from '@/components/Chat';
- import { format } from "@/utils/format";
- import { waterApi } from '@/api/water';
- import { CustomModal } from "./components";
- import { useInfinite } from '@/composables/useInfinite';
- import { useRecommend } from '@/composables/useRecommend';
- import { useFetchStream } from '@/composables/useFetchStream';
- import { useScroll } from '@/composables/useScroll';
- const { recommendList } = useRecommend({type: 1});
- const { scrollRef, scrollToTop, scrollToBottomIfAtBottom } = useScroll();
- const { streamData, refetch } = useFetchStream("/grpc/decisionStream", { methdos: 'POST' }, false);
- const { recordList, isFetching, onScrolltolower, onRestore, addHistoryRecord } = useInfinite('/front/bigModel/warning/pageList', { type: 0, warningStatus: 0 });
- let controller = new AbortController();
- // 获取最终回答流数据参数
- const flowParams = {
- feedback: '',
- category: '',
- warningId: ''
- };
- const reportAnswer = ref('');
- const alertAnswer = ref([]);
- const checkedAlertData = ref([]);
- const textDataSources = ref(null);
- const answerAlertDataSources = ref([]);
- // 进出水数据
- const jsTableData = ref([]);
- const csTableData = ref([]);
- const visible = ref(false);
- const renderRowDom = ({ row, key }) => {
- const { exceed, value } = row[key] || {};
- const cls = exceed ? 'text-[#F44C49] font-bold' : 'text-[1A2029]'
- return (<span class={ cls }>{value} {exceed && <i>↑</i>}</span>)
- }
- const columns = [
- {
- title: '流量(m³/h)',
- key: 'name',
- titleAlign: 'center',
- align: 'center',
- className: 'small',
- width: '80px',
- render: (row) => renderRowDom({ row, key: '流量' })
- },
- {
- title: 'COD(mg/L)',
- key: 'small',
- titleAlign: 'center',
- align: 'center',
- className: 'small',
- width: '80px',
- render: (row) => renderRowDom({ row, key: 'COD' })
- },
- {
- title: 'TN(mg/L)',
- key: 'address',
- titleAlign: 'center',
- align: 'center',
- className: 'small',
- width: '80px',
- render: (row) => renderRowDom({ row, key: 'TN' })
- },
- {
- title: 'NH3-N(mg/L)',
- key: 'tags',
- titleAlign: 'center',
- align: 'center',
- className: 'small',
- width: '80px',
- render: (row) => renderRowDom({ row, key: 'NH3-N' })
- },
- {
- title: '总磷TP(mg/L)',
- key: 'COD',
- titleAlign: 'center',
- align: 'center',
- className: 'small',
- width: '80px',
- render: (row) => renderRowDom({ row, key: 'TP' })
- },
- {
- title: 'SS(mg/L)',
- key: '流量',
- titleAlign: 'center',
- align: 'center',
- className: 'age',
- width: '78px',
- render: (row) => renderRowDom({ row, key: 'SS' })
- }
- ]
- const inWaterTableData = ref([{ name: 1233, actions: "7.87" }]);
- // 新建对话
- const handleCreateDialog = () => {
- console.log("handleCreateDialog");
- }
- const handleLoad = () => {
- console.log("loading")
- }
- const handleModelVisible = () => {
- visible.value = true
- }
- /**
- * 报警详情
- */
- const handleOpenContent = async ({id, category}) => {
- const { data } = await waterApi.getWaringDetails(id);
- const showVal = JSON.parse(data.showVal);
- const { basic, jsData, csData } = showVal;
- const answer = JSON.parse(data.answer);
- const [ answerStrItem ] = answer;
- const answerObjItem = JSON.parse( answerStrItem );
- console.log( answerObjItem.biz );
- const textWhiteList = [
- { label: '报警时间', value: '', isWarning: false },
- { label: '报警值', value: 'mg/L', isWarning: true },
- { label: '管控值', value: 'mg/L', isWarning: false },
- { label: '标准值', value: 'mg/L', isWarning: false },
- { label: '报警级别', value: '', isWarning: false },
- { label: '报警次数', value: '', isWarning: false },
- { label: '状态', value: '', isWarning: false }
- ]
- if ( answerObjItem.biz === "DECISION_REPORT" ) {
- alertAnswer.value = [];
- reportAnswer.value = answer.map(item => {
- const itemParse = JSON.parse(item);
- return itemParse.message;
- }).join();
- } else {
- reportAnswer.value = '';
- 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;
- })
- console.log( parseAnswer?.message );
- alertAnswer.value = parseAnswer?.message;
- }
- textDataSources.value = format.textSorting(basic, textWhiteList);
- jsTableData.value = [jsData];
- csTableData.value = [csData];
- flowParams.category = category;
- flowParams.warningId = id;
- scrollToTop();
- }
- const onChangeTabs = warningStatus => {
- onRestore({ warningStatus })
- }
- const onRegenerate = async () => {
- let counter = 0;
- let str = 0;
- // const timer = setInterval(item => {
- // if( timer === 10 ) {
- // console.log("str", str);
- // clearInterval(timer);
- // }
- // counter++;
- // const data = {biz: "DECISION_REPORT", message: counter};
- // str += data.message + "||"
- // reportAnswer.value = str;
- // }, 1000)
- try {
- const obj = {"biz": "DECISION_ALERT", "message": {"2_30": {"id": "2_30", "mainType": "alert", "mainContent": "设备与电气类是否有故障发生", "options": ["否", "是"], "next": "", "checked": false}}}
- const result = Object.keys(obj.message).map(key => ({ ...obj.message[key], isActive: null }));
- console.log( "result", result );
- // refetch({
- // body: JSON.stringify(flowParams),
- // successHandler: data => {
- // const item = JSON.parse(data);
- // str += item.message;
- // reportAnswer.value = str;
- // scrollToBottomIfAtBottom()
- // },
- // doneHandler: () => {
- // alert("结束了")
- // }
- // })
- }
- catch(error) {
- console.log("exist error .....", error);
- }
- }
- // 回答选项点击
- const handlerAlertOptions = (item, index) => {
- const isExists = checkedAlertData.value.find(d => d.id === item.id);
- item.isActive = index;
- isExists ?? checkedAlertData.value.push( item );
- if ( unref(checkedAlertData).length === unref(alertAnswer).length ) {
-
- const tempArr = alertAnswer.value.map(({ id, options, isActive }) => ({ [id]: options[isActive] }));
- const tempArrToStr = JSON.stringify(tempArr);
- flowParams.feedback = JSON.stringify(tempArr).substring(1, tempArrToStr.length - 1);
-
- onRegenerate();
- }
- }
- </script>
- <template>
- <section class="flex items-start h-full">
- <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"
- 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?.value }}</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>
- <!-- report -->
- <ChatAnswer
- :loading="true"
- :toggleVisibleIcons="false"
- :content="reportAnswer"
- v-show="reportAnswer"
- ></ChatAnswer>
- <!-- alert -->
- <ChatBaseCard v-show="alertAnswer.length">
- <p class="mb-[15px] font-bold text-[#1A2029]">需要确定以下问题,完成决策方案:</p>
- <ul class="radio-wrapper space-y-[14px]">
- <li class="flex items-center" v-for="item in alertAnswer" :key="item.id">
- <p class="mr-[14px]">{{ item.mainContent }}</p>
- <p class="radio-btn-group space-x-[14px]">
- <span
- :key="index"
- :class="['radio-btn', { active: item.isActive === index }]"
- v-for="val,index in item.options"
- @click="handlerAlertOptions(item, index)"
- >{{ val }}</span>
- </p>
- </li>
- </ul>
- </ChatBaseCard>
- <!-- <BaseCard :loading="true">
- <div class="waring-answer-wrapper">
- <dl class="message-inner warning-info_medium ">
- <dt class="mb-[2px] font-bold text-[#1A2029]">{{ textDataSources?.title }}</dt>
- <dd><span>报警时间:2024-4-25 21:00</span></dd>
- <dd><span class="text-[#F44C49]">报警值:7.87mg/L</span></dd>
- <dd><span>标准值:7.1mg/L</span></dd>
- <dd><span>报警级别:二级</span></dd>
- <dd><span>报警次数:33</span></dd>
- <dd><span>状态:报警中</span></dd>
- </dl>
- <div class="table-inner">
- <div class="warning-table mb-[8px]">
- <div class="title">
- <span>当前进水数据:</span>
- </div>
- <div class="main">
- </div>
- </div>
- <div class="warning-table">
- <div class="title">
- <span>当前出水数据:</span>
- </div>
- <div class="main">
- </div>
- </div>
- </div>
- </div>
- </BaseCard> -->
- <!-- <BaseCard>
- <p class="flex-1 text-[15px] leading-[24px]">
- COD,即化学需氧量,是衡量水中有机物质含量的重要指标。它反映了水中可氧化有机物的量,通常用来评估水体的污染程度。水中的有机物主要来源于工业废水、生活污水、农药残留等,这些有机物不仅会导致水质变差,还会对生物和人类健康产生负面影响。因此,通过测定COD值,可以了解水中有机污染物的含量,进而评估水体的污染程度。这对于制定环境保护政策、控制污染源、保障水资源安全等方面都具有重要的指导意义
- </p>
- </BaseCard>
- <button class="
- px-[30px] py-[10px] mb-[20px]
- rounded-[8px]
- bg-white text-[13px]
- text-[#5E5E5E] hover:text-[#2454FF]"
- @click="handleModelVisible"
- >
- 水质预测推演
- </button> -->
- </TheChatView>
- </section>
- <CustomModal v-model:visible="visible"></CustomModal>
- </template>
- <style scoped lang="scss">
- .base-card-container {
- margin-bottom: 20px;
- }
- .warning-item-inner {
- position: relative;
- padding: 20px 8px 8px 8px;
- border-radius: 4px;
- background: #DDE5EF;
- .tips {
- position: absolute;
- width: 36px;
- height: 14px;
- top: 0;
- right: 0px;
- border-radius: 0px 4px 0px 4px;
- font-size: 8px;
- text-align: center;
- line-height: 14px;
- &_warning,
- &_being {
- color: #F44C49;
- background: #FFF0ED;
- }
- &_success {
- color: #51BF8E;
- background: #E9FAF2;
- }
- &_close {
- color: #999999;
- background: #D5D5D5;
- }
- }
- }
- .warning-info {
- line-height: 16px;
- font-size: 11px;
- color: #5E5E5E;
- dd {
- margin-top: 4px;
- }
- &_medium {
- line-height: 26px;
- font-size: 14px;
- color: #1A2029;
- }
- }
- // 回答区域卡片
- .waring-answer-wrapper {
- @include flex(x, start, between);
- .message-inner {
- width: 194px;
- flex-shrink: 0;
- }
- .table-inner {
- @include flex(y, end, center);
- padding-left: 20px;
- border-left: 1px solid #F1F1F1;
- .warning-table {
- .title {
- margin-bottom: 8px;
- line-height: 16px;
- font-size: 12px;
- font-weight: bold;
- color: #1A2029;
- }
- }
- }
- }
- .radio-wrapper {
- .radio-btn-group {
- @include flex(x, center, center);
- font-size: 14px;
- text-align: center;
- color: #5E5E5E;
- .radio-btn {
- width: 62px;
- height: 28px;
- border-radius: 4px;
- background: #F4F6F8;
- font-size: 14px;
- line-height: 26px;
- cursor: pointer;
- &.active, &:hover {
- color: #2454FF;
- background: #E2F1FF;
- }
- &.active {
- background: #E2F1FF url('@/assets/images/chat/bg-raido-check.png') right bottom no-repeat;
-
- }
- }
- }
- }
- </style>
|