Browse Source

feat: 碳源投加界面基本布局

sunxiao 2 weeks ago
parent
commit
60539dc7e8

BIN
src/assets/fonts/DIN-RegularAlternate.otf


BIN
src/assets/images/control/bg-play-btn.png


BIN
src/assets/images/control/bg-top.png


+ 13 - 0
src/assets/images/control/icon-end.svg

@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none">
+  <g clip-path="url(#clip0_6019_2985)">
+    <path d="M0 14C0 17.713 1.475 21.274 4.10051 23.8995C6.72601 26.525 10.287 28 14 28C17.713 28 21.274 26.525 23.8995 23.8995C26.525 21.274 28 17.713 28 14C28 10.287 26.525 6.72601 23.8995 4.10051C21.274 1.475 17.713 0 14 0C10.287 0 6.72601 1.475 4.10051 4.10051C1.475 6.72601 0 10.287 0 14Z" fill="#2454FF" fill-opacity="0.3"/>
+    <path d="M1.88159 14.0013C1.88159 15.5928 2.19505 17.1686 2.80406 18.6389C3.41307 20.1092 4.30572 21.4451 5.43102 22.5704C6.55633 23.6957 7.89227 24.5884 9.36255 25.1974C10.8328 25.8064 12.4087 26.1198 14.0001 26.1198C15.5915 26.1198 17.1674 25.8064 18.6377 25.1974C20.108 24.5884 21.4439 23.6957 22.5692 22.5704C23.6945 21.4451 24.5871 20.1092 25.1962 18.6389C25.8052 17.1686 26.1186 15.5928 26.1186 14.0013C26.1186 12.4099 25.8052 10.8341 25.1962 9.36377C24.5871 7.89349 23.6945 6.55755 22.5692 5.43224C21.4439 4.30694 20.108 3.41429 18.6377 2.80528C17.1674 2.19627 15.5915 1.88281 14.0001 1.88281C12.4087 1.88281 10.8328 2.19627 9.36255 2.80528C7.89227 3.41429 6.55633 4.30694 5.43102 5.43224C4.30572 6.55755 3.41307 7.89349 2.80406 9.36377C2.19505 10.8341 1.88159 12.4099 1.88159 14.0013Z" fill="#2454FF"/>
+    <path opacity="0.1" d="M0 14C0 21.7284 6.26667 27.9951 14 27.9951V0C6.26667 0 0 6.26667 0 14Z" fill="#FEFFFF"/>
+    <rect x="9.33325" y="9.33398" width="9.33333" height="9.33333" rx="1.55556" fill="white"/>
+  </g>
+  <defs>
+    <clipPath id="clip0_6019_2985">
+      <rect width="28" height="28" fill="white"/>
+    </clipPath>
+  </defs>
+</svg>

+ 3 - 0
src/assets/images/control/line-left-1.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="141" height="166" viewBox="0 0 141 166" fill="none">
+<path d="M140 164.58L31.9403 159.904C31.0853 159.867 30.3377 159.316 30.0493 158.51L0.532073 76.0659C0.216753 75.1852 0.520411 74.202 1.27747 73.6525L100.789 1.42188L103 4.50098" stroke="#5262FF"/>
+</svg>

+ 3 - 0
src/assets/images/control/line-left-2.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="134" height="73" viewBox="0 0 134 73" fill="none">
+<path d="M0.842285 3.15841L11.6318 0.263672L32.7162 56.0604C33.0158 56.8532 33.7617 57.3891 34.6087 57.42L134 61.0531V72.3689L25.4129 67.6929C24.5585 67.6561 23.8113 67.1061 23.5222 66.3013L0.842285 3.15841Z" fill="#2454FF"/>
+</svg>

+ 3 - 0
src/assets/images/control/line-left-3.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="111" height="7" viewBox="0 0 111 7" fill="none">
+  <path d="M0.526367 0.841797L110 5.57864" stroke="#25AB7E" stroke-width="1.57895"/>
+</svg>

BIN
src/assets/images/control/number-card-left.png


+ 29 - 0
src/assets/images/control/number-card-middle.svg

@@ -0,0 +1,29 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="188" height="188" viewBox="0 0 188 188" fill="none">
+  <g filter="url(#filter0_ii_6019_2931)">
+    <circle cx="94.0003" cy="93.9994" r="93.3333" fill="white"/>
+  </g>
+  <circle cx="94.0003" cy="93.9994" r="93.3333" fill="url(#paint0_linear_6019_2931)" fill-opacity="0.6"/>
+  <defs>
+    <filter id="filter0_ii_6019_2931" x="0.666992" y="-9.28954" width="186.667" height="204.088" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+      <feFlood flood-opacity="0" result="BackgroundImageFix"/>
+      <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+      <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+      <feOffset dy="7.46667"/>
+      <feGaussianBlur stdDeviation="6.22222"/>
+      <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
+      <feColorMatrix type="matrix" values="0 0 0 0 0.0724288 0 0 0 0 0.613512 0 0 0 0 1 0 0 0 0.5 0"/>
+      <feBlend mode="normal" in2="shape" result="effect1_innerShadow_6019_2931"/>
+      <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+      <feOffset dy="-9.95556"/>
+      <feGaussianBlur stdDeviation="6.22222"/>
+      <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
+      <feColorMatrix type="matrix" values="0 0 0 0 0.439466 0 0 0 0 0.377616 0 0 0 0 0.84149 0 0 0 0.39 0"/>
+      <feBlend mode="normal" in2="effect1_innerShadow_6019_2931" result="effect2_innerShadow_6019_2931"/>
+    </filter>
+    <linearGradient id="paint0_linear_6019_2931" x1="94.0003" y1="168.666" x2="94.0003" y2="17.466" gradientUnits="userSpaceOnUse">
+      <stop stop-color="white" stop-opacity="0.2"/>
+      <stop offset="0.495205" stop-color="white"/>
+      <stop offset="1" stop-color="white" stop-opacity="0.2"/>
+    </linearGradient>
+  </defs>
+</svg>

+ 30 - 0
src/assets/images/control/number-card-middle2.svg

@@ -0,0 +1,30 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="193" height="46" viewBox="0 0 193 46" fill="none">
+  <ellipse opacity="0.5" cx="96.5" cy="30.1051" rx="96.5" ry="15.8941" fill="url(#paint0_linear_6019_2924)"/>
+  <mask id="mask0_6019_2924" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="20" y="0" width="157" height="46">
+    <ellipse cx="98.2034" cy="23" rx="78" ry="23" fill="url(#paint1_linear_6019_2924)"/>
+  </mask>
+  <g mask="url(#mask0_6019_2924)">
+    <g opacity="0.6" filter="url(#filter0_f_6019_2924)">
+      <ellipse cx="98.2034" cy="23" rx="78" ry="23" fill="url(#paint2_linear_6019_2924)"/>
+    </g>
+  </g>
+  <defs>
+    <filter id="filter0_f_6019_2924" x="10.2034" y="-10" width="176" height="66" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+      <feFlood flood-opacity="0" result="BackgroundImageFix"/>
+      <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+      <feGaussianBlur stdDeviation="5" result="effect1_foregroundBlur_6019_2924"/>
+    </filter>
+    <linearGradient id="paint0_linear_6019_2924" x1="96" y1="30" x2="96.5" y2="45.9992" gradientUnits="userSpaceOnUse">
+      <stop stop-color="#C9CEDC" stop-opacity="0"/>
+      <stop offset="1" stop-color="#DBDDEA"/>
+    </linearGradient>
+    <linearGradient id="paint1_linear_6019_2924" x1="98.2034" y1="0" x2="98.2034" y2="46" gradientUnits="userSpaceOnUse">
+      <stop stop-color="#A2A5CD" stop-opacity="0.2"/>
+      <stop offset="1" stop-color="#A2A5CD"/>
+    </linearGradient>
+    <linearGradient id="paint2_linear_6019_2924" x1="98.0002" y1="20" x2="98.2034" y2="46" gradientUnits="userSpaceOnUse">
+      <stop stop-color="white" stop-opacity="0"/>
+      <stop offset="1" stop-color="#9A82E1"/>
+    </linearGradient>
+  </defs>
+</svg>

BIN
src/assets/images/control/number-card-right.png


+ 9 - 0
src/assets/styles/common.scss

@@ -16,6 +16,15 @@
   font-style: normal;
 }
 
