feat(登录)

This commit is contained in:
Ankkaya 2024-05-29 17:07:37 +08:00
parent c611e44858
commit c65eb5ddcd
16 changed files with 1012 additions and 910 deletions

2
.env
View File

@ -5,7 +5,7 @@ MALL_VERSION = v1.0.0
MALL_BASE_URL = http://api-dashboard.yudao.iocoder.cn
# 后端接口 - 测试环境(通过 process.env.NODE_ENV = development
MALL_DEV_BASE_URL = http://mall-backend-dev.jiandyb.cn:7001
MALL_DEV_BASE_URL = https://mall-baclend-local.jiandyb9834.xyz
# 后端接口前缀(一般不建议调整)
MALL_API_PATH = /merchant-api

View File

@ -3,8 +3,6 @@ import { onLaunch, onShow, onError } from '@dcloudio/uni-app'
import { peachInit } from './peach'
onLaunch(() => {
// 使
uni.hideTabBar()
peachInit()
})

View File

@ -8,6 +8,9 @@
},
"pages": [
//pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/redirect"
},
{
"path": "pages/order/list",
"style": {
@ -64,18 +67,17 @@
}
],
"subPackages": [
{
"root": "pages/public",
"pages": [
{
"path": "error",
"style": {
"navigationBarTitleText": "错误页面"
}
}
]
}
{
"root": "pages/public",
"pages": [
{
"path": "error",
"style": {
"navigationBarTitleText": "错误页面"
}
}
]
}
],
"tabBar": {
"list": [

View File

@ -3,34 +3,38 @@
<view :style="[{ paddingTop: peach.$platform.navBar + 'px' }]"></view>
<view class="dashboard-module ss-p-x-30">
<view class="merchant-info flex align-center">
<image class="logo" :src="state.merchantInfo.logo" mode="aspectFill"></image>
<image class="logo" :src="merchantInfo.logo" mode="aspectFill"></image>
<view class="detail flex flex-column justify-center gap-10">
<view class="name ss-font-26">{{ state.merchantInfo.name }}</view>
<view class="description ss-font-26">{{ state.merchantInfo.description }}</view>
<view class="name ss-font-26">{{ merchantInfo.name }}</view>
<view class="description ss-font-26">{{ merchantInfo?.contactPhone }}</view>
</view>
</view>
<view class="statistic ss-m-t-24">
<view class="title ss-font-24 ss-m-b-20">
{{ `今日收款金额(成功收款${state.statistic.total}笔)` }}
{{ `今日收款金额(成功收款${state.statistic.todayPaymentCount}笔)` }}
</view>
<view class="income flex justify-between align-center">
<view class="left flex align-center">
<view class="unit self-start"></view>
<view class="sincome ss-font-60">{{ state.statistic.income }}</view>
<view class="sincome ss-font-60">{{ state.statistic.todayPaymentAmount || 0 }}</view>
</view>
<button class="right-btn ss-reset-button">查看详情</button>
</view>
<view class="des ss-m-t-20">
总销售额{{ state.statistic.totalSalesAmount }} | 成功退款{{ state.statistic.refundCount }} |
退款金额{{ state.statistic.refundAmount }}
</view>
</view>
<view class="more ss-m-t-70">
<view class="title ss-m-b-30">基础数据</view>
<view class="items">
<view class="item" v-for="item in state.more">
<view class="item" v-for="item in state.more" :key="item.name">
<view class="label">{{ item.name }}</view>
<view class="value">{{ item.value }}</view>
<view class="last">昨日 {{ item.last }}</view>
<view class="last"> 昨日 {{ item.last }} </view>
</view>
</view>
</view>
@ -39,8 +43,11 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import peach from '@/peach'
import $store from '@/peach/store'
import UserUtil from '@/peach/api/member/user'
import bg from '@/static/bg-page.png'
const bgStyle = {
@ -51,43 +58,62 @@ const bgStyle = {
}
const state = ref({
merchantInfo: {
name: '测试商家',
logo: '/static/logo.png',
description: '测试商家描述',
},
statistic: {
total: 100,
income: 2222222222,
todayPaymentCount: 0,
todayPaymentAmount: 0,
totalSalesAmount: 0,
refundCount: 0,
refundAmount: 0,
},
more: [
{
name: '支付金额',
value: 10000000,
last: 100000000,
},
{
name: '访客数',
value: 100000,
last: 100000,
name: '销售金额',
key: 'Amount',
value: 0,
last: 0,
},
{
name: '订单数',
value: 100000,
last: 10000,
key: 'orderCount',
value: 0,
last: 0,
},
{
name: '客单价',
value: 10000,
last: 100,
name: '待核销',
key: 'verificationOrderCount',
value: 0,
},
{
name: '支付买家数',
value: 100,
last: 100,
name: '待配送',
key: 'deliveryOrderCount',
value: 0,
},
],
})
const userStore = $store('user')
const merchantInfo = computed(() => {
return userStore.userInfo?.particulars
})
async function getStatistic() {
let res = await UserUtil.getHomeStatistics()
for (let key of Object.keys(state.value.statistic)) {
state.value.statistic[key] = res.data[key]
}
state.value.more[0].value = res.data.todayPaymentAmount ?? 0
state.value.more[0].last = res.data.yesterdaySalesAmount ?? 0
state.value.more[1].value = res.data.orderCount
state.value.more[1].last = res.data.yesterdayOrderCount
state.value.more[2].value = res.data.verificationOrderCount
state.value.more[3].value = res.data.deliveryOrderCount
}
onShow(() => {
getStatistic()
})
</script>
<style lang="scss" scoped>
@ -136,6 +162,11 @@ const state = ref({
color: #fff;
}
}
.des {
color: var(--ui-TC-2);
font-size: 24rpx;
}
}
.more {

View File

@ -1,181 +1,194 @@
<template>
<pb-layout :leftIcon="''" navbar="inner" :bgStyle="{ backgroundColor: '#fff' }">
<view class="login-module ss-p-x-30">
<view class="login-title ss-font-56 ss-m-b-72">
{{ title }}
</view>
<uni-forms ref="smsLoginRef" v-model="state.model" :rules="state.rules" validateTrigger="bind">
<uni-forms-item name="mobile">
<uni-easyinput :styles="state.inputStyle" trim="all" placeholder="请输入手机号" v-model="state.model.mobile"
:inputBorder="false" type="number" />
</uni-forms-item>
<uni-forms-item name="code">
<uni-easyinput :styles="state.inputStyle" trim="all" placeholder="验证码" v-model="state.model.code"
:inputBorder="false" type="number" maxlength="4">
<template #right>
<button :disabled="state.isMobileEnd" :class="{ 'code-btn-end': state.isMobileEnd }"
class="ss-reset-button code-btn code-btn-start" @tap="getSmsCode('smsLogin', state.model.mobile)">
{{ getSmsTimer('smsLogin') }}
</button>
</template>
</uni-easyinput>
</uni-forms-item>
</uni-forms>
<button class="ss-reset-button login-btn-start" @tap="smsLoginSubmit">登录</button>
<view class="agreement-box ss-flex ss-row-center" :class="{ shake: state.isShaking }">
<label class="radio ss-flex" @tap="onChange">
<radio :checked="state.agreeStatus" color="var(--ui-BG-Main)" style="transform: scale(0.8)"
@tap.stop="onChange" />
<view class="agreement-text ss-flex ss-m-l-8">
我已阅读并遵守
<view class="tcp-text" @tap.stop="onProtocol('商家入驻协议')"> 商家入驻协议 </view>
</view>
</label>
</view>
</view>
</pb-layout>
<pb-layout :leftIcon="''" navbar="inner" :bgStyle="{ backgroundColor: '#fff' }">
<view class="login-module ss-p-x-30">
<view class="login-title ss-font-56 ss-m-b-72">
{{ title }}
</view>
<uni-forms ref="smsLoginRef" v-model="state.model" :rules="state.rules" validateTrigger="bind">
<uni-forms-item name="mobile">
<uni-easyinput
:styles="state.inputStyle"
trim="all"
placeholder="请输入手机号"
v-model="state.model.mobile"
:inputBorder="false"
type="number"
/>
</uni-forms-item>
<uni-forms-item name="code">
<uni-easyinput
:styles="state.inputStyle"
trim="all"
placeholder="验证码"
v-model="state.model.code"
:inputBorder="false"
type="number"
maxlength="4"
>
<template #right>
<button
:disabled="state.isMobileEnd"
:class="{ 'code-btn-end': state.isMobileEnd }"
class="ss-reset-button code-btn code-btn-start"
@tap="getSmsCode('smsLogin', state.model.mobile)"
>
{{ getSmsTimer('smsLogin') }}
</button>
</template>
</uni-easyinput>
</uni-forms-item>
</uni-forms>
<button class="ss-reset-button login-btn-start" @tap="smsLoginSubmit">登录</button>
<view class="agreement-box ss-flex ss-row-center" :class="{ shake: state.isShaking }">
<label class="radio ss-flex" @tap="onChange">
<radio
:checked="state.agreeStatus"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
@tap.stop="onChange"
/>
<view class="agreement-text ss-flex ss-m-l-8">
我已阅读并遵守
<view class="tcp-text" @tap.stop="onProtocol('商家入驻协议')"> 商家入驻协议 </view>
</view>
</label>
</view>
</view>
</pb-layout>
</template>
<script setup>
import { ref } from 'vue'
import { code, mobile } from '@/peach/validate/form';
import AuthUtil from '@/peach/api/member/auth';
import { code, mobile } from '@/peach/validate/form'
import AuthUtil from '@/peach/api/member/auth'
import peach from '@/peach'
import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/peach/hooks/useModal';
import { showAuthModal, closeAuthModal, getSmsCode, getSmsTimer } from '@/peach/hooks/useModal'
const title = ref('欢迎登录')
const smsLoginRef = ref(null)
const state = ref({
isMobileEnd: false,
agreeStatus: false,
isShaking: false,
model: {
mobile: '15036370128',
code: '9999',
},
rules: {
mobile,
code
},
inputStyle: {
backgroundColor: '#ECECEC'
}
isMobileEnd: false,
agreeStatus: false,
isShaking: false,
model: {
mobile: '15036370128',
code: '9999',
},
rules: {
mobile,
code,
},
inputStyle: {
backgroundColor: '#ECECEC',
},
})
function onChange() {
state.value.agreeStatus = !state.value.agreeStatus
state.value.agreeStatus = !state.value.agreeStatus
}
async function smsLoginSubmit() {
const validate = await smsLoginRef.value.validate().catch(err => {
console.log('err', err)
})
if (!validate) return
if (!state.value.agreeStatus) {
state.value.isShaking = true
setTimeout(() => {
state.value.isShaking = false
}, 1000)
peach.$helper.toast('请勾选同意')
return
}
const { code } = await AuthUtil.smsLogin(state.value.model)
if (code === 0) {
}
const validate = await smsLoginRef.value.validate().catch((err) => {
console.log('err', err)
})
if (!validate) return
if (!state.value.agreeStatus) {
state.value.isShaking = true
setTimeout(() => {
state.value.isShaking = false
}, 1000)
peach.$helper.toast('请勾选同意')
return
}
const { code } = await AuthUtil.smsLogin(state.value.model)
}
</script>
<style lang="scss">
.login-module {
.uni-easyinput {
::v-deep .uni-easyinput__content {
border-radius: 41rpx;
padding: 0 10rpx;
}
.is-focused {
::v-deep .content-clear-icon {
.uniui-clear {
color: red !important
.uni-easyinput {
::v-deep .uni-easyinput__content {
border-radius: 41rpx;
padding: 0 10rpx;
}
.is-focused {
::v-deep .content-clear-icon {
.uniui-clear {
color: red !important;
}
}
}
}
}
}
}
</style>
<style lang="scss" scoped>
.login-module {
margin-top: 50%;
overflow: hidden;
margin-top: 50%;
overflow: hidden;
.login-title {
color: '#1818d';
font-weight: 600;
}
.login-btn-start {
height: 82rpx;
line-height: normal;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 500;
color: #fff;
}
.code-btn-start {
width: 160rpx;
height: 56rpx;
line-height: normal;
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 400;
color: var(--ui-BG-Main);
opacity: 1;
}
.agreement-box {
margin: -10px auto 10px;
position: absolute;
bottom: 30rpx;
width: 100%;
.protocol-check {
transform: scale(0.7);
.login-title {
color: '#1818d';
font-weight: 600;
}
.agreement-text {
font-size: 26rpx;
font-weight: 500;
color: #999999;
.login-btn-start {
height: 82rpx;
line-height: normal;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 500;
color: #fff;
}
.tcp-text {
.code-btn-start {
width: 160rpx;
height: 56rpx;
line-height: normal;
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 400;
color: var(--ui-BG-Main);
}
}
}
.shake {
animation: shake 0.05s linear 4 alternate;
}
@keyframes shake {
from {
transform: translateX(-5rpx);
opacity: 1;
}
to {
transform: translateX(5rpx);
.agreement-box {
margin: -10px auto 10px;
position: absolute;
bottom: 30rpx;
width: 100%;
.protocol-check {
transform: scale(0.7);
}
.agreement-text {
font-size: 26rpx;
font-weight: 500;
color: #999999;
.tcp-text {
color: var(--ui-BG-Main);
}
}
}
.shake {
animation: shake 0.05s linear 4 alternate;
}
@keyframes shake {
from {
transform: translateX(-5rpx);
}
to {
transform: translateX(5rpx);
}
}
}
}
</style>

View File

@ -3,11 +3,11 @@
<view :style="[{ paddingTop: peach.$platform.navBar + 'px' }]"></view>
<view class="my-module ss-p-x-30">
<view class="user-info flex align-center">
<image class="avatar" :src="state.userInfo.avatar" mode="aspectFill"></image>
<image class="avatar" :src="userInfo.avatar || '/static/default_avatar.png'" mode="aspectFill"></image>
<view class="detail flex flex-column justify-center gap-10">
<view class="name ss-font-26">{{ state.userInfo.name }}</view>
<view class="description ss-font-26">{{ state.userInfo.description }}</view>
<view class="name ss-font-26">{{ userInfo.nickname }}</view>
<view class="description ss-font-26">{{ userInfo.mobile }}</view>
</view>
</view>
@ -16,17 +16,17 @@
<view class="remain flex justify-between align-center">
<view class="left flex align-center">
<view class="unit self-start"></view>
<view class="sremain ss-font-60">{{ state.statistic.remain }}</view>
<view class="sremain ss-font-60">{{ remain }}</view>
</view>
<button class="right-btn ss-reset-button">查看详情</button>
</view>
<view class="cent ss-m-t-20"> 总获取积分{{ state.statistic.cent }} </view>
<view class="cent ss-m-t-20"> 总获取积分{{ cent }} </view>
</view>
<view class="menu ss-m-t-70">
<view class="title ss-m-b-30">我的服务</view>
<view class="items">
<view class="item" v-for="item in state.menus">
<view class="item" v-for="item in state.menus" :key="item.name">
<image class="icon" :src="item.icon" mode="aspectFill"></image>
<view class="label">{{ item.name }}</view>
</view>
@ -40,26 +40,17 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import $store from '@/peach/store'
import peach from '@/peach'
const bgStyle = {
backgroundImage: '/static/bg-page.png',
imageType: 'local',
backgroundColor: '#fff',
description: '',
}
const state = ref({
userInfo: {
name: 'Ankkaya',
avatar: '/static/logo.png',
description: '测试商家描述',
},
statistic: {
remain: 2222222222,
cent: 10000,
},
menus: [
{
name: '余额提现',
@ -83,6 +74,17 @@ const state = ref({
},
],
})
const userStore = $store('user')
console.log(userStore)
const remain = computed(() => {
return userStore.userWallet?.balance
})
const userInfo = computed(() => {
return userStore.userInfo
})
</script>
<style lang="scss" scoped>

31
pages/index/redirect.vue Normal file
View File

@ -0,0 +1,31 @@
<template>
<view></view>
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app'
import $store from '@/peach/store'
import peach from '@/peach'
const userStore = $store('user')
async function redirectFn() {
//
if (!userStore.isLogin) {
userStore.logOut()
peach.$router.redirect('/pages/index/login')
} else {
await userStore.loginAfter()
if (userStore.lastRoutePage) {
peach.$router.redirect(userStore.lastRoutePage)
} else {
peach.$router.redirect('/pages/index/index')
}
}
}
onLoad(() => {
redirectFn()
})
</script>

View File

@ -1,48 +1,49 @@
import request from "@/peach/request";
import request from '@/peach/request'
const AuthUtil = {
smsLogin: (data) => {
return request({
url: "/particulars/member/auth/sms-login",
method: "POST",
data,
custom: {
showSuccess: true,
loadingMsg: "登陆中",
successMsg: "登陆成功",
},
});
},
// 发送手机验证码
sendSmsCode: (mobile, scene) => {
return request({
url: "/member/auth/send-sms-code",
method: "POST",
data: {
mobile,
scene,
},
custom: {
loadingMsg: "发送中",
showSuccess: true,
successMsg: "发送成功",
},
});
},
// 刷新令牌
refreshToken: (refreshToken) => {
return request({
url: "/member/auth/refresh-token",
method: "POST",
params: {
refreshToken,
},
custom: {
loading: false, // 不用加载中
showError: false, // 不展示错误提示
},
});
},
};
smsLogin: (data) => {
return request({
url: '/particulars/member/auth/sms-login',
method: 'POST',
data,
custom: {
showSuccess: true,
loadingMsg: '登陆中',
successMsg: '登陆成功',
auth: false,
},
})
},
// 发送手机验证码
sendSmsCode: (mobile, scene) => {
return request({
url: '/member/auth/send-sms-code',
method: 'POST',
data: {
mobile,
scene,
},
custom: {
loadingMsg: '发送中',
showSuccess: true,
successMsg: '发送成功',
},
})
},
// 刷新令牌
refreshToken: (refreshToken) => {
return request({
url: '/member/auth/refresh-token',
method: 'POST',
params: {
refreshToken,
},
custom: {
loading: false, // 不用加载中
showError: false, // 不展示错误提示
},
})
},
}
export default AuthUtil;
export default AuthUtil

29
peach/api/member/user.js Normal file
View File

@ -0,0 +1,29 @@
import request from '@/peach/request'
const UserUtil = {
// 获取用户信息
getUserInfo: () => {
return request({
url: '/particulars/member/get',
method: 'GET',
})
},
// 获取钱包
getWalletInfo: () => {
return request({
url: '/pay/wallet/get',
method: 'GET',
})
},
// 首页统计
getHomeStatistics: () => {
return request({
url: '/statistics/index/get-count',
method: 'GET',
})
},
}
export default UserUtil

View File

@ -3,34 +3,34 @@
* @description api 模块管理loading 配置请求拦截错误处理
*/
import Request from "luch-request";
import { baseUrl, apiPath } from "@/peach/config";
import peach from "@/peach";
import $store from "@/peach/store";
import AuthUtil from "@/peach/api/member/auth";
import Request from 'luch-request'
import { baseUrl, apiPath } from '@/peach/config'
import peach from '@/peach'
import $store from '@/peach/store'
import AuthUtil from '@/peach/api/member/auth'
const options = {
// 显示操作成功消息 默认不显示
showSuccess: false,
// 成功提醒 默认使用后端返回值
successMsg: "",
// 显示失败消息 默认显示
showError: true,
// 失败提醒 默认使用后端返回信息
errorMsg: "",
// 显示请求时loading模态框 默认显示
showLoading: true,
// loading提醒文字
loadingMsg: "加载中",
// 需要授权才能请求 默认放开
auth: false,
};
// 显示操作成功消息 默认不显示
showSuccess: false,
// 成功提醒 默认使用后端返回值
successMsg: '',
// 显示失败消息 默认显示
showError: true,
// 失败提醒 默认使用后端返回信息
errorMsg: '',
// 显示请求时loading模态框 默认显示
showLoading: true,
// loading提醒文字
loadingMsg: '加载中',
// 需要授权才能请求 默认放开
auth: true,
}
// Loading全局实例
let LoadingInstance = {
target: null,
count: 0,
};
target: null,
count: 0,
}
/**
* @author Ankkaya
@ -39,21 +39,21 @@ let LoadingInstance = {
* @returns {Type}
*/
function closeLoading() {
if (LoadingInstance.count > 0) LoadingInstance.count--;
if (LoadingInstance.count === 0) uni.hideLoading();
if (LoadingInstance.count > 0) LoadingInstance.count--
if (LoadingInstance.count === 0) uni.hideLoading()
}
// 请求实例
const http = new Request({
baseURL: baseUrl + apiPath,
timeout: 8000,
method: "GET",
header: {
Accept: "text/json",
"Content-Type": "application/json;charset=UTF-8",
},
custom: options,
});
baseURL: baseUrl + apiPath,
timeout: 8000,
method: 'GET',
header: {
Accept: 'text/json',
'Content-Type': 'application/json;charset=UTF-8',
},
custom: options,
})
/**
* @author Ankkaya
@ -63,39 +63,40 @@ const http = new Request({
*/
http.interceptors.request.use(
(config) => {
// 自定义处理【auth 授权】:必须登录的接口,否则提示登录
if (config.custom.auth && !$store("user").isLogin) {
// 处理登录
peach.$router.go("/pages/index/login");
return Promise.reject();
}
(config) => {
// 自定义处理【auth 授权】:必须登录的接口,否则提示登录
if (config.custom.auth && !$store('user').isLogin) {
// 处理登录
peach.$router.go('/pages/index/login')
return Promise.reject()
}
// 自定义处理【loading 加载中】:如果需要显示 loading则显示 loading
if (config.custom.showLoading) {
LoadingInstance.count++;
LoadingInstance.count === 1 &&
uni.showLoading({
title: config.custom.loadingMsg,
mask: true,
fail: () => {
uni.hideLoading();
},
});
}
// 自定义处理【loading 加载中】:如果需要显示 loading则显示 loading
if (config.custom.showLoading) {
LoadingInstance.count++
LoadingInstance.count === 1 &&
uni.showLoading({
title: config.custom.loadingMsg,
mask: true,
fail: () => {
uni.hideLoading()
},
})
}
// 增加 token 令牌、terminal 终端、tenant 租户的请求头
const token = getAccessToken();
if (token) {
config.header["Authorization"] = token;
// 增加 token 令牌、terminal 终端、tenant 租户的请求头
const token = getAccessToken()
if (token) {
config.header['Authorization'] = token
}
config.header['Accept'] = '*/*'
config.header['tenant-id'] = '1'
return config
},
(error) => {
return Promise.reject(error)
}
config.header["Accept"] = "*/*";
return config;
},
(error) => {
return Promise.reject(error);
}
);
)
/**
* @author Ankkaya
@ -104,202 +105,194 @@ http.interceptors.request.use(
* @returns {Type}
*/
http.interceptors.response.use(
(response) => {
console.log("response", response);
// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
if (
response.config.url.indexOf("/member/auth/") >= 0 &&
response.data?.data?.accessToken
) {
$store("user").setToken(
response.data.data.accessToken,
response.data.data.refreshToken
);
(response) => {
console.log('response', response)
// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
if (response.config.url.indexOf('/member/auth/') >= 0 && response.data?.data?.accessToken) {
$store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken)
}
// 自定处理【loading 加载中】:如果需要显示 loading则关闭 loading
response.config.custom.showLoading && closeLoading()
// 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示
if (response.data.code !== 0) {
// 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌
if (response.data.code === 401) {
return refreshToken(response.config)
}
// 错误提示
if (response.config.custom.showError) {
uni.showToast({
title: response.data.msg || '服务器开小差啦,请稍后再试~',
icon: 'none',
mask: true,
})
return Promise.reject(false)
}
}
// 自定义处理【showSuccess 成功提示】:如果需要显示成功提示,则显示成功提示
if (
response.config.custom.showSuccess &&
response.config.custom.successMsg !== '' &&
response.data.code === 0
) {
uni.showToast({
title: response.config.custom.successMsg,
icon: 'none',
})
}
// 返回结果:包括 code + data + msg
return Promise.resolve(response.data)
},
(error) => {
console.log('error', error)
const userStore = $store('user')
const isLogin = userStore.isLogin
let errorMessage = '网络请求出错'
if (error !== undefined) {
switch (error.statusCode) {
case 400:
errorMessage = '请求错误'
break
case 401:
errorMessage = isLogin ? '您的登陆已过期' : '请先登录'
// 正常情况下,后端不会返回 401 错误,所以这里不处理 handleAuthorized
break
case 403:
errorMessage = '拒绝访问'
break
case 404:
errorMessage = '请求出错'
break
case 408:
errorMessage = '请求超时'
break
case 429:
errorMessage = '请求频繁, 请稍后再访问'
break
case 500:
errorMessage = '服务器开小差啦,请稍后再试~'
break
case 501:
errorMessage = '服务未实现'
break
case 502:
errorMessage = '网络错误'
break
case 503:
errorMessage = '服务不可用'
break
case 504:
errorMessage = '网络超时'
break
case 505:
errorMessage = 'HTTP 版本不受支持'
break
}
if (error.errMsg.includes('timeout')) errorMessage = '请求超时'
// #ifdef H5
if (error.errMsg.includes('Network'))
errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接'
// #endif
}
if (error && error.config) {
if (error.config.custom.showError === true) {
uni.showToast({
title: error.data?.msg || errorMessage,
icon: 'none',
mask: true,
})
}
error.config.custom.showLoading && closeLoading()
}
return Promise.reject(false)
}
)
// 自定处理【loading 加载中】:如果需要显示 loading则关闭 loading
response.config.custom.showLoading && closeLoading();
// 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示
if (response.data.code !== 0) {
// 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌
if (response.data.code === 401) {
return refreshToken(response.config);
}
// 错误提示
if (response.config.custom.showError) {
uni.showToast({
title: response.data.msg || "服务器开小差啦,请稍后再试~",
icon: "none",
mask: true,
});
return Promise.reject(false);
}
}
// 自定义处理【showSuccess 成功提示】:如果需要显示成功提示,则显示成功提示
if (
response.config.custom.showSuccess &&
response.config.custom.successMsg !== "" &&
response.data.code === 0
) {
uni.showToast({
title: response.config.custom.successMsg,
icon: "none",
});
}
// 返回结果:包括 code + data + msg
return Promise.resolve(response.data);
},
(error) => {
console.log("error", error);
const userStore = $store("user");
const isLogin = userStore.isLogin;
let errorMessage = "网络请求出错";
if (error !== undefined) {
switch (error.statusCode) {
case 400:
errorMessage = "请求错误";
break;
case 401:
errorMessage = isLogin ? "您的登陆已过期" : "请先登录";
// 正常情况下,后端不会返回 401 错误,所以这里不处理 handleAuthorized
break;
case 403:
errorMessage = "拒绝访问";
break;
case 404:
errorMessage = "请求出错";
break;
case 408:
errorMessage = "请求超时";
break;
case 429:
errorMessage = "请求频繁, 请稍后再访问";
break;
case 500:
errorMessage = "服务器开小差啦,请稍后再试~";
break;
case 501:
errorMessage = "服务未实现";
break;
case 502:
errorMessage = "网络错误";
break;
case 503:
errorMessage = "服务不可用";
break;
case 504:
errorMessage = "网络超时";
break;
case 505:
errorMessage = "HTTP 版本不受支持";
break;
}
if (error.errMsg.includes("timeout")) errorMessage = "请求超时";
// #ifdef H5
if (error.errMsg.includes("Network"))
errorMessage = window.navigator.onLine
? "服务器异常"
: "请检查您的网络连接";
// #endif
}
if (error && error.config) {
if (error.config.custom.showError === true) {
uni.showToast({
title: error.data?.msg || errorMessage,
icon: "none",
mask: true,
});
}
error.config.custom.showLoading && closeLoading();
}
return Promise.reject(false);
}
);
let requestList = []; // 请求队列
let isRefreshToken = false; // 是否正在刷新中
let requestList = [] // 请求队列
let isRefreshToken = false // 是否正在刷新中
const refreshToken = async (config) => {
// 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error)
if (config.url.indexOf("/member/auth/refresh-token") >= 0) {
return Promise.reject("error");
}
// 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error)
if (config.url.indexOf('/member/auth/refresh-token') >= 0) {
return Promise.reject('error')
}
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true;
// 1. 如果获取不到刷新令牌,则只能执行登出操作
const refreshToken = getRefreshToken();
if (!refreshToken) {
return handleAuthorized();
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true
// 1. 如果获取不到刷新令牌,则只能执行登出操作
const refreshToken = getRefreshToken()
if (!refreshToken) {
return handleAuthorized()
}
// 2. 进行刷新访问令牌
try {
const refreshTokenResult = await AuthUtil.refreshToken(refreshToken)
if (refreshTokenResult.code !== 0) {
// 如果刷新不成功,直接抛出 e 触发 2.2 的逻辑
// noinspection ExceptionCaughtLocallyJS
throw new Error('刷新令牌失败')
}
// 2.1 刷新成功,则回放队列的请求 + 当前请求
config.header.Authorization = 'Bearer ' + getAccessToken()
requestList.forEach((cb) => {
cb()
})
requestList = []
return request(config)
} catch (e) {
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach((cb) => {
cb()
})
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized()
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
config.header.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(request(config))
})
})
}
// 2. 进行刷新访问令牌
try {
const refreshTokenResult = await AuthUtil.refreshToken(refreshToken);
if (refreshTokenResult.code !== 0) {
// 如果刷新不成功,直接抛出 e 触发 2.2 的逻辑
// noinspection ExceptionCaughtLocallyJS
throw new Error("刷新令牌失败");
}
// 2.1 刷新成功,则回放队列的请求 + 当前请求
config.header.Authorization = "Bearer " + getAccessToken();
requestList.forEach((cb) => {
cb();
});
requestList = [];
return request(config);
} catch (e) {
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach((cb) => {
cb();
});
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized();
} finally {
requestList = [];
isRefreshToken = false;
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
config.header.Authorization = "Bearer " + getAccessToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(request(config));
});
});
}
};
}
/** 处理 401 未登陆的错误 */
const handleAuthorized = () => {
const userStore = $store("user");
userStore.logout(true);
peach.$router.go("/pages/index/login");
// 登录超时
return Promise.reject({
code: 401,
msg: userStore.isLogin ? "您的登陆已过期" : "请先登录",
});
};
const userStore = $store('user')
userStore.logOut()
peach.$router.go('/pages/index/login')
// 登录超时
return Promise.reject({
code: 401,
msg: userStore.isLogin ? '您的登陆已过期' : '请先登录',
})
}
/** 获得访问令牌 */
const getAccessToken = () => {
return uni.getStorageSync("token");
};
return uni.getStorageSync('token')
}
/** 获得刷新令牌 */
const getRefreshToken = () => {
return uni.getStorageSync("refresh-token");
};
return uni.getStorageSync('refresh-token')
}
const request = (config) => {
return http.middleware(config);
};
return http.middleware(config)
}
export default request;
export default request

