Browse Source

feat: 碳源投加

sunxiao 7 months ago
parent
commit
e56414061c

BIN
src/assets/fonts/D-DIN-PRO-700-Bold.otf


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


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


BIN
src/assets/images/control/bg-num-one.png


BIN
src/assets/images/control/img-result-card.png


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

@@ -8,6 +8,14 @@
   font-style: normal;
 }
 
+@font-face {
+  font-display: swap;
+  font-family: 'D-DIN-PRO-700-Bold';
+  src: url('@/assets/fonts/D-DIN-PRO-700-Bold.otf') format('opentype');
+  font-weight: normal;
+  font-style: normal;
+}
+
 // chat 布局相关
 .chat-ask_icon,
 .chat-answer_icon {

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

@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" >
+  <g clip-path="url(#clip0_2566_2833)">
+    <path d="M8.00004 14.6663C11.6819 14.6663 14.6667 11.6816 14.6667 7.99967C14.6667 4.31777 11.6819 1.33301 8.00004 1.33301C4.31814 1.33301 1.33337 4.31777 1.33337 7.99967C1.33337 11.6816 4.31814 14.6663 8.00004 14.6663Z" stroke-width="1.33333" stroke-linejoin="round"/>
+    <path d="M9.88561 6.11426L6.11438 9.88549" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
+    <path d="M6.11438 6.11426L9.88561 9.88549" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
+  </g>
+  <defs>
+    <clipPath id="clip0_2566_2833">
+      <rect width="16" height="16" />
+    </clipPath>
+  </defs>
+</svg>

+ 11 - 0
src/assets/svgs/control/icon-confirm.svg

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" >
+  <g clip-path="url(#clip0_2566_2837)">
+    <path d="M8.00004 14.6663C9.84097 14.6663 11.5076 13.9201 12.7141 12.7137C13.9205 11.5073 14.6667 9.84061 14.6667 7.99967C14.6667 6.15874 13.9205 4.49207 12.7141 3.28563C11.5076 2.0792 9.84097 1.33301 8.00004 1.33301C6.15911 1.33301 4.49244 2.0792 3.28599 3.28563C2.07957 4.49207 1.33337 6.15874 1.33337 7.99967C1.33337 9.84061 2.07957 11.5073 3.28599 12.7137C4.49244 13.9201 6.15911 14.6663 8.00004 14.6663Z" stroke-width="1.33333" stroke-linejoin="round"/>
+    <path d="M5.33337 8L7.33337 10L11.3334 6" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
+  </g>
+  <defs>
+    <clipPath id="clip0_2566_2837">
+      <rect width="16" height="16" fill="white"/>
+    </clipPath>
+  </defs>
+</svg>

File diff suppressed because it is too large
+ 2 - 0
src/assets/svgs/control/icon-pump.svg


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

@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
+  <g clip-path="url(#clip0_2566_2950)">
+    <path d="M15.9 23.9996C15.4 23.9996 15.1 23.6996 15 23.2996L11.5 12.6996C11.4 12.2996 11.5 11.8996 11.7 11.6996C12 11.3996 12.4 11.2996 12.7 11.4996L23.3 14.9996C23.7 15.0996 24 15.4996 24 15.8996C24 16.2996 23.8 16.6996 23.4 16.8996L18.8 18.8996L16.8 23.4996C16.7 23.7996 16.3 23.9996 15.9 23.9996ZM14 13.9996L16.1 20.1996L17.2 17.6996C17.3 17.4996 17.5 17.2996 17.7 17.1996L20.2 16.0996L14 13.9996Z" fill="white"/>
+    <path d="M12 24C8.8 24 5.8 22.8 3.5 20.5C1.2 18.2 0 15.2 0 12C0 8.8 1.2 5.8 3.5 3.5C5.8 1.2 8.8 0 12 0C15.2 0 18.2 1.2 20.5 3.5C22.8 5.8 24 8.8 24 12C24 12.6 23.6 13 23 13C22.4 13 22 12.6 22 12C22 9.3 21 6.8 19.1 4.9C17.2 3 14.7 2 12 2C9.3 2 6.8 3 4.9 4.9C3 6.8 2 9.3 2 12C2 14.7 3 17.2 4.9 19.1C6.8 21 9.3 22 12 22C12.6 22 13 22.4 13 23C13 23.6 12.6 24 12 24Z" fill="white"/>
+    <path d="M9.8 17.5C9.7 17.5 9.5 17.5 9.4 17.4C8.8 17.1 8.3 16.7 7.8 16.2C6.6 15.1 6 13.6 6 12C6 10.4 6.6 8.9 7.8 7.8C9 6.7 10.4 6 12 6C13.6 6 15.1 6.6 16.2 7.8C16.7 8.3 17.1 8.8 17.4 9.4C17.6 9.9 17.4 10.5 16.9 10.7C16.4 10.9 15.8 10.7 15.6 10.2C15.4 9.8 15.1 9.4 14.8 9.1C13.3 7.6 10.7 7.6 9.1 9.1C8.4 9.9 8 10.9 8 12C8 13.1 8.4 14.1 9.2 14.8C9.5 15.1 9.9 15.4 10.3 15.6C10.8 15.8 11 16.4 10.8 16.9C10.5 17.3 10.2 17.5 9.8 17.5Z" fill="white"/>
+  </g>
+  <defs>
+    <clipPath id="clip0_2566_2950">
+      <rect width="24" height="24" fill="white"/>
+    </clipPath>
+  </defs>
+</svg>

+ 17 - 0
src/assets/svgs/control/icon-tips.svg

@@ -0,0 +1,17 @@
+
+        <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
+          <g clip-path="url(#clip0_2602_1562)">
+            <path
+              d="M6 11C7.3807 11 8.6307 10.4404 9.53553 9.53553C10.4404 8.6307 11 7.3807 11 6C11 4.6193 10.4404 3.3693 9.53553 2.46446C8.6307 1.55964 7.3807 1 6 1C4.6193 1 3.3693 1.55964 2.46446 2.46446C1.55964 3.3693 1 4.6193 1 6C1 7.3807 1.55964 8.6307 2.46446 9.53553C3.3693 10.4404 4.6193 11 6 11Z"
+              stroke="#9198A1" stroke-linejoin="round" />
+            <path fill-rule="evenodd" clip-rule="evenodd"
+              d="M6 9.25C6.34518 9.25 6.625 8.97018 6.625 8.625C6.625 8.27983 6.34518 8 6 8C5.65483 8 5.375 8.27983 5.375 8.625C5.375 8.97018 5.65483 9.25 6 9.25Z"
+              fill="#9198A1" />
+            <path d="M6 3V7" stroke="#9198A1" stroke-linecap="round" stroke-linejoin="round" />
+          </g>
+          <defs>
+            <clipPath id="clip0_2602_1562">
+              <rect width="12" height="12" fill="white" />
+            </clipPath>
+          </defs>
+        </svg>

+ 32 - 13
src/components/Layout/TheChatView.vue

@@ -11,6 +11,14 @@ defineProps({
   isBackBtn: {
     type: Boolean,
     default: false
+  },
+  leftTitle: {
+    type: String,
+    default: ''
+  },
+  isChatSlot: {
+    type: Boolean,
+    default: true
   }
 })
 
@@ -38,18 +46,22 @@ defineExpose({ targetScrollDom });
       <div class="chat-header flex items-center justify-between py-[24px] px-[18px] ">
         <div class="left_inner" @click="handleClickBack">
           <span v-if="isBackBtn" class="back-btn"></span>
+          <span v-if="leftTitle" class="left_title">{{ leftTitle }}</span>
         </div>
         <div class="right_inner flex items-center space-x-[16px]">
           <UserTop></UserTop>
         </div>
       </div>
-      <main class="chat-main flex flex-1 flex-col justify-between">
+      <main class="chat-main flex flex-1 flex-col justify-between" v-if="isChatSlot">
         <div class="chat-scroll" ref="targetScrollDom">
           <div class="w-[900px] m-auto ">
             <slot></slot>
           </div>
         </div>
       </main>
+      <main class="control-main" v-if="!isChatSlot">
+        <slot name="control"></slot>
+      </main>
       <footer class="chat-footer relative w-[900px] m-auto pb-[30px]" v-if="isFooter">
         <slot name="footer" />
       </footer>
@@ -66,24 +78,25 @@ defineExpose({ targetScrollDom });
     .left_inner {
       display: flex;
       align-items: center;
-      padding: 6px;
-      border-radius: 8px;
-      cursor: pointer;
 
-      &:hover {
-        background: #dceffe;
-      }
       .back-btn {
         display: inline-block;
-        width: 30px;
-        height: 30px;
+        width: 36px;
+        height: 36px;
+        border-radius: 8px;
         background: url("@/assets/images/chat/back-btn.png") no-repeat;
         background-size: cover;
+        cursor: pointer;
 
-        // &:hover {
-        //   background: url("@/assets/images/chat/back-btn-active.png") no-repeat;
-        //   background-size: cover;
-        // }
+        &:hover {
+          background: #dceffe url("@/assets/images/chat/back-btn.png") no-repeat;
+          background-size: cover;
+        }
+      }
+      .left_title {
+        font-size: 20px;
+        font-weight: bold;
+        color: #1A2029;
       }
     }
   }
