Prechádzať zdrojové kódy

【feat】【v3】

1.完善待办页面、任务列表页面
2.新增任务详情页面
ChenYL 9 mesiacov pred
rodič
commit
fa4e0c1976

+ 6 - 4
src/apis/punchInApi.js

@@ -101,14 +101,16 @@ export function revokePunchIn(params) {
 }
 
 /**
- * 查询打卡数据
+ * 查询统计数据
  * @param {Object} data
  */
-export function queryPunchInData(data) {
+export function queryStat(data) {
   return request({
-    url: "/punchIn/queryPunchInData",
+    url: "/punchIn/queryStat",
     method: "post",
-    data
+    data,
+     loading: true,
+	  loadingText: "正在查询数据,请稍后..."
   });
 }
 

+ 9 - 0
src/common/enums.js

@@ -44,4 +44,13 @@ export const REPEAT_CATEGORY = {
   WORKDAY: 'WORKDAY',
   HOLIDAY: 'HOLIDAY',
   CUSTOM: 'CUSTOM'
+}
+
+/**
+ * 统计周期类型(周-WEEK、月-MONTH、年-YEAR)
+ */
+export const STAT_PERIOD = {
+  WEEK: 'WEEK',
+  MONTH: 'MONTH',
+  YEAR: 'YEAR'
 }

+ 5 - 0
src/common/router.js

@@ -28,6 +28,11 @@ const router = {
 	 */
 	TASK_EDIT_PAGE: '/pages/task/taskEdit',
 
+	/**
+	 * 打卡任务详情页
+	 */
+	TASK_DETAIL_PAGE: '/pages/task/taskDetail',
+
 	/**
 	 * 主页
 	 */

+ 13 - 6
src/pages.json

@@ -1,10 +1,10 @@
 {
 	"easycom": {
-        "autoscan": true,
-        "custom": {
-            "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
-        }
-    },
+		"autoscan": true,
+		"custom": {
+			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
+		}
+	},
 	"pages": [
 		{
 			"path": "pages/taskTodo",
@@ -63,6 +63,13 @@
 			"style": {
 				"navigationBarTitleText": "任务编辑"
 			}
+		},
+		{
+			"path": "pages/task/taskDetail",
+			"style": {
+				"navigationBarTitleText": "任务详情",
+				"enablePullDownRefresh": true
+			}
 		}
 	],
 	"tabBar": {
@@ -110,4 +117,4 @@
 		"backgroundColor": "#F8F8F8"
 	},
 	"uniIdRouter": {}
-}
+}

+ 404 - 0
src/pages/task/taskDetail.vue

