소스 검색

feat: 碳源投加 - 联调

sunxiao 1 개월 전
부모
커밋
ffe40cad23

+ 26 - 1
src/api/control.js

@@ -4,10 +4,35 @@ export const controlApi = {
   /**
    * 碳源投加 获取echart记录 - 数据看板
    */
-  getEchartData: (type) => http.get(`/front/bigModel/smartAdd/charList/${type}`),
+  getEchartData: (type, params) => http.get(`/front/bigModel/smartAdd/charList/${type}`, { params }),
   
   /**
    * 碳源投加 基础数值数据
    */
   getNumValue: () => http.get(`/front/bigModel/smartAdd/dataInfo`),
+
+  /**
+   * 碳源投加 基础数值数据 - 新
+   */
+  getBaseData: () => http.get(`/business/medicineParamSetting/getLatestRecord`),
+  
+  /**
+   * 碳源投加 配置记录
+   */
+  getEchartList: params => http.get(`/business/medicineParamSetting/getList`, { params }),
+  
+  /**
+   * 碳源投加 配置记录
+   */
+  postAddRecord: data => http.post(`/business/medicineParamSetting`, data),
+
+  /**
+   * 碳源投加 系统是否可以投放
+   */
+  getSystemStatus: () => http.get(`/front/bigModel/smartAdd/getSwitchStatus`),
+  
+  /**
+   * 碳源投加 系统是否可以投放
+   */
+  putSystemStatus: data => http.get(`/front/bigModel/smartAdd/updateSwitchStatus`, data),
 }

+ 12 - 0
src/assets/svgs/control/icon-warning.svg

@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
+  <g clip-path="url(#clip0_3344_3853)">
+    <path d="M7.00008 12.8337C8.6109 12.8337 10.0692 12.1807 11.1249 11.1251C12.1805 10.0695 12.8334 8.61114 12.8334 7.00033C12.8334 5.38951 12.1805 3.93118 11.1249 2.87553C10.0692 1.81991 8.6109 1.16699 7.00008 1.16699C5.38927 1.16699 3.93093 1.81991 2.87529 2.87553C1.81967 3.93118 1.16675 5.38951 1.16675 7.00033C1.16675 8.61114 1.81967 10.0695 2.87529 11.1251C3.93093 12.1807 5.38927 12.8337 7.00008 12.8337Z" fill="#FF9902" stroke="#FF9902" stroke-width="1.16667" stroke-linejoin="round"/>
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M6.99992 10.7913C7.40262 10.7913 7.72909 10.4649 7.72909 10.0622C7.72909 9.65947 7.40262 9.33301 6.99992 9.33301C6.59721 9.33301 6.27075 9.65947 6.27075 10.0622C6.27075 10.4649 6.59721 10.7913 6.99992 10.7913Z" fill="white"/>
+    <path d="M7 3.5V8.16667" stroke="white" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round"/>
+  </g>
+  <defs>
+    <clipPath id="clip0_3344_3853">
+      <rect width="14" height="14" fill="white"/>
+    </clipPath>
+  </defs>
+</svg>

+ 347 - 265
src/views/control/MedicinalView.vue

@@ -1,10 +1,8 @@
 <script setup>
-import { ref, onMounted, watch } from 'vue';
+import { ref, onMounted, watch, computed, unref } from 'vue';
 import { NScrollbar, useMessage, NTabs, NTabPane } from 'naive-ui';
-import { objectCopy } from '@/utils/tools';
 import { TheChatView } from '@/components';
 
-
 import { controlApi } from "@/api/control";
 
 import BaseButton from './components/BaseButton.vue';
@@ -14,237 +12,263 @@ import BaseCard from './components/BaseCard.vue';
 import BaseRadioGroup from './components/BaseRadioGroup.vue';
 import BaseChooseItem from './components/BaseChooseItem.vue';
 import BaseInput from './components/BaseInput.vue';
-import { BaseNumberInput } from '@/components';
 
 import TheResultPanel from './components/TheResultPanel.vue';
 import TheEchartPanel from './components/TheEchartPanel.vue';
 
 const message = useMessage();
 const isVisibleBtn = ref(true);
