Explorar el Código

【feat】【第二版开发】

1.功能开发
2.联调问题修复
ChenYL hace 1 año
padre
commit
e7a26fdc3d

+ 1 - 1
common/constants/punchin.js

@@ -9,7 +9,7 @@ export const statusRange = [
 		"text": "启用"
 	},
 	{
-		"value": 2,
+		"value": 0,
 		"text": "关闭"
 	}
 ];

+ 11 - 1
common/constants/router.js

@@ -30,7 +30,17 @@ const router = {
 	/**
 	 * 结算页
 	 */
-	SETTLE_LIST_URL: '/pages/settle-list/settle-list'
+	SETTLE_LIST_URL: '/pages/settle-list/settle-list',
+	
+	/**
+	 * 奖励领取记录页
+	 */
+	REWARD_RECORD_LIST_URL: '/pages/reward-record-list/reward-record-list',
+	
+	/**
+	 * 刮刮乐投入与中奖记录页
+	 */
+	SCRATCH_RECORD_LIST_URL: '/pages/scratch-record-list/scratch-record-list'
 };
 
 export default router;

+ 7 - 52
components/main-layout/main-layout.vue

@@ -1,16 +1,16 @@
 <template>
 	<view class="layout-container">
 		<!-- 顶部填充区 -->
-		<view :style="statusBarStyle"></view>
+		<view :style="safeAreaStore.statusBarStyle"></view>
 		
 		<!-- 顶部胶囊区 -->
-		<view class="capsule-box" :style="capsuleBarStyle">
+		<view class="capsule-box" :style="safeAreaStore.capsuleBarStyle">
 			<view class="icon-wrap" v-if="showBack || showHome">
-				<view v-if="showBack" class="icon-box" :style="iconBoxStyle" @click="goBackPage">
-					<uni-icons type="back" color="#FFFFFF" :size="iconSize"></uni-icons>		
+				<view v-if="showBack" class="icon-box" :style="safeAreaStore.iconBoxStyle" @click="goBackPage">
+					<uni-icons type="back" color="#FFFFFF" :size="safeAreaStore.iconSize"></uni-icons>		
 				</view>
-				<view v-if="showHome" class="icon-box" :style="iconBoxStyle" @click="goIndexPage">
-					<uni-icons type="home" color="#FFFFFF" :size="iconSize"></uni-icons>	
+				<view v-if="showHome" class="icon-box" :style="safeAreaStore.iconBoxStyle" @click="goIndexPage">
+					<uni-icons type="home" color="#FFFFFF" :size="safeAreaStore.iconSize"></uni-icons>	
 				</view>
 			</view>
 		</view>
@@ -21,7 +21,7 @@
 		</view>
 		
 		<!-- 底部填充区 -->
-		<view :style="bottomBoxStyle"></view>
+		<view :style="safeAreaStore.bottomBoxStyle"></view>
 
 	</view>
 </template>
@@ -47,51 +47,6 @@
 	 */
 	const safeAreaStore = useSafeAreaStore();
 	
-	/**
-	 * 状态栏样式
-	 */
-	const statusBarStyle = computed(() => {
-		return {
-			height: safeAreaStore.statusBarHeight+'px'
-		}
-	});
-	
-	/**
-	 * 胶囊区样式
-	 */
-	const capsuleBarStyle = computed(() => {
-		return {
-			height: safeAreaStore.capsuleBarHeight+'px',
-			width: safeAreaStore.capsuleBarLeft+'px',
-			'padding-top': safeAreaStore.capsuleBarMarginTop+'px', 
-			'padding-bottom': safeAreaStore.capsuleBarMarginBottom+'px'
-		}
-	});
-	
-	/**
-	 * 图标容器样式
-	 */
-	const iconBoxStyle = computed(() => {
-		return {
-			width: safeAreaStore.capsuleBarContentHeight-5+'px',
-			height: safeAreaStore.capsuleBarContentHeight-5+'px'
-		}
-	});
-	
-	/**
-	 * 图标大小
-	 */
-	const iconSize = computed(() => safeAreaStore.capsuleBarContentHeight-10);
-	
-	/**
-	 * 底部填充区样式
-	 */
-	const bottomBoxStyle = computed(() => {
-		return {
-			height: safeAreaStore.bottomHeight+'px'
-		}
-	});
-	
 	/**
 	 * 返回主页
 	 */

+ 9 - 2
interceptors/naviInterceptor.js

@@ -1,12 +1,19 @@
 import router from "@/common/constants/router";