@@ -0,0 +1,404 @@
+<template>
+  <view class="tab-header">
+    <uni-segmented-control :current="currentTabIndex" :values="tabs" style-type="button" active-color="#BDE0FF"
+      @clickItem="switchTab" />
+  </view>
+
+  <!-- 月 -->
+  <view v-if="currentTabIndex === 0">
+
+    <!-- 月份选择器 -->
+    <view class="date-picker">
+      <view class="date-picker-item arrow left-arrow">
+        <uni-icons type="left" size="30" @click="plusMonth(-1)"></uni-icons>
+      </view>
+      <view class="date-picker-item">
+        <picker mode="date" fields="month" :value="currentMonth" @change="monthChange">
+          <view class="picker-view">{{ currentMonth }}</view>
+        </picker>
+      </view>
+      <view class="date-picker-item arrow right-arrow">
+        <uni-icons type="right" size="30" @click="plusMonth(1)"></uni-icons>
+      </view>
+    </view>
+
+    <!-- 统计数据 -->
+    <view class="stat-board">
+      <view class="stat-board-title">打卡统计</view>
+      <view class="stat-board-content">
+        <view class="stat-item">
+          <view class="stat-item-label">本月需打卡</view>
+          <view class="stat-item-value">{{ statMonth.punchInTotalCount }}次</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">本月已打卡</view>
+          <view class="stat-item-value">{{ statMonth.punchInCount }}次</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">完成打卡</view>
+          <view class="stat-item-value">{{ statMonth.punchInDoneCount }}次</view>
+        </view>
+      </view>
+      <view class="stat-board-content stat-mt16">
+        <view class="stat-item">
+          <view class="stat-item-label">打卡率</view>
+          <view class="stat-item-value">{{ statMonth.punchInRate }}%</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">完成率</view>
+          <view class="stat-item-value">{{ statMonth.punchInDoneRate }}%</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">本月获取积分数</view>
+          <view class="stat-item-value">{{ statMonth.points }}</view>
+        </view>
+      </view>
+    </view>
+
+    <uni-section title="打卡日志" padding="16px" type="line">
+      <uni-list>
+        <uni-list-item v-for="taskHistory in statMonth.piTaskHistoryStatVOS" :key="taskHistory.punchInDate">
+          <template v-slot:body>
+            {{ taskHistory.punchInDate }}
+            <span v-if="taskHistory.punchInResult == PUNCH_IN_RESULT.DONE"> 完成打卡</span>
+            <span v-else> 未完成打卡</span>
+            <span v-if="taskHistory.punchInMethod == PUNCH_IN_METHOD.COUNT">,打卡{{ taskHistory.countTrack }}次</span>
+            <span v-if="taskHistory.punchInMethod == PUNCH_IN_METHOD.TIMING">,打卡时长{{ taskHistory.timeTrack }}</span>
+          </template>
+        </uni-list-item>
+      </uni-list>
+      <uni-load-more status="no-more" v-if="statMonth.piTaskHistoryStatVOS.length == 0" />
+    </uni-section>
+  </view>
+
+  <!-- 年 -->
+  <view v-if="currentTabIndex === 1">
+    <!-- 年选择器 -->
+    <view class="date-picker">
+      <view class="date-picker-item arrow left-arrow">
+        <uni-icons type="left" size="30" @click="plusYear(-1)"></uni-icons>
+      </view>
+      <view class="date-picker-item">
+        <picker mode="date" fields="year" :value="currentYear" @change="yearChange">
+          <view class="picker-view">{{ currentYear }}</view>
+        </picker>
+      </view>
+      <view class="date-picker-item arrow right-arrow">
+        <uni-icons type="right" size="30" @click="plusYear(1)"></uni-icons>
+      </view>
+    </view>
+
+    <!-- 统计数据 -->
+    <view class="stat-board">
+      <view class="stat-board-title">打卡统计</view>
+      <view class="stat-board-content">
+        <view class="stat-item">
+          <view class="stat-item-label">全年需打卡</view>
+          <view class="stat-item-value">{{ statYear.punchInTotalCount }}次</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">已打卡</view>
+          <view class="stat-item-value">{{ statYear.punchInCount }}次</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">完成打卡</view>
+          <view class="stat-item-value">{{ statYear.punchInDoneCount }}次</view>
+        </view>
+      </view>
+      <view class="stat-board-content stat-mt16">
+        <view class="stat-item">
+          <view class="stat-item-label">打卡率</view>
+          <view class="stat-item-value">{{ statYear.punchInRate }}%</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">完成率</view>
+          <view class="stat-item-value">{{ statYear.punchInDoneRate }}%</view>
+        </view>
+        <view class="stat-item">
+          <view class="stat-item-label">全年获取积分数</view>
+          <view class="stat-item-value">{{ statYear.points }}</view>
+        </view>
+      </view>
+    </view>
+
+    <uni-section title="打卡日志" padding="16px" type="line">
+      <uni-list>
+        <uni-list-item v-for="taskHistory in statYear.piTaskHistoryStatVOS" :key="taskHistory.punchInDate">
+          <template v-slot:body>
+            {{ taskHistory.punchInDate }}
+            <span v-if="taskHistory.punchInResult == PUNCH_IN_RESULT.DONE"> 完成打卡</span>
+            <span v-else> 未完成打卡</span>
+            <span v-if="taskHistory.punchInMethod == PUNCH_IN_METHOD.COUNT">,打卡{{ taskHistory.countTrack }}次</span>
+            <span v-if="taskHistory.punchInMethod == PUNCH_IN_METHOD.TIMING">,打卡时长{{ taskHistory.timeTrack }}</span>
+          </template>
+        </uni-list-item>
+      </uni-list>
+      <uni-load-more status="no-more" v-if="statYear.piTaskHistoryStatVOS.length == 0" />
+    </uni-section>
+  </view>
+
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app';
+import { punchInApi } from '@/apis/apis';
+import { PUNCH_IN_RESULT, PUNCH_IN_METHOD, STAT_PERIOD } from '@/common/enums'
+
+// 属性
+/**
+ * 当前任务ID
+ */
+const currentTaskId = ref(-1);
+
+/**
+ * 当前统计周期
+ */
+const currentStatPeriod = ref(STAT_PERIOD.MONTH);
+
+/**
+ * 当前选项卡下标
+ */
+const currentTabIndex = ref(0);
+
+/**
+ * 选项卡
+ */
+const tabs = ['月', '年'];
+
+/**
+ * 当前选中的月
+ */
+const currentMonth = ref('2025-04');
+
+/**
+ * 当前选中的年
+ */
+const currentYear = ref('2025');
+
+/**
+ * 打卡统计数据-月
+ */
+const statMonth = ref({
+  punchInTotalCount: 0,
+  punchInCount: 0,
+  punchInDoneCount: 0,
+  punchInRate: 0,
+  punchInDoneRate: 0,
+  points: 0,
+  piTaskHistoryStatVOS: []
+});
+
+/**
+ * 打卡统计数据-年
+ */
+const statYear = ref({
+  punchInTotalCount: 0,
+  punchInCount: 0,
+  punchInDoneCount: 0,
+  punchInRate: 0,
+  punchInDoneRate: 0,
+  points: 0,
+  piTaskHistoryStatVOS: []
+});
+
+// 方法
+/**
+ * 切换选项卡
+ */
+const switchTab = (e) => {
+  if (currentTabIndex.value !== e.currentIndex) {
+    currentTabIndex.value = e.currentIndex
+    loadData();
+  }
+}
+
+/**
+ * 月份选择器
+ */
+const monthChange = (e) => {
+  currentMonth.value = e.detail.value
+}
+
+/**
+ * 年份选择器
+ */
+const yearChange = (e) => {
+  currentYear.value = e.detail.value
+}
+
+/**
+ * 月份加减
+ */
+const plusMonth = (num) => {
+  // 转换成date对象
+  let tempDate = new Date(currentMonth.value);
+  // 月份加减
+  tempDate.setMonth(tempDate.getMonth() + num);
+
+  // daet对象转换成字符串
+  let tempYear = tempDate.getFullYear();
+  let tempMonth = (tempDate.getMonth() + 1).toString().padStart(2, "0"); // 月份从0开始,+1后格式化为两位数
+  currentMonth.value = `${tempYear}-${tempMonth}`;
+}
+
+/**
+ * 年份加减
+ */
+const plusYear = (num) => {
+  // 先转换成数字后再进行加减
+  let tempYear = Number(currentYear.value);
+  tempYear = tempYear + num;
+
+  // 转换成字符串
+  currentYear.value = `${tempYear}`;
+}
+
+/**
+ * 加载数据
+ */
+const loadData = async () => {
+  let res = await punchInApi.queryStat({
+    "statPeriod": currentStatPeriod.value,
+    "statTime": currentStatPeriod.value == STAT_PERIOD.MONTH ? currentMonth.value : currentYear.value,
+    "taskId": currentTaskId.value
+  });
+
+  if (currentStatPeriod.value == STAT_PERIOD.MONTH) {
+    statMonth.value = res;
+  } else {
+    statYear.value = res;
+  }
+}
+
+
+// 生命周期
+onLoad(async (e) => {
+  let currentDate = new Date();
+  let tempYear = currentDate.getFullYear();
+  let tempMonth = (currentDate.getMonth() + 1).toString().padStart(2, "0");
+  currentMonth.value = `${tempYear}-${tempMonth}`;
+  currentYear.value = tempYear;
+
+  if (e.id) {
+    currentTaskId.value = e.id;
+    loadData();
+  }
+});
+
+onPullDownRefresh(() => {
+  loadData();
+  uni.stopPullDownRefresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.tab-header {
+  margin: 16rpx 0;
+  padding: 0 48rpx;
+}
+
+.date-picker {
+  display: flex;
+
+  .date-picker-item {
+    flex: 1;
+
+    picker {
+      width: 100%;
+      height: 100%;
+      display: flex;
+
+      display: flex;
+      /* 水平居中 */
+      justify-content: center;
+      /* 垂直居中 */
+      align-items: center;
+    }
+  }
+
+  .arrow {
+    /* 启用 Flexbox 布局 */
+    display: flex;
+    /* 垂直居中 */
+    align-items: center;
+  }
+
+  .left-arrow {
+    /* 将子元素对齐到右侧 */
+    justify-content: flex-end;
+  }
+
+  .right-arrow {
+    /* 将子元素对齐到右侧 */
+    justify-content: flex-start;
+  }
+}
+
+.stat-board {
+  margin-top: 16rpx;
+
+  .stat-board-title {
+    display: flex;
+    /* 水平居中 */
+    justify-content: center;
+    /* 垂直居中 */
+    align-items: center;
+
+    font-size: 32rpx;
+    font-weight: 400;
+    letter-spacing: 0rpx;
+    line-height: 46.34rpx;
+    color: #000000;
+  }
+
+  .stat-board-content {
+    display: flex;
+    margin-top: 16rpx;
+
+    .stat-item {
+      flex: 1;
+      display: inline-block;
+
+      .stat-title {
+        width: 100%;
+        height: 100%;
+        font-size: 32rpx;
+        font-weight: 400;
+        letter-spacing: 0rpx;
+        line-height: 46.34rpx;
+        color: #000000;
+        text-align: center;
+
+        display: flex;
+        /* 启用flex布局 */
+        justify-content: center;
+        /* 水平居中 */
+        align-items: center;
+        /* 垂直居中 */
+      }
+
+      .stat-item-label {
+        font-size: 24rpx;
+        font-weight: 400;
+        letter-spacing: 0rpx;
+        line-height: 34.75rpx;
+        color: rgba(0, 0, 0, 1);
+        color: #000000;
+        text-align: center;
+        vertical-align: middle;
+      }
+
+      .stat-item-value {
+        font-size: 32rpx;
+        font-weight: 400;
+        letter-spacing: 0rpx;
+        line-height: 46.34rpx;
+        color: #000000;
+        text-align: center;
+        vertical-align: middle;
+      }
+    }
+  }
+}
+</style>

+ 248 - 37
src/pages/task/taskEdit.vue

@@ -13,10 +13,10 @@
 
     <!-- 显示配置 -->
     <uni-section title="显示配置" padding="16px" type="line">
-      <uni-forms-item label="显示顺序" required name="dislayOrder">
-        <uni-easyinput v-model="taskFormData.dislayOrder" placeholder="显示顺序" type="number" />
+      <uni-forms-item label="显示顺序" required name="displayOrder">
+        <uni-easyinput v-model="taskFormData.displayOrder" placeholder="显示顺序" type="number" />
       </uni-forms-item>
-      <uni-forms-item label="显示时间" required name="displayTime">
+      <uni-forms-item label="开始显示时间" required name="displayTime">
         <picker mode="time" :value="taskFormData.displayTime" @change="displayTimeChange">
           <view class="pick-box">{{ taskFormData.displayTime }}</view>
         </picker>
@@ -72,7 +72,7 @@
         <uni-easyinput v-model="taskFormData.extraPoints" placeholder="奖励的积分(额外)" type="number" />
       </uni-forms-item>
       <uni-forms-item label="积分区间" required :rules="[{ 'required': true, errorMessage: '起始值必填' }]"
-        :name="['taskExtList', index, 'extraPoints', 'value']">
+        :name="['taskExtList', index, 'extraPoints', 'value']" v-if="taskFormData.extraMethod == EXTRA_METHOD.INTERVAL">
         <template v-for="(item, index) in taskFormData.taskExtList" :key="item.id">
           <view class="extra-box">
             <view class="extra-box-item">
@@ -85,7 +85,8 @@
           </view>
         </template>
       </uni-forms-item>
-      <button type="primary" size="mini" @click="addTaskExt()">新增拓展信息</button>
+      <button type="primary" size="mini" @click="addTaskExt()"
+        v-if="taskFormData.extraMethod == EXTRA_METHOD.INTERVAL">新增拓展信息</button>
     </uni-section>
 
     <!-- 节假日配置 -->
@@ -125,20 +126,20 @@
 
     <!-- 连续规则配置 -->
     <uni-section title="连续规则配置" padding="16px" type="line">
-      <uni-forms-item label="是否启用连续规则" required name="continuousStatus">
+      <uni-forms-item label="是否启用连续规则" required name="continueStatus">
         <uni-data-select :localdata="commonEnabledStatusCheckBoxData"
-          v-model="taskFormData.continuousStatus"></uni-data-select>
+          v-model="taskFormData.continueStatus"></uni-data-select>
       </uni-forms-item>
       <uni-forms-item label="宽限期(单位:天)" required name="graceDay"
-        v-if="taskFormData.continuousStatus == COMMON_ENABLED_STATUS.ENABLED">
+        v-if="taskFormData.continueStatus == COMMON_ENABLED_STATUS.ENABLED">
         <uni-easyinput v-model="taskFormData.graceDay" placeholder="宽限期(单位:天)" type="number" />
       </uni-forms-item>
       <uni-forms-item label="连续中断次数" required name="continueInterruptedCount"
-        v-if="taskFormData.continuousStatus == COMMON_ENABLED_STATUS.ENABLED">
+        v-if="taskFormData.continueStatus == COMMON_ENABLED_STATUS.ENABLED">
         <uni-easyinput v-model="taskFormData.continueInterruptedCount" placeholder="连续中断次数" type="number" />
       </uni-forms-item>
       <uni-forms-item label="惩罚天数(单位:天)" required name="penaltyDay"
-        v-if="taskFormData.continuousStatus == COMMON_ENABLED_STATUS.ENABLED">
+        v-if="taskFormData.continueStatus == COMMON_ENABLED_STATUS.ENABLED">
         <uni-easyinput v-model="taskFormData.penaltyDay" placeholder="惩罚天数(单位:天)" type="number" />
       </uni-forms-item>
     </uni-section>
@@ -156,12 +157,32 @@
         <uni-data-select :localdata="commonEnabledStatusCheckBoxData"
           v-model="taskFormData.taskPointsStatus"></uni-data-select>
       </uni-forms-item>
+      <uni-forms-item label="积分区间" required :rules="[{ 'required': true, errorMessage: '起始值必填' }]"
+        :name="['continueTaskExtList', index, 'extraPoints', 'value']"
+        v-if="taskFormData.taskPointsStatus == COMMON_ENABLED_STATUS.ENABLED">
+        <template v-for="(item, index) in taskFormData.continueTaskExtList" :key="item.id">
+          <view class="extra-box">
+            <view class="extra-box-item">
+              <uni-easyinput v-model="taskFormData.continueTaskExtList[index].initialValue.value"
+                placeholder="请输入起始值" />
+            </view>
+            <view class="extra-box-item">
+              <uni-easyinput v-model="taskFormData.continueTaskExtList[index].extraPoints.value"
+                placeholder="请输入奖励积分" />
+            </view>
+            <button class="button extra-box-btn" size="mini" type="default"
+              @click="deleteContinueTaskExt(item.id)">删除</button>
+          </view>
+        </template>
+      </uni-forms-item>
+      <button type="primary" size="mini" @click="addContinueTaskExt()"
+        v-if="taskFormData.taskPointsStatus == COMMON_ENABLED_STATUS.ENABLED">新增拓展信息</button>
     </uni-section>
 
     <!-- 按钮组 -->
     <view class="button-container">
-      <button type="default" style="width:300rpx;" @click="cancel">取消</button>
-      <button type="primary" style="color:#ffffff;backgroundColor:#2A82E4;width:300rpx;" @click="saveTask">保存</button>
+      <button type="default" style="width:300rpx;" @click="cancel()">取消</button>
+      <button type="primary" style="color:#ffffff;backgroundColor:#2A82E4;width:300rpx;" @click="saveTask()">保存</button>
     </view>
 
   </uni-forms>
@@ -185,18 +206,16 @@ const taskForm = ref(null);
  * 打卡任务表单数据
  */
 const taskFormData = ref({
-  "points": 5,
-  "holidayStatus": COMMON_ENABLED_STATUS.DISABLED,
-  "fullAttendanceStatus": COMMON_ENABLED_STATUS.DISABLED,
+  "repeatCategory": REPEAT_CATEGORY.EVERYDAY,
   "punchInMethod": PUNCH_IN_METHOD.SINGLE,
-  "compareRule": COMPARE_RULE.GTE,
-  "countTrack": 10,
-  "timeTrack": "00:00",
-  "continuousStatus": COMMON_ENABLED_STATUS.DISABLED,
   "extraMethod": EXTRA_METHOD.NONE,
+  "holidayStatus": COMMON_ENABLED_STATUS.DISABLED,
+  "fullAttendanceStatus": COMMON_ENABLED_STATUS.DISABLED,
+  "continueStatus": COMMON_ENABLED_STATUS.DISABLED,
   "autoStatus": COMMON_ENABLED_STATUS.DISABLED,
+  "taskPointsStatus": COMMON_ENABLED_STATUS.DISABLED,
   "taskExtList": [],
-  "continueTaskExtList": [],
+  "continueTaskExtList": []
 });
 
 /**
@@ -207,12 +226,131 @@ const taskFormRules = ref({
     rules: [{
       required: true,
       errorMessage: '任务名称不能为空'
+    },
+    {
+      minLength: 2,
+      maxLength: 30,
+      errorMessage: '长度在 {minLength} 到 {maxLength} 个字符',
+    }]
+  },
+  description: {
+    rules: [{
+      maxLength: 100,
+      errorMessage: '描述不能超过 {maxLength} 个字符'
+    }]
+  },
+  displayOrder: {
+    rules: [{
+      required: true,
+      errorMessage: '显示顺序不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  displayTime: {
+    rules: [{
+      required: true,
+      errorMessage: '显示时间不能为空'
+    }]
+  },
+  repeatCategory: {
+    rules: [{
+      required: true,
+      errorMessage: '重复类型不能为空'
+    }]
+  },
+  punchInMethod: {
+    rules: [{
+      required: true,
+      errorMessage: '打卡方式不能为空'
+    }]
+  },
+  compareRule: {
+    rules: [{
+      required: true,
+      errorMessage: '比较规则不能为空'
+    }]
+  },
+  countTrack: {
+    rules: [{
+      required: true,
+      errorMessage: '数值不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  timeTrack: {
+    rules: [{
+      required: true,
+      errorMessage: '数值不能为空'
+    }]
+  },
+  points: {
+    rules: [{
+      required: true,
+      errorMessage: '奖励积分不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  extraMethod: {
+    rules: [{
+      required: true,
+      errorMessage: '额外奖励方式不能为空'
+    }]
+  },
+  extraTimeStep: {
+    rules: [{
+      required: true,
+      errorMessage: '额外的时间间隔方式不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  extraPoints: {
+    rules: [{
+      required: true,
+      errorMessage: '奖励的积分(额外)不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  taskExtList: {
+    rules: [{
+      required: true,
+      errorMessage: '积分区间不能为空'
+    }]
+  },
+  holidayStatus: {
+    rules: [{
+      required: true,
+      errorMessage: '节假日双倍不能为空'
     }]
   },
-  rewardNum: {
+  holidayCountTrack: {
     rules: [{
       required: true,
-      errorMessage: '奖励数值不能为空'
+      errorMessage: '数值不能为空'
     }, {
       format: 'number',
       errorMessage: "请输入有效数字"
@@ -221,37 +359,43 @@ const taskFormRules = ref({
       errorMessage: "最小值{minimum}"
     }]
   },
-  weekendDoubleFlag: {
+  holidayTimeTrack: {
     rules: [{
       required: true,
-      errorMessage: '周末双倍奖励不能为空'
+      errorMessage: '时间不能为空'
     }]
   },
-  fullAttendanceFlag: {
+  fullAttendanceStatus: {
     rules: [{
       required: true,
       errorMessage: '全勤奖励不能为空'
     }]
   },
-  category: {
+  fullAttendancePeriod: {
     rules: [{
       required: true,
-      errorMessage: '任务类型不能为空'
+      errorMessage: '全勤周期不能为空'
     }]
   },
-  description: {
+  fullAttendancefaulttoleranceCnt: {
     rules: [{
-      maxLength: 100,
-      errorMessage: '描述不能超过{maxLength}个字符'
+      required: true,
+      errorMessage: '数值不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
     }]
   },
-  rule: {
+  continueStatus: {
     rules: [{
       required: true,
-      errorMessage: '判断规则不能为空'
+      errorMessage: '是否启用连续规则不能为空'
     }]
   },
-  countTrack: {
+  graceDay: {
     rules: [{
       required: true,
       errorMessage: '数值不能为空'
@@ -263,10 +407,46 @@ const taskFormRules = ref({
       errorMessage: "最小值{minimum}"
     }]
   },
-  timeTrack: {
+  continueInterruptedCount: {
     rules: [{
       required: true,
       errorMessage: '数值不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  penaltyDay: {
+    rules: [{
+      required: true,
+      errorMessage: '数值不能为空'
+    }, {
+      format: 'number',
+      errorMessage: "请输入有效数字"
+    }, {
+      minimum: 1,
+      errorMessage: "最小值{minimum}"
+    }]
+  },
+  autoStatus: {
+    rules: [{
+      required: true,
+      errorMessage: '是否启用自动打卡不能为空'
+    }]
+  },
+  taskPointsStatus: {
+    rules: [{
+      required: true,
+      errorMessage: '是否启用任务积分计算不能为空'
+    }]
+  },
+  continueTaskPointsStatus: {
+    rules: [{
+      required: true,
+      errorMessage: '是否启用任务积分计算不能为空'
     }]
   }
 });
@@ -413,7 +593,7 @@ const holidayTimeChange = (e) => {
 
 
 /**
- * 新增拓展信息
+ * 新增任务拓展信息
  */
 const addTaskExt = () => {
   taskFormData.value.taskExtList.push({
@@ -430,13 +610,38 @@ const addTaskExt = () => {
 }
 
 /**
- * 删除拓展信息
+ * 删除任务拓展信息
  */
 const deleteTaskExt = (id) => {
   let index = taskFormData.value.taskExtList.findIndex(v => v.id === id)
   taskFormData.value.taskExtList.splice(index, 1)
 }
 
+/**
+ * 新增任务拓展信息
+ */
+const addContinueTaskExt = () => {
+  taskFormData.value.continueTaskExtList.push({
+    id: Date.now(),
+    "initialValue": {
+      label: '起始值',
+      value: ''
+    },
+    "extraPoints": {
+      label: '奖励的积分(额外)',
+      value: ''
+    }
+  })
+}
+
+/**
+ * 删除任务拓展信息
+ */
+const deleteContinueTaskExt = (id) => {
+  let index = taskFormData.value.continueTaskExtList.findIndex(v => v.id === id)
+  taskFormData.value.continueTaskExtList.splice(index, 1)
+}
+
 /**
  * 保存打卡任务
  */
@@ -452,6 +657,13 @@ const saveTask = () => {
     setTimeout(() => {
       uni.navigateBack();
     }, 2000);
+  }).catch(err => {
+    console.log('表单错误信息:', err);
+    uni.showModal({
+      title: "保存失败",
+      content: "请检查表单项是否已正确填写",
+      showCancel: false
+    });
   });
 }
 
@@ -463,7 +675,6 @@ const cancel = () => {
 }
 
 onLoad(async (e) => {
-  console.log('taskEdit', e);
   if (e.id) {
     const res = await punchInApi.queryTask({ "id": e.id });
     taskFormData.value = res;

+ 11 - 12
src/pages/taskList.vue

@@ -6,11 +6,11 @@
       </view>
       <view class="task-title" v-else>任务</view>
       <view class="task-add-btn" @click="goTaskEditPage()">
-        <uni-icons type="plusempty" size="30" color="#406CE7"></uni-icons>
+        立即创建
       </view>
     </view>
     <view class="task-item" v-for="task in tasks" :key="task.id">
-      <view class="main-box" @click="goPunchInDetailPage(task.id)">
+      <view class="main-box" @click="goTaskDetailPage(task.id)">
         <view class="item-header">
           <span class="item-title">{{ task.taskName }}</span>
           <span class="item-reward">x{{ task.points }}</span>
@@ -103,11 +103,11 @@ const tasks = ref([]);
 // 方法
 
 /**
- * 跳转打卡编辑页面
+ * 跳转打卡任务详情页面
  */
-const goPunchInEditPage = () => {
+const goTaskDetailPage = (id) => {
   uni.navigateTo({
-    url: router.PUNCHIN_EDIT_URL
+    url: router.TASK_DETAIL_PAGE + "?id=" + id
   });
 };
 
@@ -284,15 +284,14 @@ onPullDownRefresh(() => {
     }
 
     .task-add-btn {
-      display: inline-flex;
-      justify-content: center;
-      align-items: center;
       position: absolute;
       right: 0rpx;
-      width: 60rpx;
-      height: 60rpx;
-      border-radius: 10rpx;
-      border: 3px solid rgba(64, 108, 231, 1);
+
+      font-size: 26rpx;
+      font-weight: 400;
+      letter-spacing: 0rpx;
+      line-height: 37.65rpx;
+      color: #2879ED;
     }
   }
 

+ 13 - 14
src/pages/taskTodo.vue

@@ -6,12 +6,12 @@
       <view class="task-title" v-if="tasks.length && tasks.length > 0">任务({{ tasks.length }}个)
       </view>
       <view class="task-title" v-else>任务</view>
-      <view class="task-add-btn" @click="goPunchInEditPage">
-        <uni-icons type="plusempty" size="30" color="#406CE7"></uni-icons>
+      <view class="task-add-btn" @click="goTaskEditPage">
+        立即创建
       </view>
     </view>
     <view class="task-item" v-for="task in tasks" :key="task.id">
-      <view class="main-box" @click="goPunchInDetailPage(task.id)">
+      <view class="main-box" @click="goTaskDetailPage(task.id)">
         <view class="item-header">
           <span class="item-title">{{ task.taskName }}</span>
           <span class="item-reward">x{{ task.points }}</span>
@@ -172,18 +172,18 @@ const doPunchIn = async (id) => {
 /**
  * 跳转打卡编辑页面
  */
-const goPunchInEditPage = () => {
+const goTaskEditPage = () => {
   uni.navigateTo({
-    url: router.PUNCHIN_EDIT_URL
+    url: router.TASK_EDIT_PAGE
   });
 };
 
 /**
  * 跳转打卡详情页面
  */
-const goPunchInDetailPage = (id) => {
+const goTaskDetailPage = (id) => {
   uni.navigateTo({
-    url: router.PUNCHIN_DETAIL_URL + "?id=" + id
+    url: router.TASK_DETAIL_PAGE + "?id=" + id
   });
 };
 
@@ -228,15 +228,14 @@ onPullDownRefresh(() => {
     }
 
     .task-add-btn {
-      display: inline-flex;
-      justify-content: center;
-      align-items: center;
       position: absolute;
       right: 0rpx;
-      width: 60rpx;
-      height: 60rpx;
-      border-radius: 10rpx;
-      border: 3px solid rgba(64, 108, 231, 1);
+
+      font-size: 26rpx;
+      font-weight: 400;
+      letter-spacing: 0rpx;
+      line-height: 37.65rpx;
+      color: #2879ED;
     }
   }