+@font-face {
+  font-display: swap;
+  font-family: 'DIN-RegularAlternate';
+  src: url('@/assets/fonts/DIN-RegularAlternate.otf') format('opentype');
+  font-weight: normal;
+  font-style: normal;
+}
+
+
 @font-face {
   // font-display: swap;
   font-family: 'YouSheBiaoTiHei';

+ 1 - 1
src/components/Layout/TheChatView.vue

@@ -53,7 +53,7 @@ defineExpose({ targetScrollDom });
       <slot name="catalog"></slot>
     </div>
     <div class="chat-wrapper w-full h-full flex flex-col rounded-[10px]" :style="{ padding: isHeader ? '0px' : '20px 0px 20px 0px' }">
-      <div class="chat-header flex items-center justify-between py-[24px] px-[18px] " v-if="isHeader">
+      <div class="chat-header flex items-center justify-between py-[12px] px-[24px] " v-if="isHeader">
         <div class="left_inner" @click="handleClickBack">
           <span v-if="isBackBtn" class="back-btn"></span>
           <span v-if="leftTitle" class="left_title">{{ leftTitle }}</span>

+ 863 - 0
src/views/control-Old/MedicinalView-Old.vue

@@ -0,0 +1,863 @@
+<script setup>
+import { ref, onMounted, computed, unref, watch } from 'vue';
+import { NScrollbar, useMessage, NTabs, NTabPane, c } from 'naive-ui';
+import { TheChatView } from '@/components';
+import { controlApi } from "@/api/control";
+import BaseTitle from './components/BaseTitle.vue';
+import BaseRadioCard from './components/BaseRadioCard.vue';
+import BaseCard from './components/BaseCard.vue';
+import BaseChooseItem from './components/BaseChooseItem.vue';
+import BaseInput from './components/BaseInput.vue';
+import TheResultPanel from './components/TheResultPanel.vue';
+import TheEchartPanel from './components/TheEchartPanel.vue';
+
+const message = useMessage();
+const isVisibleBtn = ref(true);
+const isVisibleUpdateInfo = ref(false);
+const systemStatus = ref(0);
+
+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 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
+  }
+})
+
+// 系数
+const baseSourceParams = ref({
+  numberBeng: 0,
+  type: 0
+})
+
+// 当前Tab选中的key
+const tabActiveKey = computed(() => tabKeyEnum[baseSourceParams.value.type]);
+// 计算碳氮比
+const tdbNum = computed(() => {
+  const type = tabActiveKey.value;
+
+  const [ r1, r2, rOne, rTwo] = getTotalNum();
+ 
+  const { 
+    jsLlOne, jsLlTwo,
+    jsCodOne, jsCodTwo,
+    jsTnOne, jsTnTwo
+  } = dataSourceParams.value[type];
+
+  const {
+    tydl, zhxs
+  } = baseSourceParams.value
+ 
+  if ( type === 'auto' || type === 'worker' ) {
+    const rNum = Math.max( r1, r2 );
+    const jsll = rNum == r1 ? jsLlOne: jsLlTwo;
+    const cod = rNum == r1 ? jsCodOne: jsCodTwo;
+    const jsTn = rNum == r1 ? jsTnOne: jsTnTwo;
+    const n = rNum == r1 ? rOne : rOne
+    return Number(((n*1000/jsll*tydl+cod*zhxs*tydl)/jsTn).toFixed(2));
+  }
+  if ( type === 'onePool' ) {
+    return Number(((rOne*1000/jsLlOne*tydl+jsCodOne*zhxs*tydl)/jsTnOne).toFixed(2));
+  }
+  if ( type === 'twoPool' ) {
+    return Number(((rTwo*1000/jsLlTwo*tydl+jsCodTwo*zhxs*tydl)/jsTnTwo).toFixed(2));
+  }
+})
+watch(() => tdbNum.value , tdb => {
+  columnData.value[columnData.value.length - 1].value = tdb;
+})
+
+// 编辑系数 - confirm
+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;
+  })
+
+  handleMedicateAmount();
+}
+
+// 编辑系数 - 取消
+const onEditCancel = () => {
+  isVisibleBtn.value = true;
+
+  columnData.value.map(({ key, value }) => {
+    baseSourceParams.value[key] = value;
+  })
+}
+
+const onFinalResult = () => {
+  const addStatus = systemStatus.value === 0 ? 1 : 0;
+
+  controlApi.putSystemStatus({ addStatus });
+
+  systemStatus.value = addStatus;
+
+  message.warning(addStatus === 0 ? '当前投药状态:已停用' : '当前投药状态:投放中');
+
+}
+
+const onUpdateTab = (index) => {
+  const currentData = dataSourceParams.value[tabKeyEnum[index]];
+  baseSourceParams.value.type = index;
+  if ( !Object.keys(currentData).length ) {
+    isVisibleUpdateInfo.value = false;
+    return;
+  }
+  isVisibleBtn.value = true;
+  handleMedicateAmount();
+}
+
+function 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 = Number((rOne3 < 0 || !rOne3) ? 0 : rOne3.toFixed(3)) || 0;
+  const r2 = Number((rTwo3 < 0 || !rTwo3) ? 0 : rTwo3.toFixed(3)) || 0;
+
+  return [ r1, r2, rOne2, rTwo2 ];
+}
+
+const onConfirmUpdate = async () => {
+  await controlApi.postAddRecord({
+    ...dataSourceParams.value[tabKeyEnum[baseSourceParams.value.type]],
+    ...baseSourceParams.value,
+  });
+
+  isVisibleUpdateInfo.value = false;
+
+  message.success("系统加药量,更新成功");
+
+  doseNum.value = updateNum.value;
+}
+
+// 更新投药结果
+const handleMedicateAmount = () => {
+  const type = tabActiveKey.value;
+  const lastNum = unref(updateNum);
+  const tdb = tdbNum.value;
+
+  if ( type === 'worker' ) {
+    const medicineNum = dataSourceParams.value.worker.medicineAmount;
+    if ( medicineNum || medicineNum == 0 ) {
+      updateNum.value = medicineNum;
+      isVisibleUpdateInfo.value = true;
+      baseSourceParams.value.tdb = tdb;
+      message.warning("有新投放方案, 请查看")
+    } else {
+      updateNum.value = null;
+      isVisibleUpdateInfo.value = false;
+    }
+    return;
+  }
+
+  const [r1, r2] = getTotalNum();
+  const maxR = Math.max( r1, r2 );
+
+  if ( type === 'auto' && maxR !== lastNum) {
+    updateNum.value = maxR;
+    isVisibleUpdateInfo.value = true;
+    baseSourceParams.value.tdb = tdb;
+    message.warning("有新的投放方案, 请查看");
+  }
+
+  if ( type === 'onePool' && r1 != lastNum) {
+    updateNum.value = r1;
+    isVisibleUpdateInfo.value = true;
+    baseSourceParams.value.tdb = tdb;
+    message.warning("有新的投放方案, 请查看");
+  }
+
+  if ( type === 'twoPool' && r2 != lastNum ) {
+    updateNum.value = r2;
+    isVisibleUpdateInfo.value = true;
+    baseSourceParams.value.tdb = tdb;
+    message.warning("有新的投放方案, 请查看");
+  }
+
+}
+
+onMounted(async () => {
+  // 获取最后一条记录  getSystemStatus
+  await controlApi.getBaseData().then(({ data }) => {
+    const {
+      numberBeng = 0, type = 0,
+      htfksd, jzxs, xzxs, kzxs, slfpxs, tydl, zhxs, sxps, yymd, zxqdll,
+      medicineAmount,
+      addType,
+      tytjTransientLL
+    } = data;
+ 
+    baseSourceParams.value = { 
+      ...baseSourceParams.value,
+      numberBeng, type,
+      htfksd, jzxs, xzxs, kzxs, slfpxs, tydl, zhxs, sxps, yymd, zxqdll,
+      addType
+    };
+    
+    updateNum.value = medicineAmount;
+
+    doseNum.value = medicineAmount;
+
+    flowNum.value = tytjTransientLL;
+
+    dataSourceParams.value[tabActiveKey.value] = data;
+
+  })
+
+ 
+  // 获取实时数据
+  controlApi.getNumValue().then(({ data }) => {
+
+    Object.entries(data).forEach(([key, val]) => {
+      data[key] = val;
+    });
+
+    // water实时数据
+    waterConfigParams.value = data;
+
+    // 重新计算碳氮比
+    const tdb = tdbNum.value;
+    baseSourceParams.value.tdb = tdb;
+ 
+    columnData.value = columnData.value.map(item => {
+      item.value = item.key === 'tdb' ? tdb : dataSourceParams.value[tabActiveKey.value][item.key];
+      return item;
+    })
+
+    const params = {
+      jsLlType: 1,
+      jsLlOne: data.jslYB,
+      jsLlTwo: data.jslYB,
+      jsCodType: 1,
+      jsCodOne: data.jsCodYB,
+      jsCodTwo: data.jsCodYB,
+      hycXsyType: 1,
+      hycXsyOne: data.hyXsyHYOne,
+      hycXsyTwo: data.hyXsyHYTwo,
+      qycYxyType: 1,
+      qycYxyOne: data.qyXsyHYOne,
+      qycYxyTwo: data.qyXsyHYTwo,
+      qycAdType: 1,
+      qycAdOne: data.qyAdHYOne,
+      qycAdTwo: data.qyAdHYTwo,
+      jsTnType: 1,
+      jsTnOne: data.jsTnYB,
+      jsTnTwo: data.jsTnYB
+    }
+
+    Object.keys(dataSourceParams.value).forEach(key => {
+      const item = dataSourceParams.value[key];
+      if ( !Object.keys(item).length ) {
+        dataSourceParams.value[key] = { ...params };
+      }
+    })
+  })
+
+  // 获取是否允许投药开关
+  controlApi.getSystemStatus().then(({ data }) => {
+    // 0不允许  1允许
+    systemStatus.value = data;
+  });
+})
+
+</script>
+
+<template>
+  <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="智能投加计算"></BaseTitle>
+            <n-scrollbar class="scrollbar" style="height: 100%;">
+              <div class="form-content">
+
+                <BaseCard title="选择加药泵">
+                  <BaseRadioCard v-model="baseSourceParams.numberBeng"></BaseRadioCard>
+                </BaseCard>
+
+                <BaseCard title="投加运行方式">
+                  <span class="status-bar">
+                    <i>{{ baseSourceParams.addType === 0 ? '启用智适应碳源投加' : '手动碳源投加' }}</i>
+                  </span>
+                </BaseCard>
+
+                <BaseCard title="选择池组手自动方式">
+                  <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">
+                          <span>1号池</span>
+                          <span>2号池</span>
+                        </p>
+                      </div>
+                      <div class="space-y-[12px]">
+                        <BaseChooseItem
+                          tab-key="auto"
+                          title="进水流量"
+                          unit="m³"
+                          isDouble
+                          @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: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="auto"
+                          title="进水COD"
+                          unit="mg/L"
+                          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: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
+                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
+                          ]"
+                        ></BaseChooseItem>
+                        
+                        <BaseChooseItem
+                          tab-key="auto"
+                          title="好氧池硝酸盐"
+                          unit="mg/L"
+                          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: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
+                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="auto"
+                          title="缺氧池硝酸盐"
+                          unit="mg/L"
+                          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: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="auto"
+                          title="缺氧池氨氮"
+                          unit="mg/L"
+                          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: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="auto"
+                          title="进水总氮"
+                          unit="mg/L"
+                          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: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
+                          ]"
+                        ></BaseChooseItem>
+                      </div>
+                    </n-tab-pane>
+
+                    <n-tab-pane :name="1" tab="1号池">
+                      <div class="panel-header_main">
+                        <p>设置数据来源</p>
+                        <p class="space-x-[20px] text-center">
+                          <span>1号池</span>
+                        </p>
+                      </div>
+                      <div class="space-y-[12px]">
+                        <BaseChooseItem
+                          tab-key="onePool"
+                          title="进水流量"
+                          unit="m³"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.jsLlType"
+                          v-model:value1="dataSourceParams.onePool.jsLlOne"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="onePool"
+                          title="进水COD"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.jsCodType"
+                          v-model:value1="dataSourceParams.onePool.jsCodOne"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
+                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="onePool"
+                          title="好氧池硝酸盐"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.hycXsyType"
+                          v-model:value1="dataSourceParams.onePool.hycXsyOne"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '化验', value1: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
+                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="onePool"
+                          title="缺氧池硝酸盐"
+                          unit="mg/L"
+                          @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: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="onePool"
+                          title="缺氧池氨氮"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.qycAdType"
+                          v-model:value1="dataSourceParams.onePool.qycAdOne"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '化验', value1: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="onePool"
+                          title="进水总氮"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.onePool.jsTnType"
+                          v-model:value1="dataSourceParams.onePool.jsTnOne"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '仪表', value1: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
+                          ]"
+                        ></BaseChooseItem>
+                      </div>
+                    </n-tab-pane>
+
+                    <n-tab-pane :name="2" tab="2号池">
+                      <div class="panel-header_main">
+                        <p>设置数据来源</p>
+                        <p class="space-x-[20px] text-center">
+                          <span>2号池</span>
+                        </p>
+                      </div>
+                      <div class="space-y-[12px]">
+                        <BaseChooseItem
+                          tab-key="twoPool"
+                          title="进水流量"
+                          unit="m³"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.jsLlType"
+                          v-model:value1="dataSourceParams.twoPool.jsLlTwo"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="twoPool"
+                          title="进水COD"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.jsCodType"
+                          v-model:value1="dataSourceParams.twoPool.jsCodTwo"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
+                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="twoPool"
+                          title="好氧池硝酸盐"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.hycXsyType"
+                          v-model:value1="dataSourceParams.twoPool.hycXsyTwo"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '化验', value1: waterConfigParams.hyXsyHYTwo },
+                            { label: '预测', value1: waterConfigParams.hyXsyYCTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="twoPool"
+                          title="缺氧池硝酸盐"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.qycYxyType"
+                          v-model:value1="dataSourceParams.twoPool.qycYxyTwo"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '化验', value1: waterConfigParams.qyXsyHYTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="twoPool"
+                          title="缺氧池氨氮"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.qycAdType"
+                          v-model:value1="dataSourceParams.twoPool.qycAdTwo"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '化验', value1: waterConfigParams.qyAdHYTwo }
+                          ]"
+                        ></BaseChooseItem>
+                        <BaseChooseItem
+                          tab-key="twoPool"
+                          title="进水总氮"
+                          unit="mg/L"
+                          @on-update="handleMedicateAmount"
+                          v-model:type="dataSourceParams.twoPool.jsTnType"
+                          v-model:value1="dataSourceParams.twoPool.jsTnTwo"
+                          :btn-group="[
+                            { label: '手动', value1: '', value2: '' },
+                            { label: '仪表', value1: waterConfigParams.jsTnYB }
+                          ]"
+                        ></BaseChooseItem>
+                      </div>
+                    </n-tab-pane>
+
+                    <n-tab-pane :name="3" tab="人工投放">
+                      <div class="panel-header_main">
+                        <p>设置数据来源</p>
+                        <p class="space-x-[20px] text-center">
+                          <span>人工投放</span>
+                        </p>
+                      </div>
+                      <div class="w-full flex items-center justify-between">
+                        <span>人工投放:</span>
+                        <div class="w-[200px]">
+                          <BaseInput
+                            :isCloseIcon="false"
+                            v-model="dataSourceParams.worker.medicineAmount"
+                            @on-blur="handleMedicateAmount"
+                          ></BaseInput>
+                        </div>
+                      </div>
+                    </n-tab-pane>
+                  </n-tabs>
+                </BaseCard>
+                <BaseCard title="设定参数系数" style="margin: 0" tips="建议使用默认值,非必要不修改" v-show="tabActiveKey !== 'worker'">
+                  <template #titleRight>
+                    <div>
+                      <div class="flex items-center space-x-[4px] cursor-pointer text-[#2454FF] text-[13px]"
+                        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" />
+                          <path d="M3.66663 8.90663V11.3333H6.10569L13 4.43603L10.565 2L3.66663 8.90663Z"
+                            stroke="#2454FF" stroke-linejoin="round" />
+                        </svg>
+                        <span>编辑</span>
+                      </div>
+                      <ul class="flex items-center text-[13px] space-x-[8px] cursor-pointer" v-show="!isVisibleBtn">
+                        <li class="cursor-pointer" @click="onEditConfirm" style="color: #2454FF">确定</li>
+                        <li class="cursor-pointer text-[#B0B7C0]" @click="onEditCancel">取消</li>
+                      </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">
+                        {{ 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="baseSourceParams[item.key]" isCenter placeholder="" :readonly="index === columnData.length - 1"></BaseInput>
+                      </div>
+                    </li>
+                  </ul>
+                </BaseCard>
+              </div>
+            </n-scrollbar>
+          </div>
+          <div class="right-section">
+            <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>
+    </TheChatView>
+  </section>
+</template>
+
+<style lang="scss" scoped>
+.control-container {
+  @include flex(x, start, start);
+  height: 100%;
+
+  .left-section {
+    display: flex;
+    flex-flow: column;
+    flex-shrink: 0;
+    width: 440px;
+    height: 100%;
+    border-radius: 10px;
+    background: #fff;
+
+    .status-bar {
+      display: inline-block;
+      padding: 4px 14px;
+      margin-left: 20px;
+      border-top: 0.5px solid rgba(212, 241, 255, 0.00);
+      border-bottom: 0.5px solid rgba(212, 241, 255, 0.00);
+      background: linear-gradient(90deg, rgba(240, 250, 255, 0.00) 0%, #E9F8FF 27.27%, rgba(240, 250, 255, 0.00) 100%);
+
+      i {
+        line-height: 24px;
+        background: linear-gradient(92deg, #5ABBF2 12.24%, #2454FF 63.2%);
+        background-clip: text;
+        -webkit-background-clip: text;
+        -webkit-text-fill-color: transparent;
+        font-weight: bold;
+      }
+    }
+
+    .scrollbar {
+      height: 100%;
+    }
+
+    .form-content {
+      padding: 0 16px 24px 16px;
+    }
+
+    .panel-header_main {
+      @include flex(x, center, between);
+      margin-bottom: 12px;
+      color: #86909C;
+      span {
+        display: inline-block;
+        width: 60px;
+      }
+    }
+
+  }
+
+  .data-source-list {
+    .data-soruce-item {
+      @include flex(x, center, between);
+
+      .inp-inner {
+        width: 112px;
+      }
+
+      .unit {
+        font-family: "D-DIN-PRO-700-Bold";
+        font-weight: bold;
+        font-size: 12px;
+        color: #333;
+      }
+    }
+  }
+}
+
+.right-section {
+  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%);
+  // }
+
+  // .bottom {
+  //   height: 100%;
+  //   background: pink;
+  // }
+
+}
+
+
+// 通用区域的样式
+.btn {
+  width: 80px;
+  height: 32px;
+  border-radius: 4px;
+  border: 1px solid #D3D7DD;
+  text-align: center;
+  font-size: 14px;
+  line-height: 32px;
+  color: #1A2029;
+}
+
+.btn-primary {
+  border: 0;
+  background: var(--Linear, linear-gradient(270deg, #3BD6E3 0%, #019AFE 100%));
+  font-weight: bold;
+  color: #fff;
+}
+
+.btn-info {
+  width: 44px;
+  height: 28px;
+  border-radius: 4px;
+  border: 1px solid #D3D7DD;
+  background: #fff;
+  font-size: 12px;
+  text-align: center;
+  line-height: 28px;
+  color: #1A2029;
+  cursor: pointer;
+}
+
+.btn-info_active {
+  color: #2454FF;
+  border: 1px solid #2454FF;
+  background: #EBF0FF;
+}
+
+.radio {
+  display: block;
+  width: 12px;
+  height: 12px;
+  border-radius: 100%;
+  border: 1px solid #ccc;
+  cursor: pointer;
+}
+
+.radio_big {
+  width: 16px;
+  height: 16px;
+}
+
+.radio-active {
+  transition: all .1s;
+  border: 3px solid #2454FF;
+}
+
+.radio_big.radio-active {
+  border: 4px solid #2454FF;
+}
+</style>
+
+<style lang="scss">
+.custom-tab_item {
+  @include flex (x, center, center);
+  height: 35px;
+  border-radius: 4px;
+  background: #F3F5FA;
+
+  &.n-tabs-tab--active {
+    transition: none !important;
+    border-radius: 4px;
+    transition: none !important;
+    background: url('https://static.fuxicarbon.com/bigModel/pc/tab-border-item-2x.png') -3px 0px no-repeat, linear-gradient(180deg, #F1F3FE 0%, #FFF 100%);
+    background-size: 107% 100%;
+  }
+}
+
+.control-container .left-section {
+  .n-tabs-nav-scroll-content {
+    padding-bottom: 10px;
+  }
+}
+</style>

+ 541 - 0
src/views/control-Old/MedicinalView.vue

@@ -0,0 +1,541 @@
+<script setup>
+import { ref, onMounted, computed, unref, watch } from 'vue';
+import { NScrollbar, useMessage, NTabs, NTabPane, c } from 'naive-ui';
+import { TheChatView } from '@/components';
+import { controlApi } from "@/api/control";
+import BaseTitle from './components/BaseTitle.vue';
+import BaseRadioCard from './components/BaseRadioCard.vue';
+import BaseCard from './components/BaseCard.vue';
+import BaseChooseItem from './components/BaseChooseItem.vue';
+import BaseInput from './components/BaseInput.vue';
+import TheResultPanel from './components/TheResultPanel.vue';
+import TheEchartPanel from './components/TheEchartPanel.vue';
+
+const message = useMessage();
+const isVisibleBtn = ref(true);
+const isVisibleUpdateInfo = ref(false);
+const systemStatus = ref(0);
+
+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 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
+  }
+})
+
+// 系数
+const baseSourceParams = ref({
+  numberBeng: 0,
+  type: 0
+})
+
+// 当前Tab选中的key
+const tabActiveKey = computed(() => tabKeyEnum[baseSourceParams.value.type]);
+// 计算碳氮比
+const tdbNum = computed(() => {
+  const type = tabActiveKey.value;
+
+  const [ r1, r2, rOne, rTwo] = getTotalNum();
+ 
+  const { 
+    jsLlOne, jsLlTwo,
+    jsCodOne, jsCodTwo,
+    jsTnOne, jsTnTwo
+  } = dataSourceParams.value[type];
+
+  const {
+    tydl, zhxs
+  } = baseSourceParams.value
+ 
+  if ( type === 'auto' || type === 'worker' ) {
+    const rNum = Math.max( r1, r2 );
+    const jsll = rNum == r1 ? jsLlOne: jsLlTwo;
+    const cod = rNum == r1 ? jsCodOne: jsCodTwo;
+    const jsTn = rNum == r1 ? jsTnOne: jsTnTwo;
+    const n = rNum == r1 ? rOne : rOne
+    return Number(((n*1000/jsll*tydl+cod*zhxs*tydl)/jsTn).toFixed(2));
+  }
+  if ( type === 'onePool' ) {
+    return Number(((rOne*1000/jsLlOne*tydl+jsCodOne*zhxs*tydl)/jsTnOne).toFixed(2));
+  }
+  if ( type === 'twoPool' ) {
+    return Number(((rTwo*1000/jsLlTwo*tydl+jsCodTwo*zhxs*tydl)/jsTnTwo).toFixed(2));
+  }
+})
+watch(() => tdbNum.value , tdb => {
+  columnData.value[columnData.value.length - 1].value = tdb;
+})
+
+// 编辑系数 - confirm
+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;
+  })
+
+  handleMedicateAmount();
+}
+
+// 编辑系数 - 取消
+const onEditCancel = () => {
+  isVisibleBtn.value = true;
+
+  columnData.value.map(({ key, value }) => {
+    baseSourceParams.value[key] = value;
+  })
+}
+
+const onFinalResult = () => {
+  const addStatus = systemStatus.value === 0 ? 1 : 0;
+
+  controlApi.putSystemStatus({ addStatus });
+
+  systemStatus.value = addStatus;
+
+  message.warning(addStatus === 0 ? '当前投药状态:已停用' : '当前投药状态:投放中');
+
+}
+
+const onUpdateTab = (index) => {
+  const currentData = dataSourceParams.value[tabKeyEnum[index]];
+  baseSourceParams.value.type = index;
+  if ( !Object.keys(currentData).length ) {
+    isVisibleUpdateInfo.value = false;
+    return;
+  }
+  isVisibleBtn.value = true;
+  handleMedicateAmount();
+}
+
+function 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 = Number((rOne3 < 0 || !rOne3) ? 0 : rOne3.toFixed(3)) || 0;
+  const r2 = Number((rTwo3 < 0 || !rTwo3) ? 0 : rTwo3.toFixed(3)) || 0;
+
+  return [ r1, r2, rOne2, rTwo2 ];
+}
+
+const onConfirmUpdate = async () => {
+  await controlApi.postAddRecord({
+    ...dataSourceParams.value[tabKeyEnum[baseSourceParams.value.type]],
+    ...baseSourceParams.value,
+  });
+
+  isVisibleUpdateInfo.value = false;
+
+  message.success("系统加药量,更新成功");
+
+  doseNum.value = updateNum.value;
+}
+
+// 更新投药结果
+const handleMedicateAmount = () => {
+  const type = tabActiveKey.value;
+  const lastNum = unref(updateNum);
+  const tdb = tdbNum.value;
+
+  if ( type === 'worker' ) {
+    const medicineNum = dataSourceParams.value.worker.medicineAmount;
+    if ( medicineNum || medicineNum == 0 ) {
+      updateNum.value = medicineNum;
+      isVisibleUpdateInfo.value = true;
+      baseSourceParams.value.tdb = tdb;
+      message.warning("有新投放方案, 请查看")
+    } else {
+      updateNum.value = null;
+      isVisibleUpdateInfo.value = false;
+    }
+    return;
+  }
+
+  const [r1, r2] = getTotalNum();
+  const maxR = Math.max( r1, r2 );
+
+  if ( type === 'auto' && maxR !== lastNum) {
+    updateNum.value = maxR;
+    isVisibleUpdateInfo.value = true;
+    baseSourceParams.value.tdb = tdb;
+    message.warning("有新的投放方案, 请查看");
+  }
+
+  if ( type === 'onePool' && r1 != lastNum) {
+    updateNum.value = r1;
+    isVisibleUpdateInfo.value = true;
+    baseSourceParams.value.tdb = tdb;
+    message.warning("有新的投放方案, 请查看");
+  }
+
+  if ( type === 'twoPool' && r2 != lastNum ) {
+    updateNum.value = r2;
+    isVisibleUpdateInfo.value = true;
+    baseSourceParams.value.tdb = tdb;
+    message.warning("有新的投放方案, 请查看");
+  }
+
+}
+
+onMounted(async () => {
+  // 获取最后一条记录  getSystemStatus
+  await controlApi.getBaseData().then(({ data }) => {
+    const {
+      numberBeng = 0, type = 0,
+      htfksd, jzxs, xzxs, kzxs, slfpxs, tydl, zhxs, sxps, yymd, zxqdll,
+      medicineAmount,
+      addType,
+      tytjTransientLL
+    } = data;
+ 
+    baseSourceParams.value = { 
+      ...baseSourceParams.value,
+      numberBeng, type,
+      htfksd, jzxs, xzxs, kzxs, slfpxs, tydl, zhxs, sxps, yymd, zxqdll,
+      addType
+    };
+    
+    updateNum.value = medicineAmount;
+
+    doseNum.value = medicineAmount;
+
+    flowNum.value = tytjTransientLL;
+
+    dataSourceParams.value[tabActiveKey.value] = data;
+
+  })
+
+ 
+  // 获取实时数据
+  controlApi.getNumValue().then(({ data }) => {
+
+    Object.entries(data).forEach(([key, val]) => {
+      data[key] = val;
+    });
+
+    // water实时数据
+    waterConfigParams.value = data;
+
+    // 重新计算碳氮比
+    const tdb = tdbNum.value;
+    baseSourceParams.value.tdb = tdb;
+ 
+    columnData.value = columnData.value.map(item => {
+      item.value = item.key === 'tdb' ? tdb : dataSourceParams.value[tabActiveKey.value][item.key];
+      return item;
+    })
+
+    const params = {
+      jsLlType: 1,
+      jsLlOne: data.jslYB,
+      jsLlTwo: data.jslYB,
+      jsCodType: 1,
+      jsCodOne: data.jsCodYB,
+      jsCodTwo: data.jsCodYB,
+      hycXsyType: 1,
+      hycXsyOne: data.hyXsyHYOne,
+      hycXsyTwo: data.hyXsyHYTwo,
+      qycYxyType: 1,
+      qycYxyOne: data.qyXsyHYOne,
+      qycYxyTwo: data.qyXsyHYTwo,
+      qycAdType: 1,
+      qycAdOne: data.qyAdHYOne,
+      qycAdTwo: data.qyAdHYTwo,
+      jsTnType: 1,
+      jsTnOne: data.jsTnYB,
+      jsTnTwo: data.jsTnYB
+    }
+
+    Object.keys(dataSourceParams.value).forEach(key => {
+      const item = dataSourceParams.value[key];
+      if ( !Object.keys(item).length ) {
+        dataSourceParams.value[key] = { ...params };
+      }
+    })
+  })
+
+  // 获取是否允许投药开关
+  controlApi.getSystemStatus().then(({ data }) => {
+    // 0不允许  1允许
+    systemStatus.value = data;
+  });
+})
+
+</script>
+
+<template>
+  <section class="flex items-start h-full">
+    <TheChatView leftTitle="智适应碳源投加" :isChatSlot="false" :isFooter="false">
+      <template #control>
+        <div class="control-container space-y-[16px]">
+          <div class="arg-section">
+            <BaseTitle title="智能投加计算"></BaseTitle>
+            <div class="content">
+              <div class="left-paramter"></div>
+            </div>
+          </div>
+          <div class="echart-section"></div>
+        </div>
+      </template>
+    </TheChatView>
+  </section>
+</template>
+
+<style lang="scss" scoped>
+.control-container {
+  // @include flex(x, start, start);
+  height: 100%;
+
+  .arg-section {
+    height: 57%;
+    padding: 16px 14px;
+    border: 1px solid #fff;
+    border-radius: 10px;
+    background: url(@/assets/images/control/bg-top.png) left center no-repeat;
+    background-size: 878px 100% ;
+    display: flex;
+    flex-flow: column;
+
+    .content {
+      height: 100%;
+      padding-top: 16px;
+      .left-paramter {
+        width: 338px;
+        height: 100%;
+        border: 1px solid #fff;
+        border-radius: 10px;
+        background: rgba(255, 255, 255, 0.20);
+        backdrop-filter: blur(5px);
+      }
+    }
+  }
+
+  .echart-section {
+    height: 43%;
+    background: red;
+  }
+
+  .left-section {
+    display: flex;
+    flex-flow: column;
+    flex-shrink: 0;
+    width: 440px;
+    height: 100%;
+    border-radius: 10px;
+    background: #fff;
+
+    .status-bar {
+      display: inline-block;
+      padding: 4px 14px;
+      margin-left: 20px;
+      border-top: 0.5px solid rgba(212, 241, 255, 0.00);
+      border-bottom: 0.5px solid rgba(212, 241, 255, 0.00);
+      background: linear-gradient(90deg, rgba(240, 250, 255, 0.00) 0%, #E9F8FF 27.27%, rgba(240, 250, 255, 0.00) 100%);
+
+      i {
+        line-height: 24px;
+        background: linear-gradient(92deg, #5ABBF2 12.24%, #2454FF 63.2%);
+        background-clip: text;
+        -webkit-background-clip: text;
+        -webkit-text-fill-color: transparent;
+        font-weight: bold;
+      }
+    }
+
+    .scrollbar {
+      height: 100%;
+    }
+
+    .form-content {
+      padding: 0 16px 24px 16px;
+    }
+
+    .panel-header_main {
+      @include flex(x, center, between);
+      margin-bottom: 12px;
+      color: #86909C;
+      span {
+        display: inline-block;
+        width: 60px;
+      }
+    }
+
+  }
+
+  .data-source-list {
+    .data-soruce-item {
+      @include flex(x, center, between);
+
+      .inp-inner {
+        width: 112px;
+      }
+
+      .unit {
+        font-family: "D-DIN-PRO-700-Bold";
+        font-weight: bold;
+        font-size: 12px;
+        color: #333;
+      }
+    }
+  }
+}
+
+.right-section {
+  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%);
+  // }
+
+  // .bottom {
+  //   height: 100%;
+  //   background: pink;
+  // }
+
+}
+
+
+// 通用区域的样式
+.btn {
+  width: 80px;
+  height: 32px;
+  border-radius: 4px;
+  border: 1px solid #D3D7DD;
+  text-align: center;
+  font-size: 14px;
+  line-height: 32px;
+  color: #1A2029;
+}
+
+.btn-primary {
+  border: 0;
+  background: var(--Linear, linear-gradient(270deg, #3BD6E3 0%, #019AFE 100%));
+  font-weight: bold;
+  color: #fff;
+}
+
+.btn-info {
+  width: 44px;
+  height: 28px;
+  border-radius: 4px;
+  border: 1px solid #D3D7DD;
+  background: #fff;
+  font-size: 12px;
+  text-align: center;
+  line-height: 28px;
+  color: #1A2029;
+  cursor: pointer;
+}
+
+.btn-info_active {
+  color: #2454FF;
+  border: 1px solid #2454FF;
+  background: #EBF0FF;
+}
+
+.radio {
+  display: block;
+  width: 12px;
+  height: 12px;
+  border-radius: 100%;
+  border: 1px solid #ccc;
+  cursor: pointer;
+}
+
+.radio_big {
+  width: 16px;
+  height: 16px;
+}
+
+.radio-active {
+  transition: all .1s;
+  border: 3px solid #2454FF;
+}
+
+.radio_big.radio-active {
+  border: 4px solid #2454FF;
+}
+</style>
+
+<style lang="scss">
+.custom-tab_item {
+  @include flex (x, center, center);
+  height: 35px;
+  border-radius: 4px;
+  background: #F3F5FA;
+
+  &.n-tabs-tab--active {
+    transition: none !important;
+    border-radius: 4px;
+    transition: none !important;
+    background: url('https://static.fuxicarbon.com/bigModel/pc/tab-border-item-2x.png') -3px 0px no-repeat, linear-gradient(180deg, #F1F3FE 0%, #FFF 100%);
+    background-size: 107% 100%;
+  }
+}
+
+.control-container .left-section {
+  .n-tabs-nav-scroll-content {
+    padding-bottom: 10px;
+  }
+}
+</style>

+ 73 - 0
src/views/control-Old/components/BaseButton.vue

@@ -0,0 +1,73 @@
+<script setup>
+defineProps({
+  type: {
+    type: String,
+    default: 'default'
+  },
+  isActive: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const emit = defineEmits(['on-click']);
+
+const emitClickEvent = () => emit('on-click');
+
+</script>
+
+<template>
+  <button
+    :class="[
+      'custom-button',
+      'button-type_' + type, 
+      {'type_active': isActive}
+    ]"
+    @click="emitClickEvent"
+  >
+    <slot></slot>
+  </button>
+</template>
+
+<style lang="scss" scoped>
+.custom-button {
+  width: 80px;
+  height: 32px;
+  border-radius: 4px;
+  border: 1px solid #D3D7DD;
+  text-align: center;
+  font-size: 14px;
+  line-height: 32px;
+  color: #1A2029;
+}
+
+.button-type_default {
+  background: #fff;
+}
+
+.button-type_gradual {
+  border: 0;
+  background: var(--Linear, linear-gradient(270deg, #3BD6E3 0%, #019AFE 100%));
+  font-weight: bold;
+  color: #fff;
+}
+
+.button-type_info {
+  width: 44px;
+  height: 28px;
+  border-radius: 4px;
+  border: 1px solid #D3D7DD;
+  background: #fff;
+  font-size: 12px;
+  text-align: center;
+  line-height: 28px;
+  color: #1A2029;
+  cursor: pointer;
+
+  @at-root .type_active {
+    color: #2454FF;
+    border: 1px solid #2454FF;
+    background: #EBF0FF;
+  }
+}
+</style>

+ 56 - 0
src/views/control-Old/components/BaseCard.vue

@@ -0,0 +1,56 @@
+<script setup>
+import { NTooltip } from 'naive-ui';
+import { SvgIcon } from '@/components';
+defineProps({
+  title: {
+    type: String,
+    default: ""
+  },
+  tips: {
+    type: String,
+    default: ""
+  }
+})
+</script>
+
+<template>
+  <div class="base-card_view">
+    <div class="title-wrapper">
+      <h4 class="title space-x-4">
+        <span>{{ title }}</span>
+        <template v-if="tips">
+          <n-tooltip placement="bottom" trigger="hover">
+            <template #trigger>
+              <SvgIcon name="control-icon-tips"></SvgIcon>
+            </template>
+            <span>{{ tips }}</span>
+          </n-tooltip>
+        </template>
+      </h4>
+      <slot name="titleRight"></slot>
+    </div>
+    <slot />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.base-card_view {
+  margin-bottom: 24px;
+
+  .title-wrapper {
+    @include flex(x, center, between);
+    margin-bottom: 16px;
+
+    .title {
+      @include flex(x, center, start);
+      height: 30px;
+      line-height: 30px;
+      border-radius: 4px;
+      font-size: 14px;
+      font-weight: bold;
+      color: #1A2029;
+    }
+  }
+
+}
+</style>

+ 163 - 0
src/views/control-Old/components/BaseChooseItem.vue

@@ -0,0 +1,163 @@
+<script setup>
+import { ref, computed, watch, watchEffect } from 'vue';
+import { useMessage } from 'naive-ui';
+
+import BaseButton from './BaseButton.vue';
+import BaseInput from './BaseInput.vue';
+
+const message = useMessage();
+
+const activeIndex = ref(-1);
+const inpVal = ref();
+const modelValue = defineModel();
+
+const modelType = defineModel('type');
+const modelValue1 = defineModel('value1');
+const modelValue2 = defineModel('value2');
+
+const emit = defineEmits(['on-update']);
+
+const props = defineProps({
+  title: {
+    type: String,
+    default: ''
+  },
+  btnGroup: {
+    type: Array,
+    default: false
+  },
+  unit: {
+    type: String,
+    default: ''
+  },
+  isDouble: {
+    type: Boolean,
+    default: false
+  },
+  tabKey: {
+    type: String,
+    requured: true
+  }
+})
+
+watchEffect(() => {
+  if ( modelType.value === 0 ) {
+    if ( props.tabKey == 'auto' ) {
+      inpVal.value = modelValue1.value;
+    }
+    if ( props.tabKey == 'onePool' ) {
+      inpVal.value = modelValue1.value;
+    }
+    if ( props.tabKey == 'twoPool' ) {
+      inpVal.value = modelValue1.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;
+}
+
+const onInpConfirm = (num) => {
+  if (num === Infinity || num === -Infinity) {
+    modelValue.value = null
+    return message.warning(`${props.title}的数值填写有误, 请检查`);
+  }
+  modelValue.value = num;
+}
+
+const onInput = (val) => {
+  modelValue1.value = val;
+  modelValue2.value = val;
+}
+
+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 : inpVal.value;
+  modelValue2.value = index != 0 ? item.value2 : inpVal.value;
+  onEmitEvent();
+}
+
+</script>
+
+<template>
+  <div class="base-chooseItem_view">
+    <span class="label-inner">{{ title }}:</span>
+    <div class="choose-inner">
+      <div class="top-box">
+        <ul class="btn-group space-x-[4px]">
+          <BaseButton
+            v-for="item, index in btnGroup"
+            type="info"
+            :key="index"
+            :isActive="modelType === index || btnGroup.length === 1"
+            @click="changeActive(item, index)"
+          >
+            {{ item.label }}
+          </BaseButton>
+        </ul>
+        <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>
+
+      <BaseInput
+        v-show="(modelType !== undefined && !modelType) || btnGroup.length === 1"
+        default-value=""
+        :placeholder="'请输入' + props.title"
+        :unit="unit"
+        v-model="inpVal"
+        @click:confirm="onInpConfirm"
+        @click:cancel="onInpCancel"
+        @on-input="onInput"
+        @on-blur="onEmitEvent"
+      ></BaseInput>
+    </div>
+  </div>
+</template>
+
+<style lang="scss">
+.base-chooseItem_view {
+  @include flex(x, start, start);
+
+  .label-inner {
+    width: 160px;
+    flex-shrink: 1;
+    line-height: 28px;
+  }
+
+  .choose-inner {
+    width: 100%;
+
+    .top-box {
+      @include flex(x, center, between);
+
+      .btn-group {
+        @include flex(x, center, center);
+      }
+
+      .num-group {
+        font-family: "D-DIN-PRO-700-Bold";
+        font-weight: bold;
+        font-size: 12px;
+        color: #333;
+      }
+    }
+  }
+}
+</style>

+ 202 - 0
src/views/control-Old/components/BaseInput.vue

@@ -0,0 +1,202 @@
+<script setup>
+import { ref, computed } from 'vue';
+import { NInputNumber } from 'naive-ui';
+
+const props = defineProps({
+  placeholder: {
+    type: String,
+    default: () => ""
+  },
+  size: {
+    type: String,
+    default: ''
+  },
+  type: {
+    type: String,
+    default: 'default'
+  },
+  isActive: {
+    type: Boolean,
+    default: false
+  },
+  unit: {
+    type: String,
+    default: ''
+  },
+  isNeedFlotBtn: {
+    type: Boolean,
+    default: true
+  },
+  isCenter: {
+    type: Boolean,
+    default: false
+  },
+  isCloseIcon: {
+    type: Boolean,
+    default: true
+  },
+  readonly: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const isFocusStatus = ref(false);
+
+const emit = defineEmits(['click:confirm', 'on-input', 'on-blur', 'click:cancel']);
+const modelValue = defineModel();
+
+const domClassName = computed(() => {
+  return [
+    'input_wrapper',
+    {'input_text_center': props.isCenter },
+    'input-' + props.type + "_" + props.size,
+    'inpit_' + props.size
+  ]
+})
+
+const onFocus = () => {
+  isFocusStatus.value = true;
+}
+
+const onBlur = (ev) => {
+  isFocusStatus.value = false;
+  emit('on-blur');
+}
+
+const onInput = (value) => {
+  emit('on-input', value);
+  modelValue.value = value;
+}
+
+const handleInpValue = (event, type) => {
+  if (type === 'confirm') {
+    emit('click:confirm', modelValue.value);
+  }
+  if (type === 'cancel') {
+    event.preventDefault();
+    modelValue.value = null;
+    emit('click:cancel', null);
+  }
+}
+</script>
+
+<template>
+  <div :class="[domClassName, 'base-input-wrapper']">
+    <NInputNumber
+      size="small"
+      round
+      style="width: 100%;"
+      :readonly="readonly"
+      :precision="2"
+      :max="99999.99"
+      :min="0"
+      :placeholder="placeholder"
+      :show-button="false"
+      :on-update:value="onInput"
+      :on-blur="onBlur"
+      :on-focus="onFocus"
+      :value="modelValue"
+    >
+      <template #suffix>
+        <div class="unit" v-if="unit">{{ unit }}</div>
+      </template>
+    </NInputNumber>
+    <ul class="inp-flot_group space-x-[4px]" v-show="isFocusStatus && isNeedFlotBtn">
+      <!-- <li>
+        <SvgIcon name="control-icon-confirm" size="16" @mousedown="handleInpValue($event, 'confirm')"></SvgIcon>
+      </li> -->
+      <!-- <li v-if="isCloseIcon">
+        <SvgIcon name="control-icon-cancel" size="16" @mousedown="handleInpValue($event, 'cancel')"></SvgIcon>
+      </li> -->
+    </ul>
+  </div>
+</template>
+
+
+<style lang="scss" scoped>
+.input_wrapper {
+  @include flex(x, center, between);
+  position: relative;
+  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: 60px;
+    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;
+    color: #333;
+  }
+
+  .inp-flot_group {
+    @include flex(x, center, center);
+    position: absolute;
+    right: 66px;
+    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>
+
+<style lang="scss">
+.base-input-wrapper {
+  .n-input-wrapper {
+    padding-right: 0px;
+    border: 1px solid #E6EAEE;
+    border-radius: 4px;
+    .n-input__input-el, .n-input__placeholder {
+      font-size: 12px;
+    }
+  }
+}
+
+.input_text_center {
+  .n-input-wrapper {
+    .n-input__input-el {
+      padding-right: 10px;
+      text-align: center;
+    }
+  }
+}
+</style>

+ 72 - 0
src/views/control-Old/components/BaseRadioCard.vue

@@ -0,0 +1,72 @@
+<script setup>
+import { ref } from 'vue';
+import { SvgIcon } from '@/components';
+
+const modelValue = defineModel();
+
+const defaultData = [
+  { label: '1号加药泵', key: 'first' },
+  { label: '2号加药泵', key: 'second' },
+  { label: '3号加药泵', key: 'third' },
+]
+
+const chageActive = (index) => modelValue.value = index;
+</script>
+
+<template>
+  <ul class="radio-card_group space-x-[10px]">
+    <li :class="['radio-card_item', { 'card_item_active': index === modelValue }]" v-for="item, index in defaultData"
+      :key="item.key" @click="chageActive(index)">
+      <div class="radio-wrapper">
+        <SvgIcon :name="index === modelValue ? 'control-icon-pump-active' : 'control-icon-pump'" size="16" fillColor="#2454FF" />
+        <span class="radio radio-active"></span>
+      </div>
+      <p class="text">{{ item.label }}</p>
+    </li>
+  </ul>
+</template>
+
+<style lang="scss" scoped>
+.radio-card_group {
+  @include flex(x, center, between);
+  font-size: 11px;
+  font-weight: bold;
+  line-height: 14px;
+  color: #333;
+
+  .radio-card_item {
+    @include flex(y, start, between);
+    width: 120px;
+    height: 48px;
+    padding: 6px;
+    border-radius: 4px;
+    border: 1px solid #E6EAEE;
+    transition: all .3s;
+    cursor: pointer;
+
+    .radio-wrapper {
+      @include flex(x, center, between);
+      width: 100%;
+    }
+  }
+
+  .card_item_active {
+    width: 150px;
+    border: 1px solid rgba(36, 84, 255, 0.80);
+
+    .radio {
+      transition: all .1s;
+      border: 3px solid #2454FF;
+    }
+  }
+
+  .radio {
+    display: block;
+    width: 12px;
+    height: 12px;
+    border-radius: 100%;
+    border: 1px solid #ccc;
+    cursor: pointer;
+  }
+}
+</style>

+ 67 - 0
src/views/control-Old/components/BaseRadioGroup.vue

@@ -0,0 +1,67 @@
+<script setup>
+import { ref } from 'vue';
+
+const props = defineProps({
+  data: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const modelValue = defineModel();
+
+const changeActive = (index) => {
+  modelValue.value = index;
+}
+
+</script>
+
+<template>
+  <ul class="radio-list_group space-x-[24px]">
+    <li class="radio_item space-x-[8px]" v-for="item,index in data" :key="index" @click="changeActive(index)">
+      <span
+        :class="[
+          'radio',
+          'radio_big',
+          {'radio-active': modelValue === index}
+        ]"
+        
+      ></span>
+      <span class="label">{{ item }}</span>
+    </li>
+  </ul>
+</template>
+
+<style lang="scss" scoped>
+.radio-list_group {
+  @include flex(x, center, start);
+  padding-left: 20px;
+
+  .radio_item {
+    @include flex(x, center, center);
+
+    .radio {
+      display: block;
+      width: 12px;
+      height: 12px;
+      border-radius: 100%;
+      border: 1px solid #ccc;
+      cursor: pointer;
+    }
+
+    .radio_big {
+      width: 16px;
+      height: 16px;
+    }
+
+    .radio_big.radio-active {
+      border: 4px solid #2454FF;
+    }
+
+    .label {
+      min-width: 40px;
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 62 - 0
src/views/control-Old/components/BaseTitle.vue

@@ -0,0 +1,62 @@
+<script setup>
+
+defineProps({
+  title: {
+    type: String,
+    default: ''
+  },
+  type: {
+    type: String,
+    default: 'first'
+  }
+})
+
+</script>
+
+<template>
+  <div class="header">
+    <div class="title">
+      <svg xmlns="http://www.w3.org/2000/svg" width="8" height="24" viewBox="0 0 8 24" fill="none"
+        v-if="type === 'first'">
+        <path d="M0 6.86197V3.24507L4.21274 0H7.11111L4.45741 24H1.35684L4.01053 3.85352L0 6.86197Z" fill="#2454FF" />
+      </svg>
+      <svg xmlns="http://www.w3.org/2000/svg" width="12" height="24" viewBox="0 0 12 24" fill="none" v-else>
+        <path
+          d="M7.74366 10.4333C8.43696 9.03333 8.78361 7.76667 8.78361 6.6V6.1C8.78361 5.06667 8.58552 4.26667 8.16459 3.7C7.74366 3.13333 7.19892 2.83333 6.48086 2.83333C5.7628 2.83333 5.21807 3.1 4.8219 3.66667C4.42573 4.23333 4.22764 5.1 4.22764 6.23333V7.16667H1.94966V6.1C1.94966 4.26667 2.37059 2.8 3.18769 1.66667C4.02956 0.566667 5.14379 0 6.53039 0C7.42177 0 8.23887 0.266667 8.93217 0.8C9.62547 1.3 10.1702 2.03333 10.5416 2.96667C10.9378 3.9 11.1111 4.93333 11.1111 6.03333V6.6C11.1111 7.6 10.9873 8.5 10.7149 9.4C10.4673 10.2667 10.0959 11.1667 9.57595 12.1L2.94652 21.1667H9.16145V24H0V21.6L7.74366 10.4333Z"
+          fill="#2454FF" />
+      </svg>
+      <span class="text">{{ title }}</span>
+    </div>
+    <div class="btn-group space-x-[8px]">
+      <slot name="right" />
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.header {
+  @include flex(x, center, between);
+  color: #1A2029;
+
+  .title {
+    display: flex;
+    align-items: center;
+    height: 24px;
+
+    .text {
+      height: 100%;
+      padding-left: 13px;
+      margin-left: -6px;
+      background: linear-gradient(90deg, rgba(36, 84, 255, 0.10) -0.94%, rgba(36, 84, 255, 0.00) 95.3%);
+      font-size: 15px;
+      font-weight: bold;
+      line-height: 24px
+    }
+  }
+
+  .btn-group {
+    @include flex(x, center, center);
+  }
+
+}
+</style>

+ 501 - 0
src/views/control-Old/components/TheEchartPanel.vue

@@ -0,0 +1,501 @@
+<script setup>
+import { ref, computed, onMounted, unref, onUnmounted } from 'vue';
+import { NTabs, NTab, NSelect, NDatePicker } from "naive-ui";
+import * as echarts from 'echarts';
+import { startOfDay } from "date-fns/esm"
+import { controlApi } from "@/api/control"
+import dayjs from 'dayjs';
+
+const modelValue = defineModel("height");
+
+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 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" },
+  { label: "#1缺氧池氨氮", value: 3, style: "font-size: 12px" },
+  { label: "#2缺氧池氨氮", value: 4, style: "font-size: 12px" },
+  { label: "进水COD", value: 5, style: "font-size: 12px" },
+  { label: "进水总氮", value: 6, style: "font-size: 12px" },
+  { 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(() => {
+  let name = '';
+  if ( activeIndex.value === 0) {
+    name = echartOptions.find(({ value }) => selectValue.value === value).label
+  } else {
+    name = coefficientOptions.find(item => item.value === selectValue.value).label
+  }
+  return name
+})
+
+// 切换tab选项
+const handleSwitchTab = (index) => {
+  if ( activeIndex.value === index ) return;
+  activeIndex.value = index;
+  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, type) => {
+  const option = {
+    backgroundColor: '#FFF',
+    title: {
+      show: !data.length,
+      text: '暂无数据',
+      x: 'center',
+      y: 'center',
+      textStyle: {
+        fontSize: 14,
+        fontWeight: 'normal',
+      }
+    },
+    grid: {
+      top: '40px',
+      bottom: '50px',
+      left: '6%',
+      right: '6%',
+    },
+    tooltip: {
+      trigger: 'axis',
+      label: {
+        show: true
+      },
+    },
+    xAxis: {
+      boundaryGap: false,
+      axisLine: {
+        show: false
+      },
+      splitLine: {
+        show: false
+      },
+      axisTick: {
+        show: false,
+        alignWithLabel: true
+      },
+      axisLabel: {
+        formatter: function (value) {
+          return type ? dayjs(value).format('YYYY/MM/DD') : value
+        }
+      },
+      data: data.map(({ time }) => time)
+    },
+    yAxis: {
+      axisLine: {
+        show: false
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          type: 'dashed',
+          color: '#E5E5E5'
+        }
+      },
+      axisTick: {
+        show: false
+      },
+      splitArea: {
+        show: false,
+        color: '#fff'
+      }
+    },
+    series: [
+      {
+        name: seriesName.value,
+        showSymbol: false,
+        smooth: true,
+        type: 'line',
+        symbolSize: 10,
+        lineStyle: {
+          color: '#17a6fa',
+          shadowBlur: 12,
+          shadowColor: 'rgba(0, 0, 0, 0.12)',
+          shadowOffsetX: 0,
+          shadowOffsetY: 4,
+          width: 2,
+        },
+        itemStyle: {
+          color: '#4080FF',
+          borderWidth: 3,
+          borderColor: '#4080FF'
+        },
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
+            offset: 0,
+            color: 'rgba(0, 136, 212, 0.2)'
+          }, {
+            offset: 1,
+            color: 'rgba(0, 136, 212, 0)'
+          }], false),
+        },
+        data: data.map(({ val }) => val)
+      }
+    ]
+  };
+  return option;
+}
+
+const onSwitchEchart = (item) => {
+  const echartData = echartDataSource.value[item.value];
+  isEmpty.value = !!echartData.length
+  echart.setOption(getEchartOptions(echartData));
+}
+
+// 水务相关数据格式化
+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: '在线仪表',
+    HY: '连续检测',
+    YC: '预测'
+  };
+
+  tabs.value = Object.keys(echartData).map((key, index) => {
+    if (index === 0) {
+      tabActive.value = key + '-' + selectValue.value + '-' + index;
+    }
+    if (echartData[key].length) {
+      return ({ label: enumSource[key], value: key });
+    }
+  }).filter(Boolean);
+
+  echartDataSource.value = echartData;
+
+  onSwitchEchart({ value: tabActive.value.substring(0, tabActive.value.indexOf('-')) });
+}
+
+// 系数相关数据
+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));
+}
+
+// 日期范围限制
+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();
+}
+
+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-card_view">
+    <div class="title">
+      <div class="left-inner">
+        <span class="text">数据看板</span>
+      </div>
+      <div class="right-inner">
+        <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"></div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.echart-card_view {
+  display: flex;
+  flex-flow: column;
+  height: calc(100% - 256px);
+  padding: 0px 16px 0 25px;
+  border-radius: 10px;
+
+  .title {
+    flex-shrink: 0;
+    @include flex(x, center, between);
+    padding-bottom: 16px;
+
+    .left-inner {
+      @include flex(x, center, start);
+
+      .text {
+        color: #1A2029;
+        font-size: 15px;
+        font-style: normal;
+        font-weight: 500;
+        line-height: 24px;
+      }
+
+      .tabs {
+        width: 240px;
+        margin-left: 16px;
+      }
+    }
+
+    .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: calc(100% - 72px);
+
+    .echart,
+    .empty {
+      width: 100%;
+      height: 100%;
+    }
+
+    .empty {
+      @include flex(x, center, center);
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.echart-card_view {
+  .tabs {
+    .n-tabs-tab--active {
+      .n-tabs-tab__label {
+        color: #2454FF;
+      }
+    }
+
+    .n-tabs-tab__label {
+      font-size: 12px;
+      color: #333333;
+    }
+  }
+
+  .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-label {
+      border: 0;
+      border-radius: 0;
+      background: #F2F3F5 !important;
+    }
+
+    .n-base-selection__state-border,
+    .n-base-selection__border {
+      border: 0 !important;
+    }
+
+    .n-base-selection .n-base-selection__border,
+    .n-base-selection .n-base-selection__state-border {
+      box-shadow: none;
+    }
+
+    .n-base-suffix__arrow {
+      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>

+ 275 - 0
src/views/control-Old/components/TheResultPanel.vue

@@ -0,0 +1,275 @@
+<script setup>
+import { computed } from 'vue';
+import { useMessage, NNumberAnimation } from 'naive-ui';
+import BaseTitle from './BaseTitle.vue';
+import { SvgIcon } from '@/components';
+
+const message = useMessage();
+const isVisibleBtn = defineModel();
+const modelSystemStatus = defineModel('system');
+const emit = defineEmits(['on-click', 'on-update']);
+
+const props = defineProps({
+  flowNum: {
+    type: Number,
+    default: 0
+  },
+  updateNum: {
+    type: Number,
+    default: 0
+  },
+  doseNum: {
+    type: Number,
+    default: 0
+  },
+  configurationStatus: {
+    type: Number,
+    default: 1
+  }
+});
+
+const systemSwitchType = computed(() => props.configurationStatus === 0 && modelSystemStatus.value === 1);
+
+const emitEvent = () => {
+  if ( props.configurationStatus === 1 ) {
+    return message.warning('当前组态未启用,无法投放');
+  } else {
+    emit('on-click');
+  }
+
+};
+const emitUpdate = async () => {
+  emit('on-update');
+};
+</script>
+
+<template>
+  <div>
+    <BaseTitle title="智能投加计算结果" type="second"></BaseTitle>
+    <div class="result-card_view">
+      <div class="update-message-box">
+        <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>
+      <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', {disable: configurationStatus === 1}]" @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>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+
+.result-card_view {
+  position: relative;
+  height: 190px;
+  padding: 0px 16px 24px 16px;
+
+  .update-message-box {
+    height: 37px;
+  }
+
+  .update-message {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    height: 100%;
+    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: 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%;
+
+    .result-inner {
+      border-radius: 8px;
+      @include flex(x, center, start);
+
+      .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);
+
+        .num {
+          color: #1A2029;
+          font-family: D-DIN-PRO-700-Bold;
+          font-size: 24px;
+          font-weight: bold;
+          line-height: 0;
+        }
+      }
+    }
+
+    & .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);
+      flex-flow: column;
+      width: 74px;
+      height: 74px;
+      padding: 14px;
+      border-radius: 100%;
+      background: #2454FF;
+      transition: all 0.3s;
+    }
+  }
+
+  .disable {
+    cursor: not-allowed;
+  }
+
+  // .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;
+    }
+  }
+}
+</style>

+ 311 - 486
src/views/control/MedicinalView.vue

@@ -11,6 +11,8 @@ import BaseInput from './components/BaseInput.vue';
 import TheResultPanel from './components/TheResultPanel.vue';
 import TheEchartPanel from './components/TheEchartPanel.vue';
 
+import NumberPanel from './NumberPanel.vue';
+
 const message = useMessage();
 const isVisibleBtn = ref(true);
 const isVisibleUpdateInfo = ref(false);
@@ -311,372 +313,113 @@ onMounted(async () => {
     // 0不允许  1允许
     systemStatus.value = data;
   });
+
 })
 
 </script>
 
 <template>
   <section class="flex items-start h-full">
-    <TheChatView leftTitle="智适应投药" :isChatSlot="false" :isFooter="false">
+    <TheChatView leftTitle="智适应碳源投加" :isChatSlot="false" :isFooter="false">
       <template #control>
-        <div class="control-container space-x-[12px]">
-          <div class="left-section">
-            <BaseTitle title="智能投加计算"></BaseTitle>
-            <n-scrollbar class="scrollbar" style="height: 100%;">
-              <div class="form-content">
-
-                <BaseCard title="选择加药泵">
-                  <BaseRadioCard v-model="baseSourceParams.numberBeng"></BaseRadioCard>
-                </BaseCard>
-
-                <BaseCard title="投加运行方式">
-                  <span class="status-bar">
-                    <i>{{ baseSourceParams.addType === 0 ? '启用智适应碳源投加' : '手动碳源投加' }}</i>
-                  </span>
-                </BaseCard>
-
-                <BaseCard title="选择池组手自动方式">
-                  <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">
-                          <span>1号池</span>
-                          <span>2号池</span>
-                        </p>
-                      </div>
-                      <div class="space-y-[12px]">
-                        <BaseChooseItem
-                          tab-key="auto"
-                          title="进水流量"
-                          unit="m³"
-                          isDouble
-                          @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: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="auto"
-                          title="进水COD"
-                          unit="mg/L"
-                          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: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
-                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
-                          ]"
-                        ></BaseChooseItem>
-                        
-                        <BaseChooseItem
-                          tab-key="auto"
-                          title="好氧池硝酸盐"
-                          unit="mg/L"
-                          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: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
-                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="auto"
-                          title="缺氧池硝酸盐"
-                          unit="mg/L"
-                          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: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="auto"
-                          title="缺氧池氨氮"
-                          unit="mg/L"
-                          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: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="auto"
-                          title="进水总氮"
-                          unit="mg/L"
-                          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: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
-                          ]"
-                        ></BaseChooseItem>
-                      </div>
-                    </n-tab-pane>
-
-                    <n-tab-pane :name="1" tab="1号池">
-                      <div class="panel-header_main">
-                        <p>设置数据来源</p>
-                        <p class="space-x-[20px] text-center">
-                          <span>1号池</span>
-                        </p>
-                      </div>
-                      <div class="space-y-[12px]">
-                        <BaseChooseItem
-                          tab-key="onePool"
-                          title="进水流量"
-                          unit="m³"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.onePool.jsLlType"
-                          v-model:value1="dataSourceParams.onePool.jsLlOne"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="onePool"
-                          title="进水COD"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.onePool.jsCodType"
-                          v-model:value1="dataSourceParams.onePool.jsCodOne"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
-                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="onePool"
-                          title="好氧池硝酸盐"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.onePool.hycXsyType"
-                          v-model:value1="dataSourceParams.onePool.hycXsyOne"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: waterConfigParams.hyXsyHYOne, value2: waterConfigParams.hyXsyHYTwo },
-                            { label: '预测', value1: waterConfigParams.hyXsyYCOne, value2: waterConfigParams.hyXsyYCTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="onePool"
-                          title="缺氧池硝酸盐"
-                          unit="mg/L"
-                          @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: waterConfigParams.qyXsyHYOne, value2: waterConfigParams.qyXsyHYTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="onePool"
-                          title="缺氧池氨氮"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.onePool.qycAdType"
-                          v-model:value1="dataSourceParams.onePool.qycAdOne"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: waterConfigParams.qyAdHYOne, value2: waterConfigParams.qyAdHYTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="onePool"
-                          title="进水总氮"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.onePool.jsTnType"
-                          v-model:value1="dataSourceParams.onePool.jsTnOne"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: waterConfigParams.jsTnYB, value2: waterConfigParams.jsTnYB }
-                          ]"
-                        ></BaseChooseItem>
-                      </div>
-                    </n-tab-pane>
-
-                    <n-tab-pane :name="2" tab="2号池">
-                      <div class="panel-header_main">
-                        <p>设置数据来源</p>
-                        <p class="space-x-[20px] text-center">
-                          <span>2号池</span>
-                        </p>
-                      </div>
-                      <div class="space-y-[12px]">
-                        <BaseChooseItem
-                          tab-key="twoPool"
-                          title="进水流量"
-                          unit="m³"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.twoPool.jsLlType"
-                          v-model:value1="dataSourceParams.twoPool.jsLlTwo"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: waterConfigParams.jslYB, value2: waterConfigParams.jslYB }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="twoPool"
-                          title="进水COD"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.twoPool.jsCodType"
-                          v-model:value1="dataSourceParams.twoPool.jsCodTwo"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: waterConfigParams.jsCodYB, value2: waterConfigParams.jsCodYB },
-                            { label: '化验', value1: waterConfigParams.jsCodHY, value2: waterConfigParams.jsCodHY }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="twoPool"
-                          title="好氧池硝酸盐"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.twoPool.hycXsyType"
-                          v-model:value1="dataSourceParams.twoPool.hycXsyTwo"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: waterConfigParams.hyXsyHYTwo },
-                            { label: '预测', value1: waterConfigParams.hyXsyYCTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="twoPool"
-                          title="缺氧池硝酸盐"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.twoPool.qycYxyType"
-                          v-model:value1="dataSourceParams.twoPool.qycYxyTwo"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: waterConfigParams.qyXsyHYTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="twoPool"
-                          title="缺氧池氨氮"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.twoPool.qycAdType"
-                          v-model:value1="dataSourceParams.twoPool.qycAdTwo"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '化验', value1: waterConfigParams.qyAdHYTwo }
-                          ]"
-                        ></BaseChooseItem>
-                        <BaseChooseItem
-                          tab-key="twoPool"
-                          title="进水总氮"
-                          unit="mg/L"
-                          @on-update="handleMedicateAmount"
-                          v-model:type="dataSourceParams.twoPool.jsTnType"
-                          v-model:value1="dataSourceParams.twoPool.jsTnTwo"
-                          :btn-group="[
-                            { label: '手动', value1: '', value2: '' },
-                            { label: '仪表', value1: waterConfigParams.jsTnYB }
-                          ]"
-                        ></BaseChooseItem>
-                      </div>
-                    </n-tab-pane>
-
-                    <n-tab-pane :name="3" tab="人工投放">
-                      <div class="panel-header_main">
-                        <p>设置数据来源</p>
-                        <p class="space-x-[20px] text-center">
-                          <span>人工投放</span>
-                        </p>
-                      </div>
-                      <div class="w-full flex items-center justify-between">
-                        <span>人工投放:</span>
-                        <div class="w-[200px]">
-                          <BaseInput
-                            :isCloseIcon="false"
-                            v-model="dataSourceParams.worker.medicineAmount"
-                            @on-blur="handleMedicateAmount"
-                          ></BaseInput>
-                        </div>
-                      </div>
-                    </n-tab-pane>
-                  </n-tabs>
-                </BaseCard>
-                <BaseCard title="设定参数系数" style="margin: 0" tips="建议使用默认值,非必要不修改" v-show="tabActiveKey !== 'worker'">
-                  <template #titleRight>
-                    <div>
-                      <div class="flex items-center space-x-[4px] cursor-pointer text-[#2454FF] text-[13px]"
-                        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" />
-                          <path d="M3.66663 8.90663V11.3333H6.10569L13 4.43603L10.565 2L3.66663 8.90663Z"
-                            stroke="#2454FF" stroke-linejoin="round" />
-                        </svg>
-                        <span>编辑</span>
-                      </div>
-                      <ul class="flex items-center text-[13px] space-x-[8px] cursor-pointer" v-show="!isVisibleBtn">
-                        <li class="cursor-pointer" @click="onEditConfirm" style="color: #2454FF">确定</li>
-                        <li class="cursor-pointer text-[#B0B7C0]" @click="onEditCancel">取消</li>
-                      </ul>
+        <div class="control-container space-y-[16px]">
+          <div class="arg-section">
+
+            <div class="left-card space-y-[16px]">
+              <BaseTitle title="智能投加计算"></BaseTitle>
+              <ul class="paramter-list">
+                <li class="paramter-item">
+                  <span class="label">加药设备</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">进水流量</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">进水COD</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">进水总氮</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">好氧池硝酸盐</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">好氧池氨氮</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">缺氧池硝酸盐</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+                <li class="line"></li>
+                <li class="paramter-item">
+                  <span class="label">缺氧池氨氮</span>
+                  <span class="value">1号加药泵 / 1号池</span>
+                </li>
+              </ul>
+            </div>
+
+            <div class="right-card">
+              <div class="header">
+                <h4 class="title">智能系统参数</h4>
+                <ul class="btn-list space-x-[8px]">
+                  <li class="item">
+                    <span>参数设置</span>
+                  </li>
+                  <li class="item">
+                    <span>系统告警</span>
+                    <span class="waring-circle-icon"></span>
+                  </li>
+                </ul>
+              </div>
+              <div class="result-content">
+                <div class="number_card space-x-[72px]">
+                  <NumberPanel></NumberPanel>
+                  <!-- <div class="left">
+                    <div class="animate-card left-animate">
+                      <span class="number">5.9</span>
                     </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">