+import { useUserInfoStore } from '@/stores/userInfo.js';
+
 /**
  * 导航拦截器
  */
 const naviInterceptor = () => {
+	/**
+	 * 用户信息
+	 */
+	let userInfoStore = useUserInfoStore();
+	
 	uni.addInterceptor('navigateTo', {
 	    invoke(data) {
-	        const token = uni.getStorageSync('token');
-	        if (!token) {
+	        // const token = uni.getStorageSync('token');
+	        if (!userInfoStore.isLogin) {
 	            uni.reLaunch({
 	                url: router.LOGIN_URL
 	            });

+ 6 - 2
main.js

@@ -14,9 +14,13 @@ app.$mount()
 // #ifdef VUE3
 import { createSSRApp } from 'vue';
 import * as Pinia from 'pinia';
+import { createUnistorage } from "@/uni_modules/pinia-plugin-unistorage";
+
 export function createApp() {
-  const app = createSSRApp(App)
-  app.use(Pinia.createPinia());
+  const app = createSSRApp(App);
+  const pinia = Pinia.createPinia();
+  pinia.use(createUnistorage());
+  app.use(pinia);
   return {
     app,
 	Pinia

+ 21 - 3
pages.json

@@ -28,18 +28,36 @@
 				"navigationBarTitleText" : "编辑"
 			}
 		},
+		{
+			"path" : "pages/punchin-detail/punchin-detail",
+			"style" : 
+			{
+				"navigationBarTitleText" : "",
+				"enablePullDownRefresh": true
+			}
+		},
 		{
 			"path" : "pages/settle-list/settle-list",
 			"style" : 
 			{
-				"navigationBarTitleText" : ""
+				"navigationBarTitleText" : "奖励结算记录",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path" : "pages/scratch-record-list/scratch-record-list",
+			"style" : 
+			{
+				"navigationBarTitleText" : "刮刮乐投入与中奖记录",
+				"enablePullDownRefresh": true
 			}
 		},
 		{
-			"path" : "pages/punchin-detail/punchin-detail",
+			"path" : "pages/reward-record-list/reward-record-list",
 			"style" : 
 			{
-				"navigationBarTitleText" : ""
+				"navigationBarTitleText" : "奖励领取记录",
+				"enablePullDownRefresh": true
 			}
 		}
 	],

+ 12 - 17
pages/index/index.vue

@@ -2,11 +2,12 @@
 	<main-layout>
 		<!-- 用户信息区 -->
 		<view class="user-info">
-			<view class="user-icon">
+			<span class="nickname" v-if="!userInfoStore.isLogin" @click="goUserInfoPage">登录</span>
+			<view class="user-icon" v-if="userInfoStore.isLogin">
 				<uni-icons type="person" size="30"></uni-icons>	
 			</view>
-			<span class="nickname" v-if="!isLogin" @click="goUserInfoPage">登录</span>
-			<span class="user-btn" v-if="isLogin" @click="goUserInfoPage">用户中心</span>
+			<span class="nickname" v-if="userInfoStore.isLogin" @click="goUserInfoPage">{{userInfoStore.nickname}}</span>
+			<span class="user-btn" v-if="userInfoStore.isLogin" @click="goUserInfoPage">用户中心</span>
 		</view>
 		
 		<!-- 结算 -->
@@ -95,7 +96,7 @@
 					</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-if="punchIn.punchInStatus == 'unPunchIn' || punchIn.punchInStatus == 'todayUnknown'">打卡</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)">
@@ -184,6 +185,7 @@
 	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';
+	import { useUserInfoStore } from '@/stores/userInfo.js';
 	
 	// 组件
 	/**
@@ -219,9 +221,9 @@
 	
 	// 属性
 	/**
-	 * 登录标志位
+	 * 用户信息
 	 */
-	const isLogin = ref(false);
+	const userInfoStore = useUserInfoStore();
 
 	/**
 	 * 用户信息
@@ -497,26 +499,21 @@
 	 * 加载数据
 	 */
 	const loadData = async () => {
-		let token = uni.getStorageSync("token");
 		// 如果还没登录就结束获取数据
-		console.log(token);
-		if (!token) {
-			// 没有登录
-			isLogin.value = false;
-			userInfo.value = ref({
+		if (!userInfoStore.isLogin) {
+			userInfo.value = {
 				totalRewardNum: 0,
 				unclaimedRewardNum: 0,
 				claimedRewardNum: 0,
 				lotteryInvestAmount: 0.00,
 				lotteryWinAmount: 0.00
-			});
+			};
 			punchIns.value = [];
 			return;
 		}
 		
 		// 已登录
-		isLogin.value = true;
-		console.log("开始啦啦啦");
+		// isLogin.value = true;
 		try {
 			uni.showLoading({
 				title: '加载中',
@@ -532,12 +529,10 @@
 	};
 
 	onShow(() => {
-		userInfo.value = uni.getStorageSync("userInfo");
 		loadData();
 	});
 
 	onPullDownRefresh(() => {
-		console.log('触发下拉刷星');
 		loadData();
 		uni.stopPullDownRefresh();
 	});

+ 92 - 55
pages/login/login.vue

@@ -6,10 +6,19 @@
 					<uni-icons type="person" size="60"></uni-icons>
 				</view>
 				<view class="login-nickname">
-					<input placeholder="请输入昵称" v-model="nickname"/>
+					<uni-forms ref="loginForm" :modelValue="loginFormData" :rules="loginFormRules">
+						<uni-forms-item name="nickname">
+							<uni-easyinput type="text" 
+								v-model="loginFormData.nickname" 
+								placeholder="请输入昵称" 
+								:inputBorder="false" 
+								:styles="nicknameInputStyle"/>
+						</uni-forms-item>
+					</uni-forms>
 				</view>
 				
 				<view class="login-btn" @click="login">登录</view>
+				
 			</view>
 		</template>
 	</main-layout>
@@ -18,55 +27,96 @@
 <script setup>
 	import { ref } from 'vue';
 	import { loginApi } from '@/service/apis.js';
-
+	import router from '@/common/constants/router.js';
+	import { useUserInfoStore } from '@/stores/userInfo.js';
+	
+	// 组件
+	/**
+	 * 登录表单
+	 */
+	const loginForm = ref(null);
+	
+	// 属性
 	/**
-	 * 头像
+	 * 用户信息
 	 */
-	const avatar = ref("");
+	const userInfoStore = useUserInfoStore();
 	
 	/**
-	 * 昵称
+	 * 登录表单数据
 	 */
-	const nickname = ref("");
+	const loginFormData = ref({
+		nickname: "蜗牛"
+	});
 	
+	/**
+	 * 登录表单校验规则
+	 */
+	const loginFormRules = ref({
+		nickname: {
+			rules: [{
+				required: true,
+				errorMessage: '昵称不能为空'
+			}, {
+				maxLength: 30,
+				errorMessage: '昵称不能超过{maxLength}个字符'
+			}]
+		}
+	});
+	
+	/**
+	 * 昵称输入框样式
+	 */
+	const nicknameInputStyle = {
+		width: 100+'%',
+		height: 100+'%',
+		fontSize: 36+'rpx',
+		fontWeight: 700,
+		letterSpacing: 0+'rpx',
+		lineHeight: 52.13+'rpx',
+		color: '#000000',
+		borderRadius: 24+'rpx',
+		textAlign: 'center'
+	};
 	
+	// 方法
 	const login = async () => {
-		// 获取供应商
-		let providerResult = await uni.getProvider({
-			service: 'oauth'
-		});
-		const provider = providerResult.provider[0];
-		
-		// 获取登录的code
-		let loginResult = await uni.login({
-			provider
-		});
-		
-		// 获取用户的头像和昵称
-		let userInfoResult = await uni.getUserInfo({
-			 provider
-		});
-		
-		// 获取后端token
-		let token = await loginApi.login({
-			"code": loginResult.code,
-			"avatar": userInfoResult.userInfo.avatarUrl,
-			"nickname": userInfoResult.userInfo.nickName
-		});
-		
-		// 保存token
-		uni.setStorageSync('token', token);
-		// 保存用户信息
-		uni.setStorageSync('userInfo', {
-			"avatar": userInfoResult.userInfo.avatarUrl,
-			"nickname": userInfoResult.userInfo.nickName
-		});
-		
-		// 登录结束返回主页
-		uni.navigateTo({
-			url: "/pages/index/index"
-		})
-		
+		try {
+			let formData = await loginForm.value.validate();
+			uni.showLoading({
+				title: '登录中...'
+			});
+			// 获取供应商
+			let providerResult = await uni.getProvider({ service: 'oauth' });
+			// 获取登录code
+			let loginResult = await uni.login({ provider: providerResult.provider[0] });
+			// 登录
+			let token = await loginApi.login({
+				"code": loginResult.code,
+				"nickname": formData.nickname
+			});
+			
+			// 保存用户信息(token)
+			userInfoStore.$patch({
+				token,
+				nickname: formData.nickname
+			});
+			
+			uni.showToast({
+				title: '登录成功',
+				icon: 'success',
+				duration: 1000
+			});
+			
+			// 登录结束返回主页
+			setTimeout(() => {
+				uni.navigateTo({
+					url: router.INDEX_URL
+				});
+			}, 1000);
+		} finally {
+			uni.hideLoading();
+		}
 	}
 </script>
 
@@ -107,19 +157,6 @@
 			// 阴影
 			border: 0.5px solid #E4E4E4;
 			box-shadow: 0px 1px 6px  #D8D8D8;
-			
-			input {
-				width: 100%;
-				height: 100%;
-				font-size: 36rpx;
-				font-weight: 700;
-				letter-spacing: 0rpx;
-				line-height: 52.13rpx;
-				color: #000000;
-				text-align: left;
-				border-radius: 24rpx;
-				text-align: center;
-			}
 		}
 		
 		.login-btn {

+ 91 - 42
pages/punchin-detail/punchin-detail.vue

@@ -1,21 +1,26 @@
 <template>
 	<main-layout :showHome="true" :showBack="true">
 		<uni-calendar 
-			:start-date="punchInData.calendar.startDate"
-			:end-date="punchInData.calendar.endDate"
-			:selected="punchInData.selected"
-			@change="calendarChange"
+			:start-date="punchInData.startDate"
+			:end-date="punchInData.endDate"
+			:selected="punchInData.calendarSelected"
 			@monthSwitch="calendarMonthSwitchChange"/>	
 		<viwe class="info-box">
-			<view class="left">打卡:{{punchInData.statistics.punchInTimes}}次</view>
-			<view class="right">全勤率:{{punchInData.statistics.punchInRate}}80%</view>
+			<view class="left">打卡:{{punchInData.punchInNum}}次</view>
+			<view class="right">全勤率:{{punchInData.punchInRate}}%</view>
 		</viwe>
 		<view class="log-box">
 			<uni-section title="打卡记录" type="line">
 				<uni-list>
-					<uni-list-item title="2024-12-07 08:30:22 福彩 幸运88 中奖100元" time="2020-02-02 20:20" ></uni-list-item>
-					<uni-list-item title="2024-12-07 08:30:22 福彩 幸运88 中奖100元" time="2020-02-02 20:20" ></uni-list-item>
-					<uni-list-item title="2024-12-07 08:30:22 福彩 幸运88 中奖100元" time="2020-02-02 20:20" ></uni-list-item>
+					<uni-list-item v-for="record in punchInData.punchInRecords" :key="record.punchInDate">
+						<template v-slot:body>
+							{{record.punchInDate}}
+							<span v-if="record.punchInStatus == 1 || record.punchInStatus == 3"> 完成打卡</span>
+							<span v-else> 未完成打卡</span>
+							<span v-if="record.settleCategory == 1">,打卡{{record.countTrack}}次</span>
+							<span v-if="record.settleCategory == 2">,打卡时长{{record.timeTrack}}</span>
+						</template>
+					</uni-list-item>
 				</uni-list>
 			</uni-section>
 		</view>
@@ -51,9 +56,9 @@
 				@confirm="remakeFormConfirm"
 				@close="remakeFormClose">
 				<view style="width: 100%;">
-					<uni-forms ref="timeTrackForm" :modelValue="remakeFormData" :rules="remakeFormRules" label-position="top" :label-width="150">
+					<uni-forms ref="remakeForm" :modelValue="remakeFormData" :rules="remakeFormRules" label-position="top" :label-width="150">
 						<uni-forms-item name="punchInDate">
-							<picker mode="date" :value="remakeFormData.punchInDate" @change="remakePunchInChange">
+							<picker mode="date" :value="remakeFormData.punchInDate" @change="remakePunchInDateChange">
 								<view class="pick-box">{{remakeFormData.punchInDate}}</view>
 							</picker>
 						</uni-forms-item>
@@ -67,9 +72,10 @@
 
 <script setup>
 	import { ref } from 'vue';
-	import { onLoad } from '@dcloudio/uni-app';
+	import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app';
 	import router from '@/common/constants/router';
 	import { punchInApi } from '@/service/apis.js';
+	import dateUtils from '@/utils/date';
 	
 	// 组件
 	const fabBtn = ref(null);
@@ -170,20 +176,21 @@
 		}
 	});
 	
+	/**
+	 * 查询条件
+	 */
+	const queryData = ref(null);
+	
 	/**
 	 * 打卡日历数据
 	 */
 	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 }}]
+		startDate: '2024-12-01',
+		endDate: '2024-12-31',
+		punchInNum: 0,
+		punchInRate: 0,
+		calendarSelected: [],
+		punchInRecords: []
 	});
 	
 	// 方法
@@ -201,19 +208,10 @@
 			revokeDialog.value.open();
 		}
 		if (e.item.func == 'remake') {
-			// 将日期减去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}`
+				punchInDate: dateUtils.getYesterday()
 			};
 			
 			remakeInputDialog.value.open();
