|
@@ -0,0 +1,647 @@
|
|
|
|
+<script setup>
|
|
|
|
+import { ref, unref, computed, onUnmounted, h } from 'vue';
|
|
|
|
+import { useMessage, NDatePicker, NTabs, NTab, NRadioGroup, NRadio, NCheckboxGroup, NCheckbox, NDataTable } from 'naive-ui';
|
|
|
|
+import { BaseButton, RecodeCardItem, TheSubMenu, TheChatView, ChatWelcome, SvgIcon } from '@/components';
|
|
|
|
+import { ChatAsk, ChatAnswer } from '@/components/Chat';
|
|
|
|
+import { orderApi } from "@/api/order";
|
|
|
|
+import { chatApi } from '@/api/chat';
|
|
|
|
+import { formatEchart, isNumberComprehensive } from '@/utils/format';
|
|
|
|
+import { ORDER_OPTION_ENUM } from '@/utils/enum';
|
|
|
|
+import { getOrderAreaOptions } from './config/echartOptions'
|
|
|
|
+import * as echarts from 'echarts';
|
|
|
|
+import dayjs from 'dayjs';
|
|
|
|
+
|
|
|
|
+import { useInfinite, useScroll, useChat } from '@/composables';
|
|
|
|
+
|
|
|
|
+const { recordList, isFetching, onScrolltolower, onReset } = useInfinite('/front/bigModel/qa/pageList', { module: 1 });
|
|
|
|
+const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
|
|
|
|
+const { chatDataSource, addChat, updateChat, clearChat, updateById } = useChat();
|
|
|
|
+
|
|
|
|
+let controller = new AbortController();
|
|
|
|
+let timer = null;
|
|
|
|
+
|
|
|
|
+const ANSWER_ID_KEY = '@@id@@';
|
|
|
|
+
|
|
|
|
+const chartInstance = {};
|
|
|
|
+
|
|
|
|
+const message = useMessage();
|
|
|
|
+const reportDate = ref();
|
|
|
|
+
|
|
|
|
+const workOrderParams = ref({
|
|
|
|
+ timeBegin: null,
|
|
|
|
+ timeEnd: null,
|
|
|
|
+ whichWay: 1,
|
|
|
|
+ checkGroup: [],
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const switchActive = ref(false);
|
|
|
|
+const tabActive = ref("daily");
|
|
|
|
+
|
|
|
|
+const isLoading = ref(false);
|
|
|
|
+const recordActive = ref(null);
|
|
|
|
+
|
|
|
|
+const currenSessionId = ref(null);
|
|
|
|
+
|
|
|
|
+const isExistInHistory = computed(() => (recordList.value.findIndex(({ sessionId: sId }) => sId === unref(currenSessionId)) === -1));
|
|
|
|
+const isChart = computed(() => tabActive.value == 'customDaily');
|
|
|
|
+const chatDataSourceItem = computed(() => unref(chatDataSource)[chatDataSource.value.length - 1]);
|
|
|
|
+
|
|
|
|
+const resetFormData = () => {
|
|
|
|
+ workOrderParams.value = {
|
|
|
|
+ timeBegin: null,
|
|
|
|
+ timeEnd: null,
|
|
|
|
+ whichWay: 1,
|
|
|
|
+ checkGroup: [],
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const resetState = () => {
|
|
|
|
+ tabActive.value = 'daily';
|
|
|
|
+
|
|
|
|
+ currenSessionId.value = null;
|
|
|
|
+
|
|
|
|
+ recordActive.value = null;
|
|
|
|
+
|
|
|
|
+ reportDate.value = null;
|
|
|
|
+
|
|
|
|
+ resetFormData();
|
|
|
|
+
|
|
|
|
+ clearChat();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 新建对话
|
|
|
|
+const handleCreateDialog = async () => {
|
|
|
|
+ message.destroyAll();
|
|
|
|
+
|
|
|
|
+ if (unref(isLoading)) {
|
|
|
|
+ return message.warning('当前对话生成中');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!unref(chatDataSource).length) {
|
|
|
|
+ return message.info('已切换最新会话');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ resetState();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 查询对话详情
|
|
|
|
+const handleChatDetail = async ({ sessionId }) => {
|
|
|
|
+ isLoading.value = false;
|
|
|
|
+
|
|
|
|
+ controller.abort();
|
|
|
|
+
|
|
|
|
+ if(currenSessionId.value === sessionId) return;
|
|
|
|
+
|
|
|
|
+ recordActive.value = sessionId;
|
|
|
|
+
|
|
|
|
+ const { data } = await chatApi.getAnswerHistoryDetail({ sessionId });
|
|
|
|
+
|
|
|
|
+ let echartData = [];
|
|
|
|
+ let whichWay = null;
|
|
|
|
+ chatDataSource.value = data.map(item => {
|
|
|
|
+ if ( item.remark ) {
|
|
|
|
+ const remark = JSON.parse(item.remark);
|
|
|
|
+ whichWay = remark.whichWay;
|
|
|
|
+ echartData = item.echartWithTableData = formatData(remark);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return { ...item, loading: false };
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ echartData.length && createEchart(echartData, whichWay);
|
|
|
|
+
|
|
|
|
+ currenSessionId.value = sessionId;
|
|
|
|
+
|
|
|
|
+ resetFormData();
|
|
|
|
+
|
|
|
|
+ scrollToBottom();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 请求
|
|
|
|
+const onRegenerate = async (question, options) => {
|
|
|
|
+ controller = new AbortController();
|
|
|
|
+
|
|
|
|
+ const sessionId = unref(currenSessionId);
|
|
|
|
+ const params = {
|
|
|
|
+ data: {
|
|
|
|
+ sessionId,
|
|
|
|
+ showVal: question,
|
|
|
|
+ question,
|
|
|
|
+ module: 1,
|
|
|
|
+ isStrong: Number(unref(switchActive)),
|
|
|
|
+ reportDate: reportDate.value,
|
|
|
|
+ ...options,
|
|
|
|
+ },
|
|
|
|
+ signal: controller.signal,
|
|
|
|
+ onDownloadProgress: ({ event }) => {
|
|
|
|
+ const xhr = event.target;
|
|
|
|
+ const { responseText } = xhr;
|
|
|
|
+ const [answer] = responseText.split(ANSWER_ID_KEY);
|
|
|
|
+
|
|
|
|
+ updateChat({
|
|
|
|
+ ...chatDataSourceItem.value,
|
|
|
|
+ sessionId,
|
|
|
|
+ showVal: question,
|
|
|
|
+ answer,
|
|
|
|
+ loading: true,
|
|
|
|
+ delayLoading: false,
|
|
|
|
+ innerLoading: false
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ scrollToBottomIfAtBottom();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ const { data } = await chatApi.getChatStream(params);
|
|
|
|
+
|
|
|
|
+ const [answer, id] = data.split(ANSWER_ID_KEY);
|
|
|
|
+
|
|
|
|
+ updateChat({
|
|
|
|
+ ...chatDataSourceItem.value,
|
|
|
|
+ id,
|
|
|
|
+ sessionId,
|
|
|
|
+ showVal: question,
|
|
|
|
+ answer,
|
|
|
|
+ loading: false,
|
|
|
|
+ delayLoading: false,
|
|
|
|
+ innerLoading: false
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ scrollToBottomIfAtBottom();
|
|
|
|
+ }
|
|
|
|
+ catch (error) {
|
|
|
|
+ console.log("取消了请求 - catch", error);
|
|
|
|
+ }
|
|
|
|
+ finally {
|
|
|
|
+ isLoading.value = false;
|
|
|
|
+ onReset();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 提交问题
|
|
|
|
+const handleSubmit = async (question, params) => {
|
|
|
|
+
|
|
|
|
+ if (unref(isExistInHistory)) {
|
|
|
|
+ const { data: sessionId } = await chatApi.getChatSessionTag();
|
|
|
|
+ currenSessionId.value = sessionId;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ isLoading.value = true;
|
|
|
|
+
|
|
|
|
+ const option = {
|
|
|
|
+ sessionId: unref(currenSessionId),
|
|
|
|
+ showVal: question,
|
|
|
|
+ answer: '',
|
|
|
|
+ loading: true,
|
|
|
|
+ delayLoading: true,
|
|
|
|
+ echartWithTableData: []
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (isChart.value) {
|
|
|
|
+ const { data } = await orderApi.postOrderChart(params);
|
|
|
|
+ const reuslt = formatData({...data, whichWay: workOrderParams.value.whichWay});
|
|
|
|
+
|
|
|
|
+ option.echartWithTableData = reuslt;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ addChat({
|
|
|
|
+ ...option,
|
|
|
|
+ innerLoading: true,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ timer = setTimeout(() => {
|
|
|
|
+
|
|
|
|
+ updateChat({
|
|
|
|
+ ...chatDataSourceItem.value,
|
|
|
|
+ delayLoading: true,
|
|
|
|
+ innerLoading: false,
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ createEchart(option.echartWithTableData, workOrderParams.value.whichWay);
|
|
|
|
+
|
|
|
|
+ scrollToBottom();
|
|
|
|
+
|
|
|
|
+ onRegenerate(question, params);
|
|
|
|
+
|
|
|
|
+ }, 2 * 1000);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 处理推荐问题
|
|
|
|
+const handleCreateOrder = async () => {
|
|
|
|
+ const { timeBegin, timeEnd, whichWay, checkGroup } = unref(workOrderParams);
|
|
|
|
+ const startDateTime = dayjs(timeBegin);
|
|
|
|
+ const endDateTime = dayjs(timeEnd);
|
|
|
|
+ let params = {};
|
|
|
|
+
|
|
|
|
+ let question = `${reportDate.value}智慧工单分析报告`;
|
|
|
|
+
|
|
|
|
+ if (tabActive.value === 'customDaily') {
|
|
|
|
+ const thirtyDaysBeforeEndTime = endDateTime.subtract(31, 'day');
|
|
|
|
+ const isStartAfterThirtyDaysBeforeEnd = startDateTime.isAfter(thirtyDaysBeforeEndTime);
|
|
|
|
+
|
|
|
|
+ if (!timeBegin) return message.warning('请选择开始日期');
|
|
|
|
+ if (!timeEnd) return message.warning('请选择结束日期');
|
|
|
|
+ if (!isStartAfterThirtyDaysBeforeEnd) return message.warning('只可生成一个月区间的工单报告');
|
|
|
|
+ if (!checkGroup.length) return message.warning('请至少选择一项指标项');
|
|
|
|
+
|
|
|
|
+ params = {
|
|
|
|
+ timeBegin,
|
|
|
|
+ timeEnd,
|
|
|
|
+ whichWay
|
|
|
|
+ }
|
|
|
|
+ checkGroup.forEach(key => params[key] = true);
|
|
|
|
+
|
|
|
|
+ question = `${startDateTime.format("MM月DD日")}-${endDateTime.format("MM月DD日")}的在线仪表的日报工单`
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ if (!reportDate.value) return message.warning('请选择时间');
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ handleSubmit(question, params)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 创建echart图形
|
|
|
|
+const createEchart = (echartData, whichWay) => {
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ echartData.forEach(({ key, xAxisData, yAxisData, }) => {
|
|
|
|
+ const dom = document.getElementById(key);
|
|
|
|
+ chartInstance[key] = echarts.init(dom, null, { width: 680, height: 300 });
|
|
|
|
+
|
|
|
|
+ const option = getOrderAreaOptions({ xAxisData, yAxisData, whichWay });
|
|
|
|
+
|
|
|
|
+ chartInstance[key].setOption(option);
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 格式化 echart 和 table 数据
|
|
|
|
+const formatData = (data) => {
|
|
|
|
+ const titleEnum = {
|
|
|
|
+ jsGroup: '进水指标',
|
|
|
|
+ csGroup: '出水指标',
|
|
|
|
+ hyGroup: '化验指标'
|
|
|
|
+ }
|
|
|
|
+ const { whichWay, jsGroup, csGroup, hyGroup } = data;
|
|
|
|
+ return Object.entries({jsGroup, csGroup, hyGroup}).map(([key, value]) => {
|
|
|
|
+ if ( value.length ) {
|
|
|
|
+ const [ xAxisData, yAxisData ] = formatEchart(value);
|
|
|
|
+ const [ item ] = value;
|
|
|
|
+ const columns = Object.keys(item).map(k => {
|
|
|
|
+ const wihteKeyList = ['jsSlq', 'csSlqc'];
|
|
|
|
+ let unit = '';
|
|
|
|
+
|
|
|
|
+ if(wihteKeyList.includes(k)) {
|
|
|
|
+ unit = whichWay === 1? '(m³/h)' : '(m³/d)';
|
|
|
|
+ } else if (k !== 'time') {
|
|
|
|
+ unit= '(mg/L)';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ title: ORDER_OPTION_ENUM[k] + unit,
|
|
|
|
+ key: k,
|
|
|
|
+ width: '150px',
|
|
|
|
+ align: 'center'
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ const data = value.map(item => {
|
|
|
|
+ Object.entries(item).forEach(([k, v]) => {
|
|
|
|
+ !v && v!=0 && (item[k] = '-');
|
|
|
|
+ if (isNumberComprehensive(v)) {
|
|
|
|
+ item[k] = v ? Number(v.toFixed(2)) : 0
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ return item;
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ key: key,
|
|
|
|
+ title: titleEnum[key],
|
|
|
|
+ xAxisData,
|
|
|
|
+ yAxisData,
|
|
|
|
+ columns,
|
|
|
|
+ data
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }).filter(Boolean);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 返回
|
|
|
|
+const handleback = async () => {
|
|
|
|
+ clearInterval(timer);
|
|
|
|
+ await chatApi.getStopChatStream(currenSessionId.value);
|
|
|
|
+ resetState();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 删除历史对话
|
|
|
|
+const handeChatDelete = async (id) => {
|
|
|
|
+ await chatApi.deleteHistory(id);
|
|
|
|
+ onReset();
|
|
|
|
+ clearChat();
|
|
|
|
+ message.success('删除成功');
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const handleTabChange = (val) => {
|
|
|
|
+ tabActive.value = val;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const dateStartDisabled = (timestamp) => {
|
|
|
|
+ const { timeEnd } = workOrderParams.value;
|
|
|
|
+ if (timeEnd) {
|
|
|
|
+ const currentDate = dayjs(timestamp);
|
|
|
|
+ const endDateValue = dayjs(timeEnd);
|
|
|
|
+ return !currentDate.isBefore(endDateValue);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const dateEndDisabled = (timestamp) => {
|
|
|
|
+ const { timeBegin } = workOrderParams.value;
|
|
|
|
+ if (timeBegin) {
|
|
|
|
+ const currentDate = dayjs(timestamp);
|
|
|
|
+ const endDateValue = dayjs(timeBegin);
|
|
|
|
+ return !currentDate.isAfter(endDateValue);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+onUnmounted(() => {
|
|
|
|
+ controller.abort();
|
|
|
|
+ Object.keys(chartInstance).forEach(key => chartInstance[key].clear());
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <section class="flex items-start h-full">
|
|
|
|
+
|
|
|
|
+ <TheSubMenu title="智能工单" @scrollToLower="onScrolltolower" :loading="isFetching">
|
|
|
|
+ <template #top>
|
|
|
|
+ <div class="create-btn px-[11px] pb-[22px]">
|
|
|
|
+ <BaseButton @click="handleCreateDialog" icon-name="tool-add-circle">新建工单</BaseButton>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+
|
|
|
|
+ <div class="pr-[4px] text-[#5e5e5e]">
|
|
|
|
+ <p v-show="!recordList.length" class="pt-[30px] text-[12px] text-[#999] text-center">暂无工单数据</p>
|
|
|
|
+ <RecodeCardItem v-for="item, index in recordList" :key="item.sessionId + index" :title="item.showVal"
|
|
|
|
+ :time="item.createTime" :data-item="item"
|
|
|
|
+ :class="{ 'recode-card-item_active': recordActive === item.sessionId }" @on-click="handleChatDetail"
|
|
|
|
+ @on-delete="handeChatDelete" />
|
|
|
|
+ </div>
|
|
|
|
+ </TheSubMenu>
|
|
|
|
+
|
|
|
|
+ <TheChatView ref="scrollRef" :is-footer="false" :is-back-btn="!!chatDataSource.length" @on-click-back="handleback">
|
|
|
|
+ <ChatWelcome title="您好,我是LibraAI智慧工单助手" :sub-title="[
|
|
|
|
+ '基于大语言模型的智能工单分析助手,可以为您实现数据分析及数据解读',
|
|
|
|
+ '选择日期并为您生成日报分析'
|
|
|
|
+ ]" v-if="!chatDataSource.length" />
|
|
|
|
+
|
|
|
|
+ <div class="conversation-item" v-show="chatDataSource.length">
|
|
|
|
+ <div v-for="item in chatDataSource" :key="item.sessionId">
|
|
|
|
+ <ChatAsk :content="item.showVal" :sessionId="item.sessionId"></ChatAsk>
|
|
|
|
+ <ChatAnswer
|
|
|
|
+ :id="item.id"
|
|
|
|
+ :content="item.answer"
|
|
|
|
+ :loading="item.loading"
|
|
|
|
+ :delay-loading="item.delayLoading"
|
|
|
|
+ :isSatisfied="item.isSatisfied"
|
|
|
|
+ :toggleVisibleIcons="false"
|
|
|
|
+ loadingText="数据分析中,由于数据查询需要一些时间,请您耐心等候..."
|
|
|
|
+ @on-click-icon="params => updateById(params)"
|
|
|
|
+ >
|
|
|
|
+ <main v-show="!item.innerLoading">
|
|
|
|
+ <div
|
|
|
|
+ class="area_inner"
|
|
|
|
+ v-for="(item, index) in item.echartWithTableData"
|
|
|
|
+ :key="index"
|
|
|
|
+ >
|
|
|
|
+ <div class="echart-warpper" >
|
|
|
|
+ <span class="mb-[10px] #1A2029 font-bold">{{ item.title }}</span>
|
|
|
|
+ <div :id="item.key" class="w-[680px] h-[300px]"></div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="w-[680px]" style="max-width: 680px; overflow-x: auto;">
|
|
|
|
+ <NDataTable
|
|
|
|
+ bordered
|
|
|
|
+ size="small"
|
|
|
|
+ :scroll-x="item.columns.length > 4 ? 1200: 670"
|
|
|
|
+ :max-height="250"
|
|
|
|
+ :single-line="false"
|
|
|
|
+ :columns="item.columns"
|
|
|
|
+ :data="item.data"
|
|
|
|
+ >
|
|
|
|
+ <template #empty>
|
|
|
|
+ <span class="leading-[32px]">暂无数据</span>
|
|
|
|
+ </template>
|
|
|
|
+ </NDataTable>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </main>
|
|
|
|
+ </ChatAnswer>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="order-container px-[60px] py-[30px] mt-[36px] rounded-[10px] bg-[#fff]" v-if="!chatDataSource.length">
|
|
|
|
+ <div
|
|
|
|
+ class="flex items-center justify-start space-x-[24px] pb-[20px] border-b-[1px] border-solid border-[#F1F1F1]">
|
|
|
|
+ <n-tabs type="segment" animated style="width: 188px;" size="large" :on-update:value="handleTabChange">
|
|
|
|
+ <n-tab name="daily">日报工单</n-tab>
|
|
|
|
+ <n-tab name="customDaily">自定义工单</n-tab>
|
|
|
|
+ </n-tabs>
|
|
|
|
+ <span class="text-[12px] text-[#8F959C]">
|
|
|
|
+ {{ tabActive === 'daily' ? '选择日期后为您生成日报工单' : '选择内容后为您生成工单' }}
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <main class="order pt-[20px] text-[#1A2029]" v-show="tabActive === 'daily'">
|
|
|
|
+ <div class="flex items-center justify-start text-[16px] space-x-[16px]">
|
|
|
|
+ <span class="font-bold">选择时间</span>
|
|
|
|
+ <div class="w-[164px] border-[1px] border-[#EFEFF0] rounded-[8px] overflow-hidden">
|
|
|
|
+ <NDatePicker placeholder="选择日期" :readonly="true" v-model:formatted-value="reportDate"
|
|
|
|
+ value-format="yyyy-MM-dd">
|
|
|
|
+ <template #date-icon>
|
|
|
|
+ <SvgIcon name="tool-arrow-bottom"></SvgIcon>
|
|
|
|
+ </template>
|
|
|
|
+ </NDatePicker>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <dl class="pt-[20px] text-[#1A2029] space-y-[16px]">
|
|
|
|
+ <dt class="text-[16px] font-bold leading-[22px]">报告内容</dt>
|
|
|
|
+ <dd>1、水质数据、生化数据情况</dd>
|
|
|
|
+ <dd>2、各项指标数据分析</dd>
|
|
|
|
+ <dd>3、对于工艺调整方面、数据趋势方面的措施与建议</dd>
|
|
|
|
+ </dl>
|
|
|
|
+ </main>
|
|
|
|
+
|
|
|
|
+ <main class="order pt-[20px] text-[#1A2029]" v-show="tabActive === 'customDaily'">
|
|
|
|
+ <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
|
|
|
|
+ <span class="font-bold">选择时间</span>
|
|
|
|
+ <div class="w-[164px] border-[1px] border-[#EFEFF0] rounded-[8px] overflow-hidden">
|
|
|
|
+ <NDatePicker placeholder="开始日期" :readonly="true" v-model:formatted-value="workOrderParams.timeBegin"
|
|
|
|
+ value-format="yyyy-MM-dd" :is-date-disabled="dateStartDisabled">
|
|
|
|
+ <template #date-icon>
|
|
|
|
+ <SvgIcon name="tool-arrow-bottom"></SvgIcon>
|
|
|
|
+ </template>
|
|
|
|
+ </NDatePicker>
|
|
|
|
+ </div>
|
|
|
|
+ <span class="text-[16px] text-[#8F959C]">至</span>
|
|
|
|
+ <div class="w-[164px] border-[1px] border-[#EFEFF0] rounded-[8px] overflow-hidden">
|
|
|
|
+ <NDatePicker placeholder="结束日期" :readonly="true" v-model:formatted-value="workOrderParams.timeEnd"
|
|
|
|
+ value-format="yyyy-MM-dd" :is-date-disabled="dateEndDisabled">
|
|
|
|
+ <template #date-icon>
|
|
|
|
+ <SvgIcon name="tool-arrow-bottom"></SvgIcon>
|
|
|
|
+ </template>
|
|
|
|
+ </NDatePicker>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
|
|
|
|
+ <span class="font-bold">统计方式</span>
|
|
|
|
+ <n-radio-group v-model:value="workOrderParams.whichWay" name="radiogroup2">
|
|
|
|
+ <n-radio :value="1">在线仪表</n-radio>
|
|
|
|
+ <n-radio :value="0">日报</n-radio>
|
|
|
|
+ </n-radio-group>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
|
|
|
|
+ <span class="font-bold">进水指标</span>
|
|
|
|
+ <n-checkbox-group v-model:value="workOrderParams.checkGroup" name="radiogroup2">
|
|
|
|
+ <n-checkbox value="jsSlq">进水水量</n-checkbox>
|
|
|
|
+ <n-checkbox value="jsCod">COD</n-checkbox>
|
|
|
|
+ <n-checkbox value="jsTn">总氮</n-checkbox>
|
|
|
|
+ <n-checkbox value="jsTp">总磷</n-checkbox>
|
|
|
|
+ <n-checkbox value="jsNh3">氨氮</n-checkbox>
|
|
|
|
+ <n-checkbox value="jsSs">SS</n-checkbox>
|
|
|
|
+ </n-checkbox-group>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="flex items-center justify-start mb-[20px] text-[16px] space-x-[16px]">
|
|
|
|
+ <span class="font-bold">出水指标</span>
|
|
|
|
+ <n-checkbox-group v-model:value="workOrderParams.checkGroup" name="radiogroup2">
|
|
|
|
+ <n-checkbox value="csSlqc">出水水量</n-checkbox>
|
|
|
|
+ <n-checkbox value="csCod">COD</n-checkbox>
|
|
|
|
+ <n-checkbox value="csTn">总氮</n-checkbox>
|
|
|
|
+ <n-checkbox value="csTp">总磷</n-checkbox>
|
|
|
|
+ <n-checkbox value="csNh3">氨氮</n-checkbox>
|
|
|
|
+ <n-checkbox value="csSs">SS</n-checkbox>
|
|
|
|
+ </n-checkbox-group>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="flex items-start justify-start mb-[20px] text-[16px] space-x-[16px]">
|
|
|
|
+ <span class="font-bold">化验指标</span>
|
|
|
|
+ <div class="w-[500px]">
|
|
|
|
+ <n-checkbox-group v-model:value="workOrderParams.checkGroup" name="radiogroup2">
|
|
|
|
+ <n-checkbox value="no3Hlj1Jqr">1#好氧池硝酸盐</n-checkbox>
|
|
|
|
+ <n-checkbox value="no3Hlj2Jqr">2#好氧池硝酸盐</n-checkbox>
|
|
|
|
+ <n-checkbox value="nh31Jqr">1#缺氧氨氮</n-checkbox>
|
|
|
|
+ <n-checkbox value="nh32Jqr">2#缺氧氨氮</n-checkbox>
|
|
|
|
+ <n-checkbox value="no3Qyc1Jqr">1#缺氧池硝酸盐</n-checkbox>
|
|
|
|
+ <n-checkbox value="no3Qyc2Jqr">2#缺氧池硝酸盐</n-checkbox>
|
|
|
|
+ <n-checkbox value="tpRccJqr">二沉池正磷酸盐</n-checkbox>
|
|
|
|
+ </n-checkbox-group>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <dl class="text-[#1A2029] space-y-[16px]">
|
|
|
|
+ <dt class="text-[16px] font-bold leading-[22px]">报告内容</dt>
|
|
|
|
+ <dd>1、数据情况</dd>
|
|
|
|
+ <dd>2、各项指标数据分析及措施与建议</dd>
|
|
|
|
+ </dl>
|
|
|
|
+ </main>
|
|
|
|
+
|
|
|
|
+ <footer class="pt-[24px]">
|
|
|
|
+ <button class="btn-primary" @click="handleCreateOrder">立即生成</button>
|
|
|
|
+ </footer>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ </TheChatView>
|
|
|
|
+ </section>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
+.n-input__input-el {
|
|
|
|
+ font-size: 12px !important;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.area_inner {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ flex-flow: column;
|
|
|
|
+ padding-bottom: 20px;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+ background: #eff8fc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.list-item {
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
+
|
|
|
|
+ .title {
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ line-height: 20px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .item-top {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
+
|
|
|
|
+ .num {
|
|
|
|
+ font-size: 12px;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ color: #B0B7C0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+.btn-primary {
|
|
|
|
+ width: 88px;
|
|
|
|
+ height: 32px;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ background-color: #2454FF;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ line-height: 32px;
|
|
|
|
+ color: #fff;
|
|
|
|
+
|
|
|
|
+ &:hover {
|
|
|
|
+ background: #1D43CC;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.echart-warpper {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ flex-flow: column;
|
|
|
|
+ width: 100%;
|
|
|
|
+ padding: 20px 0;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+}
|
|
|
|
+</style>
|
|
|
|
+
|
|
|
|
+<style lang="scss">
|
|
|
|
+.order {
|
|
|
|
+
|
|
|
|
+ .n-input__input-el,
|
|
|
|
+ .n-input__placeholder {
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.order-container {
|
|
|
|
+ .n-tabs-tab__label {
|
|
|
|
+ font-size: 14px !important;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ color: #5E5E5E;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .n-tabs-tab--active {
|
|
|
|
+ .n-tabs-tab__label {
|
|
|
|
+ color: #1A2029;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|