-                        {{ item.value }}
-                        {{ index === 0 ? 'mg/L' : '' }}
+                    <span class="sub-title">智能控制系数</span>
+                  </div> -->
+                  <div class="middel"></div>
+                  <div class="right">
+                    <div class="animate-card right-animate">
+                      <span class="number">
+                        <i>122</i>
+                        <span>mg/L</span>
                       </span>
-                      <div style="width: 140px;" v-show="!isVisibleBtn">
-                        <BaseInput :unit="index === 0 ? 'mg/L' : ''" size='small' :isNeedFlotBtn="false"
-                          v-model="baseSourceParams[item.key]" isCenter placeholder="" :readonly="index === columnData.length - 1"></BaseInput>
-                      </div>
-                    </li>
-                  </ul>
-                </BaseCard>
+                    </div>
+                    <span class="sub-title">硝酸盐智能设定</span>
+                  </div>
+                </div>
+                <div class="progress_card space-y-[8px]">
+                  <span class="time">模型更新时间: 2025-04-11 12:11:11</span>
+                  <div class="progress"></div>
+                  <span class="tips">Libra智能投药中...</span>
+                </div>
+                <div class="play_card">
+                  <div class="play-btn space-x-[12px]">
+                    <span class="icon"></span>
+                    <span>暂停</span>
+                  </div>
+                </div>
               </div>