@@ -288,20 +286,25 @@
 	}
 	
 	/**
-	 *补卡表单提交
+	 * 补卡时间选择监听
+	 */
+	const remakePunchInDateChange = (e) => {
+		remakeFormData.value.punchInDate = e.detail.value;
+	}
+	
+	/**
+	 * 补卡表单提交
 	 */
 	const remakeFormConfirm = async () => {
 		remakeForm.value.validate(['punchInId']).then(data => {
-			return punchInApi.revokePunchIn(data);
+			return punchInApi.remakePunchIn(data);
 		}).then(e => {
 			remakeInputDialog.value.close();
 			uni.showToast({
 				title: '补卡成功',
 				icon: 'success'
 			});
-			setTimeout(() => {
-				// TODO 这里要刷新页面数据
-			}, 1000);
+			loadData();
 		}).catch(err => {
 			uni.showToast({
 				title: '补卡失败',
@@ -328,7 +331,7 @@
 				title: '撤销成功',
 				icon: 'success'
 			});
-			// TODO 这里要重新加载任务
+			loadData();
 		})
 		.catch(err => {
 			uni.showToast({
@@ -345,19 +348,47 @@
 		revokeDialog.value.close();
 	}
 	
-	const calendarChange = (e) => {
-		console.log(e);
+	/**
+	 * 日历月份切换
+	 */
+	const calendarMonthSwitchChange = (e) => {
+		queryData.value = e;
+		loadData();
 	}
 	
-	const calendarMonthSwitchChange = (e) => {
-		console.log(e);
+	/**
+	 * 加载数据
+	 */
+	const loadData = () => {
+		punchInApi.queryPunchInData({
+			id: punchInId.value,
+			year: queryData.value.year,
+			month: queryData.value.month,
+		}).then(data => {
+			punchInData.value = data;
+		}).catch(err => {
+			uni.showToast({
+				title: '加载失败',
+				icon: 'error'
+			});
+		});
 	}
 	
 	onLoad(async (e) => {
 		if (e.id) {
 			punchInId.value = e.id;
+			// 初始化查询条件
+			queryData.value = dateUtils.getTodayYearMonthObj();
+			
+			// 加载数据
+			loadData();
 		}
 	});
+	
+	onPullDownRefresh(() => {
+		loadData();
+		uni.stopPullDownRefresh();
+	});
 </script>
 
 <style lang="scss" scoped>
@@ -377,4 +408,22 @@
 			justify-content: center;
 		}
 	}
+	
+	.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>

+ 2 - 5
pages/punchin-edit/punchin-edit.vue

@@ -196,6 +196,8 @@
 	onLoad(async (e) => {
 		if (e.id) {
 			const res = await punchInApi.queryPunchInById({"id": e.id});
+			res.fullAttendanceFlag = res.fullAttendanceFlag ? 1 : 0;
+			res.weekendDoubleFlag = res.weekendDoubleFlag ? 1 : 0;
 			punchInFormData.value = res;
 		}
 	});
@@ -226,9 +228,4 @@
 		justify-content: space-between; /* 按钮之间的空间分布 */
 		padding: 20rpx; /* 容器两侧的空白填充 */
 	}
-	
-	// .button {
-	//   flex-grow: 1; /* 按钮占据可用空间 */
-	//   margin: 0 10px; /* 按钮两侧的空白填充 */
-	// }
 </style>

+ 89 - 0
pages/reward-record-list/reward-record-list.vue

@@ -0,0 +1,89 @@
+<template>
+	<main-layout :showHome="true" :showBack="true">
+		<view class="selector">
+			<uni-datetime-picker type="daterange" v-model="datePickerValue" @change="dateChange"/>
+		</view>
+		<uni-list :border="true">
+			<uni-list-item v-for="item in listData" :key="item.id">
+				<template v-slot:body>
+					<p>{{item.claimRewardTime}}</p>
+					<p>
+						领取奖励:{{item.claimRewardNum}},领取前奖励:{{item.beforeClaimRewardNum}},领取后奖励:{{item.afterClaimRewardNum}}
+					</p>
+				</template>
+			</uni-list-item>
+		</uni-list>
+	</main-layout>
+</template>
+
+<script setup>
+	import {ref} from 'vue';
+	import { onLoad, onPullDownRefresh } from "@dcloudio/uni-app";
+	import dateUtils from '@/utils/date';
+	import { rewardApi } from '@/service/apis';
+	
+	//属性
+	/**
+	 * 日期选择器值/
+	 */
+	const datePickerValue = ref([]);
+	
+	/**
+	 * 查询条件
+	 */
+	const queryData = ref(null);
+	
+	/**
+	 * 列表数据
+	 */
+	const listData = ref(null);
+	
+	// 方法
+	/**
+	 * 日期选择监听
+	 */
+	const dateChange = (e) => {
+		if (e.length == 0) {
+			queryData.value = {};
+			return;
+		}
+		
+		queryData.value = {
+			startDate: e[0],
+			endDate: e[1]
+		};
+		loadData();
+	}
+	
+	/**
+	 * 加载数据
+	 */
+	const loadData = () => {
+		rewardApi.queryClaimRewardRecords(queryData.value).then(res => {
+			listData.value = res;
+		});
+	}
+	
+	// 生命周期
+	onLoad(() => {
+		const startDate = dateUtils.getFirstDayOfMonth();
+		const endDate = dateUtils.getLastDayOfMonth();
+		datePickerValue.value = [startDate, endDate];
+		queryData.value = {
+			startDate,
+			endDate
+		}
+		loadData();
+	});
+	
+	onPullDownRefresh(() => {
+		loadData();
+		uni.stopPullDownRefresh();
+	});
+</script>
+
+<style lang="scss" scoped>
+	.selector {
+		background-color: #FFFFFF;
+	}
+</style>

+ 96 - 0
pages/scratch-record-list/scratch-record-list.vue

@@ -0,0 +1,96 @@
+<template>
+	<main-layout :showHome="true" :showBack="true">
+		<view class="selector">
+			<uni-datetime-picker type="daterange" v-model="datePickerValue" @change="dateChange"/>
+		</view>
+		<uni-list :border="true">
+			<uni-list-item v-for="item in listData" :key="item.id">
+				<template v-slot:body>
+					<p>{{item.creationTime}}</p>
+					<p>
+						<span v-if="item.actionType == 0">购买</span>
+						<span v-if="item.source == 'WELFARE_LOTTERY'">福彩</span>
+						<span v-if="item.source == 'SPORTS_LOTTERY'">体彩</span>
+						<span v-if="item.category == 'XINYUN88'">幸运88</span>
+						<span v-if="item.category == 'CHAOGEILI'">超给力</span>
+						<span v-if="item.actionType == 0">花费</span>
+						<span v-if="item.actionType == 1">中奖</span>
+						{{item.amount}}元
+					</p>
+				</template>
+			</uni-list-item>
+		</uni-list>
+	</main-layout>
+</template>
+
+<script setup>
+	import {ref} from 'vue';
+	import { onLoad, onPullDownRefresh } from "@dcloudio/uni-app";
+	import dateUtils from '@/utils/date';
+	import { scratchApi } from '@/service/apis';
+	
+	//属性
+	/**
+	 * 日期选择器值/
+	 */
+	const datePickerValue = ref([]);
+	
+	/**
+	 * 查询条件
+	 */
+	const queryData = ref(null);
+	
+	/**
+	 * 列表数据
+	 */
+	const listData = ref(null);
+	
+	// 方法
+	/**
+	 * 日期选择监听
+	 */
+	const dateChange = (e) => {
+		if (e.length == 0) {
+			queryData.value = {};
+			return;
+		}
+		
+		queryData.value = {
+			startDate: e[0],
+			endDate: e[1]
+		};
+		loadData();
+	}
+	
+	/**
+	 * 加载数据
+	 */
+	const loadData = () => {
+		scratchApi.queryScratchRecord(queryData.value).then(res => {
+			listData.value = res;
+		});
+	}
+	
+	// 生命周期
+	onLoad(() => {
+		const startDate = dateUtils.getFirstDayOfMonth();
+		const endDate = dateUtils.getLastDayOfMonth();
+		datePickerValue.value = [startDate, endDate];
+		queryData.value = {
+			startDate,
+			endDate
+		}
+		loadData();
+	});
+	
+	onPullDownRefresh(() => {
+		loadData();
+		uni.stopPullDownRefresh();
+	});
+</script>
+
+<style lang="scss" scoped>
+	.selector {
+		background-color: #FFFFFF;
+	}
+</style>

+ 66 - 62
pages/settle-list/settle-list.vue

@@ -1,82 +1,86 @@
 <template>
 	<main-layout :showHome="true" :showBack="true">
-		<!-- <view class="selector">
-			<view class="wrap selected">月</view>
-			<view class="wrap">年</view>
-		</view>
-		<view class="datetime-selector">
-			<view class="left-btn"><uni-icons type="left" size="30"></uni-icons></view>
-			<view class="middle-btn">
-				<picker mode="date" :value="date" fields="month">
-					<view class="datetime-view">{{date}}</view>
-					<view class="down-btn"><uni-icons type="down" size="30"></uni-icons></view>
-				</picker>
-			</view>
-			<view class="right-btn"><uni-icons type="right" size="30"></uni-icons></view>
-		</view> -->
-		<view>
-			<uni-datetime-picker type="daterange" />
+		<view class="selector">
+			<uni-datetime-picker type="daterange" v-model="datePickerValue" @change="dateChange"/>
 		</view>
 		<uni-list :border="true">
-			<uni-list-item v-for="i in 10" title="2024-12-07 完成3个打卡任务,结算奖励4,结算前奖励6,结算后奖励10" time="2020-02-02 20:20" ></uni-list-item>
+			<uni-list-item v-for="settle in settleData" :key="settle.id">
+				<template v-slot:body>
+					结算时间:{{settle.settlementTime}} 结算前奖励:{{settle.beforeSettleRewardNum}},结算奖励:{{settle.settleRewardNum}},结算后奖励:{{settle.afterSettleRewardNum}}
+				</template>
+			</uni-list-item>
 		</uni-list>
 	</main-layout>
 </template>
 
 <script setup>
 	import {ref} from 'vue';
+	import { onLoad, onPullDownRefresh } from "@dcloudio/uni-app";
+	import dateUtils from '@/utils/date';
+	import { settleApi } from '@/service/apis';
 	
-	const date = ref('2025-01');
-</script>
-
-<style lang="scss" scoped>
-	.selector {
-		width: 220rpx;
-		height: 88rpx;
-		opacity: 1;
-		border-radius: 8rpx;
-		background: #CCCCCC;
-		
-		/** 文本1 */
-		font-size: 36rpx;
-		font-weight: 400;
-		letter-spacing: 0rpx;
-		// line-height: 52.13rpx;
-		color: #000000;
-
-		padding: 12rpx;
-		display: flex;
-		
-		.wrap {
-			flex-grow: 1;
-			opacity: 1;
-			border-radius: 8rpx;
-			
-			display: inline-flex;
-			justify-content: center;
-			align-content: center;
+	//属性
+	/**
+	 * 日期选择器值/
+	 */
+	const datePickerValue = ref([]);
+	
+	/**
+	 * 查询条件
+	 */
+	const queryData = ref(null);
+	
+	/**
+	 * 结算数据
+	 */
+	const settleData = ref(null);
+	
+	// 方法
+	/**
+	 * 日期选择监听
+	 */
+	const dateChange = (e) => {
+		if (e.length == 0) {
+			queryData.value = {};
+			return;
 		}
 		
-		.selected {
-			background: #FFFFFF;
-		}
+		queryData.value = {
+			startDate: e[0],
+			endDate: e[1]
+		};
+		loadData();
 	}
 	
-	.left-btn, .middle-btn, .right-btn, .datetime-view, .down-btn {
-		display: inline-flex;
+	/**
+	 * 加载数据
+	 */
+	const loadData = () => {
+		settleApi.querySettle(queryData.value).then(res => {
+			settleData.value = res;
+		});
 	}
 	
-	.datetime-selector {
-		width: 100%;
-		
-		display: flex;
-		justify-content: center;
-		align-content: center;
-		
-		.middle-btn>picker {
-			display: flex;
-			justify-content: center;
-			align-content: center;
+	// 生命周期
+	onLoad(() => {
+		const startDate = dateUtils.getFirstDayOfMonth();
+		const endDate = dateUtils.getLastDayOfMonth();
+		datePickerValue.value = [startDate, endDate];
+		queryData.value = {
+			startDate,
+			endDate
 		}
+		loadData();
+	});
+	
+	onPullDownRefresh(() => {
+		loadData();
+		uni.stopPullDownRefresh();
+	});
+</script>
+
+<style lang="scss" scoped>
+	.selector {
+		background-color: #FFFFFF;
 	}
 </style>

+ 119 - 60
pages/user-info/user-info.vue

@@ -1,52 +1,63 @@
 <template>
-	<main-layout :showHome="true">
+	<main-layout :showBack="true">
 		<template>
 			<view class="header">
 				<view class="avatar">
 					<uni-icons type="person" size="70"></uni-icons>	
 				</view>
-				<span class="nickname">昵称</span>
+				<span class="nickname">{{userInfoStore.nickname}}</span>
 			</view>
+			
 			<view class="func-wrap">
-				<view class="func">
-					<uni-icons type="person" size="30"></uni-icons>	
-					<span class="name">头像</span>
-					<span class="right-icon">
-						<uni-icons type="right" size="30"></uni-icons>	
-					</span>
-				</view>
-				<view class="func">
-					<uni-icons type="person" size="30"></uni-icons>	
-					<span class="name">昵称</span>
-					<span class="right-icon">
-						<uni-icons type="right" size="30"></uni-icons>	
-					</span>	
-				</view>
+				<uni-list :border="true">
+					<uni-list-item title="修改昵称"
+						:showArrow="true" :showExtraIcon="true" 
+						:extraIcon="{color: '#000000',size: 22, type: 'compose'}"
+						:clickable="true" @click="modifyNickname">
+					</uni-list-item>
+				</uni-list>
 			</view>
+			
 			<view class="func-wrap">
-				<view class="func" @click="goSettleListPage">
-					<uni-icons type="person" size="30"></uni-icons>	
-					<span class="name">奖励结算记录</span>
-					<span class="right-icon">
-						<uni-icons type="right" size="30"></uni-icons>	
-					</span>
-				</view>
-				<view class="func" @click="goClaimRewardListPage">
-					<uni-icons type="person" size="30"></uni-icons>
-					<span class="name">奖励领取记录</span>
-					<span class="right-icon">
-						<uni-icons type="right" size="30"></uni-icons>	
-					</span>
-				</view>
-				<view class="func" @click="goScratchListPage">
-					<uni-icons type="person" size="30"></uni-icons>
-					<span class="name">刮刮乐投入与中奖记录</span>
-					<span class="right-icon">
-						<uni-icons type="right" size="30"></uni-icons>	
-					</span>
-				</view>
+				<uni-list :border="true">
+					<uni-list-item title="奖励结算记录" 
+						:showArrow="true" :showExtraIcon="true" 
+						:extraIcon="{color: '#000000',size: 22, type: 'list'}"
+						link="navigateTo" :to="router.SETTLE_LIST_URL">
+					</uni-list-item>
+					<uni-list-item title="奖励领取记录"
+						:showArrow="true" :showExtraIcon="true" 
+						:extraIcon="{color: '#000000',size: 22, type: 'list'}"
+						link="navigateTo" :to="router.REWARD_RECORD_LIST_URL">
+					</uni-list-item>
+					<uni-list-item title="刮刮乐投入与中奖记录"
+						:showArrow="true" :showExtraIcon="true" 
+						:extraIcon="{color: '#000000',size: 22, type: 'list'}"
+						link="navigateTo" :to="router.SCRATCH_RECORD_LIST_URL">
+					</uni-list-item>
+				</uni-list>
 			</view>
+
 			<view class="cancel-btn" @click="logout">注销</view>
+			
+			<!-- 修改昵称弹出框 -->
+			<uni-popup ref="nicknameInputDialog" type="dialog" :is-mask-click="false">
+				<uni-popup-dialog
+					mode="input" 
+					:before-close="true" 
+					title="修改昵称" 
+					confirmText="保存" 
+					@confirm="nicknameFormConfirm"
+					@close="nicknameFormClose">
+					<view style="width: 100%;">
+						<uni-forms ref="nicknameForm" :modelValue="nicknameFormData" :rules="nicknameFormRules" label-position="top" :label-width="150">
+							<uni-forms-item name="nickname">
+								<uni-easyinput type="text" placeholder="请输入昵称" v-model="nicknameFormData.nickname"></uni-easyinput>
+							</uni-forms-item>
+						</uni-forms>
+					</view>
+				</uni-popup-dialog>
+			</uni-popup>
 		</template>
 	</main-layout>
 </template>
@@ -54,9 +65,54 @@
 <script setup>
 	import { ref } from 'vue';
 	import router from '@/common/constants/router.js';
+	import { useUserInfoStore } from '@/stores/userInfo';
+	import { userApi } from '@/service/apis.js';
+	
+	// 组件
+	/**
+	 * 昵称弹出框
+	 */
+	const nicknameInputDialog = ref(null);
+
+	/**
+	 * 昵称表单
+	 */
+	const nicknameForm = ref(null);
+	
+	// 属性
+	/**
+	 * 用户信息
+	 */
+	const userInfoStore = useUserInfoStore();
+	
+	/**
+	 * 昵称表单数据
+	 */
+	const nicknameFormData = ref({
+		nickname: userInfoStore.nickname
+	});
+	
+	/**
+	 * 昵称表单验证规则
+	 */
+	const nicknameFormRules = ref({
+		nickname: {
+			rules: [{
+				required: true,
+				errorMessage: '昵称不能为空'
+			}, {
+				maxLength: 30,
+				errorMessage: '昵称不能超过{maxLength}个字符'
+			}],
+		}	
+	})
 	
+	// 方法
+	/**
+	 * 注销登录
+	 */
 	const logout = () => {
-		uni.removeStorageSync("token");
+		userInfoStore.$reset();
 		uni.reLaunch({
 			url: router.INDEX_URL
 		});
@@ -88,6 +144,31 @@
 			url: router.SCRATCH_LIST_URL
 		});
 	}
+	
+	const modifyNickname = (e) => {
+		nicknameInputDialog.value.open();
+	}
+	
+	/**
+	 * 领取奖励表单提交
+	 */
+	const nicknameFormConfirm = async () => {
+		let data = await nicknameForm.value.validate();
+		await userApi.modifyNickname(data);
+		userInfoStore.nickname = data.nickname;
+		nicknameInputDialog.value.close();
+		uni.showToast({
+			title: '修改成功',
+			icon: 'success'
+		});
+	}
+	
+	/**
+	 * 领取奖励表单取消
+	 */
+	const nicknameFormClose = async () => {
+		nicknameInputDialog.value.close();
+	}
 </script>
 
 <style lang="scss" scoped>
@@ -122,30 +203,8 @@
 	}
 	
 	.func-wrap {
-		border-radius: 24rpx;
 		background: #FFFFFF;
-		padding: 16rpx 24rpx;
 		margin-top: 24rpx;
-		position: relative;
-		
-		.func {
-			display: flex;
-			justify-content: left;
-			align-items: center;
-			
-			.name {
-				font-size: 26rpx;
-				font-weight: 400;
-				letter-spacing: 0rpx;
-				line-height: 37.65rpx;
-				color: #000000;
-			}
-			
-			.right-icon {
-				position: absolute;
-				right: 0rpx;
-			}
-		}
 	}
 	
 	.cancel-btn {

+ 3 - 1
service/apis.js

@@ -3,11 +3,13 @@ import * as punchInApi from './punchInApi.js';
 import * as rewardApi from './rewardApi.js';
 import * as userApi from './userApi.js';
 import * as scratchApi from './scratchApi.js';
+import * as settleApi from './settleApi.js';
 
 export {
 	loginApi,
 	punchInApi,
 	rewardApi,
 	userApi,
-	scratchApi
+	scratchApi,
+	settleApi
 }

+ 17 - 3
service/punchInApi.js

@@ -73,7 +73,9 @@ export function queryPunchInById(data) {
   return request({
     url: "/punchIn/queryPunchInById",
     method: "post",
-    data
+    data,
+	loading: true,
+	loadingText: "正在查询打卡任务,请稍后..."
   });
 }
 
@@ -93,9 +95,9 @@ export function archivePunchIn(data) {
  * 补卡
  * @param {Object} data
  */
-export function remakPunchIn(data) {
+export function remakePunchIn(data) {
   return request({
-    url: "/punchIn/remakPunchIn",
+    url: "/punchIn/remakePunchIn",
     method: "post",
     data
   });
@@ -111,4 +113,16 @@ export function revokePunchIn(data) {
     method: "post",
     data
   });
+}
+
+/**
+ * 查询打卡数据
+ * @param {Object} data
+ */
+export function queryPunchInData(data) {
+  return request({
+    url: "/punchIn/queryPunchInData",
+    method: "post",
+    data
+  });
 }

+ 4 - 3
service/rewardApi.js

@@ -3,10 +3,11 @@ import request from "@/utils/request";
 /**
  * 查询奖励
  */
-export function queryReward() {
+export function queryClaimRewardRecords(data) {
 	return request({
-		url: '/reward/queryReward',
-		method: 'get'
+		url: '/reward/queryClaimRewardRecords',
+		method: 'post',
+		data
 	});
 }
 

+ 14 - 1
service/scratchApi.js

@@ -3,7 +3,7 @@
 import request from "@/utils/request";
 
 /**
- * 获取用户信息
+ * 新增投入/中奖记录
  */
 export function addScratchRecord(data) {
 	return request({
@@ -13,4 +13,17 @@ export function addScratchRecord(data) {
 		loading: true,
 		loadingText: '保存中...'
 	});
+}
+
+/**
+ * 新增投入/中奖记录
+ */
+export function queryScratchRecord(data) {
+	return request({
+		url: '/scratch/queryScratchRecord',
+		method: 'post',
+		data,
+		loading: true,
+		loadingText: '查询中...'
+	});
 }

+ 15 - 0
service/settleApi.js

@@ -0,0 +1,15 @@
+import request from "@/utils/request";
+
+/**
+ * 查询结算记录
+ * @param {Object} data
+ */
+export function querySettle(data) {
+  return request({
+    url: "/settle/querySettle",
+    method: "post",
+    data,
+	loading: true,
+	loadingText: "查询中..."
+  });
+}

+ 13 - 0
service/userApi.js

@@ -9,3 +9,16 @@ export function queryUserInfo() {
 		method: 'get'
 	});
 }
+
+/**
+ * 修改用户昵称
+ */
+export function modifyNickname(data) {
+	return request({
+		url: '/user/modifyNickname',
+		method: 'post',
+		data,
+		loading: true,
+		loadingText: '正在修改...'
+	});
+}

+ 36 - 4
stores/safeArea.js

@@ -1,6 +1,4 @@
-import {
-	defineStore
-} from 'pinia';
+import { defineStore } from 'pinia';
 
 /**
  * 安全区
@@ -26,5 +24,39 @@ export const useSafeAreaStore = defineStore('safeArea', {
 		capsuleBarMarginTop: 0,
 		capsuleBarMarginBottom: 0,
 		capsuleBarContentHeight: 0
-	})
+	}),
+	getters: {
+		/**
+		 * 状态栏样式
+		 */
+		statusBarStyle: (state) => ({
+			height: state.statusBarHeight+'px'
+		}),
+		/**
+		 * 胶囊区样式
+		 */
+		capsuleBarStyle: (state) => ({
+			height: state.capsuleBarHeight+'px',
+			width: state.capsuleBarLeft+'px',
+			'padding-top': state.capsuleBarMarginTop+'px', 
+			'padding-bottom': state.capsuleBarMarginBottom+'px'
+		}),
+		/**
+		 * 图标容器样式
+		 */
+		iconBoxStyle: (state) => ({
+			width: state.capsuleBarContentHeight-5+'px',
+			height: state.capsuleBarContentHeight-5+'px'
+		}),
+		/**
+		 * 图标大小
+		 */
+		iconSize: (state) => state.capsuleBarContentHeight-10,
+		/**
+		 * 底部填充区样式
+		 */
+		bottomBoxStyle: (state) => ({
+			height: state.bottomHeight+'px'
+		})
+	}
 });

+ 27 - 0
stores/userInfo.js

@@ -0,0 +1,27 @@
+import { defineStore } from 'pinia';
+
+/**
+ * 用户信息
+ */
+export const useUserInfoStore = defineStore('userInfo', {
+	state: () => ({
+		/**
+		 * 登录凭据
+		 */
+		token: null,
+		
+		/**
+		 * 昵称
+		 */
+		nickname: null
+	}),
+	unistorage: true,
+	getters: {
+		/**
+		 * 登录状态判断
+		 * true-已登录
+		 * false-未登录
+		 */
+	    isLogin: (state) => Boolean(state.token),
+	}
+});

+ 31 - 0
uni_modules/pinia-plugin-unistorage/changelog.md

@@ -0,0 +1,31 @@
+## 0.1.2(2024-07-17)
+chore: 移除冗余的 typescript 依赖
+## 0.1.1(2024-07-17)
+fix: 修复 createUnistorage 导出
+## 0.1.0(2024-07-10)
+fix!: 更新 pinia 类型
+## 0.0.21(2024-07-10)
+chore!: 继承 pinia-plugin-persistedstate
+## 0.0.19(2024-01-18)
+
+fix: 重新构建,不需要默认参数
+
+## 0.0.16(2023-05-06)
+
+fix: 修复全局 key 移除
+
+## 0.0.14(2023-04-29)
+
+fix: 修复全局 global key 选项
+
+## 0.0.12(2023-04-07)
+
+- fix: 修复类型错误
+
+## 0.0.11(2023-03-22)
+
+- chore: ts 支持
+
+## 0.0.7(2022-04-29)
+
+- 更新 README

+ 112 - 0
uni_modules/pinia-plugin-unistorage/index.d.ts

@@ -0,0 +1,112 @@
+import * as pinia from 'pinia';
+import { StateTree, PiniaPluginContext, PiniaPlugin } from 'pinia';
+
+type Prettify<T> = {
+    [K in keyof T]: T[K];
+};
+type StorageLike = Pick<Storage, 'getItem' | 'setItem'>;
+interface Serializer {
+    /**
+     * Serializes state into string before storing
+     * @default JSON.stringify
+     */
+    serialize: (value: StateTree) => string;
+    /**
+     * Deserializes string into state before hydrating
+     * @default JSON.parse
+     */
+    deserialize: (value: string) => StateTree;
+}
+interface PersistedStateOptions {
+    /**
+     * Storage key to use.
+     * @default $store.id
+     */
+    key?: string | ((id: string) => string);
+    /**
+     * Where to store persisted state.
+     * @default localStorage
+     */
+    storage?: StorageLike;
+    /**
+     * Dot-notation paths to partially save state. Saves everything if undefined.
+     * @default undefined
+     */
+    paths?: Array<string>;
+    /**
+     * Customer serializer to serialize/deserialize state.
+     */
+    serializer?: Serializer;
+    /**
+     * Hook called before state is hydrated from storage.
+     * @default null
+     */
+    beforeRestore?: (context: PiniaPluginContext) => void;
+    /**
+     * Hook called after state is hydrated from storage.
+     * @default undefined
+     */
+    afterRestore?: (context: PiniaPluginContext) => void;
+    /**
+     * Logs errors in console when enabled.
+     * @default false
+     */
+    debug?: boolean;
+}
+type PersistedStateFactoryOptions = Prettify<Pick<PersistedStateOptions, 'storage' | 'serializer' | 'afterRestore' | 'beforeRestore' | 'debug'> & {
+    /**
+     * Global key generator, allows pre/postfixing store keys.
+     * @default storeKey => storeKey
+     */
+    key?: (storeKey: string) => string;
+    /**
+     * Automatically persists all stores, opt-out individually.
+     * @default false
+     */
+    auto?: boolean;
+}>;
+declare module 'pinia' {
+    interface DefineStoreOptionsBase<S extends StateTree, Store> {
+        /**
+         * Persists store in storage.
+         * @see https://prazdevs.github.io/pinia-plugin-persistedstate
+         */
+        persist?: boolean | PersistedStateOptions | PersistedStateOptions[];
+        unistorage?: boolean | PersistedStateOptions | PersistedStateOptions[];
+    }
+    interface PiniaCustomProperties {
+        /**
+         * Rehydrates store from persisted state
+         * Warning: this is for advances usecases, make sure you know what you're doing.
+         * @see https://prazdevs.github.io/pinia-plugin-persistedstate/guide/advanced.html#forcing-the-rehydration
+         */
+        $hydrate: (opts?: {
+            runHooks?: boolean;
+        }) => void;
+        /**
+         * Persists store into configured storage
+         * Warning: this is for advances usecases, make sure you know what you're doing.
+         * @see https://prazdevs.github.io/pinia-plugin-persistedstate/guide/advanced.html#forcing-the-persistence
+         */
+        $persist: () => void;
+    }
+}
+
+/**
+ * Creates a pinia persistence plugin
+ * @param factoryOptions global persistence options
+ * @returns pinia plugin
+ */
+declare function createPersistedState(factoryOptions?: PersistedStateFactoryOptions): PiniaPlugin;
+
+declare const _default: pinia.PiniaPlugin;
+
+export { PersistedStateFactoryOptions, PersistedStateOptions, Serializer, StorageLike, createPersistedState, _default as default, createUnistorage };
+
+/**
+ * Creates a pinia persistence plugin with uniapp
+ * @param factoryOptions global persistence options
+ * @returns pinia plugin
+ */
+declare function createUnistorage(factoryOptions?: PersistedStateFactoryOptions): PiniaPlugin;
+      

+ 162 - 0
uni_modules/pinia-plugin-unistorage/index.js

@@ -0,0 +1,162 @@
+// src/normalize.ts
+function isObject(v) {
+  return typeof v === "object" && v !== null;
+}
+function normalizeOptions(options, factoryOptions) {
+  options = isObject(options) ? options : /* @__PURE__ */ Object.create(null);
+  return new Proxy(options, {
+    get(target, key, receiver) {
+      if (key === "key")
+        return Reflect.get(target, key, receiver);
+      return Reflect.get(target, key, receiver) || Reflect.get(factoryOptions, key, receiver);
+    }
+  });
+}
+
+// src/pick.ts
+function get(state, path) {
+  return path.reduce((obj, p) => {
+    return obj == null ? void 0 : obj[p];
+  }, state);
+}
+function set(state, path, val) {
+  return path.slice(0, -1).reduce((obj, p) => {
+    if (/^(__proto__)$/.test(p))
+      return {};
+    else
+      return obj[p] = obj[p] || {};
+  }, state)[path[path.length - 1]] = val, state;
+}
+function pick(baseState, paths) {
+  return paths.reduce((substate, path) => {
+    const pathArray = path.split(".");
+    return set(substate, pathArray, get(baseState, pathArray));
+  }, {});
+}
+
+// src/plugin.ts
+function parsePersistence(factoryOptions, store) {
+  return (o) => {
+    var _a;
+    try {
+      const {
+        storage = localStorage,
+        beforeRestore = void 0,
+        afterRestore = void 0,
+        serializer = {
+          serialize: JSON.stringify,
+          deserialize: JSON.parse
+        },
+        key = store.$id,
+        paths = null,
+        debug = false
+      } = o;
+      return {
+        storage,
+        beforeRestore,
+        afterRestore,
+        serializer,
+        key: ((_a = factoryOptions.key) != null ? _a : (k) => k)(typeof key == "string" ? key : key(store.$id)),
+        paths,
+        debug
+      };
+    } catch (e) {
+      if (o.debug)
+        console.error("[pinia-plugin-persistedstate]", e);
+      return null;
+    }
+  };
+}
+function hydrateStore(store, { storage, serializer, key, debug }) {
+  try {
+    const fromStorage = storage == null ? void 0 : storage.getItem(key);
+    if (fromStorage)
+      store.$patch(serializer == null ? void 0 : serializer.deserialize(fromStorage));
+  } catch (e) {
+    if (debug)
+      console.error("[pinia-plugin-persistedstate]", e);
+  }
+}
+function persistState(state, { storage, serializer, key, paths, debug }) {
+  try {
+    const toStore = Array.isArray(paths) ? pick(state, paths) : state;
+    storage.setItem(key, serializer.serialize(toStore));
+  } catch (e) {
+    if (debug)
+      console.error("[pinia-plugin-persistedstate]", e);
+  }
+}
+function createPersistedState(factoryOptions = {}) {
+  return (context) => {
+    const { auto = false } = factoryOptions;
+    const {
+      options: { persist = auto },
+      store,
+      pinia
+    } = context;
+    if (!persist)
+      return;
+    if (!(store.$id in pinia.state.value)) {
+      const original_store = pinia._s.get(store.$id.replace("__hot:", ""));
+      if (original_store)
+        Promise.resolve().then(() => original_store.$persist());
+      return;
+    }
+    const persistences = (Array.isArray(persist) ? persist.map((p) => normalizeOptions(p, factoryOptions)) : [normalizeOptions(persist, factoryOptions)]).map(parsePersistence(factoryOptions, store)).filter(Boolean);
+    store.$persist = () => {
+      persistences.forEach((persistence) => {
+        persistState(store.$state, persistence);
+      });
+    };
+    store.$hydrate = ({ runHooks = true } = {}) => {
+      persistences.forEach((persistence) => {
+        const { beforeRestore, afterRestore } = persistence;
+        if (runHooks)
+          beforeRestore == null ? void 0 : beforeRestore(context);
+        hydrateStore(store, persistence);
+        if (runHooks)
+          afterRestore == null ? void 0 : afterRestore(context);
+      });
+    };
+    persistences.forEach((persistence) => {
+      const { beforeRestore, afterRestore } = persistence;
+      beforeRestore == null ? void 0 : beforeRestore(context);
+      hydrateStore(store, persistence);
+      afterRestore == null ? void 0 : afterRestore(context);
+      store.$subscribe(
+        (_mutation, state) => {
+          persistState(state, persistence);
+        },
+        {
+          detached: true
+        }
+      );
+    });
+  };
+}
+
+function createUnistorage(globalOptions = {}) {
+  const persistedState = createPersistedState({
+    storage: {
+      getItem(key) {
+        return uni.getStorageSync(key);
+      },
+      setItem(key, value) {
+        uni.setStorageSync(key, value);
+      }
+    },
+    serializer: {
+      deserialize: JSON.parse,
+      serialize: JSON.stringify
+    },
+    ...globalOptions
+  });
+  return (ctx) => {
+    if (ctx.options.unistorage) {
+      ctx.options.persist = ctx.options.unistorage;
+    }
+    return persistedState(ctx);
+  };
+}
+
+export { createPersistedState, createUnistorage };

+ 94 - 0
uni_modules/pinia-plugin-unistorage/package.json

@@ -0,0 +1,94 @@
+{
+  "id": "pinia-plugin-unistorage",
+  "displayName": "pinia-plugin-unistorage",
+  "version": "0.1.2",
+  "description": "uniapp 下 pinia 的本地数据缓存插件",
+  "keywords": [
+    "pinia",
+    "uniapp",
+    "storage",
+    "pinia-plugin",
+    "persistence"
+],
+  "type": "module",
+  "main": "./index.js",
+  "types": "./index.d.ts",
+  "exports": {
+    ".": {
+      "import": "./index.js",
+      "types": "./index.d.ts"
+    }
+  },
+  "engines": {
+    "HBuilderX": "^3.4.7"
+  },
+  "dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/pinia-plugin-unistorage",
+    "type": "sdk-js"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "y",
+          "飞书": "y",
+          "京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        }
+      }
+    }
+  }
+}

+ 226 - 0
uni_modules/pinia-plugin-unistorage/readme.md

@@ -0,0 +1,226 @@
+<div align="center">
+    <img width="200px" height="200px" src="https://gitee.com/dishait/pinia-plugin-unistorage/raw/main/static/favicon.png" />
+    <h1>pinia-plugin-unistorage</h1>
+    <p>uniapp 下 pinia 的本地数据缓存插件</p>
+</div>
+
+<br />
+<br />
+
+<div align="center">
+    <img width="100%" height="100%" src="https://gitee.com/dishait/pinia-plugin-unistorage/raw/main/static/pinia-plugin-unistorage.gif" />
+</div>
+
+<br />
+<br />
+
+## 引用
+
+该插件是
+[pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate)
+的 `uniapp` 版本,如果你需要在纯 `vue` 或者 `nuxt` 项目中使用 `pinia`
+的本地数据缓存,请使用
+[pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate)。
+
+<br />
+<br />
+
+## 动机
+
+为了实现多端的更简单的全局本地数据缓存
+
+<br />
+<br />
+
+## 组织 🦔
+
+欢迎关注 **帝莎编程**
+
+- [官网](http://dishaxy.dishait.cn/)
+- [Gitee](https://gitee.com/dishait)
+- [Github](https://github.com/dishait)
+- [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585)
+
+<br />
+<br />
+
+## 使用
+
+### 安装
+
+#### 1. `cli` 创建的 `uniapp` 项目
+
+```shell
+npm i pinia-plugin-unistorage -D
+```
+
+```js
+// main.js
+import { createSSRApp } from "vue";
+import * as Pinia from "pinia";
+import { createUnistorage } from "pinia-plugin-unistorage";
+
+export function createApp() {
+  const app = createSSRApp(App);
+
+  const store = Pinia.createPinia();
+
+  // 关键代码 👇
+  store.use(createUnistorage());
+
+  app.use(store);
+
+  return {
+    app,
+    Pinia, // 此处必须将 Pinia 返回
+  };
+}
+```
+
+<br />
+
+#### 2. `hbuilderx` 创建的 `uniapp` 项目
+
+直接插件市场安装后引入注册
+
+```js
+// main.js
+import { createSSRApp } from "vue";
+import * as Pinia from "pinia";
+import { createUnistorage } from "./uni_modules/pinia-plugin-unistorage";
+
+export function createApp() {
+  const app = createSSRApp(App);
+
+  const store = Pinia.createPinia();
+
+  // 关键代码 👇
+  store.use(createUnistorage());
+
+  app.use(store);
+
+  return {
+    app,
+    Pinia, // 此处必须将 Pinia 返回
+  };
+}
+```
+
+### 基础
+
+```js
+import { defineStore } from "pinia";
+
+export const useStore = defineStore("main", {
+  state() {
+    return {
+      someState: "hello pinia",
+    };
+  },
+  unistorage: true, // 开启后对 state 的数据读写都将持久化
+});
+```
+
+或者 `setup` 语法也是支持的
+
+```js
+import { defineStore } from "pinia";
+
+export const useStore = defineStore(
+  "main",
+  () => {
+    const someState = ref("hello pinia");
+    return { someState };
+  },
+  {
+    unistorage: true, // 开启后对 state 的数据读写都将持久化
+  },
+);
+```
+
+<br />
+
+### 选项
+
+#### 钩子
+
+```js
+import { defineStore } from "pinia";
+
+export const useStore = defineStore("main", {
+  state() {
+    return {
+      someState: "hello pinia",
+    };
+  },
+  unistorage: {
+    // 初始化恢复前触发
+    beforeRestore(ctx) {},
+    // 初始化恢复后触发
+    afterRestore(ctx) {},
+  },
+});
+```
+
+<br />
+
+#### 序列化
+
+大多数情况下你并不需要了解该选项
+
+```js
+import { defineStore } from "pinia";
+
+export const useStore = defineStore("main", {
+  state() {
+    return {
+      someState: "hello pinia",
+    };
+  },
+  unistorage: {
+    serializer: {
+      // 序列化,默认为 JSON.stringify
+      serialize(v) {
+        return JSON.stringify(v);
+      },
+      // 反序列化,默认为 JSON.parse
+      deserialize(v) {
+        return JSON.parse(v);
+      },
+    },
+  },
+});
+```
+
+<br />
+
+#### 其他
+
+```js
+import { defineStore } from "pinia";
+
+export const useStore = defineStore("main", {
+  state() {
+    return {
+      foo: "foo",
+      nested: {
+        data: "nested pinia",
+      },
+      someState: "hello pinia",
+    };
+  },
+  unistorage: {
+    key: "foo", // 缓存的键,默认为该 store 的 id,这里是 main,
+    paths: ["foo", "nested.data"], // 需要缓存的路径,这里设置 foo 和 nested 下的 data 会被缓存
+  },
+});
+```
+
+<br />
+<br />
+
+## License
+
+Made with [markthree](https://github.com/markthree)
+
+Published under [MIT License](./LICENSE).

+ 35 - 0
uni_modules/pinia-plugin-unistorage/src/index.ts

@@ -0,0 +1,35 @@
+import {
+  createPersistedState,
+  type PersistedStateFactoryOptions,
+} from "pinia-plugin-persistedstate";
+
+export * from "pinia-plugin-persistedstate";
+
+export function createUnistorage(
+  globalOptions: PersistedStateFactoryOptions = {},
+) {
+  const persistedState = createPersistedState({
+    storage: {
+      getItem(key) {
+        // @ts-ignore
+        return uni.getStorageSync(key);
+      },
+      setItem(key, value) {
+        // @ts-ignore
+        uni.setStorageSync(key, value);
+      },
+    },
+    serializer: {
+      deserialize: JSON.parse,
+      serialize: JSON.stringify,
+    },
+    ...globalOptions,
+  });
+  // @ts-ignore
+  return (ctx) => {
+    if (ctx.options.unistorage) {
+      ctx.options.persist = ctx.options.unistorage;
+    }
+    return persistedState(ctx);
+  };
+}

+ 84 - 0
utils/date.js

@@ -0,0 +1,84 @@
+// 日期
+
+/**
+ * 获取今天对应的年、月
+ * @returns {object} 包含年、月的对象
+ */
+const getTodayYearMonthObj = () => {
+	// 获取今天对应的年、月
+	const today = new Date();
+	const year = today.getFullYear();
+	const month = (today.getMonth() + 1).toString().padStart(2, '0');
+	
+	return {
+		year,
+		month
+	}
+}
+
+/**
+ * 获取昨天的日期
+ * @returns {string} 格式为YYYY-MM-DD的字符串
+ */
+const getYesterday = () => {
+	// 将日期减去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'); // 日期格式化为两位数
+	
+	// 格式化输出
+	return `${year}-${month}-${day}`;
+}
+
+/**
+ * 获取本月的第一天
+ * @returns {string} 格式为YYYY-MM-DD的字符串
+ */
+const getFirstDayOfMonth = () => {
+	const today = new Date();
+	let year = today.getFullYear();
+	let month = today.getMonth();
+	const firstDay = new Date(year, month, 1); // 设置日期为1,自动回到本月第一天
+	
+	// 获取年、月、日
+	year = firstDay.getFullYear();
+	month = (firstDay.getMonth() + 1).toString().padStart(2, '0'); // 月份是从0开始的,所以加1
+	const day = firstDay.getDate().toString().padStart(2, '0'); // 日期格式化为两位数
+	
+	// 格式化输出
+	return `${year}-${month}-${day}`;
+}
+
+/**
+ * 获取本月的最后一天
+ */
+const getLastDayOfMonth = () => {	
+	const today = new Date();
+	let year = today.getFullYear();
+	let month = today.getMonth();
+	// 下一个月的第一天的前一天就是本月的最后一天
+	const lastDay = new Date(year, month + 1, 0);
+ 
+	// 获取年、月、日
+	year = lastDay.getFullYear();
+	month = (lastDay.getMonth() + 1).toString().padStart(2, '0'); // 月份是从0开始的,所以加1
+	const day = lastDay.getDate().toString().padStart(2, '0'); // 日期格式化为两位数
+	
+	// 格式化输出
+	return `${year}-${month}-${day}`;
+}
+
+/**
+ * 默认导出对象
+ */
+const dateUtils = {
+	getTodayYearMonthObj,
+	getYesterday,
+	getFirstDayOfMonth,
+	getLastDayOfMonth
+}
+export default dateUtils;

+ 5 - 1
utils/request.js

@@ -1,3 +1,7 @@
+import { useUserInfoStore } from '@/stores/userInfo.js';
+
+const userInfoStore = useUserInfoStore();
+
 export default function request(config = {}) {
 	let {
 		url,
@@ -11,7 +15,7 @@ export default function request(config = {}) {
 	// 拼接url TODO 这里要修改,改成不同环境取不同的url
 	url = process.env.BASE_API_URL + url;
 	// 添加token
-	header['Authorization'] = uni.getStorageSync('token');
+	header['Authorization'] = userInfoStore.token;
 	// 是否显示loading
 	if (loading) {
 		uni.showLoading({