浏览代码

【feat】【第二版开发】

1.功能开发
ChenYL 1 年之前
父节点
当前提交
4193b23253

+ 34 - 0
common/constants/punchin.js

@@ -0,0 +1,34 @@
+// 打卡任务常量
+
+/**
+ * 选项
+ */
+export const statusRange = [
+	{
+		"value": 1,
+		"text": "启用"
+	},
+	{
+		"value": 2,
+		"text": "关闭"
+	}
+];
+
+/**
+ * 打卡类型
+ */
+export const categoryRange = [
+    { value: 0, text: "单次打卡" },
+    { value: 1, text: "计数" },
+    { value: 2, text: "计时" },
+  ];
+  
+/**
+ * 比较规则
+ */
+export const ruleRange = [
+	{ value: 0, text: "大于等于" },
+	{ value: 1, text: "小于等于" },
+	{ value: 2, text: "大于" },
+	{ value: 3, text: "小于" },
+];

+ 27 - 0
common/constants/scratch.js

@@ -0,0 +1,27 @@
+//  刮刮乐常量
+
+/**
+ * 动作类型:投入
+ */
+export const SCRATCH_ACTION_TYPE_INVEST = 0;
+
+/**
+ * 动作类型:中奖
+ */
+export const SCRATCH_ACTION_TYPE_WIN = 1;
+
+/**
+ * 来源范围
+ */
+export const scratchSourceRange = [
+	{ value: 'WELFARE_LOTTERY', text: "福利彩票" },
+	{ value: 'SPORTS_LOTTERY', text: "体育彩票" }
+];
+  
+  /**
+   * 种类范围
+   */
+export const scratchCategoryRange = [
+	{ value: 'XINYUN88', text: "幸运88" },
+	{ value: 'CHAOGEILI', text: "超给力" }
+];

+ 413 - 108
pages/index/index.vue

@@ -1,126 +1,227 @@
 <template>
 	<main-layout>
-		<template>
-			<!-- 用户信息区 -->
-			<view class="user-info">
-				<view class="user-icon">
-					<uni-icons type="person" size="30"></uni-icons>	
-				</view>
-				<span class="nickname" v-if="!userInfo" @click="goUserInfoPage">登录</span>
-				<span class="user-btn" v-if="userInfo" @click="goUserInfoPage">用户中心</span>
+		<!-- 用户信息区 -->
+		<view class="user-info">
+			<view class="user-icon">
+				<uni-icons type="person" size="30"></uni-icons>	
 			</view>
-			
-			<!-- 结算 -->
-			<view class="settle-container">
-				<!-- 刮刮乐、奖励数相关区域 -->
-				<view class="left-box">
-					<view class="split-box">
-						<view class="line-box">
-							<span>投入</span>
-							<span class="number-box">¥{{userInfo.lotteryInvestAmount}}</span>
-						</view>
-						<view class="under-line-box">
-							<span>中奖</span>
-							<span class="number-box">¥{{userInfo.lotteryWinAmount}}</span>
-						</view>
+			<span class="nickname" v-if="!isLogin" @click="goUserInfoPage">登录</span>
+			<span class="user-btn" v-if="isLogin" @click="goUserInfoPage">用户中心</span>
+		</view>
+		
+		<!-- 结算 -->
+		<view class="settle-container">
+			<!-- 刮刮乐、奖励数相关区域 -->
+			<view class="left-box">
+				<view class="split-box">
+					<view class="line-box">
+						<span>投入</span>
+						<span class="number-box" @click="addScratchRecord(SCRATCH_ACTION_TYPE_INVEST)">
+							¥{{userInfo.lotteryInvestAmount}}
+							<uni-icons type="compose" color="#FFFFFF"></uni-icons>
+						</span>
+					</view>
+					<view class="under-line-box">
+						<span>中奖</span>
+						<span class="number-box"  @click="addScratchRecord(SCRATCH_ACTION_TYPE_WIN)">
+							¥{{userInfo.lotteryWinAmount}}
+							<uni-icons type="compose" color="#FFFFFF"></uni-icons>
+						</span>
 					</view>
-					<view class="split-box">
-						<view class="line-box">
-							<span>已领取</span>
-							<span class="number-box">{{userInfo.claimedRewardNum}}</span>
-						</view>
-						<view  class="under-line-box">
-							<span>总奖励</span>
-							<span class="number-box">{{userInfo.totalRewardNum}}</span>
-						</view>
-					</view>	
 				</view>
-				<!-- 待领取奖励区 -->
-				<view class="right-box">
-					<view class="reward-title">待领取奖励数</view>
-					<view class="reward-num">{{userInfo.unclaimedRewardNum}}</view>
-					<view class="reward-btn-group">
-						<view class="reward-btn-left">全部领取</view>
-						<view class="reward-btn-right">部分领取</view>
+				<view class="split-box">
+					<view class="line-box">
+						<span>已领取</span>
+						<span class="number-box">{{userInfo.claimedRewardNum}}</span>
+					</view>
+					<view  class="under-line-box">
+						<span>总奖励</span>
+						<span class="number-box">{{userInfo.totalRewardNum}}</span>
 					</view>
+				</view>	
+			</view>
+			<!-- 待领取奖励区 -->
+			<view class="right-box">
+				<view class="reward-title">待领取奖励数</view>
+				<view class="reward-num">{{userInfo.unclaimedRewardNum}}</view>
+				<view class="reward-btn-group">
+					<view class="reward-btn-left" @click="claimReward('all')">全部领取</view>
+					<view class="reward-btn-right" @click="claimReward('part')">部分领取</view>
 				</view>
