Jelajahi Sumber

完成登录注册组件及其相关流程(未调接口,仅模拟)

htc 6 jam lalu
induk
melakukan
202464a60b

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+node_modules
+unpackage

+ 24 - 0
common/composables/useGlobalShare.js

@@ -0,0 +1,24 @@
+import { ref } from 'vue';
+import { onLoad } from '@dcloudio/uni-app';
+
+export function useGlobalShare() {
+	// 1. 共享的数据
+	const shareData = ref('这是来自Composable的数据');
+	
+	// 2. 共享的方法
+	const shareLog = (message) => {
+		console.log(`[来自Composable的LOG]: ${message}`);
+	};
+	
+	// 3. 共享的生命周期逻辑
+	onLoad(() => {
+		console.log('--- Composable onLoad 触发 (仅页面) ---');
+		shareLog('页面加载了');
+	});
+	
+	// 将需要暴露出去的数据和方法返回
+	return {
+		shareData,
+		shareLog
+	};
+}

+ 0 - 1
common/mixins/system.js

@@ -4,7 +4,6 @@ export default {
 			h:uni.getSystemInfoSync().windowHeight,//屏幕高度
 			mt:uni.getSystemInfoSync().statusBarHeight + 44,//自定义头部组件高度(使用自定义头部组件的上边距)
 			th:50+uni.getSystemInfoSync().safeAreaInsets.bottom,//底部tabbar高度
-			imgBase:this.$imgBase
 		}
 	},
 	onShow(){

+ 70 - 0
common/stores/user.js

@@ -0,0 +1,70 @@
+import { defineStore } from 'pinia';
+
+export const useUserStore = defineStore('user', {
+	state: () => ({
+		isRegister: false,     // 是否注册成功
+		isLogin: false,        // 用户是否已登录
+		showLoginModal: false, // 是否显示登录弹框
+		userInfo: null,        // 用户信息
+	}),
+	
+	getters: {
+		// 方便在模板中直接使用
+		isLoggedIn: (state) => state.isLogin,
+	},
+
+	actions: {
+		// 打开登录弹框
+		openLoginModal() {
+			this.showLoginModal = true;
+		},
+
+		// 关闭登录弹框
+		closeLoginModal() {
+			this.isRegister = false;
+			this.showLoginModal = false;
+		},
+		
+		async register(userFrom) {
+			await new Promise(resolve => setTimeout(resolve, 1000));
+			this.isRegister = true;
+			
+			uni.showToast({
+				title: '注册成功',
+				icon: 'success'
+			});
+		},
+
+		// 模拟登录操作
+		async login(loginDto) {
+			console.log('正在登录...', loginDto);
+			
+			// --- 这里应该是调用你的后端 API ---
+			// 模拟一个异步请求
+			await new Promise(resolve => setTimeout(resolve, 1000));
+			
+			// 模拟登录成功
+			this.isLogin = true;
+			this.userInfo = { name: '张三', token: 'fake-token-string' };
+			
+			// 登录成功后关闭弹框
+			this.closeLoginModal();
+			
+			uni.showToast({
+				title: '登录成功',
+				icon: 'success'
+			});
+		},
+
+		// 登出操作
+		logout() {
+			this.isRegister = false;
+			this.isLogin = false;
+			this.userInfo = null;
+			uni.showToast({
+				title: '已退出登录',
+				icon: 'none'
+			});
+		},
+	},
+});

+ 2 - 2
common/utils/global.js

@@ -1,4 +1,4 @@
-import regTest from '@/common/utils/reg.js'
+import reg from '@/common/utils/reg.js'
 import api from '@/common/api/index.js'
 
 const showToast = (title, duration = 2000, icon = "none") => {
@@ -17,7 +17,7 @@ const showModal = content => {
 
 export const globalProperties = {
   $api: api,
-  $regTest: regTest,
+  $reg: reg,
   $showToast: showToast,
   $showModal: showModal
 };

+ 324 - 0
components/pages/loginRegister/index.vue

@@ -0,0 +1,324 @@
+<template>
+	<view class="login-register" v-if="userStore.showLoginModal">
+		<div class="lr-box" v-if="!userStore.isRegister">
+			<image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/c79318ee-e219-4f01-9dfb-91d7180a0972.png" class="lr-box-topbg" mode="widthFix"></image>
+			<div class="lr-box-top adfacjb">
+				<image class="lr-box-top-left" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/64d76db1-be28-4ba2-a8f0-57e2408beebc.png"></image>
+				<image class="lr-box-top-right" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/c3c4fa76-32d2-4d11-915d-5e655c6b5c72.png" @tap="close"></image>
+			</div>
+			<div class="lr-box-title">
+				<p class="p">欢迎来到善行少年</p>
+				<p class="p tip">首次登录需要简单填写信息即可进入</p>
+			</div>
+			<div class="lr-box-form">
+				<div class="lr-box-form-item adfacjb">
+					<div class="lr-box-form-item-left adfac">
+						<div class="text" style="margin-right: 32rpx;"><span style="color: #F4657A;">*</span>手机号码</div>
+						<up-input v-model="register.phone" border="none" placeholder="请输入手机号码"></up-input>
+					</div>
+					<div class="lr-box-form-item-right">
+						<button class="yjsq" open-type="getPhoneNumber" @getphonenumber="decryptPhoneNumber">一键授权</button>
+					</div>
+				</div>
+				<div class="lr-box-form-item adfacjb">
+					<div class="lr-box-form-item-left adfac">
+						<div class="text"><span style="color: #F4657A;">*</span>头像</div>
+						<image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/a2944f51-2c7b-41e7-8206-8a3be1f76d11.png" v-if="!register.avatar"></image>
+						<image :src="register.avatar"></image>
+					</div>
+					<div class="lr-box-form-item-right">
+						<button class="yjsq" open-type="chooseAvatar" @bindchooseavatar="onChooseAvatar">设置</button>
+					</div>
+				</div>
+				<div class="lr-box-form-item adfacjb">
+					<div class="lr-box-form-item-left">
+						<div class="text"><span style="color: #F4657A;">*</span>用户名</div>
+					</div>
+					<div class="lr-box-form-item-right">
+						<up-input v-model="register.userName" border="none" inputAlign="right" placeholder="请输入真实姓名"></up-input>
+					</div>
+				</div>
+				<div class="lr-box-form-item adfacjb">
+					<div class="lr-box-form-item-left">
+						<div class="text">家庭公益名称</div>
+					</div>
+					<div class="lr-box-form-item-right">
+						<up-input v-model="register.nonprofitName" border="none" inputAlign="right" placeholder="请输入至少三个字"></up-input>
+					</div>
+				</div>
+				<div class="lr-box-form-item adfacjb">
+					<div class="lr-box-form-item-left">
+						<div class="text">家庭公益口号</div>
+					</div>
+					<div class="lr-box-form-item-right">
+						<up-input v-model="register.nonprofitNumber" border="none" inputAlign="right" placeholder="家庭公益口号"></up-input>
+					</div>
+				</div>
+			</div>
+			<div class="lr-box-btns">
+				<div class="btn" @tap="toRegister">立即注册</div>
+				<div class="btn login" @tap="readLogin">已有账号,去登录</div>
+			</div>
+		</div>
+		<div class="lr-box login" v-else>
+			<image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/c79318ee-e219-4f01-9dfb-91d7180a0972.png" class="lr-box-topbg" mode="widthFix"></image>
+			<div class="lr-box-top login adfacjb">
+				<image class="lr-box-top-left login" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/d0f892c8-1f96-4c86-b62b-a8b027b4bf6e.png"></image>
+				<image class="lr-box-top-right" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/c3c4fa76-32d2-4d11-915d-5e655c6b5c72.png" @tap="close"></image>
+			</div>
+			<div class="lr-box-memo">公益献爱心 真情暖人心!</div>
+			<div class="lr-box-agree adfac">
+				<image v-if="agree" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/87b5b244-d14f-43cd-991b-4ac9f48d909e.png" @tap="changeAgree"></image>
+				<image v-else src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/09/12/b8a5cabd-57f8-4ad7-9677-f6372423c50a.png" @tap="changeAgree"></image>
+				<div class="text">我已阅读并同意<span>《善行少年小程序隐私政策》</span>及<span>《善行少年服务协议》</span></div>
+			</div>
+			<div class="lr-box-login">
+				<button class="phone-login" open-type="getPhoneNumber" @getphonenumber="decryptPhoneNumberLogin">手机号登录</button>
+			</div>
+		</div>
+	</view>
+</template>
+
+<script setup name="">
+	import { ref, getCurrentInstance } from 'vue'
+	const { proxy } = getCurrentInstance()
+	import { useUserStore } from '@/common/stores/user.js';
+	const userStore = useUserStore();
+	
+	const register = ref({
+		phone:null,
+		avatar:null,
+		userName:null,
+		nonprofitName:null,
+		nonprofitNumber:null,
+	})
+	const agree = ref(false)
+	
+	const close = () => {
+		userStore.closeLoginModal();
+	}
+	
+	const decryptPhoneNumber = e => {
+		if(e.detail.code) getPhone(e.detail.code);
+	}
+	
+	const onChooseAvatar = e => {
+		
+	}
+	
+	const getPhone = code => {
+		
+	}
+	
+	const toRegister = () => {
+		if(!proxy.$reg.mobile(register.value.phone)) return proxy.$showToast('请输入正确的手机号码')
+		if(!register.value.avatar) return proxy.$showToast('请设置头像')
+		if(!register.value.userName) return proxy.$showToast('请输入真实姓名')
+		
+		userStore.register();
+	}
+	
+	const readLogin = () => {
+		userStore.isRegister = true;
+	}
+	
+	const changeAgree = () => {
+		agree.value = !agree.value;
+	}
+	
+	
+	const decryptPhoneNumberLogin = e => {
+		if(e.detail.code) toPhoneLogin(e.detail.code);
+	}
+	
+	const toPhoneLogin = code => {
+		if(!agree.value) return proxy.$showToast('请勾选隐私政策和服务协议')
+		userStore.login({});
+	}
+</script>
+
+<style scoped lang="scss">
+	.login-register{
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: rgba(0, 0, 0, .4);
+		z-index: 9999;
+		display: flex;
+		flex-direction: column;
+		justify-content: flex-end;
+		.lr-box{
+			background: #F7F7F7;
+			border-radius: 24rpx 24rpx 0rpx 0rpx;
+			padding: 58rpx 30rpx 68rpx;
+			position: relative;
+			&.login{
+				padding: 32rpx 40rpx 88rpx;
+			}
+			&-topbg{
+				width: 100%;
+				position: absolute;
+				left: 0;
+				top: 0;
+			}
+			&-top{
+				position: relative;
+				padding: 0 10rpx 0 34rpx;
+				&.login{
+					padding: 0;
+				}
+				&-left{
+					width: 158rpx;
+					height: 88rpx;
+					&.login{
+						width: 224rpx;
+						height: 48rpx;
+					}
+				}
+				&-right{
+					width: 28rpx;
+					height: 28rpx;
+				}
+			}
+			&-title{
+				position: relative;
+				margin-top: 18rpx;
+				padding-left: 34rpx;
+				.p{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 600;
+					font-size: 44rpx;
+					color: #151B29;
+					line-height: 62rpx;
+					&.tip{
+						font-weight: 400;
+						font-size: 26rpx;
+						color: rgba(21,27,41,0.61);
+						line-height: 37rpx;
+						margin-top: 10rpx;
+					}
+				}
+			}
+			&-form{
+				position: relative;
+				margin-top: 10rpx;
+				&-item{
+					height: 120rpx;
+					background: #FFFFFF;
+					border-radius: 16rpx;
+					border: 1rpx solid #E0E1E4;
+					padding: 0 30rpx;
+					margin-top: 20rpx;
+					&-left{
+						width: 370rpx;
+						.text{
+							font-family: PingFangSC, PingFang SC;
+							font-weight: 400;
+							font-size: 30rpx;
+							color: #151B29;
+							line-height: 42rpx;
+						}
+						image{
+							width: 60rpx;
+							height: 60rpx;
+							margin-left: 28rpx;
+						}
+					}
+					&-right{
+						width: calc(100% - 370rpx);
+						padding-left: 20rpx;
+						box-sizing: border-box;
+						.yjsq{
+							font-family: PingFangSC, PingFang SC;
+							font-weight: 400;
+							font-size: 28rpx;
+							color: #0593FE;
+							line-height: 40rpx;
+							background-color: transparent;
+							text-align: right;
+							padding: 0;
+							&::after{
+								width: auto;
+								border: none;
+							}
+						}
+					}
+				}
+			}
+			&-btns{
+				margin-top: 20rpx;
+				padding: 0 10rpx;
+				overflow: hidden;
+				.btn{
+					width: 100%;
+					height: 90rpx;
+					background: #B7F358;
+					border-radius: 45rpx;
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					color: #151B29;
+					line-height: 90rpx;
+					text-align: center;
+					letter-spacing: 2rpx;
+					margin-top: 20rpx;
+					&.login{
+						background: #ECEEF5;	
+					}
+				}
+			}
+			&-memo{
+				position: relative;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 26rpx;
+				color: #151B29;
+				line-height: 36rpx;
+				letter-spacing: 4rpx;
+				margin-top: 41rpx;
+			}
+			&-agree{
+				position: relative;
+				margin-top: 95rpx;
+				image{
+					width: 48rpx;
+					height: 48rpx;
+				}
+				.text{
+					width: calc(100% - 48rpx);
+					padding-left: 19rpx;
+					box-sizing: border-box;
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 26rpx;
+					color: #151B29;
+					line-height: 36rpx;
+					label{
+						color: #E77687;
+					}
+				}
+			}
+			&-login{
+				margin-top: 122rpx;
+				.phone-login{
+					width: 100%;
+					height: 90rpx;
+					background: #B7F358;
+					border-radius: 45rpx;
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					color: #151B29;
+					line-height: 90rpx;
+					text-align: center;
+					letter-spacing: 2rpx;
+					&::after{
+						border: none;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 8 - 1
components/pages/nonprofitActivety/index.vue

@@ -24,13 +24,20 @@
 		</div>
 		<div class="na-bottom adfacjb">
 			<div class="na-bottom-left adf">已报名&nbsp;&nbsp;<strong>{{234}}</strong>/{{300}}&nbsp;&nbsp;人</div>
-			<div class="na-bottom-right">立即报名</div>
+			<div class="na-bottom-right" @tap="toApply">立即报名</div>
 		</div>
+		<login-register></login-register>
 	</view>
 </template>
 
 <script setup name="nonprofitActivety">
 	import { ref } from 'vue'
+	import { useUserStore } from '@/common/stores/user';
+	const userStore = useUserStore();
+	
+	const toApply = () => {
+		userStore.openLoginModal();
+	}
 </script>
 
 <style scoped lang="scss">

+ 6 - 4
main.js

@@ -12,22 +12,24 @@ app.$mount()
 // #endif
 
 // #ifdef VUE3
-import {
-	createSSRApp
-} from 'vue'
-
+import { createSSRApp } from 'vue'
 import uviewPlus from 'uview-plus'
 import Skeleton from '@/components/CusSkeleton/index.vue'
+import LoginRegister from '@/components/pages/loginRegister/index.vue'
 import system from '@/common/mixins/system.js'
 import { globalProperties } from '@/common/utils/global.js'
+import { createPinia } from 'pinia';
 
 
 export function createApp() {
 	const app = createSSRApp(App)
+	const pinia = createPinia();
 
+	app.use(pinia);
 	app.use(uviewPlus)
 	app.mixin(system)
 	app.component('u-skeleton', Skeleton)
+	app.component('login-register', LoginRegister)
 
 	for (const key in globalProperties) {
 		app.config.globalProperties[key] = globalProperties[key]

+ 645 - 8
package-lock.json

@@ -1,10 +1,11 @@
 {
-  "name": "SHSN_WX",
+  "name": "SxsnWechat",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "dependencies": {
+        "pinia": "^3.0.3",
         "uview-plus": "^3.5.41"
       },
       "devDependencies": {
@@ -12,6 +13,52 @@
         "sass-loader": "^10.4.1"
       }
     },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "peer": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+      "peer": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.4.tgz",
+      "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+      "peer": true,
+      "dependencies": {
+        "@babel/types": "^7.28.4"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.4.tgz",
+      "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+      "peer": true,
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.13",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -48,7 +95,6 @@
       "version": "1.5.5",
       "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
       "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
-      "dev": true,
       "peer": true
     },
     "node_modules/@jridgewell/trace-mapping": {
@@ -107,6 +153,136 @@
         "undici-types": "~7.10.0"
       }
     },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
+      "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
+      "peer": true,
+      "dependencies": {
+        "@babel/parser": "^7.28.3",
+        "@vue/shared": "3.5.21",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
+      "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-core": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
+      "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
+      "peer": true,
+      "dependencies": {
+        "@babel/parser": "^7.28.3",
+        "@vue/compiler-core": "3.5.21",
+        "@vue/compiler-dom": "3.5.21",
+        "@vue/compiler-ssr": "3.5.21",
+        "@vue/shared": "3.5.21",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.18",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
+      "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
+      "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
+      "dependencies": {
+        "@vue/devtools-kit": "^7.7.7"
+      }
+    },
+    "node_modules/@vue/devtools-kit": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
+      "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
+      "dependencies": {
+        "@vue/devtools-shared": "^7.7.7",
+        "birpc": "^2.3.0",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.2"
+      }
+    },
+    "node_modules/@vue/devtools-shared": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
+      "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
+      "dependencies": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.21.tgz",
+      "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
+      "peer": true,
+      "dependencies": {
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
+      "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
+      "peer": true,
+      "dependencies": {
+        "@vue/reactivity": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
+      "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
+      "peer": true,
+      "dependencies": {
+        "@vue/reactivity": "3.5.21",
+        "@vue/runtime-core": "3.5.21",
+        "@vue/shared": "3.5.21",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
+      "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.21",
+        "@vue/shared": "3.5.21"
+      },
+      "peerDependencies": {
+        "vue": "3.5.21"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.21.tgz",
+      "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
+      "peer": true
+    },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.14.1",
       "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -409,6 +585,14 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/birpc": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz",
+      "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
     "node_modules/braces": {
       "version": "3.0.3",
       "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
@@ -533,6 +717,26 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/copy-anything": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
+      "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+      "dependencies": {
+        "is-what": "^4.1.8"
+      },
+      "engines": {
+        "node": ">=12.13"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "peer": true
+    },
     "node_modules/dayjs": {
       "version": "1.11.18",
       "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.18.tgz",
@@ -573,6 +777,18 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "peer": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/es-module-lexer": {
       "version": "1.7.0",
       "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
@@ -637,6 +853,12 @@
         "node": ">=4.0"
       }
     },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "peer": true
+    },
     "node_modules/events": {
       "version": "3.3.0",
       "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
@@ -746,6 +968,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
+    },
     "node_modules/immutable": {
       "version": "4.3.7",
       "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.7.tgz",
@@ -794,6 +1021,17 @@
         "node": ">=0.12.0"
       }
     },
+    "node_modules/is-what": {
+      "version": "4.1.16",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
+      "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
+      "engines": {
+        "node": ">=12.13"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
     "node_modules/jest-worker": {
       "version": "27.5.1",
       "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
@@ -867,6 +1105,15 @@
         "node": ">=8.9.0"
       }
     },
+    "node_modules/magic-string": {
+      "version": "0.30.19",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.19.tgz",
+      "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -897,6 +1144,29 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "peer": true,
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
     "node_modules/neo-async": {
       "version": "2.6.2",
       "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
@@ -919,11 +1189,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
+    },
     "node_modules/picocolors": {
       "version": "1.1.1",
       "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
-      "dev": true,
       "peer": true
     },
     "node_modules/picomatch": {
@@ -938,6 +1212,54 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
+    "node_modules/pinia": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz",
+      "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==",
+      "dependencies": {
+        "@vue/devtools-api": "^7.7.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.4.4",
+        "vue": "^2.7.0 || ^3.5.11"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "peer": true,
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
     "node_modules/punycode": {
       "version": "2.3.1",
       "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
@@ -979,6 +1301,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
+    },
     "node_modules/safe-buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -1113,7 +1440,6 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
       "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -1129,6 +1455,25 @@
         "source-map": "^0.6.0"
       }
     },
+    "node_modules/speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/superjson": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
+      "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
+      "dependencies": {
+        "copy-anything": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
     "node_modules/supports-color": {
       "version": "8.1.1",
       "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
@@ -1348,6 +1693,27 @@
         "uni-app-x": ""
       }
     },
+    "node_modules/vue": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.21.tgz",
+      "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.21",
+        "@vue/compiler-sfc": "3.5.21",
+        "@vue/runtime-dom": "3.5.21",
+        "@vue/server-renderer": "3.5.21",
+        "@vue/shared": "3.5.21"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/watchpack": {
       "version": "2.4.4",
       "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz",
@@ -1480,6 +1846,37 @@
     }
   },
   "dependencies": {
+    "@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "peer": true
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+      "peer": true
+    },
+    "@babel/parser": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.4.tgz",
+      "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+      "peer": true,
+      "requires": {
+        "@babel/types": "^7.28.4"
+      }
+    },
+    "@babel/types": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.4.tgz",
+      "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+      "peer": true,
+      "requires": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.27.1"
+      }
+    },
     "@jridgewell/gen-mapping": {
       "version": "0.3.13",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -1513,7 +1910,6 @@
       "version": "1.5.5",
       "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
       "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
-      "dev": true,
       "peer": true
     },
     "@jridgewell/trace-mapping": {
@@ -1572,6 +1968,133 @@
         "undici-types": "~7.10.0"
       }
     },
+    "@vue/compiler-core": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
+      "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
+      "peer": true,
+      "requires": {
+        "@babel/parser": "^7.28.3",
+        "@vue/shared": "3.5.21",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
+      "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
+      "peer": true,
+      "requires": {
+        "@vue/compiler-core": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
+      "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
+      "peer": true,
+      "requires": {
+        "@babel/parser": "^7.28.3",
+        "@vue/compiler-core": "3.5.21",
+        "@vue/compiler-dom": "3.5.21",
+        "@vue/compiler-ssr": "3.5.21",
+        "@vue/shared": "3.5.21",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.18",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
+      "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
+      "peer": true,
+      "requires": {
+        "@vue/compiler-dom": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "@vue/devtools-api": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
+      "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
+      "requires": {
+        "@vue/devtools-kit": "^7.7.7"
+      }
+    },
+    "@vue/devtools-kit": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
+      "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
+      "requires": {
+        "@vue/devtools-shared": "^7.7.7",
+        "birpc": "^2.3.0",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.2"
+      }
+    },
+    "@vue/devtools-shared": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
+      "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
+      "requires": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "@vue/reactivity": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.21.tgz",
+      "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
+      "peer": true,
+      "requires": {
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
+      "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
+      "peer": true,
+      "requires": {
+        "@vue/reactivity": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
+      "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
+      "peer": true,
+      "requires": {
+        "@vue/reactivity": "3.5.21",
+        "@vue/runtime-core": "3.5.21",
+        "@vue/shared": "3.5.21",
+        "csstype": "^3.1.3"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
+      "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
+      "peer": true,
+      "requires": {
+        "@vue/compiler-ssr": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.21.tgz",
+      "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
+      "peer": true
+    },
     "@webassemblyjs/ast": {
       "version": "1.14.1",
       "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -1835,6 +2358,11 @@
       "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
       "dev": true
     },
+    "birpc": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz",
+      "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ=="
+    },
     "braces": {
       "version": "3.0.3",
       "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
@@ -1911,6 +2439,20 @@
       "dev": true,
       "peer": true
     },
+    "copy-anything": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
+      "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+      "requires": {
+        "is-what": "^4.1.8"
+      }
+    },
+    "csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "peer": true
+    },
     "dayjs": {
       "version": "1.11.18",
       "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.18.tgz",
@@ -1945,6 +2487,12 @@
         "tapable": "^2.2.0"
       }
     },
+    "entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "peer": true
+    },
     "es-module-lexer": {
       "version": "1.7.0",
       "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
@@ -1996,6 +2544,12 @@
       "dev": true,
       "peer": true
     },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "peer": true
+    },
     "events": {
       "version": "3.3.0",
       "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
@@ -2076,6 +2630,11 @@
       "dev": true,
       "peer": true
     },
+    "hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
+    },
     "immutable": {
       "version": "4.3.7",
       "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.7.tgz",
@@ -2112,6 +2671,11 @@
       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
       "dev": true
     },
+    "is-what": {
+      "version": "4.1.16",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
+      "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="
+    },
     "jest-worker": {
       "version": "27.5.1",
       "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
@@ -2167,6 +2731,15 @@
         "json5": "^2.1.2"
       }
     },