-            </n-scrollbar>
+            </div>
+
           </div>
-          <div class="right-section">
-            <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 class="echart-section">
+
           </div>
         </div>
       </template>
@@ -686,157 +429,239 @@ onMounted(async () => {
 
 <style lang="scss" scoped>
 .control-container {
-  @include flex(x, start, start);
+  // @include flex(x, start, start);
   height: 100%;
 
-  .left-section {
-    display: flex;
-    flex-flow: column;
-    flex-shrink: 0;
-    width: 440px;
-    height: 100%;
+  .arg-section {
+    @include flex(x, start, start);
+    height: 57%;
+    padding: 16px 14px;
+    border: 1px solid #fff;
     border-radius: 10px;
-    background: #fff;
-
-    .status-bar {
-      display: inline-block;
-      padding: 4px 14px;
-      margin-left: 20px;
-      border-top: 0.5px solid rgba(212, 241, 255, 0.00);
-      border-bottom: 0.5px solid rgba(212, 241, 255, 0.00);
-      background: linear-gradient(90deg, rgba(240, 250, 255, 0.00) 0%, #E9F8FF 27.27%, rgba(240, 250, 255, 0.00) 100%);
-
-      i {
-        line-height: 24px;
-        background: linear-gradient(92deg, #5ABBF2 12.24%, #2454FF 63.2%);
-        background-clip: text;
-        -webkit-background-clip: text;
-        -webkit-text-fill-color: transparent;
-        font-weight: bold;
-      }
-    }
+    background: url(@/assets/images/control/bg-top.png) left center no-repeat;
+    background-size: 878px 100% ;
 
-    .scrollbar {
+    .left-card {      
+      flex-shrink: 0;  
+      width: 338px;
       height: 100%;
-    }
-
-    .form-content {
-      padding: 0 16px 24px 16px;
-    }
-
-    .panel-header_main {
-      @include flex(x, center, between);
-      margin-bottom: 12px;
-      color: #86909C;
-      span {
-        display: inline-block;
-        width: 60px;
+      display: flex;
+      flex-flow: column;
+
+      .paramter-list {
+        flex: 1;
+        display: flex;
+        justify-content: space-between;
+        flex-flow: column;
+        padding: 14px;
+        border: 1px solid #fff;
+        border-radius: 10px;
+        background: rgba(255, 255, 255, 0.20);
+        backdrop-filter: blur(5px);
+        font-size: 13px;
+        color: #585858;
+        line-height: 22px;
+
+        .line {
+          width: 100%;
+          height: 1px;
+          background: #EEE;
+        }
+        .paramter-item {
+          @include flex(x, center, between);
+          .value {
+            color: #1A2029;
+            font-weight: bold;
+          }
+        }
       }
     }
 
-  }
-
-  .data-source-list {
-    .data-soruce-item {
-      @include flex(x, center, between);
+    .right-card {
+      flex: 1;
+      height: 100%;
 
-      .inp-inner {
-        width: 112px;
+      .header {
+        height: 40px;
+        position: relative;
+        text-align: center;
+        .title {
+          color: #1A2029;
+          font-size: 15px;
+          font-weight: bold;
+          line-height: 24px;
+        }
+        .btn-list {
+          position: absolute;
+          top: 0;
+          right: 0;
+          @include flex(x, center, center);
+          color: #1A2029;
+          font-size: 12px;
+          font-weight: 400;
+
+          .item {
+            position: relative;
+            @include flex(x, center, center);
+            width: 68px;
+            height: 28px;
+            border-radius: 4px;
+            background: #fff;
+            cursor: pointer;
+
+            .waring-circle-icon {
+              position: absolute;
+              top: 2px;
+              right: 2px;
+              width: 5px;
+              height: 5px;
+              border-radius: 50%;
+              background: #FF4920;
+            }
+          }
+        }
       }
 
-      .unit {
-        font-family: "D-DIN-PRO-700-Bold";
-        font-weight: bold;
-        font-size: 12px;
-        color: #333;
+      .result-content {
+        @include flex(y, center, around);
+        height: calc(100% - 40px);
+        padding: 0px 86px 0 86px;
+        // background: #0059FF;
+        background-clip: padding-box;
+        
+        .number_card {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          .left, .right {
+            text-align: center;
+            .left-animate {
+              background: url("@/assets/images/control/number-card-left.png") center center no-repeat;
+              .number {
+                right: 30px;
+              }
+            }
+            .right-animate {
+              background: url("@/assets/images/control/number-card-right.png") center center no-repeat;
+              .number {
+                @include flex(y, start, center);
+                left: 30px;
+                span {
+                  color: #000;
+                  font-family: "PingFang SC";
+                  font-size: 12px;
+                  font-style: normal;
+                  font-weight: 400;
+                  line-height: normal;
+                }
+              }
+            }
+            .animate-card {
+              position: relative;
+              width: 194px;
+              height: 164px;
+              // background-size: 140px 100%;
+              background-size: cover;
+
+              .number {
+                position: absolute;
+                top: 30px;
+                color: #1A2029;
+                // font-family: "DIN-RegularAlternate";
+                font-family: "D-DIN-PRO-700-Bold";
+                font-size: 52px;
+                font-style: normal;
+                font-weight: 700;
+              }
+            }
+            .sub-title {
+              color: #333;
+              font-family: "PingFang SC";
+              font-size: 12px;
+              font-style: normal;
+              font-weight: 400;
+              line-height: 20px;
+            }
+          }
+
+          .middel {
+            width: 186px;
+            height: 186px;
+            background: url("@/assets/images/control/number-card-middle.svg") center center no-repeat,
+            url("@/assets/images/control/number-card-middle2.svg") center bottom no-repeat;
+            background-size: contain;
+          }
+        }
+
+        .progress_card {
+          @include flex(y, center, center);
+          padding-top: 6px;
+          text-align: center;
+
+          .time {
+            color: #F6A52C;
+            font-size: 10px;
+            font-style: normal;
+            font-weight: 400;
+            line-height: 16px;
+          }
+
+          .tips {
+            text-align: center;
+            font-size: 12px;
+            font-weight: 500;
+            line-height: 24px;
+            background: linear-gradient(90deg, #0059FF 0%, #29C2FA 100%);
+            background-clip: text;
+            -webkit-background-clip: text;
+            -webkit-text-fill-color: transparent;
+          }
+
+          .progress {
+            width: 165px;
+            height: 10px;
+            background: red;
+          }
+        }
+
+        .play_card {
+          @include flex(x, center, center);
+          .play-btn {
+            @include flex(x, center, center);
+            // width: 136px;
+            // height: 64px;
+            width: 120px;
+            height: 48px;
+            background: url("@/assets/images/control/bg-play-btn.png") center center no-repeat;
+            background-size: cover;
+            color: #2454FF;
+            font-size: 14px;
+            font-weight: 500;
+            line-height: 16px;
+            cursor: pointer;
+            .icon {
+              display: block;
+              width: 28px;
+              height: 28px;
+              background: url("@/assets/images/control/icon-end.svg") center center no-repeat;
+              background-size: cover;
+            }
+          }
+        }
       }
     }
   }
-}
-
-.right-section {
-  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%);
-  // }
-
-  // .bottom {
-  //   height: 100%;
-  //   background: pink;
-  // }
-
-}
-
-
-// 通用区域的样式
-.btn {
-  width: 80px;
-  height: 32px;
-  border-radius: 4px;
-  border: 1px solid #D3D7DD;
-  text-align: center;
-  font-size: 14px;
-  line-height: 32px;
-  color: #1A2029;
-}
-
-.btn-primary {
-  border: 0;
-  background: var(--Linear, linear-gradient(270deg, #3BD6E3 0%, #019AFE 100%));
-  font-weight: bold;
-  color: #fff;
-}
 
-.btn-info {
-  width: 44px;
-  height: 28px;
-  border-radius: 4px;
-  border: 1px solid #D3D7DD;
-  background: #fff;
-  font-size: 12px;
-  text-align: center;
-  line-height: 28px;
-  color: #1A2029;
-  cursor: pointer;
-}
-
-.btn-info_active {
-  color: #2454FF;
-  border: 1px solid #2454FF;
-  background: #EBF0FF;
-}
+  .echart-section {
+    height: 43%;
+    background: red;
+  }
 
-.radio {
-  display: block;
-  width: 12px;
-  height: 12px;
-  border-radius: 100%;
-  border: 1px solid #ccc;
-  cursor: pointer;
 }
 
-.radio_big {
-  width: 16px;
-  height: 16px;
-}
 
-.radio-active {
-  transition: all .1s;
-  border: 3px solid #2454FF;
-}
 
-.radio_big.radio-active {
-  border: 4px solid #2454FF;
-}
 </style>
 
 <style lang="scss">

+ 32 - 0
src/views/control/NumberPanel.vue

@@ -0,0 +1,32 @@
+<script setup>
+
+</script>
+
+<template>
+  <div class="number-panel-container space-y-[6px]">
+    <div class="number-panel-inner direction-left"></div>
+    <div>
+      <span>智能控制系数</span>
+    </div>
+  </div>
+</template>
+
+
+<style lang="scss">
+.number-panel-container {
+  width: 194px;
+  background: #ccc;
+
+  .number-panel-inner {
+    width: 140px;
+    height: 164px;
+  }
+
+  .direction-left {
+    background: url("@/assets/images/control/line-left-1.svg") left center no-repeat,
+    url("@/assets/images/control/line-left-2.svg") right bottom no-repeat,
+    url("@/assets/images/control/line-left-3.svg") right bottom no-repeat;
+    background: red;
+  }
+}
+</style>

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

@@ -16,16 +16,19 @@ defineProps({
 <template>
   <div class="header">
     <div class="title">
-      <svg xmlns="http://www.w3.org/2000/svg" width="8" height="24" viewBox="0 0 8 24" fill="none" v-if="type === 'first'">
+      <svg xmlns="http://www.w3.org/2000/svg" width="8" height="24" viewBox="0 0 8 24" fill="none"
+        v-if="type === 'first'">
         <path d="M0 6.86197V3.24507L4.21274 0H7.11111L4.45741 24H1.35684L4.01053 3.85352L0 6.86197Z" fill="#2454FF" />
       </svg>
       <svg xmlns="http://www.w3.org/2000/svg" width="12" height="24" viewBox="0 0 12 24" fill="none" v-else>
-        <path d="M7.74366 10.4333C8.43696 9.03333 8.78361 7.76667 8.78361 6.6V6.1C8.78361 5.06667 8.58552 4.26667 8.16459 3.7C7.74366 3.13333 7.19892 2.83333 6.48086 2.83333C5.7628 2.83333 5.21807 3.1 4.8219 3.66667C4.42573 4.23333 4.22764 5.1 4.22764 6.23333V7.16667H1.94966V6.1C1.94966 4.26667 2.37059 2.8 3.18769 1.66667C4.02956 0.566667 5.14379 0 6.53039 0C7.42177 0 8.23887 0.266667 8.93217 0.8C9.62547 1.3 10.1702 2.03333 10.5416 2.96667C10.9378 3.9 11.1111 4.93333 11.1111 6.03333V6.6C11.1111 7.6 10.9873 8.5 10.7149 9.4C10.4673 10.2667 10.0959 11.1667 9.57595 12.1L2.94652 21.1667H9.16145V24H0V21.6L7.74366 10.4333Z" fill="#2454FF"/>
+        <path
+          d="M7.74366 10.4333C8.43696 9.03333 8.78361 7.76667 8.78361 6.6V6.1C8.78361 5.06667 8.58552 4.26667 8.16459 3.7C7.74366 3.13333 7.19892 2.83333 6.48086 2.83333C5.7628 2.83333 5.21807 3.1 4.8219 3.66667C4.42573 4.23333 4.22764 5.1 4.22764 6.23333V7.16667H1.94966V6.1C1.94966 4.26667 2.37059 2.8 3.18769 1.66667C4.02956 0.566667 5.14379 0 6.53039 0C7.42177 0 8.23887 0.266667 8.93217 0.8C9.62547 1.3 10.1702 2.03333 10.5416 2.96667C10.9378 3.9 11.1111 4.93333 11.1111 6.03333V6.6C11.1111 7.6 10.9873 8.5 10.7149 9.4C10.4673 10.2667 10.0959 11.1667 9.57595 12.1L2.94652 21.1667H9.16145V24H0V21.6L7.74366 10.4333Z"
+          fill="#2454FF" />
       </svg>
       <span class="text">{{ title }}</span>
     </div>
     <div class="btn-group space-x-[8px]">
-      <slot name="right"/>
+      <slot name="right" />
     </div>
   </div>
 </template>
@@ -33,9 +36,6 @@ defineProps({
 <style lang="scss" scoped>
 .header {
   @include flex(x, center, between);
-  height: 75px;
-  flex-shrink: 1;
-  padding: 24px 16px 24px 16px;
   color: #1A2029;
 
   .title {