@@ -92,6 +105,12 @@ defineExpose({ targetScrollDom });
     border: 1px solid #fff;
     background: linear-gradient(180deg, rgba(238, 253, 255, 0.5) 0%, rgba(231, 243, 252, 0.5) 100%);
 
+    .control-main {
+      // flex: 1;
+      height: calc(100vh - 124px);
+      padding: 0 24px 24px 24px;
+    }
+
     .chat-main {
       min-height: calc(100% - 310px);
       color: #1A2029;

+ 134 - 0
src/components/Layout/TheControlView.vue

@@ -0,0 +1,134 @@
+<script setup>
+import { ref, unref, computed } from 'vue';
+
+import UserTop from './userTop.vue';
+
+defineProps({
+  isFooter: {
+    type: Boolean,
+    default: true
+  },
+  isBackBtn: {
+    type: Boolean,
+    default: false
+  },
+  leftTitle: {
+    type: String,
+    default: ''
+  },
+  isControlSlot: {
+    type: Boolean,
+    default: true
+  }
+})
+
+const emit = defineEmits(['onClickBack'])
+
+const targetScrollDom = ref(null);
+const voiceSwitchStatus = ref(false);
+
+const voiceName = computed(() => unref(voiceSwitchStatus) ? 'tool-voice-close' : 'tool-voice-open')
+
+const changeVoiceStatus = () => {
+  voiceSwitchStatus.value = !voiceSwitchStatus.value;
+}
+
+const handleClickBack = () => {
+  emit("onClickBack");
+}
+
+defineExpose({ targetScrollDom });
+</script>
+
+<template>
+  <div class="flex-1 h-full chat-container">
+    <div class="chat-wrapper w-full h-full flex flex-col rounded-[20px]">
+      <div class="chat-header flex items-center justify-between py-[24px] px-[18px] ">
+        <div class="left_inner" @click="handleClickBack">
+          <span v-if="isBackBtn" class="back-btn"></span>
+          <span v-if="leftTitle" class="left_title">{{ leftTitle }}</span>
+        </div>
+        <div class="right_inner flex items-center space-x-[16px]">
+          <UserTop></UserTop>
+        </div>
+      </div>
+      <main class="chat-main flex flex-1 flex-col justify-between" v-if="isChatSlot">
+        <div class="chat-scroll" ref="targetScrollDom">
+          <div class="w-[900px] m-auto ">
+            <slot></slot>
+          </div>
+        </div>
+      </main>
+      <main class="control-main" v-if="!isChatSlot">
+        <slot name="control"></slot>
+      </main>
+      <footer class="chat-footer relative w-[900px] m-auto pb-[30px]" v-if="isFooter">
+        <slot name="footer" />
+      </footer>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.chat-container {
+  padding: 20px 20px 20px 0;
+  overflow: hidden;
+
+  .chat-header {
+    .left_inner {
+      display: flex;
+      align-items: center;
+      border-radius: 8px;
+
+      .back-btn {
+        display: inline-block;
+        width: 30px;
+        height: 30px;
+        padding: 6px;
+        background: url("@/assets/images/chat/back-btn.png") no-repeat;
+        background-size: cover;
+        cursor: pointer;
+
+        &:hover {
+          background: #dceffe;
+        }
+      }
+      .left_title {
+        font-size: 20px;
+        font-weight: bold;
+        color: #1A2029;
+      }
+    }
+  }
+
+  .chat-wrapper {
+    border: 1px solid #fff;
+    background: linear-gradient(180deg, rgba(238, 253, 255, 0.5) 0%, rgba(231, 243, 252, 0.5) 100%);
+
+    .control-main {
+      flex: 1;
+      padding: 0 24px 24px 24px;
+    }
+
+    .chat-main {
+      min-height: calc(100% - 310px);
+      color: #1A2029;
+
+      .chat-scroll {
+        overflow-x: hidden;
+        overflow-y: auto;
+
+        &::-webkit-scrollbar-thumb,
+        &::-webkit-scrollbar-track {
+          background: transparent;
+        }
+
+        &::-webkit-scrollbar {
+          width: 0px;
+          overflow-y: scroll;
+        }
+      }
+    }
+  }
+}
+</style>

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

@@ -76,7 +76,7 @@ const menuOptions = [
       {
         label: '智能投药',
         icon: renderChildrenIcon({ name: 'menu-cost-drug' }),
-        key: 'normal-1',
+        key: 'medicate',
       },
       {
         label: '精准曝气',

+ 5 - 1
src/components/SvgIcon/index.vue

@@ -1,7 +1,7 @@
 <template>
   <!-- <n-icon :size="size" :color="color"> -->
     <svg aria-hidden="true" :width="size" :height="size" class="svg-icon">
-      <use :xlink:href="symbolId" fill="currentColor"/>
+      <use :xlink:href="symbolId" :fill="fillColor"/>
     </svg>
   <!-- </n-icon> -->
 </template>
@@ -23,6 +23,10 @@ const props = defineProps({
     type: String,
     default: '#fff'
   },
+  fillColor: {
+    type: String,
+    default: ''
+  },
   size: {
     type: String,
     default: '1em'

+ 2 - 0
src/components/index.js

@@ -5,6 +5,7 @@ import BaseCard from './BaseCard';
 import BaseTable from './BaseTable';
 
 import TheChatView from './Layout/TheChatView';
+import TheControlView from './Layout/TheControlView';
 import TheLogo from './Layout/TheLogo';
 import TheMenu from './Layout/TheMenu';
 import ThePublicLayout from './Layout/ThePublicLayout';
@@ -34,6 +35,7 @@ export {
   BaseTable,
 
   TheChatView,
+  TheControlView,
   TheLogo,
   TheMenu,
   ThePublicLayout,

+ 9 - 1
src/router/index.js

@@ -115,7 +115,15 @@ const constantRouterMap = [
         meta: {
           title: '智能办公'
         }
-      }
+      },
+      {
+        path: 'medicate',
+        name: 'MedicateView',
+        component: () => import('@/views/control/MedicateView.vue'),
+        meta: {
+          title: '智能投药'
+        }
+      },
     ]
   },
   {

+ 1 - 1
src/utils/request.ts

@@ -132,5 +132,5 @@ export const streamHttp  = new Request({
 
 export default new Request({
   baseURL,
-  timeout: 10 * 1000
+  timeout: 20 * 1000
 });

+ 1 - 0
src/views/answer/AnswerView.vue

@@ -82,6 +82,7 @@ const onRegenerate = async ({ question, realQuestion, tools }) => {
       module: 0,
       isStrong: Number(unref(switchActive)),
       tools,
+      prompt: null
       // TODO: 后续大概率需要删除
       // topP: 0.9,
       // temperature: 0.7

+ 326 - 0
src/views/control/MedicateView.vue

@@ -0,0 +1,326 @@
+<script setup>
+import { ref } from 'vue';
+import { NScrollbar } from 'naive-ui';
+import { TheChatView, SvgIcon } from '@/components';
+
+import BaseButton from './components/BaseButton.vue';
+import BaseTitle from './components/BaseTitle.vue';
+import BaseRadioCard from './components/BaseRadioCard.vue';
+import BaseCard from './components/BaseCard.vue';
+import BaseRadioGroup from './components/BaseRadioGroup.vue';
+import BaseChooseItem from './components/BaseChooseItem.vue';
+import BaseInput from './components/BaseInput.vue';
+
+import TheResultCard from './components/TheResultCard.vue';
+import EchartPanel from './components/EchartPanel.vue';
+
+const isVisibleBtn = ref(true);
+
+</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="智能投加计算">
+              <template #right>
+                <BaseButton>重置</BaseButton>
+                <BaseButton type="gradual">投加计算</BaseButton>
+              </template>
+            </BaseTitle>
+
+            <n-scrollbar class="scrollbar" style="height: 100%;">
+              <div class="form-content">
+                <BaseCard title="选择加药泵">
+                  <BaseRadioCard></BaseRadioCard>
+                </BaseCard>
+
+                <BaseCard title="选择运行方式">
+                  <BaseRadioGroup :data="['自动', '手动']"></BaseRadioGroup>
+                </BaseCard>
+
+                <BaseCard title="选择池组手自动方式">
+                  <BaseRadioGroup :data="['自动', '手动']"></BaseRadioGroup>
+                </BaseCard>
+
+                <BaseCard title="确定智适应碳源设置">
+                  <BaseRadioGroup :data="['1号池', '2号池']"></BaseRadioGroup>
+                </BaseCard>
+                
+                <BaseCard title="设定数据来源及取数方式">
+                  <div class="space-y-[12px]">
+                    <BaseChooseItem
+                      title="进水流量"
+                      :btn-group="[
+                        { label: '手动', key: 'hand' }
+                      ]"
+                      unit="m³"
+                    ></BaseChooseItem>
+  
+                    <BaseChooseItem
+                      title="进水COD"
+                      :btn-group="[
+                        { label: '手动', key: 'hand' },
+                        { label: '仪表', key: 'laboratory' },
+                        { label: '化验', key: 'assay' },
+                      ]"
+                      unit="mg/L"
+                    ></BaseChooseItem>
+  
+                    <BaseChooseItem
+                      title="好氧池硝酸盐"
+                      :btn-group="[
+                        { label: '手动', key: 'hand' },
+                        { label: '化验', key: 'forecast' },
+                        { label: '预测', key: 'forecast' },
+                      ]"
+                      unit="mg/L"
+                    ></BaseChooseItem>
+  
+                    <BaseChooseItem
+                      title="缺氧池硝酸盐"
+                      :btn-group="[
+                        { label: '手动', key: 'hand' },
+                        { label: '化验', key: 'forecast' },
+                      ]"
+                      unit="mg/L"
+                    ></BaseChooseItem>
+  
+                    <BaseChooseItem
+                      title="缺氧池氨氮"
+                      :btn-group="[
+                        { label: '手动', key: 'hand' },
+                        { label: '化验', key: 'forecast' },
+                      ]"
+                      unit="mg/L"
+                    ></BaseChooseItem>
+  
+                    <BaseChooseItem
+                      title="进水总氮"
+                      :btn-group="[
+                        { label: '手动', key: 'hand' },
+                        { label: '仪表', key: 'laboratory' }
+                      ]"
+                      unit="mg/L"
+                    ></BaseChooseItem>
+                  </div>
+                </BaseCard>
+
+                <BaseCard title="设定数据来源及取数方式" style="margin: 0" tips="建议使用默认值,非必要不修改">
+                  <template #titleRight>
+                    <div>
+                      <div
+                        class="flex items-center space-x-[4px] cursor-pointer"
+                        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 style="color: #2454FF">编辑</span>
+                      </div>
+                      <ul class="flex items-center text-[13px] space-x-[8px] cursor-pointer" v-show="!isVisibleBtn">
+                        <li class="cursor-pointer">确定</li>
+                        <li class="cursor-pointer text-[#B0B7C0]" @click="isVisibleBtn = true">取消</li>
+                      </ul>
+                    </div>
+                  </template>
+                  <ul class="data-source-list space-y-[12px]">
+                    <li class="data-soruce-item">
+                      <span>后反馈设定:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23 mg/L</span>
+                      <BaseInput unit="mg/L" size='small' :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>基准系数:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>修正系数:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>控制系数:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>水量分配系数:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>碳源当量:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>转换系数:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>稀释配属:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>药剂密度:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>最小启动流量:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                    <li class="data-soruce-item">
+                      <span>碳氮比:</span>
+                      <span class="unit" v-show="isVisibleBtn">12.23</span>
+                      <BaseInput size='small' type="text" :isNeedFlotBtn="false" v-show="!isVisibleBtn"></BaseInput>
+                    </li>
+                  </ul>
+                </BaseCard>
+              </div>
+            </n-scrollbar>
+          </div>
+          <div class="right-section">
+            <TheResultCard></TheResultCard>
+            <EchartPanel></EchartPanel>
+          </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;
+    width: 400px;
+    height: 100%;
+    border-radius: 10px;
+    background: #fff;
+
+    .scrollbar {
+      height: 100%;
+    }
+
+    .form-content {
+      padding: 24px 16px;
+    }
+  }
+
+  .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: 14px;
+        color: #333;
+      }
+    }
+  }
+}
+
+
+.right-section {
+  display: flex;
+  flex-flow: column;
+  width: 100%;
+  height: 100%;
+
+  .top {
+    flex-shrink: 1;
+    height: 214px;
+    border-radius: 8px;
+    border: 0.5px solid #FFF;
+    background: linear-gradient(90deg, #E0E8FC 0%, #F2F4FF 100%);
+  }
+
+  .bottom {
+    height: 100%;
+    background: pink;
+  }
+
+}
+
+
+// 通用区域的样式
+
+.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>

+ 73 - 0
src/views/control/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>

+ 58 - 0
src/views/control/components/BaseCard.vue

@@ -0,0 +1,58 @@
+<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;
+      padding-left: 9px;
+      line-height: 30px;
+      border-radius: 4px;
+      background: linear-gradient(90deg, #F6F7F9 0%, #FFF 100%);
+      font-size: 14px;
+      font-weight: bold;
+      color: #1A2029;
+    }
+  }
+
+}
+</style>

