From e895212fa4215c50ce79ce4b448e064caf394776 Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期四, 03 七月 2025 17:35:46 +0800
Subject: [PATCH] 2025.7.3 动态溯源(待完成)

---
 src/utils/chart/chart-option.js                           |    4 
 src/views/sourcetrace/SourceTrace.vue                     |  171 +++++++++++++++----
 src/components/map/MapLocate.vue                          |    0 
 src/views/sourcetrace/component/PollutedExceptionItem.vue |   32 ++-
 src/components.d.ts                                       |    2 
 src/views/HomePage.vue                                    |    2 
 src/views/sourcetrace/component/ClueRecordItem.vue        |  182 +++++++++++++++++---
 src/views/sourcetrace/component/SourceTraceFilter.vue     |   99 +++++++++++
 README.md                                                 |    4 
 src/components/chart/RealTimeLineChart.vue                |    8 
 10 files changed, 421 insertions(+), 83 deletions(-)

diff --git a/README.md b/README.md
index bec6a41..a7d00bd 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,7 @@
 鈹�  鈹�  鈹溾攢 map
 鈹�  鈹�  鈹�  鈹溾攢 BaseMap.vue
 鈹�  鈹�  鈹�  鈹溾攢 ConfigManage.vue
-鈹�  鈹�  鈹�  鈹溾攢 MapLocation.vue
+鈹�  鈹�  鈹�  鈹溾攢 MapLocate.vue
 鈹�  鈹�  鈹�  鈹溾攢 MapScene.vue
 鈹�  鈹�  鈹�  鈹斺攢 MapToolbox.vue
 鈹�  鈹�  鈹溾攢 MessageBox.vue
@@ -263,4 +263,4 @@
 鈹溾攢 vite.config.js
 鈹斺攢 vitest.config.js
 
-```
\ No newline at end of file
+```
diff --git a/src/components.d.ts b/src/components.d.ts
index 4fb05dd..9231243 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -65,7 +65,7 @@
     GaugeChart: typeof import('./components/chart/GaugeChart.vue')['default']
     GridSearch: typeof import('./components/grid/GridSearch.vue')['default']
     HistoricalTrajectory: typeof import('./components/animation/HistoricalTrajectory.vue')['default']
-    MapLocation: typeof import('./components/map/MapLocation.vue')['default']
+    MapLocate: typeof import('./components/map/MapLocate.vue')['default']
     MapScene: typeof import('./components/map/MapScene.vue')['default']
     MapToolbox: typeof import('./components/map/MapToolbox.vue')['default']
     MessageBox: typeof import('./components/MessageBox.vue')['default']
diff --git a/src/components/chart/RealTimeLineChart.vue b/src/components/chart/RealTimeLineChart.vue
index a66e2ac..cc72183 100644
--- a/src/components/chart/RealTimeLineChart.vue
+++ b/src/components/chart/RealTimeLineChart.vue
@@ -22,9 +22,15 @@
       //   };
       // }
     },