+    "magic-string": {
+      "version": "0.30.19",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.19.tgz",
+      "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
+      "peer": true,
+      "requires": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
     "merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -2191,6 +2764,17 @@
         "mime-db": "1.52.0"
       }
     },
+    "mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
+    },
+    "nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "peer": true
+    },
     "neo-async": {
       "version": "2.6.2",
       "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
@@ -2210,11 +2794,15 @@
       "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
       "dev": true
     },
+    "perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
+    },
     "picocolors": {
       "version": "1.1.1",
       "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
-      "dev": true,
       "peer": true
     },
     "picomatch": {
@@ -2223,6 +2811,25 @@
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
       "dev": true
     },
+    "pinia": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz",
+      "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==",
+      "requires": {
+        "@vue/devtools-api": "^7.7.2"
+      }
+    },
+    "postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "peer": true,
+      "requires": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      }
+    },
     "punycode": {
       "version": "2.3.1",
       "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
@@ -2255,6 +2862,11 @@
       "dev": true,
       "peer": true
     },
+    "rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
+    },
     "safe-buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -2328,8 +2940,7 @@
     "source-map-js": {
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
-      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
-      "dev": true
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
     },
     "source-map-support": {
       "version": "0.5.21",
@@ -2342,6 +2953,19 @@
         "source-map": "^0.6.0"
       }
     },
+    "speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="
+    },
+    "superjson": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
+      "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
+      "requires": {
+        "copy-anything": "^3.0.2"
+      }
+    },
     "supports-color": {
       "version": "8.1.1",
       "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
@@ -2481,6 +3105,19 @@
         "dayjs": "^1.11.3"
       }
     },
+    "vue": {
+      "version": "3.5.21",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.21.tgz",
+      "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
+      "peer": true,
+      "requires": {
+        "@vue/compiler-dom": "3.5.21",
+        "@vue/compiler-sfc": "3.5.21",
+        "@vue/runtime-dom": "3.5.21",
+        "@vue/server-renderer": "3.5.21",
+        "@vue/shared": "3.5.21"
+      }
+    },
     "watchpack": {
       "version": "2.4.4",
       "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz",

+ 1 - 0
package.json

@@ -4,6 +4,7 @@
     "sass-loader": "^10.4.1"
   },
   "dependencies": {
+    "pinia": "^3.0.3",
     "uview-plus": "^3.5.41"
   }
 }