From d8eda80eaf2f328ed43dc4f5c783b7dee5a4092c Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期六, 12 十月 2024 17:39:39 +0800
Subject: [PATCH] 1. 新增巡查计划功能(待完成); 2. 完成任务的各场景下的分别移除和添加;

---
 src/views/fysp/task/components/CompMonitorObj.vue  |   44 +++-
 src/api/fysp/taskApi.js                            |   23 ++
 src/components.d.ts                                |    3 
 src/components/list-item/ItemSubTask.vue           |   38 ++++
 src/views/fysp/task/MonitorObjEdit.vue             |   42 +++
 src/router/index.js                                |    6 
 src/views/fysp/task/MonitorPlanEdit.vue            |   99 +++++++++++
 src/views/fysp/task/TaskManage.vue                 |   53 ++++-
 src/views/fysp/task/components/CompMonitorPlan.vue |  225 +++++++++++++++++++++++++
 9 files changed, 500 insertions(+), 33 deletions(-)

diff --git a/src/api/fysp/taskApi.js b/src/api/fysp/taskApi.js
index 6284221..6e4e66a 100644
--- a/src/api/fysp/taskApi.js
+++ b/src/api/fysp/taskApi.js
@@ -50,6 +50,18 @@
   },
 
   /**
+   * 鑾峰彇姣忔棩浠诲姟缁熻淇℃伅
+   * @param {String} topTaskId 鎬讳换鍔′富閿甶d
+   * @param {String} userId 鐢ㄦ埛id锛屽綋鐢ㄦ埛绫诲瀷userType涓�1锛堢洃绠$敤鎴凤級鏃讹紝浼氭牴鎹敤鎴穒d鑾峰彇鍏舵潈闄愬唴鐨勭粺璁′俊鎭�
+   * @param {String} userType 鐢ㄦ埛绫诲瀷锛�0锛氱鐞嗗憳锛�1锛氱洃绠$敤鎴凤紱2锛氭斂搴滈儴闂紱3锛氫紒涓�
+   */
+  fetchDayTasks(topTaskId, userId = '', userType = '0') {
+    return $fysp
+      .get(`task/dayTask/${topTaskId}?userId=${userId}&userType=${userType}`)
+      .then((res) => res.data);
+  },
+
+  /**
    * 鑾峰彇瀛愪换鍔$粺璁′俊鎭�
    */
   getSubtaskSummary({ topTaskId = undefined, sceneTypeId = undefined }) {
@@ -64,6 +76,17 @@
   },
 
   /**
+   * 鑾峰彇鍏蜂綋瀛愪换鍔′俊鎭�
+   * @param {String} dayTaskId 鏃ヤ换鍔′富閿甶d
+   * @param {String} userId 鐢ㄦ埛id锛屽綋鐢ㄦ埛绫诲瀷userType涓�1锛堢洃绠$敤鎴凤級鏃讹紝浼氭牴鎹敤鎴穒d鑾峰彇鍏舵潈闄愬唴鐨勭粺璁′俊鎭�
+   * @param {String} userType 鐢ㄦ埛绫诲瀷锛�0锛氱鐞嗗憳锛�1锛氱洃绠$敤鎴凤紱2锛氭斂搴滈儴闂紱3锛氫紒涓�
+   */
+  fetchSubtaskByDayTask(dayTaskId, userId = '', userType = '0') {
+    const params = `?dayTaskId=${dayTaskId}&userId=${userId}&userType=${userType}`;
+    return $fysp.get(`subtask/byDayTaskId${params}`).then((res) => res.data);
+  },
+
+  /**
    * 鑾峰彇瀛愪换鍔¢棶棰樿鎯�
    */
   getProBySubtask(id) {
diff --git a/src/components.d.ts b/src/components.d.ts
index f171213..425e47b 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -22,6 +22,7 @@
     ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
+    ElCalendar: typeof import('element-plus/es')['ElCalendar']
     ElCard: typeof import('element-plus/es')['ElCard']
     ElCascader: typeof import('element-plus/es')['ElCascader']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
@@ -58,6 +59,7 @@
     ElSegmented: typeof import('element-plus/es')['ElSegmented']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSpace: typeof import('element-plus/es')['ElSpace']
+    ElStatistic: typeof import('element-plus/es')['ElStatistic']
     ElStep: typeof import('element-plus/es')['ElStep']
     ElSteps: typeof import('element-plus/es')['ElSteps']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
@@ -92,6 +94,7 @@
     Header: typeof import('./components/core/Header.vue')['default']
     ItemMonitorObj: typeof import('./components/list-item/ItemMonitorObj.vue')['default']
     ItemScene: typeof import('./components/list-item/ItemScene.vue')['default']
+    ItemSubTask: typeof import('./components/list-item/ItemSubTask.vue')['default']
     ItemUser: typeof import('./components/list-item/ItemUser.vue')['default']
     MenuItems: typeof import('./components/core/MenuItems.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
diff --git a/src/components/list-item/ItemSubTask.vue b/src/components/list-item/ItemSubTask.vue
new file mode 100644
index 0000000..52b43f0
--- /dev/null
+++ b/src/components/list-item/ItemSubTask.vue
@@ -0,0 +1,38 @@
+<template>
+  <div class="wrapper">
+    <div>
+      <el-text truncated>{{ item.name }}</el-text>
+    </div>
+    <div>
+      <el-space fill>
+        <el-text truncated class="w-250px" size="small">{{ item.scenseaddress }}</el-text>
+        <el-text truncated class="w-250px" size="small">{{
+          $fm.formatYMDH(item.planstarttime)
+        }}</el-text>
+        <el-text truncated class="w-250px" size="small">{{ item.executorrealtimes }}</el-text>
+      </el-space>
+    </div>
+    <el-row justify="end" style="margin-top: 4px">
+      <slot :item="item"></slot>
+    </el-row>
+  </div>
+</template>
+<script setup>
+/**
+ * 鐩戠瀵硅薄
+ */
+const props = defineProps({
+  item: {
+    type: Object,
+    default: () => {}
+  }
+});
+</script>
+<style scoped>
+.wrapper {
+  /* width: 300px; */
+  border: 1px solid var(--el-border-color);
+  border-radius: var(--el-border-radius-base);
+  padding: 4px 8px;
+}
+</style>
diff --git a/src/router/index.js b/src/router/index.js
index 912d204..c56817d 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -73,6 +73,12 @@
     component: () => import('@/views/fysp/task/MonitorObjEdit.vue')
   },
   {
+    //鐩戠浠诲姟璁″垝缂栬緫
+    name: 'monitorPlanEdit',
+    path: '/fysp/task/plan/edit',
+    component: () => import('@/views/fysp/task/MonitorPlanEdit.vue')
+  },
+  {
     //闂瀹℃牳
     name: 'procheck',
     path: '/fysp/procheck',
diff --git a/src/views/fysp/task/MonitorObjEdit.vue b/src/views/fysp/task/MonitorObjEdit.vue
index e2c021a..636899b 100644
--- a/src/views/fysp/task/MonitorObjEdit.vue
+++ b/src/views/fysp/task/MonitorObjEdit.vue
@@ -1,7 +1,7 @@
 <template>
   <el-affix :offset="60">
     <div class="page-header">
-      <el-page-header @back="$router.back()">
+      <el-page-header @back="goBack">
         <template #content>
           <span> 鎬讳换鍔$紪杈� </span>
         </template>
@@ -24,10 +24,11 @@
       <el-divider />
       <CompMonitorObj
         :data="curMonitorObjList"
-        show-delete
+        show-btn
         v-model:tabName="curSceneType"
         v-model:showData="showMonitorObjList"
-        @delete-item="deleteMov"
+        :tabOptions="sceneTypeOptions"
+        @item-click="deleteMov"
       >
       </CompMonitorObj>
     </el-col>
@@ -41,7 +42,6 @@
           <el-segmented v-model="curSceneType" :options="sceneTypeOptions" />
         </div>
         <FYInfoSearch
-          label=""
           placeholder="璇疯緭鍏ュ満鏅悕绉板叧閿瓧"
           :data="showSceneList"
           :on-search="searchScene"
@@ -92,9 +92,14 @@
 import svUserApi from '@/api/fysp/userApi';
 import taskApi from '@/api/fysp/taskApi';
 import sceneApi from '@/api/fysp/sceneApi';
-import { ElMessage, ElNotification } from 'element-plus';
+import { ElMessage, ElNotification, ElMessageBox } from 'element-plus';
 
 export default {
+  async beforeRouteLeave(to, from) {
+    // 鍦ㄥ鑸寮�娓叉煋璇ョ粍浠剁殑瀵瑰簲璺敱鏃惰皟鐢�
+    // 涓� `beforeRouteUpdate` 涓�鏍凤紝瀹冨彲浠ヨ闂粍浠跺疄渚� `this`
+    // return this.routerChangeCheck();
+  },
   components: { CompMonitorObj },
   props: {},
   data() {
@@ -104,7 +109,6 @@
       // 褰撳墠绛涢�夌殑鍦烘櫙绫诲瀷
       curSceneType: undefined,
       showMonitorObjList: [],
-
 
       // 琛屾斂鍖哄垝
       area: {},
@@ -140,7 +144,7 @@
         return index == -1 && v.type == this.curSceneType;
       });
     },
-    sceneTypeOptions(){
+    sceneTypeOptions() {
       const list = [];
       this.sceneList.forEach((d) => {
         if (list.indexOf(d.type) == -1) list.push(d.type);
@@ -302,6 +306,30 @@
       // return Promise.all([p1, p2, p3]).finally(() => {
       //   this.saveLoading = false;
       // });
+    },
+    async goBack() {
+      // const answer = await this.routerChangeCheck()
+      // if (answer) {
+      //   this.$router.back();
+      // }
+      this.$router.back();
+    },
+    async routerChangeCheck() {
+      if (this.isEdit) {
+        const answer = await ElMessageBox.confirm('鏄惁鏀惧純宸蹭慨鏀圭殑鎬讳换鍔★紵', '鍙栨秷鎬讳换鍔′慨鏀�', {
+          confirmButtonText: '纭',
+          cancelButtonText: '鍙栨秷',
+          type: 'warning'
+        })
+          .then(() => {
+            return true;
+          })
+          .catch(() => {
+            return false;
+          });
+        return answer;
+      }
+      return true;
     }
   },
   mounted() {
diff --git a/src/views/fysp/task/MonitorPlanEdit.vue b/src/views/fysp/task/MonitorPlanEdit.vue
new file mode 100644
index 0000000..1720fee
--- /dev/null
+++ b/src/views/fysp/task/MonitorPlanEdit.vue
@@ -0,0 +1,99 @@
+<template>
+  <el-affix :offset="60">
+    <div class="page-header">
+      <el-page-header @back="goBack">
+        <template #content>
+          <span> 璁″垝璋冩暣 </span>
+        </template>
+        <template #extra>
+          <div>
+            <el-button type="primary" :disabled="!isEdit" :loading="saveLoading" @click="saveEdit"
+              >淇濆瓨淇敼</el-button
+            >
+          </div>
+        </template>
+      </el-page-header>
+      <el-divider />
+    </div>
+  </el-affix>
+  <el-row gutter="20">
+    <el-col :span="16">
+      <el-affix :offset="140">
+        <div>
+          <el-text>鐩戠璁″垝</el-text>
+        </div>
+        <el-divider />
+        <CompMonitorPlan :task="task" @date-change="onDateChange"></CompMonitorPlan>
+      </el-affix>
+    </el-col>
+    <el-col :span="8">
+      <el-row justify="space-between">
+        <el-text>鍗曟棩璁″垝</el-text>
+        <el-button type="success" size="small" @click="editTask">鏂板</el-button>
+      </el-row>
+      <el-divider />
+      <ItemSubTask v-for="stask in curSubTaskList" :key="stask.guid" :item="stask">
+        <template #default="{ item }">
+          <el-button type="danger" size="small" @click="editTask">绉婚櫎</el-button>
+        </template>
+      </ItemSubTask>
+      <!-- <CompMonitorObj
+        :data="curMonitorObjList"
+        show-btn
+        v-model:tabName="curSceneType"
+        v-model:showData="showMonitorObjList"
+        btn-name="娣诲姞"
+        @item-click="deleteMov"
+      >
+      </CompMonitorObj> -->
+    </el-col>
+  </el-row>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import CompMonitorPlan from './components/CompMonitorPlan.vue';
+import CompMonitorObj from './components/CompMonitorObj.vue';
+import taskApi from '@/api/fysp/taskApi';
+
+const route = useRoute();
+const router = useRouter();
+
+const curMonitorObjList = ref([]);
+const task = ref({});
+const curSceneType = ref({});
+const showMonitorObjList = ref({});
+const curSubTaskList = ref([]);
+
+function onDateChange(dayTask) {
+  if (dayTask) {
+    fetchSubTask(dayTask.guid);
+  } else {
+    curSubTaskList.value = [];
+  }
+}
+
+function fetchSubTask(dayTaskId) {
+  taskApi.fetchSubtaskByDayTask(dayTaskId).then((res) => {
+    curSubTaskList.value = res;
+  });
+}
+
+onMounted(() => {
+  // 鐩戠鍦烘櫙淇℃伅
+  curMonitorObjList.value = JSON.parse(decodeURIComponent(route.query.data));
+  // 鏍规嵁鎬讳换鍔¤幏鍙栬鏀垮尯鍒掍俊鎭�
+  task.value = JSON.parse(decodeURIComponent(route.query.task));
+});
+
+function goBack() {
+  router.back();
+}
+</script>
+
+<style scoped>
+.page-header {
+  background-color: white;
+}
+</style>
diff --git a/src/views/fysp/task/TaskManage.vue b/src/views/fysp/task/TaskManage.vue
index 0e2de57..4239a94 100644
--- a/src/views/fysp/task/TaskManage.vue
+++ b/src/views/fysp/task/TaskManage.vue
@@ -31,11 +31,17 @@
         class="el-scrollbar"
         v-loading="mainLoading"
       >
-        <div><el-text>鐩戠璁″垝</el-text></div>
+        <el-row justify="space-between">
+          <div><el-text>鐩戠璁″垝</el-text></div>
+          <el-button type="warning" size="small" @click="editPlan">璁″垝璋冩暣</el-button>
+          <CompMonitorPlan :task="curTask.data"></CompMonitorPlan>
+        </el-row>
         <el-divider></el-divider>
-        <el-button type="primary" size="small" @click="editTask">鍦烘櫙璋冩暣</el-button>
-        <div><el-text>鐩戠鍦烘櫙</el-text></div>
-        <CompMonitorObj :data="curMonitorObjList" v-model:showData="showMonitorObjList"></CompMonitorObj>
+        <el-row justify="space-between">
+          <div><el-text>鐩戠鍦烘櫙</el-text></div>
+          <el-button type="warning" size="small" @click="editTask">鍦烘櫙璋冩暣</el-button>
+        </el-row>
+        <CompMonitorObj :data="curMonitorObjList"></CompMonitorObj>
         <!-- <div><el-text>鐩戠鍦烘櫙</el-text></div>
         <div>
           <el-space wrap>
@@ -55,8 +61,19 @@
 <script>
 import taskApi from '@/api/fysp/taskApi';
 import CompMonitorObj from './components/CompMonitorObj.vue';
+import CompMonitorPlan from './components/CompMonitorPlan.vue';
 export default {
-  components: { CompMonitorObj },
+  beforeRouteEnter(to, from, next) {
+    // 鍦ㄦ覆鏌撹缁勪欢鐨勫搴旇矾鐢辫楠岃瘉鍓嶈皟鐢�
+    // 涓嶈兘鑾峰彇缁勪欢瀹炰緥 `this` 锛�
+    // 鍥犱负褰撳畧鍗墽琛屾椂锛岀粍浠跺疄渚嬭繕娌¤鍒涘缓锛�
+    next((vm) => {
+      if (from.name == 'monitorObjEdit' && vm.task) {
+        vm.chooseTask(vm.task);
+      }
+    });
+  },
+  components: { CompMonitorObj, CompMonitorPlan },
   data() {
     return {
       formSearch: {
@@ -79,14 +96,14 @@
       curTask: {},
       //鎿嶄綔鎸夐挳
       buttons: [
-        {
-          name: '璁″垝璋冩暣',
-          color: 'success'
-        },
-        {
-          name: '鍦烘櫙璋冩暣',
-          color: 'warning'
-        }
+        // {
+        //   name: '璁″垝璋冩暣',
+        //   color: 'success'
+        // },
+        // {
+        //   name: '鍦烘櫙璋冩暣',
+        //   color: 'warning'
+        // }
       ]
     };
   },
@@ -143,6 +160,7 @@
       return type;
     },
     chooseTask(task) {
+      this.task = task;
       this.sideLoading = false;
       this.mainLoading = true;
       taskApi
@@ -163,6 +181,15 @@
           task: encodeURIComponent(JSON.stringify(this.curTask.data))
         }
       });
+    },
+    editPlan(){
+      this.$router.push({
+        name: 'monitorPlanEdit',
+        query: {
+          data: encodeURIComponent(JSON.stringify(this.curMonitorObjList)),
+          task: encodeURIComponent(JSON.stringify(this.curTask.data)),
+        }
+      });
     }
   },
   mounted() {
diff --git a/src/views/fysp/task/components/CompMonitorObj.vue b/src/views/fysp/task/components/CompMonitorObj.vue
index 9093a2b..327787e 100644
--- a/src/views/fysp/task/components/CompMonitorObj.vue
+++ b/src/views/fysp/task/components/CompMonitorObj.vue
@@ -1,14 +1,14 @@
 <template>
   <div>
-    <el-segmented :model-value="tabName" :options="tabs" @change="tabChange" />
+    <el-segmented :model-value="activeName" :options="activeTabs" @change="tabChange" />
   </div>
   <el-space wrap>
     <ItemMonitorObj v-for="obj in activeData" :key="obj.movid" :item="obj">
       <template #default="{ item }">
         <!-- <slot :item="item"></slot> -->
-        <el-button v-if="showDelete" size="small" type="danger" @click="deleteMov(item)"
-          >绉婚櫎</el-button
-        >
+        <el-button v-if="showBtn" size="small" type="danger" @click="itemClick(item)">{{
+          btnName
+        }}</el-button>
       </template>
     </ItemMonitorObj>
   </el-space>
@@ -26,12 +26,17 @@
       type: String,
       default: defaultTabName
     },
+    tabOptions: Array,
     showData: Array,
     // 鏄惁娣诲姞榛樿鐨勫叏閮ㄩ�夐」
     allOption: Boolean,
-    showDelete: Boolean
+    showBtn: Boolean,
+    btnName: {
+      type: String,
+      default: '绉婚櫎'
+    }
   },