+    // 鎶樼嚎鍥惧睍绀洪珮搴�
     chartHeight: {
       type: String,
       default: '140px'
+    },
+    // 鎶樼嚎鍥綴杞村埢搴﹂棿璺�
+    yMinInterval: {
+      type: Number,
+      default: 1
     }
   },
   data() {
@@ -45,7 +51,7 @@
     refreshChart() {
       const { xAxis, series } = this.modelValue;
       if (!this.option) {
-        this.option = smallLineOption(xAxis, series);
+        this.option = smallLineOption(xAxis, series, this.yMinInterval);
       } else {
         this.option.xAxis[0].data = xAxis;
         this.option.series = series;
diff --git a/src/components/map/MapLocation.vue b/src/components/map/MapLocate.vue
similarity index 100%
rename from src/components/map/MapLocation.vue
rename to src/components/map/MapLocate.vue
diff --git a/src/utils/chart/chart-option.js b/src/utils/chart/chart-option.js
index 346c602..99337d0 100644
--- a/src/utils/chart/chart-option.js
+++ b/src/utils/chart/chart-option.js
@@ -121,7 +121,7 @@
 }
 
 // 鎶樼嚎鍥�
-function smallLineOption(_xAxis, _series) {
+function smallLineOption(_xAxis, _series, yMinInterval) {
   var fontSize = fGetChartFontSize();
   return {
     animationEasing: 'elasticOut',
@@ -198,7 +198,7 @@
         splitLine: {
           show: false
         },
-        minInterval: 1,
+        minInterval: yMinInterval ? yMinInterval : 1,
         intervel: 1,
         min: function (value) {
           return Math.floor(value.min);
diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue
index b31d689..28ddf5d 100644
--- a/src/views/HomePage.vue
+++ b/src/views/HomePage.vue
@@ -7,7 +7,7 @@
       <!-- <SatelliteTelemetry></SatelliteTelemetry> -->
       <!-- <MissionManage></MissionManage> -->
       <ConfigManage></ConfigManage>
-      <!-- <MapLocation></MapLocation> -->
+      <!-- <MapLocate></MapLocate> -->
       <SceneSearch></SceneSearch>
       <MapScene></MapScene>
       <GridSearch></GridSearch>
diff --git a/src/views/sourcetrace/SourceTrace.vue b/src/views/sourcetrace/SourceTrace.vue
index d4c97a6..0145b9a 100644
--- a/src/views/sourcetrace/SourceTrace.vue
+++ b/src/views/sourcetrace/SourceTrace.vue
@@ -18,17 +18,30 @@
     <el-col v-show="show" span="10">
       <BaseCard>
         <template #content>
-          <el-checkbox-group v-model="selectedMsgTypes" size="default" :min="1">
-            <el-space>
-              <el-checkbox value="1">寮傚父鍒囩墖</el-checkbox>
-              <el-checkbox value="2">姹℃煋绾跨储</el-checkbox>
-            </el-space>
-          </el-checkbox-group>
+          <el-row
+            justify="space-between"
+            align="middle"
+            style="border-bottom: 1px solid white"
+          >
+            <!-- 鏁版嵁鍒囩墖绛涢�夋潯浠� -->
+            <SourceTraceFilter
+              v-model:data-slice="selectedMsgTypes"
+              v-model:factor-type="selectedFactorTypes"
+              :factor-options="factorOptions"
+              v-model:scene-type="selectedSceneTypes"
+              :scene-options="sceneOptions"
+            ></SourceTraceFilter>
+            <!-- <el-divider direction="vertical"></el-divider> -->
+            <!-- 鏁版嵁鍒囩墖缁熻 -->
+            <div style="border-left: 1px solid white" class="p-l-8">
+              <el-space direction="vertical">
+                <el-text type="primary">婧簮锛歿{ countMsg1.type1 }}鏉�</el-text>
+                <el-text type="primary">绾跨储锛歿{ countMsg1.type2 }}鏉�</el-text>
+                <el-text type="primary">鎻愰啋锛歿{ countMsg1.type3 }}鏉�</el-text>
+              </el-space>
+            </div>
+          </el-row>
           <el-scrollbar ref="scrollbarRef" class="scrollbar">
-            <!-- <div
-              ref="scrollContentRef"
-              style="display: flex; width: fit-content"
-            > -->
             <TransitionGroup name="list">
               <div
                 v-for="item in filterStreams"
@@ -40,7 +53,6 @@
                 ></ClueRecordItem>
               </div>
             </TransitionGroup>
-            <!-- </div> -->
           </el-scrollbar>
         </template>
       </BaseCard>
@@ -72,6 +84,9 @@
 import PollutedExceptionItem from './component/PollutedExceptionItem.vue';
 import ClueRecordItem from './component/ClueRecordItem.vue';
 import PollutedClueItem from '@/views/sourcetrace/component/PollutedClueItem.vue';
+import SourceTraceFilter from '@/views/sourcetrace/component/SourceTraceFilter.vue';
+
+const NO_SCENE = 'no_scene';
 
 const props = defineProps({
   factorType: String
@@ -89,7 +104,11 @@
 
 const selectedException = ref();
 const selectedClue = ref();
-const selectedMsgTypes = ref(['1', '2']);
+const selectedMsgTypes = ref(['1', '2', '3']);
+const selectedFactorTypes = ref([]);
+const factorOptions = ref([]);
+const selectedSceneTypes = ref([]);
+const sceneOptions = ref([]);
 
 function scrollToBottom() {
   const h1 = scrollContentRef.value.clientHeight + 100;
@@ -107,8 +126,57 @@
 const streams = reactive([]);
 const filterStreams = computed(() => {
   return streams.filter((v) => {
-    return selectedMsgTypes.value.indexOf(v._type) != -1;
+    // 鍒ゆ柇娑堟伅绫诲瀷鏄惁閫変腑
+    const b1 = selectedMsgTypes.value.indexOf(v._type) != -1;
+    let b2, b3;
+    switch (v._type) {
+      case '1':
+      case '3':
+        // 鍒ゆ柇鐩戞祴鍥犲瓙绫诲瀷鏄惁閫変腑
+        b2 = selectedFactorTypes.value.indexOf(v.pollutedData.factorId) != -1;
+        // 鍒ゆ柇鍦烘櫙绫诲瀷鏄惁閫変腑
+        if (v.pollutedSource.sceneList.length == 0) {
+          b3 = selectedSceneTypes.value.indexOf(NO_SCENE) != -1;
+        } else {
+          b3 =
+            v.pollutedSource.sceneList.findIndex(
+              (v) => selectedSceneTypes.value.indexOf(v.typeId) != -1
+            ) != -1;
+        }
+        break;
+      case '2':
+        b2 = true;
+        b3 =
+          v.sortedSceneList.findIndex(
+            (v) => selectedSceneTypes.value.indexOf(v.first.typeId) != -1
+          ) != -1;
+        break;
+    }
+
+    return b1 && b2 && b3;
   });
+});
+// 缁熻鍚勭被鍨嬫秷鎭殑鏁伴噺
+const countMsg1 = computed(() => {
+  const count = {
+    type1: 0,
+    type2: 0,
+    type3: 0
+  };
+  streams.forEach((v) => {
+    switch (v._type) {
+      case '1':
+        count.type1++;
+        break;
+      case '2':
+        count.type2++;
+        break;
+      case '3':
+        count.type3++;
+        break;
+    }
+  });
+  return count;
 });
 
 const inputVal = ref('');
@@ -116,27 +184,16 @@
   websocket.send(inputVal.value);
 };
 
-let showFirstClueTask;
 function dealMsg(data) {
   const { type, content } = websocketMsgParser.parseMsg(data);
+  const obj = reactive(JSON.parse(content));
+  obj._type = type;
 
   // 姹℃煋绾跨储 PollutedClue
   if (type == '1') {
-    const obj = reactive(JSON.parse(content));
-    obj._type = type;
-    // obj.showMore = true;
     obj.showMore = false;
     console.log('姹℃煋寮傚父鍒囩墖: ', obj);
 
-    // if (streams.length == 0) {
-    //   streams.push(obj);
-    // } else {
-    //   // streams.forEach((s) => {
-    //   //   showMarksAndPolygon(s);
-    //   // });
-    //   // hideAll();
-    //   streams.unshift(obj);
-    // }
     addNewMsg(obj);
     show.value = true;
 
@@ -144,20 +201,61 @@
     // scrollToTop();
     // drawPolygon(obj.pollutedArea);
     parseChartData(obj);
-
-    // if (showFirstClueTask) {
-    //   clearTimeout(showFirstClueTask);
-    // }
-    // showFirstClueTask = setTimeout(() => {
-    //   showMarksAndPolygon(obj);
-    // }, 1000);
   } else if (type == '2') {
-    const obj = JSON.parse(content);
-    obj._type = type;
+    // const obj = JSON.parse(content);
+    // obj._type = type;
     console.log('姹℃煋绾跨储缁撴灉: ', obj);
     obj._timestr = timeFormatter(obj.time);
-    // streams.unshift(obj);
     addNewMsg(obj);
+  } else if (type == '3') {
+    console.log('姹℃煋鎻愰啋鍒囩墖: ', obj);
+    addNewMsg(obj);
+    parseChartData(obj);
+  }
+
+  optionsFilte(obj);
+}
+
+// 瀵规暟鎹繘琛岀瓫閫夛紝鍖呮嫭鐩戞祴鍥犲瓙鍜屽満鏅被鍨�
+function optionsFilte(objData) {
+  switch (objData._type) {
+    case '1':
+    case '3':
+      // 绛涢�夌洃娴嬪洜瀛愮被鍨�
+      if (
+        factorOptions.value.findIndex(
+          (v) => v.value == objData.pollutedData.factorId
+        ) == -1
+      ) {
+        factorOptions.value.push({
+          label: objData.pollutedData.factorName,
+          value: objData.pollutedData.factorId
+        });
+        selectedFactorTypes.value.push(objData.pollutedData.factorId);
+      }
+      // 绛涢�夊満鏅被鍨�
+      if (objData.pollutedSource.sceneList.length == 0) {
+        // 鑻ユ病鏈夋壘鍒伴闄╂簮鏃讹紝灏嗚鍒嗙被璁惧畾涓簄ull
+        if (sceneOptions.value.findIndex((v) => v.value == NO_SCENE) == -1) {
+          sceneOptions.value.push({
+            label: '鏃�',
+            value: NO_SCENE
+          });
+          selectedSceneTypes.value.push(NO_SCENE);
+        }
+      } else {
+        objData.pollutedSource.sceneList.forEach((s) => {
+          if (sceneOptions.value.findIndex((v) => v.value == s.typeId) == -1) {
+            sceneOptions.value.push({
+              label: s.type,
+              value: s.typeId
+            });
+            selectedSceneTypes.value.push(s.typeId);
+          }
+        });
+      }
+    // case '2':
+    //   break;
   }
 }
 
@@ -202,6 +300,7 @@
 function handleOpen(item) {
   switch (item._type) {
     case '1':
+    case '3':
       if (selectedException.value) {
         selectedException.value._selected = false;
       }
diff --git a/src/views/sourcetrace/component/ClueRecordItem.vue b/src/views/sourcetrace/component/ClueRecordItem.vue
index 28f2918..a8aecbb 100644
--- a/src/views/sourcetrace/component/ClueRecordItem.vue
+++ b/src/views/sourcetrace/component/ClueRecordItem.vue
@@ -1,35 +1,64 @@
 <template>
   <div :class="'wrapper' + (item._selected ? ' wrapper-select' : '')">
-    <div v-if="item._type == '1'">
-      <el-row justify="space-between">
-        <el-space>
-          <el-tag v-if="noWarn" type="info" effect="dark" size="small"
-            >寮傚父</el-tag
-          >
-          <el-tag v-else type="warning" effect="dark" size="small">寮傚父</el-tag>
-          <el-text type="primary">{{
-            item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
-          }}</el-text>
-        </el-space>
-        <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link>
-      </el-row>
-      <el-col :span="24">
-        <el-text type="primary">{{
-          item.pollutedData.factorName +
-          formatException(item.pollutedData.exceptionType) +
-          '锛�' +
-          formatDistanceType(item.pollutedArea.distanceType)
-        }}</el-text>
-        <el-text :type="noWarn ? 'primary' : 'warning'">
-          {{
-            item.pollutedSource.sceneList.length == 0
-              ? '鏈壘鍒板彲鐤戞薄鏌撴簮'
-              : '鎵惧埌' + item.pollutedSource.sceneList.length + '涓彲鐤戞薄鏌撴簮'
-          }}
-        </el-text>
+    <el-row v-if="item._type == '1'">
+      <el-col :span="3">
+        <el-tag :type="noWarn ? 'info' : 'warning'" effect="dark" size="small"
+          >婧簮</el-tag
+        >
       </el-col>
-      <!-- <el-col :span="2"> </el-col> -->
-    </div>
+      <el-col :span="21">
+        <el-row justify="space-between">
+          <el-space>
+            <el-text type="primary" size="default">
+              <el-icon><Timer /></el-icon>
+              {{
+                item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
+              }}
+            </el-text>
+          </el-space>
+          <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link>
+        </el-row>
+        <div>
+          <el-tag
+            effect="plain"
+            type="info"
+            size="small"
+            hit
+            round
+            class="m-r-4"
+          >
+            <div v-html="formatFactorName(item.pollutedData.factorName)"></div>
+          </el-tag>
+          <el-text type="primary">
+            {{ item.pollutedData.exception + '锛�' }}
+          </el-text>
+          <el-text type="primary">{{
+            formatDistanceType(item.pollutedArea.distanceType)
+          }}</el-text>
+          <el-text :type="noWarn ? 'primary' : 'warning'">
+            {{
+              item.pollutedSource.sceneList.length == 0
+                ? '鏈壘鍒伴闄╂簮'
+                : '鎵惧埌' + item.pollutedSource.sceneList.length + '涓闄╂簮'
+            }}
+          </el-text>
+        </div>
+        <div v-if="item.pollutedSource.sceneList.length > 0">
+          <div v-for="s in item.pollutedSource.sceneList" :key="s.guid">
+            <img style="width: 24px" :src="sceneIcon(s.typeId)" :alt="s.type" />
+            <el-text
+              type="warning"
+              tag="ins"
+              truncated
+              class="text-link"
+              @click="handleSetCenter(item, s)"
+            >
+              {{ s.name }}
+            </el-text>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
     <div v-else-if="item._type == '2'">
       <el-row justify="space-between">
         <el-tag type="danger" effect="dark" size="small">绾跨储</el-tag>
@@ -37,10 +66,73 @@
       </el-row>
       <el-text type="danger">{{ item.advice }}</el-text>
     </div>
+    <el-row v-else-if="item._type == '3'">
+      <el-col :span="3">
+        <el-tag type="primary" effect="dark" size="small">鎻愰啋</el-tag>
+      </el-col>
+      <el-col :span="21">
+        <el-row justify="space-between">
+          <el-space>
+            <el-text type="primary" size="default">
+              <el-icon><Timer /></el-icon>
+              {{
+                item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
+              }}
+            </el-text>
+          </el-space>
+          <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link>
+        </el-row>
+        <div>
+          <el-tag
+            effect="plain"
+            type="info"
+            size="small"
+            hit
+            round
+            class="m-r-4"
+          >
+            <div v-html="formatFactorName(item.pollutedData.factorName)"></div>
+          </el-tag>
+          <el-text type="primary">{{ item.pollutedData.exception }}</el-text>
+        </div>
+        <div v-if="item.pollutedSource.sceneList.length > 0">
+          <div v-for="s in item.pollutedSource.sceneList" :key="s.guid">
+            <img style="width: 24px" :src="sceneIcon(s.typeId)" :alt="s.type" />
+            <el-text
+              type="warning"
+              tag="ins"
+              truncated
+              class="text-link"
+              @click="handleSetCenter(item, s)"
+            >
+              {{ s.name }}
+            </el-text>
+          </div>
+        </div>
+      </el-col>
+
+      <!-- <el-row justify="space-between">
+        <el-space>
+          <el-tag type="primary" effect="dark" size="small">鎻愰啋</el-tag>
+          <el-text type="primary">{{
+            item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
+          }}</el-text>
+        </el-space>
+        <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link>
+      </el-row>
+      <el-col :span="24">
+        <el-tag effect="plain" type="info" size="small" hit round class="m-r-4">
+          <div v-html="formatFactorName(item.pollutedData.factorName)"></div>
+        </el-tag>
+        <el-text type="primary">{{ item.pollutedData.exception }}</el-text>
+      </el-col> -->
+    </el-row>
   </div>
 </template>
 <script setup>
 import { computed } from 'vue';
+import { sceneTypes, sceneIcon } from '@/constant/scene-types';
+import MapUtil from '@/utils/map/util';
 
 const props = defineProps({
   item: Object
@@ -78,6 +170,34 @@
       break;
   }
 }
+
+function formatFactorName(name) {
+  switch (name) {
+    case 'PM25':
+      return 'PM<sub>2.5</sub>';
+    // return '<span>PM2.5</span>';
+    case 'PM10':
+      return 'PM<sub>10</sub>';
+    case 'NO2':
+      return 'NO<sub>2</sub>';
+    case 'H2S':
+      return 'H<sub>2</sub>S';
+    case 'SO2':
+      return 'SO<sub>2</sub>';
+    case 'O3':
+      return 'O<sub>3</sub>';
+    case 'VOC':
+      return 'VOC<sub>s</sub>';
+
+    default:
+      break;
+  }
+}
+
+function handleSetCenter(item, scene) {
+  MapUtil.setCenter([scene.longitude, scene.latitude], true);
+  emits('open', item);
+}
 </script>
 <style scoped>
 .wrapper {
@@ -93,4 +213,8 @@
 .no-warning {
   color: var(--el-text-color-disabled) !important;
 }
+.text-link {
+  width: 90%;
+  cursor: pointer;
+}
 </style>
diff --git a/src/views/sourcetrace/component/PollutedExceptionItem.vue b/src/views/sourcetrace/component/PollutedExceptionItem.vue
index e7defcc..0aeedff 100644
--- a/src/views/sourcetrace/component/PollutedExceptionItem.vue
+++ b/src/views/sourcetrace/component/PollutedExceptionItem.vue
@@ -10,34 +10,43 @@
   <BaseCard v-if="item" v-show="item.showMore">
     <template #content>
       <el-scrollbar class="clue-card">
-        <el-row justify="space-between">
-          <!-- <el-tag v-if="index == 0" type="danger">鏈�鏂�</el-tag> -->
-          <el-text type="primary">{{
-            '鍒囩墖鏃堕棿锛�' +
-            item.pollutedData.startTime +
-            ' - ' +
-            item.pollutedData.endTime
-          }}</el-text>
+        <el-row justify="space-between" align="bottom">
+          <el-text type="warning" size="large"> 鍏稿瀷鍒囩墖 </el-text>
+
           <el-link
             type="primary"
             :underline="true"
             @click="showMarksAndPolygon(item)"
           >
-            {{ item.showMore ? '鏀惰捣寮傚父' : '瀹氫綅寮傚父' }}
+            {{ item.showMore ? '鏀惰捣' : '瀹氫綅' }}
+            <el-icon size="large"><CircleClose /></el-icon>
           </el-link>
         </el-row>
         <div>
           <el-text type="primary">
-            姹℃煋鍖哄煙锛歿{ item.pollutedArea.address }}
+            <el-icon><Timer /></el-icon>
+            {{
+              '鍒囩墖鏃舵锛�' +
+              item.pollutedData.startTime +
+              ' - ' +
+              item.pollutedData.endTime
+            }}
           </el-text>
         </div>
         <div>
+          <el-text type="primary">
+            <el-icon><MapLocation /></el-icon>
+            {{ '椋庨櫓鍖哄煙锛�' + item.pollutedArea.address }}
+          </el-text>
+        </div>
+        <!-- <div>
           <el-text type="primary">
             婧簮璺濈锛歿{ formatDistanceType(item.pollutedArea.distanceType) }}
           </el-text>
-        </div>
+        </div> -->
         <div>
           <el-text type="primary">
+            <el-icon><BellFilled /></el-icon>
             寮傚父绫诲瀷锛歿{ item.pollutedData.exception }}
           </el-text>
         </div>
@@ -92,6 +101,7 @@
           :key="index1"
           :model-value="item1"
           chart-height="80px"
+          :y-min-interval="20"
         ></RealTimeLineChart>
         <!-- </div> -->
         <div class="border-dashed">
diff --git a/src/views/sourcetrace/component/SourceTraceFilter.vue b/src/views/sourcetrace/component/SourceTraceFilter.vue
new file mode 100644
index 0000000..6368300
--- /dev/null
+++ b/src/views/sourcetrace/component/SourceTraceFilter.vue
@@ -0,0 +1,99 @@
+<template>
+  <div>
+    <div>
+      <el-space>
+        <el-text type="primary">鏁版嵁鍒囩墖</el-text>
+        <el-checkbox-group
+          :model-value="dataSlice"
+          @update:model-value="(e) => emits('update:data-slice', e)"
+          size="default"
+          :min="1"
+        >
+          <el-space>
+            <el-checkbox value="1">婧簮</el-checkbox>
+            <el-checkbox value="2">绾跨储</el-checkbox>
+            <el-checkbox value="3">鎻愮ず</el-checkbox>
+          </el-space>
+        </el-checkbox-group>
+      </el-space>
+    </div>
+    <div>
+      <el-space>
+        <el-text type="primary">鐩戞祴鍥犲瓙</el-text>
+        <el-checkbox-group
+          :model-value="factorType"
+          @update:model-value="(e) => emits('update:factor-type', e)"
+          size="default"
+          :min="1"
+        >
+          <el-space>
+            <el-checkbox
+              v-for="item in factorOptions"
+              :value="item.value"
+              :key="item.label"
+            >
+              {{ item.label }}
+            </el-checkbox>
+          </el-space>
+        </el-checkbox-group>
+      </el-space>
+    </div>
+    <div>
+      <el-space>
+        <el-text type="primary">鍦烘櫙绫诲瀷</el-text>
+        <el-checkbox-group
+          :model-value="sceneType"
+          @update:model-value="(e) => emits('update:scene-type', e)"
+          size="default"
+          :min="1"
+        >
+          <el-space>
+            <el-checkbox
+              v-for="item in sceneOptions"
+              :value="item.value"
+              :key="item.label"
+            >
+              {{ item.label }}
+            </el-checkbox>
+          </el-space>
+        </el-checkbox-group>
+      </el-space>
+    </div>
+  </div>
+</template>
+<script setup>
+import { ref } from 'vue';
+
+const props = defineProps({
+  // 鏁版嵁鍒囩墖锛岀嚎绱€�佹彁绀恒�佹函婧�
+  dataSlice: Array,
+  // 鐩戞祴鍥犲瓙
+  factorType: Array,
+  factorOptions: Array,
+  // 鍦烘櫙绫诲瀷
+  sceneType: Array,
+  sceneOptions: Array
+});
+
+const emits = defineEmits([
+  'update:data-slice',
+  'update:factor-type',
+  'update:scene-type'
+]);
+</script>
+<style scoped>
+:deep(.el-checkbox) {
+  --el-checkbox-text-color: white;
+  --main-color: #23dad1;
+  --el-checkbox-checked-text-color: var(--main-color);
+  --el-checkbox-checked-input-border-color: var(--main-color);
+  --el-checkbox-checked-bg-color: var(--main-color);
+  --el-checkbox-input-border-color-hover: var(--main-color);
+
+  --el-checkbox-disabled-checked-input-fill: var(--main-color);
+  --el-checkbox-disabled-checked-input-border-color: var(--main-color);
+  --el-checkbox-disabled-checked-icon-color: white;
+  margin-right: 6px;
+  /* height: initial; */
+}
+</style>

--
Gitblit v1.9.3