+ 143 - 0
src/views/control/components/BaseChooseItem.vue

@@ -0,0 +1,143 @@
+<script setup>
+import { ref } from 'vue';
+
+import BaseButton from './BaseButton.vue';
+import BaseInput from './BaseInput.vue';
+
+defineProps({
+  title: {
+    type: String,
+    default: ''
+  },
+  btnGroup: {
+    type: Boolean,
+    default: false
+  },
+  unit: {
+    type: String,
+    default: ''
+  }
+})
+
+const activeIndex = ref(0);
+
+const changeActive = (index) => {
+  activeIndex.value = index;
+}
+</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
+            type="info"
+            :key="item.key"
+            :isActive="activeIndex === index"
+            v-for="item, index in btnGroup"
+            @click="changeActive(index)"
+          >
+            {{ item.label }}
+          </BaseButton>
+        </ul>
+        <span class="unit">4018.00 {{ unit }}</span>
+      </div>
+      <BaseInput v-show="!activeIndex" :unit="unit"></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);
+      }
+  
+      .unit {
+        font-family: "D-DIN-PRO-700-Bold";
+        font-weight: bold;
+        font-size: 14px;
+        color: #333;
+      }
+    }
+  
+    .bottom-box {
+      position: relative;
+      @include flex(x, center, between);
+      margin-top: 4px;
+  
+      .inp {
+        width: 100%;
+        height: 28px;
+        padding: 0px 56px 0 10px;
+        border-radius: 4px 0px 0px 4px;
+        border: 1px solid #E6EAEE;
+        background: #fff;
+        outline: none;
+        font-size: 12px;
+  
+        &:focus {
+          border: 1px solid #2454FF;
+        }
+      }
+  
+      .unit {
+        flex-shrink: 1;
+        width: 46px;
+        height: 28px;
+        border-radius: 0px 4px 4px 0px;
+        border: 1px solid #E6EAEE;
+        border-left: 0;
+        background: #F0F2F5;
+        text-align: center;
+        line-height: 28px;
+        font-size: 12px;
+        font-weight: bold;
+      }
+  
+      .inp-flot_group {
+        position: absolute;
+        @include flex(x, center, center);
+        right: 50px;
+        top: 50%;
+        transform: translateY(-50%);
+  
+        li {
+          width: 16px;
+          height: 16px;
+          border-radius: 100%;
+          color: #DFE2E6;
+          cursor: pointer;
+  
+          svg,
+          svg path {
+            fill: #e0e2e6;
+            stroke: #e0e2e6;
+          }
+  
+          &:hover svg {
+            fill: #b3c4e3;
+            stroke: #b3c4e3;
+          }
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 158 - 0
src/views/control/components/BaseInput.vue

@@ -0,0 +1,158 @@
+<script setup>
+import { ref, computed } from 'vue';
+import { SvgIcon } from '@/components';
+
+const props = defineProps({
+  size: {
+    type: String,
+    default: ''
+  },
+  type: {
+    type: String,
+    default: 'default'
+  },
+  isActive: {
+    type: Boolean,
+    default: false
+  },
+  unit: {
+    type: String,
+    default: ''
+  },
+  isNeedFlotBtn: {
+    type: Boolean,
+    default: true
+  }
+})
+
+const inpRef = ref(null);
+const inpValue = ref('');
+const isFocusStatus = ref(false);
+const activeIndex = ref(0);
+
+const domClassName = computed(() => {
+  return [
+    'input_wrapper',
+    'input-' + props.type + "_" + props.size,
+    'inpit_' + props.size
+  ]
+})
+
+const onFocus = () => {
+  isFocusStatus.value = true;
+}
+
+const onBlur = () => {
+  isFocusStatus.value = false;
+}
+
+const handleInpValue = (event, type) => {
+  if ( type === 'confirm' ) {
+    
+  }
+  if ( type === 'cancel' ) {
+    event.preventDefault();
+    inpValue.value = '';
+  }
+}
+</script>
+
+<template>
+  <div :class="domClassName">
+    <input
+      ref="inpRef"
+      v-model="inpValue"
+      type="text"
+      class="inp"
+      @focus="onFocus"
+      @blur="onBlur"
+    >
+    <div class="unit" v-if="unit">{{ unit }}</div>
+    <ul class="inp-flot_group space-x-[4px]" v-show="isFocusStatus && isNeedFlotBtn">
+      <li>
+        <SvgIcon name="control-icon-confirm" size="16" @click="handleInpValue($event, 'confirm')"></SvgIcon>
+      </li>
+      <li>
+        <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;
+  }
+
+  .inp-flot_group {
+    @include flex(x, center, center);
+    position: absolute;
+    right: 50px;
+    top: 50%;
+    transform: translateY(-50%);
+
+    li {
+      width: 16px;
+      height: 16px;
+      border-radius: 100%;
+      color: #DFE2E6;
+      cursor: pointer;
+
+      svg,
+      svg path {
+        fill: #e0e2e6;
+        stroke: #e0e2e6;
+      }
+
+      &:hover svg {
+        fill: #b3c4e3;
+        stroke: #b3c4e3;
+      }
+    }
+  }
+}
+
+.inpit_small, .input-text_small {
+  width: 112px;
+  .inp {
+    text-align: center;
+    padding: 0 10px;
+  }
+}
+
+.input-text_small {
+  .inp {
+    border-radius: 4px;
+  }
+}
+</style>

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

@@ -0,0 +1,72 @@
+<script setup>
+import { ref } from 'vue';
+import { SvgIcon } from '@/components';
+
+const defaultData = [
+  { label: '1号加药泵', key: 'first' },
+  { label: '2号加药泵', key: 'second' },
+  { label: '3号加药泵', key: 'third' },
+]
+
+const activeIndex = ref(0);
+
+const chageActive = (index) => activeIndex.value = index;
+</script>
+
+<template>
+  <ul class="radio-card_group space-x-[10px]">
+    <li :class="['radio-card_item', { 'card_item_active': index === activeIndex }]" v-for="item, index in defaultData"
+      :key="item.key" @click="chageActive(index)">
+      <div class="radio-wrapper">
+        <SvgIcon name="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: 100px;
+    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>

+ 68 - 0
src/views/control/components/BaseRadioGroup.vue

@@ -0,0 +1,68 @@
+<script setup>
+import { ref } from 'vue';
+
+defineProps({
+  data: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const activeIndex = ref(0);
+
+const changeActive = (index) => {
+  activeIndex.value = index;
+  console.log(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': activeIndex === 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>

+ 59 - 0
src/views/control/components/BaseTitle.vue

@@ -0,0 +1,59 @@
+<script setup>
+
+defineProps({
+  type: {
+    type: String,
+    default: 'first'
+  }
+})
+
+</script>
+
+<template>
+  <div class="header" :style="[{ 'border-bottom': type === 'first' ? '1px solid #eee' : 'none' }]">
+    <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">智能投加计算</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);
+  height: 75px;
+  flex-shrink: 1;
+  padding: 24px 16px 18px 16px;
+  border-bottom: 1px solid #EEE;
+  color: #1A2029;
+
+  .title {
+    display: flex;
+    align-items: center;
+    height: 24px;
+
+    .text {
+      height: 100%;
+      padding-left: 13px;
+      margin-left: -3px;
+      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;
+    }
+  }
+
+  .btn-group {
+    @include flex(x, center, center);
+  }
+
+}
+</style>

+ 161 - 0
src/views/control/components/EchartPanel.vue

@@ -0,0 +1,161 @@
+<script setup>
+import { ref, unref, computed, onMounted } from 'vue';
+import { NTabs, NTab, NSelect } from "naive-ui";
+import * as echarts from 'echarts';
+const selectValue = ref('water');
+
+const echartRef = ref(null);
+
+const options = [
+  {
+    label: "信义污水厂信义污水厂",
+    value: 'water',
+    style: "font-size: 12px"
+  },
+  {
+    label: "数据看板数据看板",
+    value: 'water1',
+    style: "font-size: 12px"
+  }
+]
+
+onMounted(() => {
+  const option = {
+    xAxis: {
+      type: 'category',
+      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+    },
+    yAxis: {
+      type: 'value'
+    },
+    series: [
+      {
+        data: [150, 230, 224, 218, 135, 147, 260],
+        type: 'line'
+      }
+    ]
+  };
+
+  const echart = echarts.init(echartRef.value, 'light');
+
+  echart.setOption(option);
+})
+</script>
+
+<template>
+  <div class="echart-panel-wrapper">
+    <div class="title">
+      <div class="left-inner">
+        <span class="text">数据看板</span>
+        <n-tabs type="segment" animated size="small" class="tabs">
+          <n-tab name="在线仪表"></n-tab>
+          <n-tab name="连续检测"></n-tab>
+          <n-tab name="预测"></n-tab>
+        </n-tabs>
+      </div>
+      <div class="right-inner">
+        <NSelect v-model:value="selectValue" class="w-[180px]" size="small" :options="options"
+          :consistent-menu-width="false" />
+      </div>
+    </div>
+    <div class="echart-wrapper">
+      <div class="echart" ref="echartRef">echart</div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.echart-panel-wrapper {
+  display: flex;
+  flex-flow: column;
+  height: 100%;
+  padding: 25px 16px 0 25px;
+  margin-top: 12px;
+  border-radius: 10px;
+  background: #FFF;
+
+  .title {
+    flex-shrink: 0;
+    @include flex(x, center, between);
+
+    .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);
+    }
+  }
+
+  .echart-wrapper {
+    height: 100%;
+    padding-top: 16px;
+
+    .echart {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.echart-panel-wrapper {
+  .tabs {
+    .n-tabs-tab--active {
+      .n-tabs-tab__label {
+        color: #2454FF;
+      }
+    }
+
+    .n-tabs-tab__label {
+      font-size: 12px;
+      color: #333333;
+    }
+  }
+
+  .right-inner {
+    .n-base-selection__border {
+      border: 0;
+    }
+
+    .n-base-selection-input__content {
+      font-size: 12px;
+      color: #1A2029;
+    }
+
+    .n-base-selection-label {
+      border: 0;
+      border-radius: 0;
+      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;
+    }
+  }
+}
+</style>

+ 118 - 0
src/views/control/components/TheResultCard.vue

@@ -0,0 +1,118 @@
+<script setup>
+import BaseTitle from './BaseTitle.vue';
+import { TheChatView, SvgIcon } from '@/components';
+</script>
+
+<template>
+  <div class="result-card_view">
+    <BaseTitle title="智能投加计算" type="second"></BaseTitle>
+    <div class="content">
+      <div class="reult-list space-y-[4px]">
+        <div class="title">请您完成左侧「1.智能投加计算」</div>
+        <!-- <div class="desc">核算完成后为您生成建议碳源投加量</div> -->
+        <ul class="text">
+          <li>
+            <span>仪表瞬时流量:</span>
+            <i>2827 m³</i>
+          </li>
+          <li>
+            <span>系统加药量:</span>
+            <i>40 m3/h</i>
+          </li>
+        </ul>
+      </div>
+      <div class="round-btn">
+        <div class="inner space-y-[4px]">
+          <SvgIcon name="control-icon-result-btn" size="24" />
+          <span>一键投放</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.result-card_view {
+  display: flex;
+  flex-flow: column;
+  flex-shrink: 0;
+  width: 100%;
+  height: 214px;
+  padding: 0px 16px 20px 16px;
+  border-radius: 8px;
+  border: 1px solid #fff;
+  background: url('@/assets/images/control/bg-control-top.png') center right no-repeat, linear-gradient(90deg, #E0E8FC 0%, #F2F4FF 100%);
+  background-size: auto 90%, auto;
+  background-position: top right;
+  overflow: hidden;
+
+  .content {
+    @include flex(x, start, between);
+    height: 100%;
+    padding: 23px 153px 23px 54px;
+    border-radius: 8px;
+    background: linear-gradient(90deg, rgba(255, 255, 255, 0.80) 0%, rgba(255, 255, 255, 0.50) 100%);
+    backdrop-filter: blur(2px);
+
+    .reult-list {
+      padding-top: 7px;
+      .title {
+        font-size: 20px;
+        background: linear-gradient(92.49deg, #5ABBF2 12.24%, #2454FF 36.2%);
+        -webkit-background-clip: text;
+        -webkit-text-fill-color: transparent;
+        line-height: 24px;
+        font-size: 15px;
+        font-weight: bold;
+      }
+
+      .desc {
+        font-size: 14px;
+        color: #1A2029;
+      }
+
+      .text {
+        line-height: 24px;
+        font-weight: 500;
+        font-size: 14px;
+        color: #1A2029;
+
+        li:nth-child(2) span{
+          letter-spacing: 2.8px;
+        }
+      }
+    }
+
+    .round-btn {
+      @include flex(x, center, center);
+      width: 88px;
+      height: 88px;
+      border: 2px solid #E6EFFE;
+      border-radius: 50%;
+      background: #D7DDFF;
+      font-size: 10px;
+      font-weight: bold;
+      color: #fff;
+      cursor: pointer;
+
+      .inner {
+        @include flex(x, center, center);
+        flex-flow: column;
+        width: 74px;
+        height: 74px;
+        padding: 14px;
+        border-radius: 100%;
+        background: #8E9EFB;
+        transition: all 0.3s;
+      }
+
+      &:hover {
+        background: #898EFE;
+        .inner {
+          background: #2454FF;
+        }
+      }
+    }
+  }
+}
+</style>

Some files were not shown because too many files changed in this diff