-				<!-- 刷新按钮 -->
-				<view class="refresh-btn" @click="getUserInfo">
-					<uni-icons type="refreshempty" color="#406CE7"></uni-icons>	
+			</view>
+			<!-- 刷新按钮 -->
+			<view class="refresh-btn" @click="getUserInfo">
+				<uni-icons type="refreshempty" color="#406CE7"></uni-icons>	
+			</view>
+		</view>
+		
+		<!-- 打卡任务 -->
+		<view class="task-container">
+			<view class="task-header">
+				<view class="task-title" v-if="punchIns.length && punchIns.length > 0">任务({{punchIns.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>
 			</view>
-			
-			<!-- 打卡任务 -->
-			<view class="task-container">
-				<view class="task-header">
-					<view class="task-title" v-if="punchIns.length && punchIns.length > 0">任务({{punchIns.length}}个)
+			<view class="task-item" v-for="punchIn in punchIns" :key="punchIn.punchInId">
+				<view class="main-box" @click="goPunchInDetailPage(punchIn.punchInId)">
+					<view class="item-header">
+						<span class="item-title">{{punchIn.taskName}}</span>
+						<span class="item-reward">x{{punchIn.rewardNum}}</span>
+						<view class="item-tag" v-if="punchIn.fullAttendanceFlag">全勤奖励</view>
+						<view class="item-tag" v-if="punchIn.weekendDoubleFlag">周末双倍</view>
 					</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="item-desc">
+						规则:{{punchIn.description}}
 					</view>
-				</view>
-				<view class="task-item" v-for="punchIn in punchIns" :key="punchIn.punchInId">
-					<view class="main-box" @click="goPunchInDetailPage(punchIn.punchInId)">
-						<view class="item-header">
-							<span class="item-title">{{punchIn.taskName}}</span>
-							<span class="item-reward">x{{punchIn.rewardNum}}</span>
-							<view class="item-tag" v-if="punchIn.fullAttendanceFlag">全勤奖励</view>
-							<view class="item-tag" v-if="punchIn.weekendDoubleFlag">周末双倍</view>
-						</view>
-						<view class="item-desc">
-							规则:{{punchIn.description}}
-						</view>
-						<view class="item-detail-list">
-							<view class="item-detail" v-for="punchInRecord in punchIn.punchInRecords"
-								:key="punchInRecord.punchInDate">
-								<view class="detail-text">
-									<uni-dateformat :date="punchInRecord.punchInDate" format="M/d"></uni-dateformat>
-								</view>
-								<view class="detail-box" style="background-color: #E5E5E5;"
-									v-if="punchInRecord.punchInStatus == 'uncreated'"></view>
-								<view class="detail-box" style="background-color: #A5D63F;"
-									v-if="punchInRecord.punchInStatus == 'punchIn'"></view>
-								<view class="detail-box" style="background-color: #D43030;"
-									v-if="punchInRecord.punchInStatus == 'unPunchIn'"></view>
-								<view class="detail-box"
-									v-if="punchInRecord.punchInStatus == 'futureTime' || punchInRecord.punchInStatus == 'todayUnknown'">
-								</view>
+					<view class="item-detail-list">
+						<view class="item-detail" v-for="punchInRecord in punchIn.punchInRecords"
+							:key="punchInRecord.punchInDate">
+							<view class="detail-text">
+								<uni-dateformat :date="punchInRecord.punchInDate" format="M/d"></uni-dateformat>
+							</view>
+							<view class="detail-box" style="background-color: #E5E5E5;"
+								v-if="punchInRecord.punchInStatus == 'uncreated'"></view>
+							<view class="detail-box" style="background-color: #A5D63F;"
+								v-if="punchInRecord.punchInStatus == 'punchIn'"></view>
+							<view class="detail-box" style="background-color: #D43030;"
+								v-if="punchInRecord.punchInStatus == 'unPunchIn'"></view>
+							<view class="detail-box"
+								v-if="punchInRecord.punchInStatus == 'futureTime' || punchInRecord.punchInStatus == 'todayUnknown'">
 							</view>
 						</view>
 					</view>
-					<view class="func-box" v-if="punchIn.category == 0" @click="doPunchIn(punchIn.punchInId)">
-						<span>完成</span>
-					</view>
-					<view class="func-box" v-if="punchIn.category == 1" @click="doPunchIn(punchIn.punchInId)">
-						<span>{{punchIn.countTrack}}</span>
-						<span>计数</span>
-					</view>
-					<view class="func-box" v-if="punchIn.category == 2" @click="doPunchIn(punchIn.punchInId)">
-						<span>
-							{{punchIn.timeTrack.slice(0, 5)}}
-						</span>
-						<span>计时</span>
-					</view>
+				</view>
+				<view :class="punchIn.punchInStatus == 'punchIn'?'func-box func-box-finish':'func-box func-box-unfinish'" v-if="punchIn.category == 0" @click="doPunchIn(punchIn.punchInId)">
+					<span v-if="punchIn.punchInStatus == 'unPunchIn'">完成</span>
+					<span v-else>已完成</span>
+				</view>
+				<view :class="punchIn.punchInStatus == 'punchIn'?'func-box func-box-finish':'func-box func-box-unfinish'" v-if="punchIn.category == 1" @click="doPunchIn(punchIn.punchInId)">
+					<span>{{punchIn.countTrack}}</span>
+					<span>计数</span>
+				</view>
+				<view :class="punchIn.punchInStatus == 'punchIn'?'func-box func-box-finish':'func-box func-box-unfinish'" v-if="punchIn.category == 2" @click="doPunchInForTimeTrack(punchIn.punchInId, punchIn.timeTrack)">
+					<span>
+						{{punchIn.timeTrack.slice(0, 5)}}
+					</span>
+					<span>计时</span>
 				</view>
 			</view>
+		</view>
+		
+		<!-- 刮刮乐弹出框 -->
+		<uni-popup ref="scratchInputDialog" type="dialog" :is-mask-click="false">
+			<uni-popup-dialog 
+				mode="input" 
+				:before-close="true" 
+				:title="scratchFormData.actionType == SCRATCH_ACTION_TYPE_INVEST ? '投入金额' : '中奖金额'" 
+				confirmText="保存" 
+				@confirm="scratchFormConfirm"
+				@close="scratchFormClose">
+				<view style="width: 100%;">
+					<uni-forms ref="scratchForm" :modelValue="scratchFormData" :rules="scratchFormRules" label-position="top" :label-width="150">
+						<uni-forms-item label="刮刮乐来源" name="source" required>
+							<uni-data-select :localdata="scratchSourceRange" v-model="scratchFormData.source"></uni-data-select>
+						</uni-forms-item>
+						<uni-forms-item label="刮刮乐种类" name="category" required>
+							<uni-data-select :localdata="scratchCategoryRange" v-model="scratchFormData.category"></uni-data-select>
+						</uni-forms-item>
+						<uni-forms-item :label="scratchFormData.actionType == SCRATCH_ACTION_TYPE_INVEST ? '投入金额' : '中奖金额'" name="amount" required>
+							<uni-easyinput type="digit" placeholder="请输入金额" v-model="scratchFormData.amount"></uni-easyinput>
+						</uni-forms-item>
+					</uni-forms>
+				</view>
+			</uni-popup-dialog>
+		</uni-popup>
+		
+		<!-- 领取奖励弹出框 -->
+		<uni-popup ref="claimRewardInputDialog" type="dialog" :is-mask-click="false">
+			<uni-popup-dialog
+				mode="input" 
+				:before-close="true" 
+				title="领取奖励" 
+				confirmText="领取" 
+				@confirm="claimRewardFormConfirm"
+				@close="claimRewardFormClose">
+				<view style="width: 100%;">
+					<uni-forms ref="claimRewardForm" :modelValue="claimRewardFormData" :rules="claimRewardFormRules" label-position="top" :label-width="150">
+						<uni-forms-item name="claimRewardNum">
+							<uni-easyinput type="digit" placeholder="请输入领取的奖励数" v-model="claimRewardFormData.claimRewardNum"></uni-easyinput>
+						</uni-forms-item>
+					</uni-forms>
+				</view>
+			</uni-popup-dialog>
+		</uni-popup>
 			
-			<!-- 弹出框 -->
-			<view>
-				<uni-popup ref="claimRewardDialog" type="dialog">
-					<uni-popup-dialog ref="inputClose" mode="input" title="领取奖励" value="对话框预置提示内容!" placeholder="请输入领取的奖励数"
-						@confirm="claimRewardConfirm"></uni-popup-dialog>
-				</uni-popup>
-			</view>
-		</template>
+		<!-- 计时弹出框 -->
+		<uni-popup ref="timeTrackInputDialog" type="dialog" :is-mask-click="false">
+			<uni-popup-dialog
+				mode="input" 
+				:before-close="true" 
+				title="计时记录" 
+				confirmText="保存" 
+				@confirm="timeTrackFormConfirm"
+				@close="timeTrackFormClose">
+				<view style="width: 100%;">
+					<uni-forms ref="timeTrackForm" :modelValue="timeTrackFormData" :rules="timeTrackFormRules" label-position="top" :label-width="150">
+						<uni-forms-item name="timeTrack">
+							<picker mode="time" :value="timeTrackFormData.timeTrack" @change="timeTrackChange">
+								<view class="pick-box">{{timeTrackFormData.timeTrack}}</view>
+							</picker>
+						</uni-forms-item>
+					</uni-forms>
+				</view>
+			</uni-popup-dialog>
+		</uni-popup>
 	</main-layout>
 </template>
 
 <script setup>
 	import { onMounted, ref } from 'vue';
 	import { onLoad, onPullDownRefresh, onShow } from "@dcloudio/uni-app";
-	import { rewardApi, punchInApi, userApi } from '@/service/apis.js';
+	import { rewardApi, punchInApi, userApi, scratchApi } from '@/service/apis.js';
 	import router from '@/common/constants/router.js';
+	import { scratchSourceRange, scratchCategoryRange, SCRATCH_ACTION_TYPE_INVEST, SCRATCH_ACTION_TYPE_WIN } from '@/common/constants/scratch';
+	
+	// 组件
+	/**
+	 * 刮刮乐弹出框
+	 */
+	const scratchInputDialog = ref(null);
+	
+	/**
+	 * 刮刮乐表单
+	 */
+	const scratchForm = ref(null);
+	
+	/**
+	 * 领取奖励弹出框
+	 */
+	const claimRewardInputDialog = ref(null);
+	
+	/**
+	 * 领取奖励表单
+	 */
+	const claimRewardForm = ref(null);
+	
+	/**
+	 * 计时弹出框
+	 */
+	const timeTrackInputDialog = ref(null);
+	
+	/**
+	 * 计时表单
+	 */
+	const timeTrackForm = ref(null);
+	
+	
+	// 属性
+	/**
+	 * 登录标志位
+	 */
+	const isLogin = ref(false);
 
 	/**
 	 * 用户信息
@@ -138,6 +239,88 @@
 	 */
 	const punchIns = ref([]);
 	
+	/**
+	 * 刮刮乐表单数
+	 */
+	const scratchFormData = ref({});
+	
+	/**
+	 * 刮刮乐表单校验规则
+	 */
+	const scratchFormRules = ref({
+		"source": {
+			rules: [{
+				required: true,
+				errorMessage: "请选择来源"
+			}]
+		},
+		"category": {
+			rules: [{
+				required: true,
+				errorMessage: "请选择种类"
+			}]
+		},
+		"amount": {
+			rules: [{
+				required: true,
+				errorMessage: "请输入金额"
+			}, {
+				format: 'number',
+				errorMessage: "请输入有效数字"
+			}, {
+				minimum: 0.01,
+				errorMessage: "最小{minimum}元"
+			}]
+		}
+	
+	});
+	
+	/**
+	 * 领取奖励表单数据
+	 */
+	const claimRewardFormData = ref({
+		claimRewardNum: 1
+	});
+	
+	/**
+	 * 领取奖励表单规则
+	 */
+	const claimRewardFormRules = ref({
+		claimRewardNum: {
+			rules: [{
+				required: true,
+				errorMessage: "请输入领取的奖励数"
+			}, {
+				format: 'number',
+				errorMessage: "请输入有效数字"
+			}, {
+				minimum: 1,
+				errorMessage: "最少领取{minimum}个"
+			}]
+		}
+	});
+	
+	/**
+	 * 计时表单数据
+	 */
+	const timeTrackFormData = ref({
+		timeTrack: '00:00'
+	});
+	
+	/**
+	 * 计时表单规则
+	 */
+	const timeTrackFormRules = ref({
+		timeTrack: {
+			rules: [{
+				required: true,
+				errorMessage: "请输入时间"
+			}]
+		}
+	});
+	
+	
+	// 方法
 	/**
 	 * 获取用户信息
 	 */
@@ -154,19 +337,73 @@
 		punchIns.value = res;
 	}
 	
-
 	/**
-	 * 领取奖励对话框
+	 * 增加刮刮乐记录
 	 */
-	const claimRewardDialog = ref(null);
-
+	const addScratchRecord = async (e) => {
+		// 重置上一轮的表单数据
+		scratchFormData.value = {
+			actionType: e
+		};
+		scratchInputDialog.value.open();
+	}
+	
+	/**
+	 * 刮刮乐表单提交
+	 */
+	const scratchFormConfirm = async () => {
+		scratchForm.value.validate(['actionType']).then(data => {
+			return scratchApi.addScratchRecord(data);
+		}).then(e => {
+			scratchInputDialog.value.close();
+			getUserInfo();
+		});
+	}
+	
+	/**
+	 * 刮刮乐表单取消
+	 */
+	const scratchFormClose = async () => {
+		scratchInputDialog.value.close();
+	}
+	
+	/**
+	 * 领取奖励(部分)
+	 * @param {String} claimType 部分领取或全部领取(part-部分/all-全部)
+	 */
+	const claimReward = (claimType) => {
+		if (userInfo.value.unclaimedRewardNum == 0) {
+			uni.showToast({
+				title: "没有可领取的奖励",
+				icon: "none"
+			});
+			return;
+		}
+		// 重置上一轮的表单数据
+		claimRewardFormData.value = {
+			claimRewardNum: claimType == 'part' ? 1 : userInfo.value.unclaimedRewardNum
+		};
+		claimRewardInputDialog.value.open();
+	}
+	
 	
-
 	/**
-	 * 领取奖励
+	 * 领取奖励表单提交
 	 */
-	const claimReward = () => {
-		claimRewardDialog.value.open();
+	const claimRewardFormConfirm = async () => {
+		claimRewardForm.value.validate().then(data => {
+			return rewardApi.claimReward(data);
+		}).then(e => {
+			claimRewardInputDialog.value.close();
+			getUserInfo();
+		});
+	}
+	
+	/**
+	 * 领取奖励表单取消
+	 */
+	const claimRewardFormClose = async () => {
+		claimRewardInputDialog.value.close();
 	}
 
 	/**
@@ -179,6 +416,44 @@
 		// getReward();
 		claimRewardDialog.value.close();
 	}
+	
+	/**
+	 * 计时时间选择监听
+	 */
+	const timeTrackChange = (e) => {
+		timeTrackFormData.value.timeTrack = e.detail.value;
+	}
+	
+	/**
+	 * 打卡(计时)
+	 */
+	const doPunchInForTimeTrack = (id, timeTrack) => {
+		// 重置上一轮的表单数据
+		timeTrackFormData.value = {
+			id,
+			timeTrack: timeTrack ? timeTrack.slice(0, 5) : '00:00'
+		};
+		timeTrackInputDialog.value.open();
+	}
+	
+	/**
+	 * 计时表单提交
+	 */
+	const timeTrackFormConfirm = async () => {
+		timeTrackForm.value.validate(['id']).then(data => {
+			return punchInApi.doPunchIn(data);
+		}).then(e => {
+			timeTrackInputDialog.value.close();
+			getPunchIns();
+		});
+	}
+	
+	/**
+	 * 刮刮乐表单取消
+	 */
+	const timeTrackFormClose = async () => {
+		timeTrackInputDialog.value.close();
+	}
 
 
 	/**
@@ -226,6 +501,8 @@
 		// 如果还没登录就结束获取数据
 		console.log(token);
 		if (!token) {
+			// 没有登录
+			isLogin.value = false;
 			userInfo.value = ref({
 				totalRewardNum: 0,
 				unclaimedRewardNum: 0,
@@ -237,6 +514,8 @@
 			return;
 		}
 		
+		// 已登录
+		isLogin.value = true;
 		console.log("开始啦啦啦");
 		try {
 			uni.showLoading({
@@ -612,7 +891,7 @@
 				flex-shrink: 0;
 				width: 160rpx;
 				border-radius: 0rpx 24rpx 24rpx 0rpx;
-				background: #406CE7;
+				
 				
 				font-size: 36rpx;
 				font-weight: 400;
@@ -625,6 +904,32 @@
 				align-items: center;
 				flex-direction: column;
 			}
+			
+			.func-box-finish {
+				background: #F2607A;
+			}
+			
+			.func-box-unfinish {
+				background: #406CE7;
+			}
 		}
 	}
+	
+	.pick-box {
+		display: flex;
+		box-sizing: border-box;
+		flex-direction: row;
+		align-items: center;
+		border: 1px solid #dcdfe6;
+		border-radius: 4px;
+		
+		width: auto;
+		position: relative;
+		overflow: hidden;
+		flex: 1;
+		line-height: 1;
+		font-size: 14px;
+		height: 35px;
+		padding-left: 10px;
+	}
 </style>

+ 279 - 16
pages/punchin-detail/punchin-detail.vue

@@ -1,9 +1,14 @@
 <template>
 	<main-layout :showHome="true" :showBack="true">
-		<uni-calendar />	
+		<uni-calendar 
+			:start-date="punchInData.calendar.startDate"
+			:end-date="punchInData.calendar.endDate"
+			:selected="punchInData.selected"
+			@change="calendarChange"
+			@monthSwitch="calendarMonthSwitchChange"/>	
 		<viwe class="info-box">
-			<view class="left">打卡:12次</view>
-			<view class="right">全勤率:80%</view>
+			<view class="left">打卡:{{punchInData.statistics.punchInTimes}}次</view>
+			<view class="right">全勤率:{{punchInData.statistics.punchInRate}}80%</view>
 		</viwe>
 		<view class="log-box">
 			<uni-section title="打卡记录" type="line">
@@ -14,8 +19,49 @@
 				</uni-list>
 			</uni-section>
 		</view>
-		<uni-fab ref="fab" :content="content" horizontal="right" vertical="bottom"
-					direction="vertical"  @trigger="trigger"/>
+		<uni-fab ref="fabBtn" :content="fabContent" :pattern="fabPattern" horizontal="right" vertical="bottom" direction="vertical"  @trigger="trigger"/>
+		
+		<!-- 删除确认框 -->
+		<uni-popup ref="deleteDialog" type="dialog">
+			<uni-popup-dialog  mode="base" :before-close="true"
+				title="删除提示" content="确认删除当前任务?"
+				@confirm="deleteDialogConfirm" @close="deleteDialogClose"></uni-popup-dialog>
+		</uni-popup>
+		
+		<!-- 归档确认框 -->
+		<uni-popup ref="archiveDialog" type="dialog">
+			<uni-popup-dialog  mode="base" :before-close="true"
+				title="归档提示" content="确认归档当前任务?"
+				@confirm="archiveDialogConfirm" @close="archiveDialogClose"></uni-popup-dialog>
+		</uni-popup>
+		
+		<!-- 撤销确认框 -->
+		<uni-popup ref="revokeDialog" type="dialog">
+			<uni-popup-dialog  mode="base" :before-close="true"
+				title="撤销提示" content="确认撤销打卡?"
+				@confirm="revokeDialogConfirm" @close="revokeDialogClose"></uni-popup-dialog>
+		</uni-popup>
+		
+		<!-- 补卡弹出框 -->
+		<uni-popup ref="remakeInputDialog" type="dialog" :is-mask-click="false">
+			<uni-popup-dialog
+				mode="input" 
+				:before-close="true" 
+				title="补卡" 
+				@confirm="remakeFormConfirm"
+				@close="remakeFormClose">
+				<view style="width: 100%;">
+					<uni-forms ref="timeTrackForm" :modelValue="remakeFormData" :rules="remakeFormRules" label-position="top" :label-width="150">
+						<uni-forms-item name="punchInDate">
+							<picker mode="date" :value="remakeFormData.punchInDate" @change="remakePunchInChange">
+								<view class="pick-box">{{remakeFormData.punchInDate}}</view>
+							</picker>
+						</uni-forms-item>
+					</uni-forms>
+				</view>
+			</uni-popup-dialog>
+		</uni-popup>
+		
 	</main-layout>
 </template>
 
@@ -25,9 +71,50 @@
 	import router from '@/common/constants/router';
 	import { punchInApi } from '@/service/apis.js';
 	
+	// 组件
+	const fabBtn = ref(null);
+	/**
+	 * 删除弹出框
+	 */
+	const deleteDialog = ref(null);
+	
+	/**
+	 * 归档弹出框
+	 */
+	const archiveDialog = ref(null);
+	
+	/**
+	 * 补卡弹出框
+	 */
+	const remakeInputDialog = ref(null);
+	
+	/**
+	 * 补卡表单
+	 */
+	const remakeForm = ref(null);
+	
+	/**
+	 * 撤销弹出框
+	 */
+	const revokeDialog = ref(null);
+	
+	// 属性
+	/**
+	 * 任务ID
+	 */
 	const punchInId = ref(null);
+	
+	/**
+	 * 悬浮菜单样式
+	 */
+	const fabPattern = ref({
+		icon: 'compose'
+	});
 
-	const content = [{
+	/**
+	 * 悬浮按钮菜单
+	 */
+	const fabContent = [{
 			text: '删除',
 			active: false,
 			iconPath: '/static/delete.svg',
@@ -64,35 +151,211 @@
 		}
 	];
 	
+	/**
+	 * 补卡表单数据
+	 */
+	const remakeFormData = ref({
+		punchIndate: '2024-12-16'
+	});
+	
+	/**
+	 * 补卡表单校验规则
+	 */
+	const remakeFormRules = ref({
+		punchInDate: {
+			rules: [{
+				required: true,
+				errorMessage: '补卡日期不能为空'
+			}]
+		}
+	});
+	
+	/**
+	 * 打卡日历数据
+	 */
+	const punchInData = ref({
+		calendar: {
+			startDate: '2024-12-01',
+			endDate: '2024-12-31',
+			selectd: []
+		},
+		statistics: {
+			punchInTimes: 0,
+			punchInRate: 0
+		},
+		selected: [{date: '2024-12-13', info: '签到', data: { custom: '自定义信息', name: '自定义消息头', id: 12123 }}]
+	});
+	
+	// 方法
+	/**
+	 * 悬浮按钮点击事件
+	 */
 	const trigger = (e) => {
 		if (e.item.func == 'delete') {
-			punchInApi.deletePunchIn({id: punchInId.value});
-			return;
+			deleteDialog.value.open();
 		}
 		if (e.item.func == 'archive') {
-			punchInApi.archivePunchIn({id: punchInId.value});
-			return;
+			archiveDialog.value.open();
 		}
 		if (e.item.func == 'revoke') {
-			punchInApi.revokePunchIn({id: punchInId.value});
-			return;
+			revokeDialog.value.open();
 		}
 		if (e.item.func == 'remake') {
-			punchInApi.revokePunchIn({id: punchInId.value});
-			return;
+			// 将日期减去1天,得到昨天的日期
+			const yesterday = new Date();
+			yesterday.setDate(yesterday.getDate() - 1);
+			
+			// 获取年、月、日
+			const year = yesterday.getFullYear();
+			const month = (yesterday.getMonth() + 1).toString().padStart(2, '0'); // 月份是从0开始的,所以加1
+			const day = yesterday.getDate().toString().padStart(2, '0'); // 日期格式化为两位数
+			
+			// 初始化上一轮的数据
+			remakeFormData.value = {
+				punchInId: punchInId.value,
+				punchInDate: `${year}-${month}-${day}`
+			};
+			
+			remakeInputDialog.value.open();
 		}
 		if (e.item.func == 'edit') {
 			uni.navigateTo({
 				url: router.PUNCHIN_EDIT_URL + "?id=" + punchInId.value
 			});
-			return;
 		}
+		
+		fabBtn.value.close();
+	}
+	
+	/**
+	 * 删除确认
+	 */
+	const deleteDialogConfirm = async () => {
+		punchInApi.deletePunchIn({id: punchInId.value})
+		.then(ret => {
+			deleteDialog.value.close();
+			uni.showToast({
+				title: '删除成功',
+				icon: 'success'
+			});
+			setTimeout(() => {
+				uni.navigateBack();
+			}, 1000);
+		})
+		.catch(err => {
+			uni.showToast({
+				title: '删除失败',
+				icon: 'error'
+			});
+		});
+	}
+	
+	/**
+	 * 删除取消
+	 */
+	const deleteDialogClose = () => {
+		deleteDialog.value.close();
+	}
+	
+	/**
+	 * 归档确认
+	 */
+	const archiveDialogConfirm = async () => {
+		punchInApi.archivePunchIn({id: punchInId.value})
+		.then(ret => {
+			archiveDialog.value.close();
+			uni.showToast({
+				title: '归档成功',
+				icon: 'success'
+			});
+			setTimeout(() => {
+				uni.navigateBack();
+			}, 1000);
+		})
+		.catch(err => {
+			uni.showToast({
+				title: '归档失败',
+				icon: 'error'
+			});
+		});
+	}
+	
+	/**
+	 * 归档取消
+	 */
+	const archiveDialogClose = () => {
+		archiveDialog.value.close();
+	}
+	
+	/**
+	 *补卡表单提交
+	 */
+	const remakeFormConfirm = async () => {
+		remakeForm.value.validate(['punchInId']).then(data => {
+			return punchInApi.revokePunchIn(data);
+		}).then(e => {
+			remakeInputDialog.value.close();
+			uni.showToast({
+				title: '补卡成功',
+				icon: 'success'
+			});
+			setTimeout(() => {
+				// TODO 这里要刷新页面数据
+			}, 1000);
+		}).catch(err => {
+			uni.showToast({
+				title: '补卡失败',
+				icon: 'error'
+			});
+		})
+	}
+	
+	/**
+	 * 补卡表单取消
+	 */
+	const remakeFormClose = async () => {
+		remakeInputDialog.value.close();
+	}
+	
+	/**
+	 * 撤销确认
+	 */
+	const revokeDialogConfirm = async () => {
+		punchInApi.revokePunchIn({id: punchInId.value})
+		.then(ret => {
+			revokeDialog.value.close();
+			uni.showToast({
+				title: '撤销成功',
+				icon: 'success'
+			});
+			// TODO 这里要重新加载任务
+		})
+		.catch(err => {
+			uni.showToast({
+				title: '撤销失败',
+				icon: 'error'
+			});
+		});
+	}
+	
+	/**
+	 * 撤销取消
+	 */
+	const revokeDialogClose = () => {
+		revokeDialog.value.close();
+	}
+	
+	const calendarChange = (e) => {
+		console.log(e);
+	}
+	
+	const calendarMonthSwitchChange = (e) => {
+		console.log(e);
 	}
 	
 	onLoad(async (e) => {
 		if (e.id) {
 			punchInId.value = e.id;
-			console.log(punchInId.value);
 		}
 	});
 </script>

+ 121 - 68
pages/punchin-edit/punchin-edit.vue

@@ -1,42 +1,42 @@
 <template>
 	<main-layout :showHome="true" :showBack="true">
 		<template>
-			<uni-forms ref="baseForm" :modelValue="punchInData" :label-width="80">
+			<uni-forms ref="punchInForm" :modelValue="punchInFormData" :label-width="80" :rules="punchInFormRules">
 				<uni-section title="基本信息" padding="16px" type="line">
-					<uni-forms-item label="任务名称" required>
-						<uni-easyinput v-model="punchInData.taskName" placeholder="请输入任务名称" />
+					<uni-forms-item label="任务名称" required name="taskName">
+						<uni-easyinput v-model="punchInFormData.taskName" placeholder="请输入任务名称" />
 					</uni-forms-item>
-					<uni-forms-item label="奖励数" required>
-						<uni-easyinput v-model="punchInData.rewardNum" placeholder="请输入奖励数" type="number" />
+					<uni-forms-item label="奖励数" required name="rewardNum">
+						<uni-easyinput v-model="punchInFormData.rewardNum" placeholder="请输入奖励数" type="number" />
 					</uni-forms-item>
-					<uni-forms-item label="周末双倍" required>
-						<uni-data-checkbox v-model="punchInData.weekendDoubleFlag" :localdata="range"></uni-data-checkbox>
+					<uni-forms-item label="周末双倍" required name="weekendDoubleFlag">
+						<uni-data-checkbox v-model="punchInFormData.weekendDoubleFlag" :localdata="statusRange"></uni-data-checkbox>
 					</uni-forms-item>
-					<uni-forms-item label="全勤双倍" required>
-						<uni-data-checkbox v-model="punchInData.fullAttendanceFlag" :localdata="range"></uni-data-checkbox>
+					<uni-forms-item label="全勤双倍" required name="fullAttendanceFlag">
+						<uni-data-checkbox v-model="punchInFormData.fullAttendanceFlag" :localdata="statusRange"></uni-data-checkbox>
 					</uni-forms-item>
-					<uni-forms-item label="打卡类型" required>
-						<uni-data-select :localdata="punchInTypeRange" v-model="punchInData.category"></uni-data-select>
+					<uni-forms-item label="打卡类型" required name="category">
+						<uni-data-select :localdata="categoryRange" v-model="punchInFormData.category"></uni-data-select>
 					</uni-forms-item>
-					<uni-forms-item label="规则描述">
-						<uni-easyinput type="textarea" v-model="punchInData.description" placeholder="请输入任务描述" />
+					<uni-forms-item label="规则描述" name="description">
+						<uni-easyinput type="textarea" v-model="punchInFormData.description" placeholder="请输入任务描述" />
 					</uni-forms-item>
 				</uni-section>
-				<uni-section title="计数配置" padding="16px" type="line" v-if="punchInData.category == 1">
-					<uni-forms-item label="判断规则" required>
-						<uni-data-select :localdata="judgeTypeRange" v-model="punchInData.rule"></uni-data-select>
+				<uni-section title="计数配置" padding="16px" type="line" v-if="punchInFormData.category == 1">
+					<uni-forms-item label="判断规则" required name="rule">
+						<uni-data-select :localdata="ruleRange" v-model="punchInFormData.rule"></uni-data-select>
 					</uni-forms-item>
-					<uni-forms-item label="数值" required>
-						<uni-easyinput v-model="punchInData.countTrack" placeholder="请输入目标数值" type="digit" />
+					<uni-forms-item label="数值" required name="countTrack">
+						<uni-easyinput v-model="punchInFormData.countTrack" placeholder="请输入目标数值" type="digit" />
 					</uni-forms-item>
 				</uni-section>
-				<uni-section title="计时配置" padding="16px" type="line" v-if="punchInData.category == 2">
-					<uni-forms-item label="判断规则" required>
-						<uni-data-select :localdata="judgeTypeRange" v-model="punchInData.rule"></uni-data-select>
+				<uni-section title="计时配置" padding="16px" type="line" v-if="punchInFormData.category == 2">
+					<uni-forms-item label="判断规则" required name="rule">
+						<uni-data-select :localdata="ruleRange" v-model="punchInFormData.rule"></uni-data-select>
 					</uni-forms-item>
-					<uni-forms-item label="时间" required>
-						<picker mode="time" :value="punchInData.timeTrack" @change="timeChange">
-							<view class="pick-box">{{punchInData.timeTrack}}</view>
+					<uni-forms-item label="时间" required name="timeTrack">
+						<picker mode="time" :value="punchInFormData.timeTrack" @change="timeChange">
+							<view class="pick-box">{{punchInFormData.timeTrack}}</view>
 						</picker>
 					</uni-forms-item>
 				</uni-section>
@@ -53,16 +53,19 @@
 	import { ref } from 'vue';
 	import { onLoad } from '@dcloudio/uni-app';
 	import { punchInApi } from '@/service/apis';
+	import { ruleRange, categoryRange, statusRange } from '@/common/constants/punchin.js';
 	
+	// 组件
 	/**
-	 * 表单
+	 * 打卡任务表单
 	 */
-	const baseForm = ref(null);
+	const punchInForm = ref(null);
 	
+	// 属性
 	/**
-	 * 表单数据
+	 * 打卡任务表单数据
 	 */
-	const punchInData = ref({
+	const punchInFormData = ref({
 		"rewardNum": 1,
 		"weekendDoubleFlag": 1,
 		"fullAttendanceFlag": 1,
@@ -73,55 +76,105 @@
 	});
 	
 	/**
-	 * 选项
+	 * 打卡任务表单规则
 	 */
-	const range = [
-		{
-			"value": 1,
-			"text": "启用"
+	const punchInFormRules = ref({
+		taskName: {
+			rules: [{
+				required: true,
+				errorMessage: '任务名称不能为空'
+			}]
 		},
-		{
-			"value": 2,
-			"text": "关闭"
-		}
-	];
-	
-	/**
-	 * 打卡类型
-	 */
-	const punchInTypeRange = [
-        { value: 0, text: "单次打卡" },
-        { value: 1, text: "计数" },
-        { value: 2, text: "计时" },
-      ];
-	  
-	const judgeTypeRange = [
-		{ value: 0, text: "大于等于" },
-		{ value: 1, text: "小于等于" },
-		{ value: 2, text: "大于" },
-		{ value: 2, text: "小于" },
-	];
-	
-	onLoad(async (e) => {
-		if (e.id) {
-			const res = await punchInApi.queryPunchInById({"id": e.id});
-			punchInData.value = res;
+		rewardNum: {
+			rules: [{
+				required: true,
+				errorMessage: '奖励数值不能为空'
+			}, {
+				format: 'number',
+				errorMessage: "请输入有效数字"
+			}, {
+				minimum:1,
+				errorMessage: "最小值{minimum}"
+			}]
+		},
+		weekendDoubleFlag: {
+			rules: [{
+				required: true,
+				errorMessage: '周末双倍奖励不能为空'
+			}]
+		},
+		fullAttendanceFlag: {
+			rules: [{
+				required: true,
+				errorMessage: '全勤奖励不能为空'
+			}]
+		},
+		category: {
+			rules: [{
+				required: true,
+				errorMessage: '任务类型不能为空'
+			}]
+		},
+		description: {
+			rules: [{
+				maxLength: 100,
+				errorMessage: '描述不能超过{maxLength}个字符'
+			}]
+		},
+		rule: {
+			rules: [{
+				required: true,
+				errorMessage: '判断规则不能为空'
+			}]
+		},
+		countTrack: {
+			rules: [{
+				required: true,
+				errorMessage: '数值不能为空'
+			}, {
+				format: 'number',
+				errorMessage: "请输入有效数字"
+			}, {
+				minimum:1,
+				errorMessage: "最小值{minimum}"
+			}]
+		},
+		timeTrack: {
+			rules: [{
+				required: true,
+				errorMessage: '数值不能为空'
+			}]
 		}
 	});
 
+	/**
+	 * 周末双倍奖励开关改变监听
+	 */
 	const weekendDoubleSwitchChange = (e) => {
-		punchInData.value.weekendDoubleFlag = e.detail.value;
+		punchInFormData.value.weekendDoubleFlag = e.detail.value;
 	}
 	
+	/**
+	 * 全勤奖励开关改变监听
+	 */
 	const fullAttendanceSwitchChange = (e) => {
-		punchInData.value.fullAttendanceFlag = e.detail.value;
+		punchInFormData.value.fullAttendanceFlag = e.detail.value;
+	}
+	
+	/**
+	 * 时间选择监听
+	 */
+	const timeChange = (e) => {
+		punchInFormData.value.timeTrack = e.detail.value;
 	}
 	
 	/**
 	 * 保存打卡任务
 	 */
 	const savePunchIn = () => {
-		punchInApi.addPunchIn(punchInData.value).then(() => {
+		punchInForm.value.validate(['id']).then(() => {
+			return punchInApi.addPunchIn(punchInFormData.value)
+		}).then(datt => {
 			uni.showToast({
 				title: '保存成功',
 				icon: 'success',
@@ -140,12 +193,12 @@
 		uni.navigateBack();
 	}
 	
-	/**
-	 * 时间选择监听
-	 */
-	const timeChange = (e) => {
-		punchInData.value.timeTrack = e.detail.value;
-	}
+	onLoad(async (e) => {
+		if (e.id) {
+			const res = await punchInApi.queryPunchInById({"id": e.id});
+			punchInFormData.value = res;
+		}
+	});
 </script>
 
 <style lang="scss" scoped>

+ 3 - 1
service/apis.js

@@ -2,10 +2,12 @@ import * as loginApi from './loginApi.js';
 import * as punchInApi from './punchInApi.js';
 import * as rewardApi from './rewardApi.js';
 import * as userApi from './userApi.js';
+import * as scratchApi from './scratchApi.js';
 
 export {
 	loginApi,
 	punchInApi,
 	rewardApi,
-	userApi
+	userApi,
+	scratchApi
 }

+ 16 - 0
service/scratchApi.js

@@ -0,0 +1,16 @@
+// 刮刮乐API
+
+import request from "@/utils/request";
+
+/**
+ * 获取用户信息
+ */
+export function addScratchRecord(data) {
+	return request({
+		url: '/scratch/addScratchRecord',
+		method: 'post',
+		data,
+		loading: true,
+		loadingText: '保存中...'
+	});
+}