-const dataSource = ref({});
-const chooseItemRef = ref([]);
-
-const tabList = ref([
-  { label: '自动', key: 'auto' },
-  { label: '1号池', key: 'onePool' },
-  { label: '2号池', key: 'twoPool' },
-  { label: '人工投放', key: 'worker' },
+const isVisibleUpdateInfo = ref(false);
+const systemStatus = ref(0);
+const configureStatus = ref(1);
+
+const columnData = ref([
+  { label: '后反馈设定', key: 'htfksd', value: '' },
+  { label: '基准系数', key: 'jzxs', value: '' },
+  { label: '修正系数', key: 'xzxs', value: '' },
+  { label: '控制系数', key: 'kzxs', value: '' },
+  { label: '水量分配系数', key: 'slfpxs', value: '' },
+  { label: '碳源当量', key: 'tydl', value: '' },
+  { label: '转换系数', key: 'zhxs', value: '' },
+  { label: '稀释配属', key: 'sxps', value: '' },
+  { label: '药剂密度', key: 'yymd', value: '' },
+  { label: '最小启动流量', key: 'zxqdll', value: '' },
+  { label: '碳氮比', key: 'tdb', value: '' }
 ])
 
-const columnData = [
-  { label: '后反馈设定', key: 'hfksd' },
-  { label: '基准系数', key: 'jzxs' },
-  { label: '修正系数', key: 'xzxs' },
-  { label: '控制系数', key: 'kzxs' },
-  { label: '水量分配系数', key: 'sffpxs' },
-  { label: '碳源当量', key: 'tydl' },
-  { label: '转换系数', key: 'zhxs' },
-  { label: '稀释配属', key: 'xsbs' },
-  { label: '药剂密度', key: 'yjmd' },
-  { label: '最小启动流量', key: 'zxqdll' },
-  { label: '碳氮比', key: 'tdb' }
-]
-
-// 基础参数 - 按钮选择
-const paramData = ref({
-  pump: 0,           // 加药泵
-  running: 0,        // 运行方式
-  pond: 0,            // 池组手自动方式
-  setting: 0,        // 智适应碳源设置 1# 2#
-  jslYB: null,        // 进水流量
-  jscod: null,       // 进水COD
-  hycxsy: null,      // 好氧池硝酸盐
-  qycxsy: null,      // 缺氧池硝酸盐
-  qycad: null,       // 缺氧池氨氮
-  jszd: null         // 进水总氮
+const doseNum = ref(null);
+const flowNum = ref(null);
+const updateNum = ref(null);
+
+const tabKeyEnum = {
+  0: 'auto',
+  1: 'onePool',
+  2: 'twoPool',
+  3: 'worker'
+}
+
+// water实时数据
+const waterConfigParams = ref({});
+
+// 基础数据
+const dataSourceParams = ref({
+  auto: {},
+  onePool: {},
+  twoPool: {},
+  worker: {
+    medicineAmount: null
+  }
 })
 
-// 基础系数 - input输入
-const factorData = ref({
-  hfksd: 12.00,   // 后反馈设置定
-  jzxs: 3.10,     // 基准系数
-  xzxs: 1.00,     // 修正系数
-  kzxs: 5.20,     // 控制系数
-  sffpxs: 1.00,   // 水量分配系数
-  tydl: 0.90,     // 碳源当量
-  zhxs: 0.50,     // 转换系数
-  xsbs: 1.00,     // 稀释倍数
-  yjmd: 1.10,     // 药剂密度
-  zxqdll: 0.02,   // 最小启动流量
-  tdb: 3.54       // 碳氮比
+// 系数
+const baseSourceParams = ref({
+  numberBeng: 0,
+  type: 0
 })
 
-const originParamData = objectCopy(paramData.value);
-const originFactorData = objectCopy(factorData.value);
+// 当前Tab选中的key
+const tabActiveKey = computed(() => tabKeyEnum[baseSourceParams.value.type]);
 
-const factorInpData = ref(objectCopy(factorData.value));
+// 系统状态开关
+const systemSwitchType = computed(() => baseSourceParams.value.addType === 0 && systemStatus.value === 1);
 
-const doseNum = ref(null);
-const flowNum = ref(null);
+const onEditConfirm = () => {
+  isVisibleBtn.value = true;
+  columnData.value = columnData.value.map(item => {
+    Object.entries(baseSourceParams.value).forEach(([key, value]) => {
+      if (key === item.key) {
+        item.value = value;
+      }
+    })
+    return item;
+  })
+}
 
-watch(() => paramData.value.setting, () => {
-  handelReset("变化了");
-});
+const onEditCancel = () => {
+  isVisibleBtn.value = true;
 
-// 重置
-const handelReset = () => {
+  columnData.value.map(({ key, value }) => {
+    baseSourceParams.value[key] = value;
+  })
+}
 
-  if (!isVisibleBtn.value) {
-    return message.warning("设定参数系数的值未保存")
-  }
+const onFinalResult = () => {
+  const addStatus = systemStatus.value === 0 ? 1 : 0;
 
-  chooseItemRef.value.forEach(item => item.resetInpVal());
+  controlApi.putSystemStatus({ addStatus })
 
-  paramData.value = objectCopy({ ...originParamData, setting: paramData.value.setting });
-  factorData.value = objectCopy(originFactorData);
-  factorInpData.value = objectCopy(originFactorData);
+  systemStatus.value = addStatus;
 
-  doseNum.value = '';
-  flowNum.value = '';
-}
+  message.warning(addStatus === 0 ? '当前投药状态,已停用' : '当前投药状态,投放中');
 
-// 计算最终结果
-const handleResult = () => {
+}
 
-  if (!isVisibleBtn.value) {
-    return message.warning("设定参数系数的值未保存")
+const onUpdateTab = (index) => {
+  const currentData = dataSourceParams.value[tabKeyEnum[index]];
+  baseSourceParams.value.type = index;
+  if ( !Object.keys(currentData).length ) {
+    isVisibleUpdateInfo.value = false;
+    return;
   }
+  updateNum.value = null;
+  handleMedicateAmount();
+}
 
-  const codeSetEnum = {
-    jslYB: '进水流量',
-    jscod: '进水COD',
-    hycxsy: '好氧池硝酸盐',
-    qycxsy: '缺氧池硝酸盐',
-    qycad: '缺氧池氨氮',
-    jszd: '进水总氮',
-
-    hfksd: '后反馈设置',
-    jzxs: '基准系数',
-    xzxs: '修正系数',
-    kzxs: '控制系数',
-    sffpxs: '水量分配系数',
-    tydl: '碳源当量',
-    zhxs: '转换系数',
-    xsbs: '稀释倍数',
-    yjmd: '药剂密度',
-    zxqdll: '最小启动流量',
-    tdb: '碳氮比'
-  }
-  const whitelist = ['pump', 'running', 'pond', 'setting'];
-  const mergeData = { ...paramData.value, ...factorData.value };
-  const keyList = Object.keys(mergeData);
-  let isErrorItem = null;
-
-  for (let i = 0; i < keyList.length; i++) {
-    const key = keyList[i];
-    const val = mergeData[key]
-
-    if (!val && !whitelist.includes(key)) {
-      isErrorItem = { key, val, label: codeSetEnum[key] }
-      break
-    }
-  }
+const getTotalNum = () => {
+  const {
+    hycXsyOne = 0, hycXsyTwo = 0,
+    qycAdOne = 0, qycAdTwo = 0,
+    qycYxyOne = 0, qycYxyTwo = 0,
+    jsLlOne = 0, jsLlTwo = 0,
+    jsCodOne = 0, jsCodTwo = 0
+  } = dataSourceParams.value[tabActiveKey.value];
+
+  const {
+    htfksd, xzxs, kzxs, slfpxs, zhxs, tydl, jzxs, yymd, sxps
+  } = baseSourceParams.value;
+
+  const rOne1 = (((2*hycXsyOne-htfksd)+((qycAdOne+qycYxyOne)*xzxs-htfksd))*(jzxs-1))*(jsLlOne*slfpxs)/1000;
+  const rOne2 = (rOne1*kzxs-(jsLlOne*slfpxs*jsCodOne*zhxs/1000))/tydl;
+  const rOne3 = rOne2/yymd/1000*sxps
+  
+  const rTwo1 = (((2*hycXsyTwo-htfksd)+((qycAdTwo+qycYxyTwo)*xzxs-htfksd))*(jzxs-1))*(jsLlTwo*slfpxs)/1000;
+  const rTwo2 = (rTwo1*kzxs-(jsLlTwo*slfpxs*jsCodTwo*zhxs/1000))/tydl;
+  const rTwo3 = rTwo2/yymd/1000*sxps
+
+  const r1 = (rOne3 < 0 || !rOne3) ? 0 : rOne3.toFixed(3);
+  const r2 = (rTwo3 < 0 || !rTwo3) ? 0 : rTwo3.toFixed(3);
+
+  return [ r1, r2 ];
+}
 
-  if (isErrorItem) {
-    return message.warning(`${isErrorItem.label}未填写`)
-  }
+const onConfirmUpdate = async () => {
 
-  const stepOne = (((2 * paramData.value.hycxsy - factorData.value.hfksd) + ((paramData.value.qycad + paramData.value.qycxsy) * factorData.value.xzxs - factorData.value.hfksd)) * (factorData.value.jzxs - 1)) * (paramData.value.jslYB * factorData.value.sffpxs) / 1000
+  const data = await controlApi.postAddRecord({
+    ...dataSourceParams.value[tabKeyEnum[baseSourceParams.value.type]],
+    ...baseSourceParams.value,
+  });
 
-  const setpTwo = (stepOne * factorData.value.kzxs - (paramData.value.jslYB * factorData.value.sffpxs * paramData.value.jscod * factorData.value.zhxs / 1000)) / factorData.value.tydl
+  isVisibleUpdateInfo.value = false;
 
-  const setpThree = setpTwo / factorData.value.yjmd / 1000 * factorData.value.xsbs
+  message.success("系统加药量,更新成功");
 
-  doseNum.value = setpThree.toFixed(3);
-  flowNum.value = paramData.value.jslYB;
+  doseNum.value = updateNum.value;
 }
 
-const onEditConfirm = () => {
-  const keys = Object.keys(factorInpData.value);
-  let isError = false;
-
-  for (let i = 0; i < keys.length; i++) {
-    const key = keys[i];
-    const val = factorInpData.value[key];
-    if (!val || val === Infinity) {
-      message.warning("数据来源填写有误,请检查")
-      isError = true;
-      break;
+// 更新投药结果
+const handleMedicateAmount = () => {
+  const type = tabActiveKey.value;
+  const lastNum = unref(updateNum);
+
+  if ( tabActiveKey === 'worker' ) {
+    const medicineNum = dataSourceParams.value.worker.medicineAmount;
+    if ( medicineNum && lastNum != medicineNum ) {
+      updateNum.value = medicineNum;
+      isVisibleUpdateInfo.value = true;
     }
+    return;
   }
 
-  if (isError) return;
+  const [r1, r2] = getTotalNum();
 
-  isVisibleBtn.value = true;
-  factorData.value = JSON.parse(JSON.stringify(factorInpData.value));
-}
-
-const onEditCancel = () => {
-  isVisibleBtn.value = true;
-  factorInpData.value = JSON.parse(JSON.stringify(factorData.value));
-}
+  if ( type === 'auto') {
+    const maxR = Math.max( r1, r2 );
+    if ( maxR != lastNum ) {
+      updateNum.value = maxR;
+      isVisibleUpdateInfo.value = true;
+    }
+  }
+  console.log( r1, lastNum );
+  if ( type === 'onePool' && r1 !== lastNum ) {
+    updateNum.value = r1;
+    isVisibleUpdateInfo.value = true;
+  }
 
-const onFinalResult = () => {
-  if (!flowNum.value) {
-    return message.warning('您还未完成投加计算,请完成后再试');
+  if ( type === 'twoPool' && r2 !== lastNum) {
+    updateNum.value = r2;
+    isVisibleUpdateInfo.value = true;
   }
-  message.warning('您还未接入组态系统,请接入后再试');
+
 }
 
 onMounted(async () => {
-  const { data } = await controlApi.getNumValue();
-  let result = {};
-  Object.entries(data).forEach(([key, val]) => {
-    result[key] = val ? Number(val.toFixed(2)) : val;
+  // 获取最后一条记录  getSystemStatus
+  controlApi.getBaseData().then(({ data }) => {
+    const {
+      numberBeng = 0, type = 0,
+      htfksd, jzxs, xzxs, kzxs, slfpxs, tydl, zhxs, sxps, yymd, zxqdll, tdb,
+      medicineAmount,
+      addType,
+      tytjTransientLL
+    } = data;
+ 
+    baseSourceParams.value = { 
+      ...baseSourceParams.value,
+      numberBeng, type,
+      htfksd, jzxs, xzxs, kzxs, slfpxs, tydl, zhxs, sxps, yymd, zxqdll, tdb,
+      addType
+    };
+
+    updateNum.value = medicineAmount
+
+    doseNum.value = medicineAmount;
+
+    flowNum.value = tytjTransientLL;
+
+    dataSourceParams.value[tabActiveKey.value] = data;
+    
+    columnData.value = columnData.value.map(item => {
+      item.value = data[item.key];
+      return item;
+    })
+  })
+
+  // 获取实时数据
+  controlApi.getNumValue().then(({ data }) => {
+    Object.entries(data).forEach(([key, val]) => {
+      data[key] = val;
+    });
+    // water实时数据
+    waterConfigParams.value = data;
+  })
+
+  // 获取是否允许投药开关
+  controlApi.getSystemStatus().then(({ data }) => {
+    // 0不允许  1允许
+    systemStatus.value = data;
+    console.log( "获取是否允许投药开关", data );
   });
-  dataSource.value = result;
-})
-
-const datas = ref({
-  auto: {
-  },
-  onePool: {
-
-  },
-  twoPool: {
-
-  },
-  worker: {
-  }
 })
 
 </script>
 
 <template>
+  {{ systemSwitchType }}
   <section class="flex items-start h-full">
     <TheChatView leftTitle="智适应碳源投加" :isChatSlot="false" :isFooter="false">
       <template #control>
         <div class="control-container space-x-[12px]">
           <div class="left-section">
 
-            <BaseTitle title="智能投加计算">
-              <!-- <template #right>
-                <BaseButton @click="handelReset">重置</BaseButton>
-                <BaseButton type="gradual" @on-click="handleResult">投加计算</BaseButton>
-              </template> -->
-            </BaseTitle>
+            <BaseTitle title="智能投加计算"></BaseTitle>
 
             <n-scrollbar class="scrollbar" style="height: 100%;">
               <div class="form-content">
+
                 <BaseCard title="选择加药泵">
-                  <BaseRadioCard v-model="paramData.pump"></BaseRadioCard>
+                  <BaseRadioCard v-model="baseSourceParams.numberBeng"></BaseRadioCard>
                 </BaseCard>
 
                 <BaseCard title="投加运行方式">
                   <span class="status-bar">
-                    <i>启用智适应碳源投加</i>
+                    <i>{{ baseSourceParams.addType === 0 ? '启用智适应碳源投加' : '手动碳源投加' }}</i>
                   </span>
                 </BaseCard>
 
                 <BaseCard title="选择池组手自动方式">
-                  <n-tabs justify-content="space-between" v-model:value="name" type="line" :bar-width="40"
-                    tab-style="min-width: 89px;" tab-class="custom-tab_item" animated>
-                    <n-tab-pane name="自动">
+                  <n-tabs justify-content="space-between" type="line" :bar-width="40"
+                    tab-style="min-width: 89px;" tab-class="custom-tab_item" animated :on-update:value="onUpdateTab" :value="baseSourceParams.type">
+                    <n-tab-pane :name="0" tab="自动">
                       <div class="panel-header_main">
                         <p>设置数据来源</p>
                         <p class="space-x-[20px] text-center">
@@ -254,77 +278,96 @@ const datas = ref({
                       </div>
                       <div class="space-y-[12px]">
                         <BaseChooseItem
+                          tab-key="auto"
                           title="进水流量"
                           unit="m³"
                           isDouble
-                          v-model:type="datas.auto.jsLlType"
-                          v-model:value1="datas.auto.jsLlOne"
-                          v-model:value2="datas.auto.jsLlTwo"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.auto.jsLlType"
+                          v-model:value1="dataSourceParams.auto.jsLlOne"
+                          v-model:value2="dataSourceParams.auto.jsLlTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB }
+                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="auto"
                           title="进水COD"
                           unit="mg/L"
-                          v-model:type="datas.auto.jsCodType"
-                          v-model:value1="datas.auto.jsCodOne"
-                          v-model:value2="datas.auto.jsCodTwo"
+                          isDouble
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.auto.jsCodType"
+                          v-model:value1="dataSourceParams.auto.jsCodOne"
+                          v-model:value2="dataSourceParams.auto.jsCodTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB },
-                            { label: '化验', value1: 3344, value2: 22222 }
+                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
+                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
                           ]"
                         ></BaseChooseItem>
+                        
                         <BaseChooseItem
+                          tab-key="auto"
                           title="好氧池硝酸盐"
                           unit="mg/L"
-                          v-model:type="datas.auto.hycXsyType"
-                          v-model:value1="datas.auto.hycXsyOne"
-                          v-model:value2="datas.auto.hycXsyTwo"
+                          isDouble
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.auto.hycXsyType"
+                          v-model:value1="dataSourceParams.auto.hycXsyOne"
+                          v-model:value2="dataSourceParams.auto.hycXsyTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB },
-                            { label: '预测', value1: 3344, value2: 22222 }
+                            { label: '化验', value1: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
+                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="auto"
                           title="缺氧池硝酸盐"
                           unit="mg/L"
-                          v-model:type="datas.auto.qycYxyType"
-                          v-model:value1="datas.auto.qycYxyOne"
-                          v-model:value2="datas.auto.qycYxyTwo"
+                          isDouble
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.auto.qycYxyType"
+                          v-model:value1="dataSourceParams.auto.qycYxyOne"
+                          v-model:value2="dataSourceParams.auto.qycYxyTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB }
+                            { label: '化验', value1: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="auto"
                           title="缺氧池氨氮"
                           unit="mg/L"
-                          v-model:type="datas.auto.qycAdType"
-                          v-model:value1="datas.auto.qycAdOne"
-                          v-model:value2="datas.auto.qycAdTwo"
+                          isDouble
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.auto.qycAdType"
+                          v-model:value1="dataSourceParams.auto.qycAdOne"
+                          v-model:value2="dataSourceParams.auto.qycAdTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB }
+                            { label: '化验', value1: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="auto"
                           title="进水总氮"
                           unit="mg/L"
-                          v-model:type="datas.auto.jsTnType"
-                          v-model:value1="datas.auto.jsTnOne"
-                          v-model:value2="datas.auto.jsTnTwo"
+                          isDouble
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.auto.jsTnType"
+                          v-model:value1="dataSourceParams.auto.jsTnOne"
+                          v-model:value2="dataSourceParams.auto.jsTnTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB }
+                            { label: '仪表', value1: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
                           ]"
                         ></BaseChooseItem>
                       </div>
                     </n-tab-pane>
-                    <n-tab-pane name="1号池">
+
+                    <n-tab-pane :name="1" tab="1号池">
                       <div class="panel-header_main">
                         <p>设置数据来源</p>
                         <p class="space-x-[20px] text-center">
@@ -333,71 +376,84 @@ const datas = ref({
                       </div>
                       <div class="space-y-[12px]">
                         <BaseChooseItem
+                          tab-key="onePool"
                           title="进水流量"
                           unit="m³"
-                          v-model:type="datas.onePool.jsLlType"
-                          v-model:value1="datas.onePool.jsLlOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.jsLlType"
+                          v-model:value1="dataSourceParams.onePool.jsLlOne"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB }
+                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="onePool"
                           title="进水COD"
                           unit="mg/L"
-                          v-model:type="datas.onePool.jsCodType"
-                          v-model:value1="datas.onePool.jsCodOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.jsCodType"
+                          v-model:value1="dataSourceParams.onePool.jsCodOne"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB },
-                            { label: '化验', value1: 3344, value2: 22222 }
+                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
+                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="onePool"
                           title="好氧池硝酸盐"
                           unit="mg/L"
-                          v-model:type="datas.onePool.hycXsyType"
-                          v-model:value1="datas.onePool.hycXsyOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.hycXsyType"
+                          v-model:value1="dataSourceParams.onePool.hycXsyOne"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB },
-                            { label: '预测', value1: 3344, value2: 22222 }
+                            { label: '化验', value1: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
+                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="onePool"
                           title="缺氧池硝酸盐"
                           unit="mg/L"
-                          v-model:type="datas.onePool.qycYxyType"
-                          v-model:value1="datas.onePool.qycYxyOne"
-                          v-model:value2="datas.onePool.qycYxyTwo"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.qycYxyType"
+                          v-model:value1="dataSourceParams.onePool.qycYxyOne"
+                          v-model:value2="dataSourceParams.onePool.qycYxyTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB }
+                            { label: '化验', value1: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="onePool"
                           title="缺氧池氨氮"
                           unit="mg/L"
-                          v-model:type="datas.onePool.qycAdType"
-                          v-model:value1="datas.onePool.qycAdOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.qycAdType"
+                          v-model:value1="dataSourceParams.onePool.qycAdOne"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB }
+                            { label: '化验', value1: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="onePool"
                           title="进水总氮"
                           unit="mg/L"
-                          v-model:type="datas.onePool.jsTnType"
-                          v-model:value1="datas.onePool.jsTnOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.jsTnType"
+                          v-model:value1="dataSourceParams.onePool.jsTnOne"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB }
+                            { label: '仪表', value1: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
                           ]"
                         ></BaseChooseItem>
                       </div>
                     </n-tab-pane>
-                    <n-tab-pane name="2号池">
+
+                    <n-tab-pane :name="2" tab="2号池">
                       <div class="panel-header_main">
                         <p>设置数据来源</p>
                         <p class="space-x-[20px] text-center">
@@ -405,72 +461,85 @@ const datas = ref({
                         </p>
                       </div>
                       <div class="space-y-[12px]">
+                        {{  }}
                         <BaseChooseItem
+                          tab-key="twoPool"
                           title="进水流量"
                           unit="m³"
-                          v-model:type="datas.twoPool.jsLlType"
-                          v-model:value1="datas.twoPool.jsLlOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.jsLlType"
+                          v-model:value1="dataSourceParams.twoPool.jsLlTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB }
+                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="twoPool"
                           title="进水COD"
                           unit="mg/L"
-                          v-model:type="datas.twoPool.jsCodType"
-                          v-model:value1="datas.twoPool.jsCodOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.jsCodType"
+                          v-model:value1="dataSourceParams.twoPool.jsCodTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB },
-                            { label: '化验', value1: 3344, value2: 22222 }
+                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
+                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="twoPool"
                           title="好氧池硝酸盐"
                           unit="mg/L"
-                          v-model:type="datas.twoPool.hycXsyType"
-                          v-model:value1="datas.twoPool.hycXsyOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.hycXsyType"
+                          v-model:value1="dataSourceParams.twoPool.hycXsyTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB },
-                            { label: '预测', value1: 3344, value2: 22222 }
+                            { label: '化验', value1: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
+                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="twoPool"
                           title="缺氧池硝酸盐"
                           unit="mg/L"
-                          v-model:type="datas.twoPool.qycYxyType"
-                          v-model:value1="datas.twoPool.qycYxyOne"
-                          v-model:value2="datas.twoPool.qycYxyTwo"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.qycYxyType"
+                          v-model:value1="dataSourceParams.twoPool.qycYxyTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB }
+                            { label: '化验', value1: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="twoPool"
                           title="缺氧池氨氮"
                           unit="mg/L"
-                          v-model:type="datas.twoPool.qycAdType"
-                          v-model:value1="datas.twoPool.qycAdOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.qycAdType"
+                          v-model:value1="dataSourceParams.twoPool.qycAdTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: 3344, value2: dataSource.jslYB }
+                            { label: '化验', value1: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
                           ]"
                         ></BaseChooseItem>
                         <BaseChooseItem
+                          tab-key="twoPool"
                           title="进水总氮"
                           unit="mg/L"
-                          v-model:type="datas.twoPool.jsTnType"
-                          v-model:value1="datas.twoPool.jsTnOne"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.jsTnType"
+                          v-model:value1="dataSourceParams.twoPool.jsTnTwo"
                           :btn-group="[
                             { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: 3344, value2: dataSource.jslYB }
+                            { label: '仪表', value1: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
                           ]"
                         ></BaseChooseItem>
                       </div>
                     </n-tab-pane>
-                    <n-tab-pane name="人工投放">
+
+                    <n-tab-pane :name="3" tab="人工投放">
                       <div class="panel-header_main">
                         <p>设置数据来源</p>
                         <p class="space-x-[20px] text-center">
@@ -480,7 +549,11 @@ const datas = ref({
                       <div class="w-full flex items-center justify-between">
                         <span>人工投放:</span>
                         <div class="w-[200px]">
-                          <BaseInput :isCloseIcon="false"></BaseInput>
+                          <BaseInput
+                            :isCloseIcon="false"
+                            v-model="dataSourceParams.worker.medicineAmount"
+                            @on-blur="handleMedicateAmount"
+                          ></BaseInput>
                         </div>
                       </div>
                     </n-tab-pane>
@@ -490,8 +563,7 @@ const datas = ref({
                   <template #titleRight>
                     <div>
                       <div class="flex items-center space-x-[4px] cursor-pointer text-[#2454FF] text-[13px]"
-                        v-show="isVisibleBtn" @click="isVisibleBtn = false
-                          ">
+                        v-show="isVisibleBtn" @click="isVisibleBtn = false">
                         <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                           <path d="M2.33337 14H14.3334" stroke="#2454FF" stroke-linecap="round"
                             stroke-linejoin="round" />
@@ -506,16 +578,17 @@ const datas = ref({
                       </ul>
                     </div>
                   </template>
+               
                   <ul class="data-source-list space-y-[12px]">
                     <li class="data-soruce-item" v-for="item, index in columnData">
                       <span>{{ item.label }}:</span>
                       <span class="unit" v-show="isVisibleBtn">
-                        {{ factorData[item.key].toFixed(2) }}
+                        {{ item.value }}
                         {{ index === 0 ? 'mg/L' : '' }}
                       </span>
                       <div style="width: 140px;" v-show="!isVisibleBtn">
                         <BaseInput :unit="index === 0 ? 'mg/L' : ''" size='small' :isNeedFlotBtn="false"
-                          v-model="factorInpData[item.key]" isCenter placeholder=""></BaseInput>
+                          v-model="baseSourceParams[item.key]" isCenter placeholder=""></BaseInput>
                       </div>
                     </li>
                   </ul>
@@ -524,8 +597,17 @@ const datas = ref({
             </n-scrollbar>
           </div>
           <div class="right-section">
-            <TheResultPanel :doseNum="doseNum" :flowNum="flowNum" @on-click="onFinalResult"></TheResultPanel>
-            <TheEchartPanel></TheEchartPanel>
+            <TheResultPanel
+              :updateNum="updateNum"
+              :flowNum="flowNum"
+              :doseNum="doseNum"
+              :configuration-status="baseSourceParams.addType"
+              v-model:system="systemStatus"
+              v-model="isVisibleUpdateInfo"
+              @on-click="onFinalResult"
+              @on-update="onConfirmUpdate"
+            ></TheResultPanel>
+            <TheEchartPanel></TheEchartPanel> 
           </div>
         </div>
       </template>
@@ -570,7 +652,7 @@ const datas = ref({
     }
 
     .form-content {
-      padding: 24px 16px;
+      padding: 0 16px 24px 16px;
     }
 
     .panel-header_main {
@@ -596,7 +678,7 @@ const datas = ref({
       .unit {
         font-family: "D-DIN-PRO-700-Bold";
         font-weight: bold;
-        font-size: 14px;
+        font-size: 12px;
         color: #333;
       }
     }
@@ -604,23 +686,24 @@ const datas = ref({
 }
 
 .right-section {
-  display: flex;
-  flex-flow: column;
   width: 100%;
   height: 100%;
+  border-radius: 8px;
+  background: #fff;
+  overflow: hidden;
 
-  .top {
-    flex-shrink: 1;
-    height: 214px;
-    border-radius: 8px;
-    border: 0.5px solid #FFF;
-    background: linear-gradient(90deg, #E0E8FC 0%, #F2F4FF 100%);
-  }
+  // .top {
+  //   flex-shrink: 1;
+  //   height: 214px;
+  //   border-radius: 8px;
+  //   border: 0.5px solid #FFF;
+  //   background: linear-gradient(90deg, #E0E8FC 0%, #F2F4FF 100%);
+  // }
 
-  .bottom {
-    height: 100%;
-    background: pink;
-  }
+  // .bottom {
+  //   height: 100%;
+  //   background: pink;
+  // }
 
 }
 
@@ -691,8 +774,7 @@ const datas = ref({
 .custom-tab_item {
   @include flex (x, center, center);
   height: 35px;
-  border-top-left-radius: 4px;
-  border-top-right-radius: 4px;
+  border-radius: 4px;
   background: #F3F5FA;
 
   &.n-tabs-tab--active {

+ 39 - 95
src/views/control/components/BaseChooseItem.vue

@@ -1,7 +1,6 @@
 <script setup>
-import { ref, computed, unref } from 'vue';
+import { ref, computed, watch } from 'vue';
 import { useMessage } from 'naive-ui';
-import { BaseNumberInput } from '@/components';
 
 import BaseButton from './BaseButton.vue';
 import BaseInput from './BaseInput.vue';
@@ -16,6 +15,8 @@ const modelType = defineModel('type');
 const modelValue1 = defineModel('value1');
 const modelValue2 = defineModel('value2');
 
+const emit = defineEmits(['on-update']);
+
 const props = defineProps({
   title: {
     type: String,
@@ -32,17 +33,29 @@ const props = defineProps({
   isDouble: {
     type: Boolean,
     default: false
+  },
+  tabKey: {
+    type: String,
+    requured: true
   }
 })
 
-const data = ref(props.btnGroup);
+watch(() => modelType.value, (num) => {
+  if ( num === 0 ) {
+    if ( props.tabKey == 'auto' ) {
+      inpVal.value = modelValue1.value;
+    }
+    if ( props.tabKey == 'onePool' ) {
+      inpVal.value = modelValue1.value;
+    }
+    if ( props.tabKey == 'twoPool' ) {
+      inpVal.value = modelValue2.value;
+    }
+  }
+})
 
-const currentNumValue = computed(() => {
-  const curIndex = props.btnGroup.length === 1 ? 0 : unref(activeIndex.value);
-  return curIndex < 0
-    ? "" : curIndex === 0
-      ? modelValue.value: props.btnGroup[curIndex].value
-});
+const onePoolNum = computed(() => (modelValue1.value && modelValue1.value !=0 ) ?  Number((modelValue1.value || 0).toFixed(2)) : '--');
+const twoPoolNum = computed(() => (modelValue2.value && modelValue2.value !=0 ) ?  Number((modelValue2.value || 0).toFixed(2)) : '--');
 
 const onInpCancel = (val) => {
   inpVal.value = val;
@@ -59,33 +72,26 @@ const onInpConfirm = (num) => {
 const onInput = (val) => {
   modelValue1.value = val;
   modelValue2.value = val;
-  // inpVal.value = val
 }
 
-const onBlur = () => {
-  if (inpVal.value === Infinity || inpVal.value === -Infinity) {
-    modelValue.value = null;
-    inpVal.value = null;
-    return message.warning(`${props.title}的数值填写有误, 请检查`);
+const onEmitEvent = () => {
+  if ( activeIndex.value === 0 ) {
+    if ( inpVal.value ) {
+      emit('on-update');
+    }
+  } else {
+    emit('on-update');
   }
 }
 
 const changeActive = (item, index) => {
+  activeIndex.value = index;
   modelType.value = index;
-  modelValue1.value = index != 0 ? item.value1 : modelValue.value;
-  if (props.isDouble) {
-    modelValue2.value = index != 0 ? item.value2 : modelValue.value;
-  }
+  modelValue1.value = index != 0 ? item.value1 : inpVal.value;
+  modelValue2.value = index != 0 ? item.value2 : inpVal.value;
+  onEmitEvent();
 }
 
-const resetInpVal = () => {
-  inpVal.value = null;
-  activeIndex.value = -1;
-}
-
-defineExpose({
-  resetInpVal
-})
 </script>
 
 <template>
@@ -104,9 +110,9 @@ defineExpose({
             {{ item.label }}
           </BaseButton>
         </ul>
-        <ul class="flex space-x-[20px] text-center">
-          <li class="w-[60px]">{{ modelValue1 }}</li>
-          <li class="w-[60px]" v-if="isDouble">{{ modelValue2 }}</li>
+        <ul class="num-group flex space-x-[20px] text-center">
+          <li class="w-[60px]">{{ onePoolNum }}</li>
+          <li class="w-[60px]" v-if="isDouble">{{ twoPoolNum }}</li>
         </ul>
       </div>
 
@@ -115,11 +121,11 @@ defineExpose({
         default-value=""
         :placeholder="'请输入' + props.title"
         :unit="unit"
-        v-model="modelValue"
+        v-model="inpVal"
         @click:confirm="onInpConfirm"
         @click:cancel="onInpCancel"
         @on-input="onInput"
-        @on-blur="onBlur"
+        @on-blur="onEmitEvent"
       ></BaseInput>
     </div>
   </div>
@@ -145,75 +151,13 @@ defineExpose({
         @include flex(x, center, center);
       }
 
-      .unit {
+      .num-group {
         font-family: "D-DIN-PRO-700-Bold";
         font-weight: bold;
         font-size: 12px;
         color: #333;
       }
     }
-
-    .bottom-box {
-      position: relative;
-      @include flex(x, center, between);
-      margin-top: 4px;
-
-      .inp {
-        width: 100%;
-        height: 28px;
-        padding: 0px 56px 0 10px;
-        border-radius: 4px 0px 0px 4px;
-        border: 1px solid #E6EAEE;
-        background: #fff;
-        outline: none;
-        font-size: 12px;
-
-        &:focus {
-          border: 1px solid #2454FF;
-        }
-      }
-
-      .unit {
-        flex-shrink: 1;
-        width: 46px;
-        height: 28px;
-        border-radius: 0px 4px 4px 0px;
-        border: 1px solid #E6EAEE;
-        border-left: 0;
-        background: #F0F2F5;
-        text-align: center;
-        line-height: 28px;
-        font-size: 12px;
-        font-weight: bold;
-      }
-
-      .inp-flot_group {
-        position: absolute;
-        @include flex(x, center, center);
-        right: 50px;
-        top: 50%;
-        transform: translateY(-50%);
-
-        li {
-          width: 16px;
-          height: 16px;
-          border-radius: 100%;
-          color: #DFE2E6;
-          cursor: pointer;
-
-          svg,
-          svg path {
-            fill: #e0e2e6;
-            stroke: #e0e2e6;
-          }
-
-          &:hover svg {
-            fill: #b3c4e3;
-            stroke: #b3c4e3;
-          }
-        }
-      }
-    }
   }
 }
 </style>

+ 1 - 1
src/views/control/components/BaseRadioCard.vue

@@ -36,7 +36,7 @@ const chageActive = (index) => modelValue.value = index;
 
   .radio-card_item {
     @include flex(y, start, between);
-    width: 100px;
+    width: 120px;
     height: 48px;
     padding: 6px;
     border-radius: 4px;

+ 1 - 1
src/views/control/components/BaseTitle.vue

@@ -35,7 +35,7 @@ defineProps({
   @include flex(x, center, between);
   height: 75px;
   flex-shrink: 1;
-  padding: 24px 16px 18px 16px;
+  padding: 24px 16px 24px 16px;
   color: #1A2029;
 
   .title {

+ 256 - 40
src/views/control/components/TheEchartPanel.vue

@@ -1,18 +1,31 @@
 <script setup>
 import { ref, watch, computed, onMounted, unref, onUnmounted } from 'vue';
-import { NTabs, NTab, NSelect } from "naive-ui";
+import { NTabs, NTab, NSelect, NDatePicker, NRadio, NRadioButton } from "naive-ui";
 import * as echarts from 'echarts';
+import { startOfDay } from "date-fns/esm"
 import { controlApi } from "@/api/control"
+import dayjs from 'dayjs';
 
 let echart = null;
+let tempTabItemOneKey = 0;
+let tempTabItemTwoKey = 'jzxs';
+const datePickerValue = ref(null);
+const dateRangeRef = ref(null);
 const tabs = ref([]);
 const tabActive = ref(null);
 const selectValue = ref(0);
+const coefficientDataSource = ref([]);
 const echartDataSource = ref({});
 const echartRef = ref(null);
 const isEmpty = ref(false);
+const activeIndex = ref(0);
+const value = ref();
 
-const selectOptions = [
+const tabList = ['水质', '系数'];
+
+const selectOptions = ref([]);
+
+const echartOptions = [
   { label: "进水流量", value: 0, style: "font-size: 12px" },
   { label: "#1好氧池硝酸盐", value: 1, style: "font-size: 12px" },
   { label: "#2好氧池硝酸盐", value: 2, style: "font-size: 12px" },
@@ -23,20 +36,75 @@ const selectOptions = [
   { label: "碳源投加量", value: 7, style: "font-size: 12px" }
 ]
 
+const coefficientOptions = [
+  { label: "基准系数", value: 'jzxs', style: "font-size: 12px" },
+  { label: "修正系数", value: 'xzxs', style: "font-size: 12px" },
+  { label: "水量分配系数", value: 'slfpxs', style: "font-size: 12px" },
+  { label: "碳源当量", value: 'tydl', style: "font-size: 12px" },
+  { label: "转换系数", value: 'zhxs', style: "font-size: 12px" },
+  { label: "稀释倍数", value: 'sxps', style: "font-size: 12px" },
+  { label: "密度", value: 'yymd', style: "font-size: 12px" },
+]
+
 const seriesName = computed(() => {
-  return selectOptions.find(({ value }) => selectValue.value === value).label
+  let name = '';
+  if ( activeIndex.value === 0) {
+    name = selectOptions.value.find(({ value }) => selectValue.value === value).label
+  } else {
+    name = coefficientOptions.find(item => item.value === selectValue.value).label
+  }
+  return name
 })
 
+// 切换tab选项
+const handleSwitchTab = (index) => {;
+  activeIndex.value = index;
+  console.log( "dateRangeRef", dateRangeRef.value );
+  console.log( "datePickerValue", datePickerValue.value );
+  if ( !index ) {
+    // echart
+    tempTabItemTwoKey = selectValue.value;
+    selectValue.value = tempTabItemOneKey;
+    selectOptions.value = echartOptions;
+    datePickerValue.value = null;
+    initWaterEchartData();
+  } else {
+     // 系数
+    tempTabItemOneKey = selectValue.value;
+    selectValue.value = tempTabItemTwoKey;
+    selectOptions.value = coefficientOptions;
+    datePickerValue.value = null;
+    intiCoefficientEchartData();
+  }
+}
+
+// select option change
+const handleSelectOptions = (val) => {
+  selectValue.value = val;
+
+  activeIndex.value === 0 ? initWaterEchartData() : intiCoefficientEchartData();
+}
+
 const windowResize = () => echart.resize();
 
-const getEchartOptions = (data) => {
+const getEchartOptions = (data, type) => {
   const option = {
     backgroundColor: '#FFF',
+    title: {
+      show: !data.length,
+      text: '暂无数据',
+      x: 'center',
+      y: 'center',
+      textStyle: {
+        fontSize: 14,
+        fontWeight: 'normal',
+      }
+    },
     grid: {
-      top: '20px',
+      top: '40px',
       bottom: '50px',
-      left: '8%',
-      right: '20px'
+      left: '6%',
+      right: '6%'
     },
     tooltip: {
       trigger: 'axis',
@@ -56,6 +124,11 @@ const getEchartOptions = (data) => {
         show: false,
         alignWithLabel: true
       },
+      axisLabel: {
+        formatter: function (value) {
+          return type ? dayjs(value).format('YYYY/MM/DD') : value
+        }
+      },
       data: data.map(({ time }) => time)
     },
     yAxis: {
@@ -113,18 +186,19 @@ const getEchartOptions = (data) => {
   return option;
 }
 
-const createEchart = (data) => {
-  echart.setOption(getEchartOptions(data));
-}
-
 const onSwitchEchart = (item) => {
   const echartData = echartDataSource.value[item.value];
   isEmpty.value = !!echartData.length
-  createEchart(echartData);
+  echart.setOption(getEchartOptions(echartData));
 }
 
-const initEchartData = async () => {
-  const { data: echartData } = await controlApi.getEchartData(unref(selectValue));
+// 水务相关数据格式化
+const initWaterEchartData = async () => {
+  const [tBegin, tEnd] = datePickerValue.value || [];
+
+  const timeBegin = tBegin ? dayjs(tBegin).format('YYYY/MM/DD') : null;
+  const timeEnd = tEnd ? dayjs(tEnd).format('YYYY/MM/DD') : null;
+  const { data: echartData } = await controlApi.getEchartData(unref(selectValue), { timeBegin, timeEnd });
 
   const enumSource = {
     YB: '在线仪表',
@@ -136,7 +210,7 @@ const initEchartData = async () => {
     if (index === 0) {
       tabActive.value = key + '-' + selectValue.value + '-' + index;
     }
-    if ( echartData[key].length ) {
+    if (echartData[key].length) {
       return ({ label: enumSource[key], value: key });
     }
   }).filter(Boolean);
@@ -146,57 +220,135 @@ const initEchartData = async () => {
   onSwitchEchart({ value: tabActive.value.substring(0, tabActive.value.indexOf('-')) });
 }
 
-watch(selectValue, initEchartData)
+// 系数相关数据
+const intiCoefficientEchartData = async () => {
+  const [timeBegin, timeEnd] = datePickerValue.value || [];
+  const { data } = await controlApi.getEchartList({ timeBegin, timeEnd });
+  coefficientDataSource.value = data;
+  
+  const d = data.map(item => ({
+    time: dayjs(item.createTime).format('YYYY/MM/DD HH'),
+    val: item[selectValue.value]
+  }));
+
+  echart.setOption(getEchartOptions(d));
+}
 
-onMounted(async () => {
+// 日期范围限制
+const isRangeDateDisabled = (ts, type, range) => {
+  const d = 864e5;
+  if (type === "start" && range !== null) {
+    return startOfDay(range[1]).valueOf() - startOfDay(ts).valueOf() >= d * 10;
+  }
+  if (type === "end" && range !== null) {
+    return startOfDay(ts).valueOf() - startOfDay(range[0]).valueOf() >= d * 10;
+  }
+  return false;
+}
+
+const onDatePickerConfirm = (ts) => {
+  datePickerValue.value = ts.map(t => dayjs(t).format('YYYY-MM-DD'));
+  activeIndex.value === 0 ? initWaterEchartData() : intiCoefficientEchartData();
+}
 
-  initEchartData();
+const onDatePickerClear = () => {
+  datePickerValue.value = null;
+  activeIndex.value === 0 ? initWaterEchartData() : intiCoefficientEchartData();
+}
+
+onMounted(async () => {
+  selectOptions.value = echartOptions;
 
   echart = echarts.init(echartRef.value, 'light');
 
+  await initWaterEchartData();
+
   window.addEventListener("resize", windowResize);
+
 })
 
 onUnmounted(() => {
   window.removeEventListener("resize", windowResize);
   echart && echart.dispose();
 })
+
 </script>
 
 <template>
-  <div class="echart-panel-wrapper">
+  <div class="echart-card_view">
     <div class="title">
       <div class="left-inner">
         <span class="text">数据看板</span>
-        <n-tabs type="segment" animated size="small" class="tabs" v-model:value="tabActive">
-          <n-tab v-for="item, index in tabs" :key="item.value" :name="item.value + '-' + selectValue + '-' + index"
-            @click="onSwitchEchart(item)">{{ item.label }}</n-tab>
-        </n-tabs>
       </div>
       <div class="right-inner">
-        <NSelect v-model:value="selectValue" class="w-[180px]" size="small" :options="selectOptions"
-          :consistent-menu-width="false" />
+        <ul class="custom-radio-group">
+          <li :class="{ active: activeIndex === index }" v-for="item, index in tabList" :key="item"
+            @click="handleSwitchTab(index)">{{ item }}</li>
+        </ul>
+      </div>
+    </div>
+
+    <div class="select-wrapper">
+      <div>
+        <n-tabs
+          animated
+          type="segment"
+          size="small"
+          class="tabs"
+          style="width: 200px;"
+          v-model:value="tabActive"
+          v-show="activeIndex === 0"
+        >
+          <n-tab
+            v-for="item, index in tabs"
+            :key="item.value"
+            :name="item.value + '-' + selectValue + '-' + index"
+            @click="onSwitchEchart(item)"
+          >{{ item.label }}</n-tab>
+        </n-tabs>
+      </div>
+      <div class="flex space-x-[10px]">
+        <NDatePicker
+          clearable
+          class="w-[300px]"
+          size="small"
+          type="daterange"
+          ref="dateRangeRef"
+          :is-date-disabled="isRangeDateDisabled"
+          :on-confirm="onDatePickerConfirm"
+          :on-clear="onDatePickerClear"
+          v-model:formatted-value="datePickerValue"
+        ></NDatePicker>
+        <!-- v-model:formatted-value="datePickerValue" -->
+        <!--  -->
+        <!-- v-model:value="selectValue" -->
+        <NSelect
+          class="w-[150px]"
+          :options="selectOptions"
+          :value="selectValue"
+          :on-update:value="handleSelectOptions"
+          size="small" />
       </div>
     </div>
+
     <div class="echart-wrapper">
-      <div class="echart" ref="echartRef">echart</div>
+      <div class="echart" ref="echartRef"></div>
     </div>
   </div>
 </template>
 
 <style lang="scss" scoped>
-.echart-panel-wrapper {
+.echart-card_view {
   display: flex;
   flex-flow: column;
-  height: 100%;
-  padding: 25px 16px 0 25px;
-  margin-top: 12px;
+  height: calc(100% - 228px);
+  padding: 0px 16px 0 25px;
   border-radius: 10px;
-  background: #FFF;
 
   .title {
     flex-shrink: 0;
     @include flex(x, center, between);
+    padding-bottom: 16px;
 
     .left-inner {
       @include flex(x, center, start);
@@ -217,14 +369,44 @@ onUnmounted(() => {
 
     .right-inner {
       @include flex(x, center, start);
+
+      .custom-radio-group {
+        @include flex(x, center, center);
+        width: 104px;
+        height: 24px;
+        border-radius: 4px;
+        border: 1px solid #D3D7DD;
+
+        li {
+          width: 50%;
+          font-size: 12px;
+          text-align: center;
+          line-height: 24px;
+          color: #333;
+          cursor: pointer;
+
+          &:nth-child(1) {
+            border-right: 1px solid #D3D7DD;
+          }
+        }
+
+        li.active {
+          color: #2454FF;
+        }
+      }
     }
   }
 
+  .select-wrapper {
+    @include flex(x, center, between);
+    height: 32px;
+  }
+
   .echart-wrapper {
-    height: 100%;
-    padding-top: 16px;
+    height: calc(100% - 72px);
 
-    .echart, .empty {
+    .echart,
+    .empty {
       width: 100%;
       height: 100%;
     }
@@ -237,7 +419,7 @@ onUnmounted(() => {
 </style>
 
 <style lang="scss">
-.echart-panel-wrapper {
+.echart-card_view {
   .tabs {
     .n-tabs-tab--active {
       .n-tabs-tab__label {
@@ -251,16 +433,16 @@ onUnmounted(() => {
     }
   }
 
+  .n-base-selection .n-base-selection-label .n-base-selection-input {
+    font-size: 12px;
+    color: #333333 !important;
+  }
+
   .right-inner {
     .n-base-selection__border {
       border: 0;
     }
 
-    .n-base-selection-input__content {
-      font-size: 12px;
-      color: #1A2029;
-    }
-
     .n-base-selection-label {
       border: 0;
       border-radius: 0;
@@ -281,5 +463,39 @@ onUnmounted(() => {
       color: #4E5969;
     }
   }
+
+  .n-input--pair {
+    background: #f0f1f3 !important;
+  }
+
+  .n-base-selection-input__content {
+    font-size: 12px;
+  }
+
+  .n-base-selection {
+    background: #f0f1f3 !important;
+  }
+
+  .n-base-selection-label {
+    background: #f0f1f3;
+    border-radius: 3px;
+  }
+
+  .n-base-selection__border,
+  .n-base-selection__state-border {
+    display: none !important;
+  }
+
+  .n-date-picker {
+    .n-input__input-el,
+    .n-input__placeholder {
+      font-size: 12px !important;
+    }
+  }
+
+  .n-base-selection:not(.n-base-selection--disabled).n-base-selection--active .n-base-selection-label {
+    background: #f0f1f3 !important;
+  }
 }
+
 </style>

+ 222 - 102
src/views/control/components/TheResultPanel.vue

@@ -1,46 +1,107 @@
 <script setup>
+import { ref, computed } from 'vue';
+import { useMessage, NNumberAnimation } from 'naive-ui';
 import BaseTitle from './BaseTitle.vue';
 import { SvgIcon } from '@/components';
 
-const emit = defineEmits(['on-click']);
+const message = useMessage();
+const isVisibleBtn = defineModel();
+const modelSystemStatus = defineModel('system');
+const emit = defineEmits(['on-click', 'on-update']);
+const isVisible = ref(false);
+
 const props = defineProps({
   flowNum: {
     type: Number,
     default: 0
   },
+  updateNum: {
+    type: Number,
+    default: ''
+  },
   doseNum: {
     type: Number,
     default: ''
+  },
+  configurationStatus: {
+    type: Number,
+    default: 1
   }
 });
 
-const emitEvent = () => emit('on-click');
+const systemSwitchType = computed(() => props.configurationStatus === 0 && modelSystemStatus.value === 1);
+
+const emitEvent = () => {
+  console.log( props.configurationStatus, modelSystemStatus.value );
+
+  if ( props.configurationStatus === 1 ) {
+    return message.warning('当前组态未启用,无法投放');
+  } else {
+    emit('on-click');
+  }
+
+};
+const emitUpdate = async () => {
+  message.success("系统加药量,更新成功");
+  emit('on-update');
+};
 </script>
 
 <template>
-  <div class="result-card_view">
+  <div>
     <BaseTitle title="智能投加计算结果" type="second"></BaseTitle>
-    <div class="content">
-      <div class="reult-list space-y-[4px]">
-        <div class="title">
-          {{ !doseNum ? '请您完成左侧「1.智能投加计算」' : '根据投加设定,计算结果如下:' }}
+    <div class="result-card_view">
+      <ul class="update-message space-x-[16px]" v-show="isVisibleBtn">
+        <li class="flex space-x-[4px]">
+          <SvgIcon name="control-icon-warning" size="16"></SvgIcon>
+          <span>有新投放方案,系统加药量计算为:{{ updateNum }}m³/h,是否更新?</span>
+        </li>
+        <li class="space-x-[10px]">
+          <span class="text-[#ed742f] cursor-pointer" @click="emitUpdate">更新投放量</span>
+          <span class="text-[#88909b] cursor-pointer" @click="isVisibleBtn = false">取消</span>
+        </li>
+      </ul>
+      <div class="result-card">
+        <div class="result-inner space-x-[8px]">
+          <div class="result-card_item">
+            <div class="h-full flex flex-col justify-between">
+              <h4>
+                <span>碳源投加瞬时流量</span>
+                <span class="block w-[12px] h-[3px] mt-[4px] bg-[#1D2129]"></span>
+              </h4>
+              <p class="num-group space-x-[4px]">
+                <span class="num">
+                  <NNumberAnimation :from="0" :to="flowNum" :duration="1000" :precision="flowNum < 1 ? 3 : 3"></NNumberAnimation>
+                </span>
+                <span class="text-[12px] text-[#86909C]">m³/h</span>
+              </p>
+            </div>
+          </div>
+          <div class="result-card_item">
+            <div class="h-full flex flex-col justify-between">
+              <h4>
+                <span>系统加药量</span>
+                <span class="block w-[12px] h-[3px] mt-[4px] bg-[#1D2129]"></span>
+              </h4>
+              <p class="num-group space-x-[4px]">
+                <span class="num">
+                  <NNumberAnimation :from="0" :to="doseNum" :duration="1000" :precision="3"></NNumberAnimation>
+                </span>
+                <span class="text-[12px] text-[#86909C]">m³/h</span>
+              </p>
+            </div>
+          </div>
+        </div>
+        <div class="btn-card">
+          <div :class="['round-btn', { active: doseNum }]" @click="emitEvent">
+            <div class="circle1" v-show="systemSwitchType"></div>
+            <div class="circle2" v-show="systemSwitchType"></div>
+            <div class="circle3" v-show="systemSwitchType"></div>
+            <div class="inner space-y-[4px]">
+              <SvgIcon name="control-icon-result-btn" size="24" />
+              <span>{{ systemSwitchType ? "投放中" : "未启用" }}</span>
+            </div>
           </div>
-        <div class="desc" v-show="!doseNum">核算完成后为您生成建议碳源投加量</div>
-        <ul class="text" v-show="doseNum">
-          <li>
-            <span>仪表瞬时流量:</span>
-            <i>{{ flowNum }} m³</i>
-          </li>
-          <li>
-            <span>系统加药量:</span>
-            <i>{{ doseNum }} m3/h</i>
-          </li>
-        </ul>
-      </div>
-      <div :class="['round-btn', { active: doseNum }]" @click="emitEvent">
-        <div class="inner space-y-[4px]">
-          <SvgIcon name="control-icon-result-btn" size="24" />
-          <span>一键投放</span>
         </div>
       </div>
     </div>
@@ -48,98 +109,157 @@ const emitEvent = () => emit('on-click');
 </template>
 
 <style lang="scss" scoped>
+
 .result-card_view {
-  display: flex;
-  flex-flow: column;
-  flex-shrink: 0;
-  width: 100%;
-  height: 214px;
-  padding: 0px 16px 20px 16px;
-  border-radius: 8px;
-  border: 1px solid #fff;
-  background: url('@/assets/images/control/bg-control-top.png') center right no-repeat, linear-gradient(90deg, #E0E8FC 0%, #F2F4FF 100%);
-  background-size: auto 90%, auto;
-  background-position: top right;
-  overflow: hidden;
-
-  .content {
-    @include flex(x, start, between);
-    height: 100%;
-    padding: 23px 153px 23px 54px;
+  padding: 0px 16px 24px 16px;
+
+  .update-message {
+    display: flex;
+    align-items: center;
+    height: 37px;
+    padding-left: 16px;
+    padding-bottom: 7px;
+    flex-shrink: 0;
+    border-radius: 8px 8px 0px 0px;
+    border: 1px solid #FFE9CE;
+    background: #FFF5E5;
+    font-size: 12px;
+    color: #7E604F;
+  }
+
+  .result-card {
+    @include flex(x, center, between);
+    padding: 16px 35px 16px 16px;
+    margin-top: -9px;
+    border: 1px solid #fff;
     border-radius: 8px;
-    background: linear-gradient(90deg, rgba(255, 255, 255, 0.80) 0%, rgba(255, 255, 255, 0.50) 100%);
-    backdrop-filter: blur(2px);
-
-    .reult-list {
-      padding-top: 7px;
-      overflow: hiddden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-      .title {
-        font-size: 20px;
-        background: linear-gradient(92.49deg, #5ABBF2 12.24%, #2454FF 36.2%);
-        -webkit-background-clip: text;
-        -webkit-text-fill-color: transparent;
-        line-height: 24px;
-        font-size: 15px;
-        font-weight: bold;
-      }
+    background: url('@/assets/images/control/bg-control-top.png') center right no-repeat, linear-gradient(90deg, #E0E8FC 0%, #F2F4FF 100%);
+    background-size: auto 100%, auto;
+    background-position: 50% 10%;
 
-      .desc {
-        font-size: 14px;
-        color: #1A2029;
-      }
+    .result-inner {
+      border-radius: 8px;
+      @include flex(x, center, start);
 
-      .text {
-        line-height: 24px;
-        font-weight: 500;
-        font-size: 14px;
-        color: #1A2029;
+      .result-card_item {
+        width: 254px;
+        height: 104px;
+        padding: 20px 0 20px 20px;
+        border-radius: inherit;
+        border: 1px solid #FFF;
+        background: #fff;
+        background: linear-gradient(90deg, #FFF 50%, rgba(255, 255, 255, 0.50) 100%);
+        backdrop-filter: blur(2px);
 
-        li:nth-child(2) span{
-          letter-spacing: 2.8px;
+        .num {
+          color: #1A2029;
+          font-family: D-DIN-PRO-700-Bold;
+          font-size: 24px;
+          font-weight: bold;
+          line-height: 0;
         }
       }
     }
 
-    .round-btn {
+    & .result-card_item:nth-child(2) {
+      border: 1px solid #FFF;
+      background: linear-gradient(90deg, #FFF 50%, rgba(255, 255, 255, 0.50) 100%);
+      backdrop-filter: blur(2px);
+    }
+  }
+
+  .round-btn {
+    position: relative;
+    @include flex(x, center, center);
+    width: 88px;
+    height: 88px;
+    border: 2px solid #E6EFFE;
+    border-radius: 50%;
+    background: #898EFE;
+    font-size: 10px;
+    font-weight: bold;
+    color: #fff;
+    cursor: pointer;
+    transition: all 0.5s;
+
+    .inner {
+      position: relative;
       @include flex(x, center, center);
-      width: 88px;
-      height: 88px;
-      border: 2px solid #E6EFFE;
-      border-radius: 50%;
-      background: #D7DDFF;
-      font-size: 10px;
-      font-weight: bold;
-      color: #fff;
-      cursor: pointer;
-      transition: all 0.5s;
-
-      .inner {
-        @include flex(x, center, center);
-        flex-flow: column;
-        width: 74px;
-        height: 74px;
-        padding: 14px;
-        border-radius: 100%;
-        background: #8E9EFB;
-        transition: all 0.3s;
-      }
+      flex-flow: column;
+      width: 74px;
+      height: 74px;
+      padding: 14px;
+      border-radius: 100%;
+      background: #2454FF;
+      transition: all 0.3s;
+    }
+  }
 
-      &:hover {
-        // background: #898EFE;
-        .inner {
-          // background: #2454FF;
-        }
-      }
+  .active {
+    transition: all 0.5s;
+    background: #898EFE;
+
+    .inner {
+      background: #2454FF;
     }
+  }
 
-    .active {
-      transition: all 0.5s;
-      background: #898EFE;
-      .inner {
-        background: #2454FF;
-      }
+  .circle1,
+  .circle2,
+  .circle3 {
+    position: absolute;
+    width: 40px;
+    height: 40px;
+    background: rgba(137, 142, 254, 1);
+    border: 1px solid rgba(137, 142, 254, 0.85);
+    border-radius: 999px;
+  }
+
+  .circle1,
+  .circle2,
+  .circle3 {
+    animation-name: circleChange;
+    animation-duration: 3s;
+    animation-iteration-count: infinite;
+    animation-timing-function: linear;
+  }
+
+  .circle1 {
+    animation-delay: 0.5s;
+  }
+
+  .circle2 {
+    animation-delay: 1.5s;
+  }
+
+  .circle3 {
+    animation-delay: 2.5s;
+  }
+
+  @keyframes circleChange {
+    0% {
+      transform: scale(2);
+      opacity: 0.95;
+    }
+
+    // 25% {
+    //   transform: scale(1.8);
+    //   opacity: 0.75;
+    // }
+
+    // 50% {
+    //   transform: scale(2);
+    //   opacity: 0.5;
+    // }
+
+    // 75% {
+    //   transform: scale(2.4);
+    //   opacity: 0.25;
+    // }
+
+    100% {
+      transform: scale(3);
+      opacity: 0.05;
     }
   }
 }