-  emits: ['update:tabName', 'update:showData', 'deleteItem'],
+  emits: ['update:tabName', 'update:showData', 'itemClick'],
   data() {
     return {
       activeName: defaultTabName,
@@ -41,11 +46,18 @@
   computed: {
     activeData() {
       const list = this.data.filter((v) => {
-        // return this.activeName == defaultTabName || v.sceneType == this.activeName;
-        return this.tabName == defaultTabName || v.sceneType == this.tabName;
+        return this.activeName == defaultTabName || v.sceneType == this.activeName;
+        // return this.tabName == defaultTabName || v.sceneType == this.tabName;
       });
       this.$emit('update:showData', list);
       return list;
+    },
+    activeTabs() {
+      if (this.tabOptions) {
+        return this.tabOptions;
+      } else {
+        return this.tabs;
+      }
     }
   },
   watch: {
@@ -60,11 +72,16 @@
     tabs: {
       handler(nV, oV) {
         if (nV != oV && nV.length > 0) {
-          // this.activeName = nV[0];
-          this.tabChange(nV);
+          this.activeName = nV[0];
+          this.tabChange(nV[0]);
         }
       },
       immediate: true
+    },
+    tabName(nV, oV) {
+      if (nV != oV) {
+        this.activeName = nV;
+      }
     }
   },
   methods: {
@@ -73,13 +90,14 @@
       dataList.forEach((d) => {
         if (list.indexOf(d.sceneType) == -1) list.push(d.sceneType);
       });
-      this.tabs = list;
+      this.tabs = list.sort();
     },
     tabChange(tabName) {
+      this.activeName = tabName;
       this.$emit('update:tabName', tabName);
     },
-    deleteMov(item) {
-      this.$emit('deleteItem', item);
+    itemClick(item) {
+      this.$emit('itemClick', item);
     }
   }
 };
diff --git a/src/views/fysp/task/components/CompMonitorPlan.vue b/src/views/fysp/task/components/CompMonitorPlan.vue
new file mode 100644
index 0000000..e59c37a
--- /dev/null
+++ b/src/views/fysp/task/components/CompMonitorPlan.vue
@@ -0,0 +1,225 @@
+<template>
+  <el-calendar
+    v-loading="dayTaskLoading"
+    v-model="dateValue"
+    :range="dateRange"
+    @update:model-value="onDateChange"
+  >
+    <template #header="{ date }">
+      <span>{{ title }}</span>
+      <el-space>
+        <el-tag>鎬昏锛歿{ taskStatistic.total }}</el-tag>
+        <el-tag>瀹屾垚锛歿{ taskStatistic.complete }}</el-tag>
+        <el-tag>鏁存敼锛歿{ taskStatistic.changed }}</el-tag>
+      </el-space>
+      <!-- <span>{{ date }}</span> -->
+    </template>
+    <template #date-cell="{ data }">
+      <div :class="calendarDayClz(data.day)">
+        <div style="background-color: #f8f4f4">{{ getDay(data.day) }}</div>
+        <template v-if="computeDayTask(data.day)">
+          <!-- <el-divider></el-divider> -->
+          <el-row justify="space-between" class="m-t-16">
+            <el-space direction="vertical">
+              <el-text size="small">鎬昏</el-text>
+              <el-text>{{computeDayTask(data.day).totalTaskNum}}</el-text>
+            </el-space>
+            <el-space direction="vertical">
+              <el-text size="small">瀹屾垚</el-text>
+              <el-text>{{computeDayTask(data.day).completeTaskNum}}</el-text>
+            </el-space>
+            <el-space direction="vertical">
+              <el-text size="small">鏁存敼</el-text>
+              <el-text>{{computeDayTask(data.day).changedTaskNum}}</el-text>
+            </el-space>
+            <!-- <el-statistic title="鎬昏" :value="computeDayTask(data.day).totalTaskNum" />
+            <el-statistic title="瀹屾垚" :value="computeDayTask(data.day).completeTaskNum" />
+            <el-statistic title="鏁存敼" :value="computeDayTask(data.day).changedTaskNum" /> -->
+          </el-row>
+          <!-- <div>浠诲姟鎬昏锛歿{ computeDayTask(data.day).totalTaskNum }}</div>
+          <div>浠诲姟瀹屾垚锛歿{ computeDayTask(data.day).completeTaskNum }}</div>
+          <div>浠诲姟鏁存敼锛歿{ computeDayTask(data.day).changedTaskNum }}</div> -->
+        </template>
+      </div>
+    </template>
+  </el-calendar>
+</template>
+<script setup>
+import { ref, computed, onMounted, watch } from 'vue';
+import taskApi from '@/api/fysp/taskApi';
+import dayjs from 'dayjs';
+
+const props = defineProps({
+  task: {
+    type: Object,
+    default: () => {}
+  }
+});
+const emit = defineEmits(['dateChange'])
+// 閫変腑鏃ユ湡
+const dateValue = ref(new Date());
+// 鏃ュ巻鏍囬
+const title = computed(() => {
+  if (props.task) {
+    // return `${props.task.name}璁″垝`;
+    return `宸℃煡璁″垝`;
+  } else {
+    return '';
+  }
+});
+// 鏃ュ巻鑼冨洿
+const startDay = computed(() => dayjs(props.task.starttime));
+const endDay = computed(() => dayjs(props.task.endtime));
+const dateRange = computed(() => [startDay.value.toDate(), endDay.value.toDate()]);
+// const dateRange = computed(() => [new Date(2024, 4, 27), new Date(2024, 5, 30)]);
+
+// 鏃ユ湡鏄惁鍦ㄤ换鍔¤寖鍥村唴
+function isDayEnable(day) {
+  const _day = dayjs(day);
+  return _day.isSameOrAfter(startDay.value, 'day') && _day.isSameOrBefore(endDay.value, 'day');
+}
+
+/********************** 鏃ユ湡鏍峰紡 *********************************/
+function calendarDayClz(day) {
+  return 'calendar-day ' + (isDayEnable(day) ? 'calendar-day-enable' : 'calendar-day-disable');
+}
+function getDay(day) {
+  return day.split('-').splice(1, 2).join('-');
+}
+
+/********************** 浠诲姟鏁版嵁 *********************************/
+
+// 鑾峰彇鏃ヤ换鍔$粺璁′俊鎭�
+const dayTaskLoading = ref(false);
+const dayTaskList = ref([]);
+function fetchDayTasks(topTaskId) {
+  dayTaskLoading.value = true;
+  taskApi
+    .fetchDayTasks(topTaskId)
+    .then((res) => {
+      dayTaskList.value = res;
+    })
+    .finally(() => (dayTaskLoading.value = false));
+}
+
+// 鏃ヤ换鍔℃暟鎹睍绀�
+const compMap = new Map();
+function computeDayTask(day) {
+  const key = props.task.tguid + day;
+  if (compMap.has(key)) {
+    return compMap.get(key).value;
+  }
+  const result = computed(() => {
+    return dayTaskList.value.find((v) => {
+      return dayjs(v.date).isSame(dayjs(day));
+    });
+  });
+  compMap.set(key, result);
+  return result.value;
+}
+
+function onDateChange(e) {
+  const day = dayjs(e).format('YYYY-MM-DD')
+  const t = computeDayTask(day)
+  console.log(t);
+  
+  emit('dateChange', t)
+}
+
+// 鎬讳换鍔$粺璁�
+const taskStatistic = computed(() => {
+  const res = { total: 0, complete: 0, changed: 0 };
+  dayTaskList.value.forEach((e) => {
+    res.total += e.totalTaskNum;
+    res.complete += e.completeTaskNum;
+    res.changed += e.changedTaskNum;
+  });
+  return res;
+});
+/********************** 鍒濆鍖� *********************************/
+
+watch(
+  () => props.task,
+  (nV) => {
+    if (nV.tguid) {
+      fetchDayTasks(nV.tguid);      
+    }
+  },
+  { immediate: true }
+);
+</script>
+<style scoped>
+.li-01 {
+  width: 31px !important;
+  height: 31px;
+  background: #fff !important;
+  color: #001529 !important;
+  border: 1.5px solid #25de9d;
+  border-radius: 50%;
+  margin-left: 50%;
+  transform: translate(-15px);
+  line-height: 30px;
+  text-align: center;
+  font-size: 16px;
+  top: 5px;
+}
+
+::v-deep .el-calendar-table .el-calendar-day {
+  height: initial;
+  padding: initial;
+}
+
+::v-deep .el-calendar-table .el-calendar-day:hover {
+  background-color: transparent;
+}
+
+/* ::v-deep .el-calendar-table td.is-selected {
+  background-color: initial;
+} */
+
+.calendar-day {
+  /* text-align: center;
+  line-height: 30px;
+  width: 30px;
+  height: 30px;
+  border: 1px solid rgb(172, 165, 165);
+  border-radius: 50%; */
+  padding: 8px;
+  height: var(--el-calendar-cell-width);
+}
+
+.calendar-day-enable {
+}
+
+.calendar-day-enable:hover {
+  background-color: var(--el-calendar-selected-bg-color);
+}
+
+.calendar-day-disable {
+  color: var(--el-text-color-disabled);
+  cursor: not-allowed;
+}
+
+/* ::v-deep .el-calendar-table tr td:first-child {
+  border-left: none !important;
+} */
+
+/* ::v-deep .el-calendar-table tr:first-child td {
+  border-top: none;
+} */
+
+/* ::v-deep .el-calendar-table td {
+  border-bottom: none;
+  border-right: none;
+  vertical-align: top;
+  -webkit-transition: background-color 0.2s ease;
+  transition: background-color 0.2s ease;
+} */
+
+::v-deep .el-calendar-table thead th {
+  padding: 12px 0;
+  color: #606266;
+  font-weight: normal;
+  background-color: #faeaea;
+}
+</style>

--
Gitblit v1.9.3