View File

@ -1,198 +1,190 @@
import $store from "@/peach/store";
import { showShareModal } from "@/peach/hooks/useModal";
import {
isNumber,
isString,
isEmpty,
startsWith,
isObject,
isNil,
clone,
} from "lodash";
import throttle from "@/peach/helper/throttle";
import $store from '@/peach/store'
import { showShareModal } from '@/peach/hooks/useModal'
import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash'
import throttle from '@/peach/helper/throttle'
const _go = (
path,
params = {},
options = {
redirect: false,
}
path,
params = {},
options = {
redirect: false,
}
) => {
let page = ""; // 跳转页面
let query = ""; // 页面参数
let url = ""; // 跳转页面完整路径
let page = '' // 跳转页面
let query = '' // 页面参数
let url = '' // 跳转页面完整路径
if (isString(path)) {
// 判断跳转类型是 path 还是http
if (startsWith(path, "http")) {
// #ifdef H5
window.location = path;
return;
// #endif
// #ifndef H5
page = `/pages/public/webview`;
query = `url=${encodeURIComponent(path)}`;
// #endif
} else if (startsWith(path, "action:")) {
handleAction(path);
return;
} else {
[page, query] = path.split("?");
if (isString(path)) {
// 判断跳转类型是 path 还是http
if (startsWith(path, 'http')) {
// #ifdef H5
window.location = path
return
// #endif
// #ifndef H5
page = `/pages/public/webview`
query = `url=${encodeURIComponent(path)}`
// #endif
} else if (startsWith(path, 'action:')) {
handleAction(path)
return
} else {
;[page, query] = path.split('?')
}
if (!isEmpty(params)) {
let query2 = paramsToQuery(params)
if (isEmpty(query)) {
query = query2
} else {
query += '&' + query2
}
}
}
if (!isEmpty(params)) {
let query2 = paramsToQuery(params);
if (isEmpty(query)) {
query = query2;
} else {
query += "&" + query2;
}
if (isObject(path)) {
page = path.url
if (!isNil(path.params)) {
query = paramsToQuery(path.params)
}
}
}
if (isObject(path)) {
page = path.url;
if (!isNil(path.params)) {
query = paramsToQuery(path.params);
const nextRoute = ROUTES_MAP[page]
// 未找到指定跳转页面
// mark: 跳转404页
if (!nextRoute) {
console.log(`%c跳转路径参数错误<${page || 'EMPTY'}>`, 'color:red;background:yellow')
return
}
}
const nextRoute = ROUTES_MAP[page];
// 页面登录拦截
if (nextRoute.meta?.auth && !$store('user').isLogin) {
uni.redirectTo({
url: '/pages/index/login',
})
return
}
// 未找到指定跳转页面
// mark: 跳转404页
if (!nextRoute) {
console.log(
`%c跳转路径参数错误<${page || "EMPTY"}>`,
"color:red;background:yellow"
);
return;
}
url = page
if (!isEmpty(query)) {
url += `?${query}`
}
// 页面登录拦截
if (nextRoute.meta?.auth && !$store("user").isLogin) {
uni.redirectTo({
url: "/pages/index/login",
});
return;
}
// 保存最新的页面地址
$store('user').lastRoutePage = url
url = page;
if (!isEmpty(query)) {
url += `?${query}`;
}
// 跳转底部导航
if (TABBAR.includes(page)) {
uni.switchTab({
url,
})
return
}
// 跳转底部导航
if (TABBAR.includes(page)) {
uni.switchTab({
url,
});
return;
}
// 使用redirect跳转
if (options.redirect) {
uni.redirectTo({
url,
})
return
}
// 使用redirect跳转
if (options.redirect) {
uni.redirectTo({
url,
});
return;
}
uni.navigateTo({
url,
});
};
uni.navigateTo({
url,
})
}
// 限流 防止重复点击跳转
function go(...args) {
throttle(() => {
_go(...args);
});
throttle(() => {
_go(...args)
})
}
function paramsToQuery(params) {
if (isEmpty(params)) {
return "";
}
// return new URLSearchParams(Object.entries(params)).toString();
let query = [];
for (let key in params) {
query.push(key + "=" + params[key]);
}
if (isEmpty(params)) {
return ''
}
// return new URLSearchParams(Object.entries(params)).toString();
let query = []
for (let key in params) {
query.push(key + '=' + params[key])
}
return query.join("&");
return query.join('&')
}
function back() {
// #ifdef H5
history.back();
// #endif
// #ifdef H5
history.back()
// #endif
// #ifndef H5
uni.navigateBack();
// #endif
// #ifndef H5
uni.navigateBack()
// #endif
}
function redirect(path, params = {}) {
go(path, params, {
redirect: true,
});
go(path, params, {
redirect: true,
})
}
// 检测是否有浏览器历史
function hasHistory() {
// #ifndef H5
const pages = getCurrentPages();
if (pages.length > 1) {
return true;
}
return false;
// #endif
// #ifndef H5
const pages = getCurrentPages()
if (pages.length > 1) {
return true
}
return false
// #endif
// #ifdef H5
return !!history.back;
// #endif
// #ifdef H5
return !!history.back
// #endif
}
function getCurrentRoute(field = "") {
let currentPage = getCurrentPage();
// #ifdef MP
currentPage.$page["route"] = currentPage.route;
currentPage.$page["options"] = currentPage.options;
// #endif
if (field !== "") {
return currentPage.$page[field];
} else {
return currentPage.$page;
}
function getCurrentRoute(field = '') {
let currentPage = getCurrentPage()
// #ifdef MP
currentPage.$page['route'] = currentPage.route
currentPage.$page['options'] = currentPage.options
// #endif
if (field !== '') {
return currentPage.$page[field]
} else {
return currentPage.$page
}
}
function getCurrentPage() {
let pages = getCurrentPages();
return pages[pages.length - 1];
let pages = getCurrentPages()
return pages[pages.length - 1]
}
function handleAction(path) {
const action = path.split(":");
switch (action[1]) {
case "showShareModal":
showShareModal();
break;
}
const action = path.split(':')
switch (action[1]) {
case 'showShareModal':
showShareModal()
break
}
}
function error(errCode, errMsg = "") {
redirect("/pages/public/error", {
errCode,
errMsg,
});
function error(errCode, errMsg = '') {
redirect('/pages/public/error', {
errCode,
errMsg,
})
}
export default {
go,
back,
hasHistory,
redirect,
getCurrentPage,
getCurrentRoute,
error,
};
go,
back,
hasHistory,
redirect,
getCurrentPage,
getCurrentRoute,
error,
}

View File

@ -1,185 +1,175 @@
import { ref } from "vue";
import { defineStore } from "pinia";
import $platform from "@/peach/platform";
import $router from "@/peach/router";
import useUserStore from "./user";
import useSysStore from "./sys";
import { ref } from 'vue'
import { defineStore } from 'pinia'
import $platform from '@/peach/platform'
import $router from '@/peach/router'
import useSysStore from './sys'
const useAppStore = defineStore(
"app",
() => {
/**
* @description 应用信息
* @param string name 应用名称
* @param string logo 应用logo
* @param string version 应用版本
* @param string copyright 版权信息
* @param string copyrightTime 版权时间
* @param string cdnurl 静态资源域名
* @param string filesystem 文件系统
*/
const info = ref({
name: "",
logo: "",
version: "",
copyright: "",
copytime: "",
cdnurl: "",
filesystem: "",
});
'app',
() => {
/**
* @description 应用信息
* @param string name 应用名称
* @param string logo 应用logo
* @param string version 应用版本
* @param string copyright 版权信息
* @param string copyrightTime 版权时间
* @param string cdnurl 静态资源域名
* @param string filesystem 文件系统
*/
const info = ref({
name: '',
logo: '',
version: '',
copyright: '',
copytime: '',
cdnurl: '',
filesystem: '',
})
/**
* @description 平台信息
* @param Array share.methods 分享方式
* @param Object share.forwardInfo 转发信息
* @param Object share.posterInfo 海报信息
* @param string share.linkAddress 分享链接地址
* @param number bindMobile 绑定手机号提醒 0: 提醒 1: 不提醒
*/
const platform = ref({
share: {
methods: [],
forwardInfo: {},
posterInfo: {},
linkAddress: "",
},
bindMobile: 0,
});
const chat = ref({});
/**
* @description 模板信息
* @param Object basic 基础模板
* @param Object tabbar 底部导航模板
*/
const template = ref({
basic: {
tabbar: {
items: [
{
activeIconUrl:
"http://mall.yudao.iocoder.cn/static/images/1-002.png",
iconUrl: "http://mall.yudao.iocoder.cn/static/images/1-001.png",
text: "首页",
url: "/pages/index/index",
/**
* @description 平台信息
* @param Array share.methods 分享方式
* @param Object share.forwardInfo 转发信息
* @param Object share.posterInfo 海报信息
* @param string share.linkAddress 分享链接地址
* @param number bindMobile 绑定手机号提醒 0: 提醒 1: 不提醒
*/
const platform = ref({
share: {
methods: [],
forwardInfo: {},
posterInfo: {},
linkAddress: '',
},
{
activeIconUrl:
"http://mall.yudao.iocoder.cn/static/images/2-002.png",
iconUrl: "http://mall.yudao.iocoder.cn/static/images/2-001.png",
text: "产品",
url: "/pages/index/product",
bindMobile: 0,
})
const chat = ref({})
/**
* @description 模板信息
* @param Object basic 基础模板
* @param Object tabbar 底部导航模板
*/
const template = ref({
basic: {
tabbar: {
items: [
{
activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/1-002.png',
iconUrl: 'http://mall.yudao.iocoder.cn/static/images/1-001.png',
text: '首页',
url: '/pages/index/index',
},
{
activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/2-002.png',
iconUrl: 'http://mall.yudao.iocoder.cn/static/images/2-001.png',
text: '产品',
url: '/pages/index/product',
},
{
activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/3-002.png',
iconUrl: 'http://mall.yudao.iocoder.cn/static/images/3-001.png',
text: '订单',
url: '/pages/order/list',
},
{
activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/4-002.png',
iconUrl: 'http://mall.yudao.iocoder.cn/static/images/4-001.png',
text: '我的',
url: '/pages/index/my',
},
],
style: {
activeColor: '#fc4141',
bgColor: '#fff',
bgType: 'color',
color: '#282828',
},
theme: 'red',
},
},
{
activeIconUrl:
"http://mall.yudao.iocoder.cn/static/images/3-002.png",
iconUrl: "http://mall.yudao.iocoder.cn/static/images/3-001.png",
text: "订单",
url: "/pages/order/list",
},
{
activeIconUrl:
"http://mall.yudao.iocoder.cn/static/images/4-002.png",
iconUrl: "http://mall.yudao.iocoder.cn/static/images/4-001.png",
text: "我的",
url: "/pages/index/my",
},
],
style: {
activeColor: "#fc4141",
bgColor: "#fff",
bgType: "color",
color: "#282828",
},
theme: "red",
},
},
});
})
// 全局分享信息
const shareInfo = ref({});
// 全局分享信息
const shareInfo = ref({})
// 小程序发货信息管理 0: 没有 1
const hasWechatTradeManaged = ref(0);
// 小程序发货信息管理 0: 没有 1
const hasWechatTradeManaged = ref(0)
/**
* @author Ankkaya
* @description 小程序初始化
* @param {Type} -
* @returns {Type}
*/
async function init() {
// 检查网络
const networkStatus = await $platform.checkNetwork();
if (!networkStatus) {
$router.error("NetworkError");
}
/**
* @author Ankkaya
* @description 小程序初始化
* @param {Type} -
* @returns {Type}
*/
async function init() {
// 检查网络
const networkStatus = await $platform.checkNetwork()
if (!networkStatus) {
$router.error('NetworkError')
}
if (true) {
this.info = {
name: "🍑商城",
logo: "https://static.iocoder.cn/ruoyi-vue-pro-logo.png",
version: "1.0.0",
copyright: "全部开源,个人与企业可 100% 免费使用",
copytime: "Copyright© 2018-2024",
if (true) {
this.info = {
name: '🍑商城',
logo: 'https://static.iocoder.cn/ruoyi-vue-pro-logo.png',
version: '1.0.0',
copyright: '全部开源,个人与企业可 100% 免费使用',
copytime: 'Copyright© 2018-2024',
cdnurl: "https://file.sheepjs.com", // 云存储域名
filesystem: "qcloud", // 云存储平台
};
this.platform = {
share: {
methods: ["poster", "link"],
linkAddress: "https://shopro.sheepjs.com/#/",
posterInfo: {
user_bg: "/static/img/shop/config/user-poster-bg.png",
goods_bg: "/static/img/shop/config/goods-poster-bg.png",
groupon_bg: "/static/img/shop/config/groupon-poster-bg.png",
},
},
bind_mobile: 0,
};
this.chat = {
chat_domain: "https://api.shopro.sheepjs.com/chat",
room_id: "admin",
};
this.has_wechat_trade_managed = 0;
cdnurl: 'https://file.sheepjs.com', // 云存储域名
filesystem: 'qcloud', // 云存储平台
}
this.platform = {
share: {
methods: ['poster', 'link'],
linkAddress: 'https://shopro.sheepjs.com/#/',
posterInfo: {
user_bg: '/static/img/shop/config/user-poster-bg.png',
goods_bg: '/static/img/shop/config/goods-poster-bg.png',
groupon_bg: '/static/img/shop/config/groupon-poster-bg.png',
},
},
bind_mobile: 0,
}
this.chat = {
chat_domain: 'https://api.shopro.sheepjs.com/chat',
room_id: 'admin',
}
this.has_wechat_trade_managed = 0
// 加载主题
const sysStore = useSysStore();
sysStore.setTheme();
// 加载主题
const sysStore = useSysStore()
sysStore.setTheme()
// 模拟用户登录
const userStore = useUserStore();
if (userStore.isLogin) {
userStore.loginAfter();
return Promise.resolve(true)
} else {
$router.error('InitError', res.msg || '加载失败')
}
}
return Promise.resolve(true);
} else {
$router.error("InitError", res.msg || "加载失败");
}
}
return {
info,
platform,
chat,
template,
shareInfo,
hasWechatTradeManaged,
init,
};
},
{
persist: {
enabled: true,
strategies: [
{
key: "app-store",
},
],
return {
info,
platform,
chat,
template,
shareInfo,
hasWechatTradeManaged,
init,
}
},
}
);
{
persist: {
enabled: true,
strategies: [
{
key: 'app-store',
},
],
},
}
)
export default useAppStore;
export default useAppStore

View File

@ -10,7 +10,6 @@ const useSysStore = defineStore(
const fontSize = ref(1)
function setTheme(stheme = '') {
console.log('setTheme', stheme)
theme.value = stheme ? stheme : 'orange'
}

View File

@ -1,110 +1,124 @@
import { ref } from "vue";
import { defineStore } from "pinia";
import $share from "@/peach/platform/share";
import { isEmpty, cloneDeep, clone } from "lodash";
import { ref } from 'vue'
import { defineStore } from 'pinia'
import $share from '@/peach/platform/share'
import $router from '@/peach/router'
import UserUtil from '@/peach/api/member/user'
import { isEmpty, cloneDeep, clone } from 'lodash'
// 默认用户信息
const defaultUserInfo = {
avatar: "", // 头像
nickname: "", // 昵称
gender: 0, // 性别
mobile: "", // 手机号
point: 0, // 积分
};
avatar: '', // 头像
nickname: '', // 昵称
mobile: '', // 手机号
point: 0, // 积分
particulars: null,
}
// 默认钱包信息
const defaultWallet = {
balance: 0, // 余额
};
balance: 0, // 余额
totalExpense: 0, // 总消费
totalRecharge: 0, // 总充值
}
// 默认订单信息
const defaultNumData = {
unusedCouponCount: 0,
orderCount: {
allCount: 0,
unpaidCount: 0,
undeliveredCount: 0,
deliveredCount: 0,
uncommentedCount: 0,
afterSaleCount: 0,
},
};
unusedCouponCount: 0,
orderCount: {
allCount: 0,
unpaidCount: 0,
undeliveredCount: 0,
deliveredCount: 0,
uncommentedCount: 0,
afterSaleCount: 0,
},
}
const useUserStore = defineStore(
"user",
() => {
const userInfo = ref(clone(defaultUserInfo));
const userWallet = ref(clone(defaultWallet));
const userNumData = ref(cloneDeep(defaultNumData));
const isLogin = ref(!!uni.getStorageSync("token"));
const lastUpdateTime = ref(0);
'user',
() => {
const userInfo = ref(cloneDeep(defaultUserInfo))
const userWallet = ref(clone(defaultWallet))
const userNumData = ref(cloneDeep(defaultNumData))
const isLogin = ref(!!uni.getStorageSync('token'))
const lastUpdateTime = ref(0)
const lastRoutePage = ref(null)
function getUserInfo() {}
async function getUserInfo() {
let res = await UserUtil.getUserInfo()
userInfo.value = res.data
}
function getWallet() {}
async function getWallet() {
let res = await UserUtil.getWalletInfo()
userWallet.value = res.data
}
function getNumData() {}
function getNumData() {}
function setToken(token, refreshToken) {
if (token === "") {
isLogin.value = false;
uni.removeStorageSync("token");
uni.removeStorageSync("refresh-token");
} else {
isLogin.value = true;
uni.setStorageSync("token", token);
uni.setStorageSync("refresh-token", refreshToken);
// 成功后处理
loginAfter();
}
return isLogin.value;
function setToken(token, refreshToken) {
if (token === '') {
isLogin.value = false
uni.removeStorageSync('token')
uni.removeStorageSync('refresh-token')
} else {
isLogin.value = true
uni.setStorageSync('token', token)
uni.setStorageSync('refresh-token', refreshToken)
// 成功后处理
loginAfter()
}
return isLogin.value
}
function resetUserData() {
setToken('')
userInfo.value = cloneDeep(defaultUserInfo)
userWallet.value = clone(defaultWallet)
userNumData.value = cloneDeep(defaultNumData)
}
async function updateUserData() {
const nowTime = new Date().getTime()
if (lastUpdateTime.value + 5000 > nowTime) {
return
}
lastUpdateTime.value = nowTime
await Promise.all([getUserInfo(), getWallet(), getNumData()])
return userInfo.value
}
async function loginAfter() {
await updateUserData()
$share.getShareInfo()
$router.go('/pages/index/index')
}
function logOut() {
resetUserData()
return !isLogin.value
}
return {
userInfo,
userWallet,
isLogin,
setToken,
logOut,
loginAfter,
lastRoutePage,
}
},
{
persist: {
enabled: true,
strategies: [
{
key: 'user-store',
},
],
},
}
)
function resetUserData() {
setToken("");
userInfo.value = clone(defaultUserInfo);
userWallet.value = clone(defaultWallet);
userNumData.value = cloneDeep(defaultNumData);
}
async function updateUserData() {
const nowTime = new Date().getTime();
if (lastUpdateTime.value + 5000 > nowTime) {
return;
}
lastUpdateTime.value = nowTime;
await getUserInfo();
getWallet();
getNumData();
return userInfo.value;
}
function loginAfter() {
updateUserData();
$share.getShareInfo();
}
function logOut() {
resetUserData();
return !isLogin.value;
}
return {
userInfo,
isLogin,
setToken,
logOut,
};
},
{
persist: true,
strategies: [
{
key: "user-store",
},
],
}
);
export default useUserStore;
export default useUserStore

View File

@ -31,6 +31,7 @@
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app'
import { computed, unref } from 'vue'
import PbSTabbar from './pb-s-tabbar.vue'
import peach from '@/peach'
@ -59,6 +60,12 @@ const props = defineProps({
path: String,
default: '',
})
onLoad(() => {
// 使
// app.vue tabbar
uni.hideTabBar()
})
</script>
<style lang="scss">

BIN
static/default_avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB