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 MALL_BASE_URL = http://api-dashboard.yudao.iocoder.cn
# 后端接口 - 测试环境(通过 process.env.NODE_ENV = development # 后端接口 - 测试环境(通过 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 MALL_API_PATH = /merchant-api

View File

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

View File

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

View File

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

View File

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

View File

@ -3,11 +3,11 @@
<view :style="[{ paddingTop: peach.$platform.navBar + 'px' }]"></view> <view :style="[{ paddingTop: peach.$platform.navBar + 'px' }]"></view>
<view class="my-module ss-p-x-30"> <view class="my-module ss-p-x-30">
<view class="user-info flex align-center"> <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="detail flex flex-column justify-center gap-10">
<view class="name ss-font-26">{{ state.userInfo.name }}</view> <view class="name ss-font-26">{{ userInfo.nickname }}</view>
<view class="description ss-font-26">{{ state.userInfo.description }}</view> <view class="description ss-font-26">{{ userInfo.mobile }}</view>
</view> </view>
</view> </view>
@ -16,17 +16,17 @@
<view class="remain flex justify-between align-center"> <view class="remain flex justify-between align-center">
<view class="left flex align-center"> <view class="left flex align-center">
<view class="unit self-start"></view> <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> </view>
<button class="right-btn ss-reset-button">查看详情</button> <button class="right-btn ss-reset-button">查看详情</button>
</view> </view>
<view class="cent ss-m-t-20"> 总获取积分{{ state.statistic.cent }} </view> <view class="cent ss-m-t-20"> 总获取积分{{ cent }} </view>
</view> </view>
<view class="menu ss-m-t-70"> <view class="menu ss-m-t-70">
<view class="title ss-m-b-30">我的服务</view> <view class="title ss-m-b-30">我的服务</view>
<view class="items"> <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> <image class="icon" :src="item.icon" mode="aspectFill"></image>
<view class="label">{{ item.name }}</view> <view class="label">{{ item.name }}</view>
</view> </view>
@ -40,26 +40,17 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, computed } from 'vue'
import $store from '@/peach/store'
import peach from '@/peach' import peach from '@/peach'
const bgStyle = { const bgStyle = {
backgroundImage: '/static/bg-page.png', backgroundImage: '/static/bg-page.png',
imageType: 'local', imageType: 'local',
backgroundColor: '#fff', backgroundColor: '#fff',
description: '',
} }
const state = ref({ const state = ref({
userInfo: {
name: 'Ankkaya',
avatar: '/static/logo.png',
description: '测试商家描述',
},
statistic: {
remain: 2222222222,
cent: 10000,
},
menus: [ menus: [
{ {
name: '余额提现', 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> </script>
<style lang="scss" scoped> <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 = { const AuthUtil = {
smsLogin: (data) => { smsLogin: (data) => {
return request({ return request({
url: "/particulars/member/auth/sms-login", url: '/particulars/member/auth/sms-login',
method: "POST", method: 'POST',
data, data,
custom: { custom: {
showSuccess: true, showSuccess: true,
loadingMsg: "登陆中", loadingMsg: '登陆中',
successMsg: "登陆成功", successMsg: '登陆成功',
}, auth: false,
}); },
}, })
// 发送手机验证码 },
sendSmsCode: (mobile, scene) => { // 发送手机验证码
return request({ sendSmsCode: (mobile, scene) => {
url: "/member/auth/send-sms-code", return request({
method: "POST", url: '/member/auth/send-sms-code',
data: { method: 'POST',
mobile, data: {
scene, mobile,
}, scene,
custom: { },
loadingMsg: "发送中", custom: {
showSuccess: true, loadingMsg: '发送中',
successMsg: "发送成功", showSuccess: true,
}, successMsg: '发送成功',
}); },
}, })
// 刷新令牌 },
refreshToken: (refreshToken) => { // 刷新令牌
return request({ refreshToken: (refreshToken) => {
url: "/member/auth/refresh-token", return request({
method: "POST", url: '/member/auth/refresh-token',
params: { method: 'POST',
refreshToken, params: {
}, refreshToken,
custom: { },
loading: false, // 不用加载中 custom: {
showError: false, // 不展示错误提示 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 配置请求拦截错误处理 * @description api 模块管理loading 配置请求拦截错误处理
*/ */
import Request from "luch-request"; import Request from 'luch-request'
import { baseUrl, apiPath } from "@/peach/config"; import { baseUrl, apiPath } from '@/peach/config'
import peach from "@/peach"; import peach from '@/peach'
import $store from "@/peach/store"; import $store from '@/peach/store'
import AuthUtil from "@/peach/api/member/auth"; import AuthUtil from '@/peach/api/member/auth'
const options = { const options = {
// 显示操作成功消息 默认不显示 // 显示操作成功消息 默认不显示
showSuccess: false, showSuccess: false,
// 成功提醒 默认使用后端返回值 // 成功提醒 默认使用后端返回值
successMsg: "", successMsg: '',
// 显示失败消息 默认显示 // 显示失败消息 默认显示
showError: true, showError: true,
// 失败提醒 默认使用后端返回信息 // 失败提醒 默认使用后端返回信息
errorMsg: "", errorMsg: '',
// 显示请求时loading模态框 默认显示 // 显示请求时loading模态框 默认显示
showLoading: true, showLoading: true,
// loading提醒文字 // loading提醒文字
loadingMsg: "加载中", loadingMsg: '加载中',
// 需要授权才能请求 默认放开 // 需要授权才能请求 默认放开
auth: false, auth: true,
}; }
// Loading全局实例 // Loading全局实例
let LoadingInstance = { let LoadingInstance = {
target: null, target: null,
count: 0, count: 0,
}; }
/** /**
* @author Ankkaya * @author Ankkaya
@ -39,21 +39,21 @@ let LoadingInstance = {
* @returns {Type} * @returns {Type}
*/ */
function closeLoading() { function closeLoading() {
if (LoadingInstance.count > 0) LoadingInstance.count--; if (LoadingInstance.count > 0) LoadingInstance.count--
if (LoadingInstance.count === 0) uni.hideLoading(); if (LoadingInstance.count === 0) uni.hideLoading()
} }
// 请求实例 // 请求实例
const http = new Request({ const http = new Request({
baseURL: baseUrl + apiPath, baseURL: baseUrl + apiPath,
timeout: 8000, timeout: 8000,
method: "GET", method: 'GET',
header: { header: {
Accept: "text/json", Accept: 'text/json',
"Content-Type": "application/json;charset=UTF-8", 'Content-Type': 'application/json;charset=UTF-8',
}, },
custom: options, custom: options,
}); })
/** /**
* @author Ankkaya * @author Ankkaya
@ -63,39 +63,40 @@ const http = new Request({
*/ */
http.interceptors.request.use( http.interceptors.request.use(
(config) => { (config) => {
// 自定义处理【auth 授权】:必须登录的接口,否则提示登录 // 自定义处理【auth 授权】:必须登录的接口,否则提示登录
if (config.custom.auth && !$store("user").isLogin) { if (config.custom.auth && !$store('user').isLogin) {
// 处理登录 // 处理登录
peach.$router.go("/pages/index/login"); peach.$router.go('/pages/index/login')
return Promise.reject(); return Promise.reject()
} }
// 自定义处理【loading 加载中】:如果需要显示 loading则显示 loading // 自定义处理【loading 加载中】:如果需要显示 loading则显示 loading
if (config.custom.showLoading) { if (config.custom.showLoading) {
LoadingInstance.count++; LoadingInstance.count++
LoadingInstance.count === 1 && LoadingInstance.count === 1 &&
uni.showLoading({ uni.showLoading({
title: config.custom.loadingMsg, title: config.custom.loadingMsg,
mask: true, mask: true,
fail: () => { fail: () => {
uni.hideLoading(); uni.hideLoading()
}, },
}); })
} }
// 增加 token 令牌、terminal 终端、tenant 租户的请求头 // 增加 token 令牌、terminal 终端、tenant 租户的请求头
const token = getAccessToken(); const token = getAccessToken()
if (token) { if (token) {
config.header["Authorization"] = 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 * @author Ankkaya
@ -104,202 +105,194 @@ http.interceptors.request.use(
* @returns {Type} * @returns {Type}
*/ */
http.interceptors.response.use( http.interceptors.response.use(
(response) => { (response) => {
console.log("response", response); console.log('response', response)
// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌 // 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
if ( if (response.config.url.indexOf('/member/auth/') >= 0 && response.data?.data?.accessToken) {
response.config.url.indexOf("/member/auth/") >= 0 && $store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken)
response.data?.data?.accessToken }
) {
$store("user").setToken( // 自定处理【loading 加载中】:如果需要显示 loading则关闭 loading
response.data.data.accessToken, response.config.custom.showLoading && closeLoading()
response.data.data.refreshToken
); // 自定义处理【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 let requestList = [] // 请求队列
response.config.custom.showLoading && closeLoading(); let isRefreshToken = false // 是否正在刷新中
// 自定义处理【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; // 是否正在刷新中
const refreshToken = async (config) => { const refreshToken = async (config) => {
// 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error) // 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error)
if (config.url.indexOf("/member/auth/refresh-token") >= 0) { if (config.url.indexOf('/member/auth/refresh-token') >= 0) {
return Promise.reject("error"); return Promise.reject('error')
} }
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) { if (!isRefreshToken) {
isRefreshToken = true; isRefreshToken = true
// 1. 如果获取不到刷新令牌,则只能执行登出操作 // 1. 如果获取不到刷新令牌,则只能执行登出操作
const refreshToken = getRefreshToken(); const refreshToken = getRefreshToken()
if (!refreshToken) { if (!refreshToken) {
return handleAuthorized(); 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 未登陆的错误 */ /** 处理 401 未登陆的错误 */
const handleAuthorized = () => { const handleAuthorized = () => {
const userStore = $store("user"); const userStore = $store('user')
userStore.logout(true); userStore.logOut()
peach.$router.go("/pages/index/login"); peach.$router.go('/pages/index/login')
// 登录超时 // 登录超时
return Promise.reject({ return Promise.reject({
code: 401, code: 401,
msg: userStore.isLogin ? "您的登陆已过期" : "请先登录", msg: userStore.isLogin ? '您的登陆已过期' : '请先登录',
}); })
}; }
/** 获得访问令牌 */ /** 获得访问令牌 */
const getAccessToken = () => { const getAccessToken = () => {
return uni.getStorageSync("token"); return uni.getStorageSync('token')
}; }
/** 获得刷新令牌 */ /** 获得刷新令牌 */
const getRefreshToken = () => { const getRefreshToken = () => {
return uni.getStorageSync("refresh-token"); return uni.getStorageSync('refresh-token')
}; }
const request = (config) => { 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 $store from '@/peach/store'
import { showShareModal } from "@/peach/hooks/useModal"; import { showShareModal } from '@/peach/hooks/useModal'
import { import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash'
isNumber, import throttle from '@/peach/helper/throttle'
isString,
isEmpty,
startsWith,
isObject,
isNil,
clone,
} from "lodash";
import throttle from "@/peach/helper/throttle";
const _go = ( const _go = (
path, path,
params = {}, params = {},
options = { options = {
redirect: false, redirect: false,
} }
) => { ) => {
let page = ""; // 跳转页面 let page = '' // 跳转页面
let query = ""; // 页面参数 let query = '' // 页面参数
let url = ""; // 跳转页面完整路径 let url = '' // 跳转页面完整路径
if (isString(path)) { if (isString(path)) {
// 判断跳转类型是 path 还是http // 判断跳转类型是 path 还是http
if (startsWith(path, "http")) { if (startsWith(path, 'http')) {
// #ifdef H5 // #ifdef H5
window.location = path; window.location = path
return; return
// #endif // #endif
// #ifndef H5 // #ifndef H5
page = `/pages/public/webview`; page = `/pages/public/webview`
query = `url=${encodeURIComponent(path)}`; query = `url=${encodeURIComponent(path)}`
// #endif // #endif
} else if (startsWith(path, "action:")) { } else if (startsWith(path, 'action:')) {
handleAction(path); handleAction(path)
return; return
} else { } else {
[page, query] = path.split("?"); ;[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 (isObject(path)) {
if (isEmpty(query)) { page = path.url
query = query2; if (!isNil(path.params)) {
} else { query = paramsToQuery(path.params)
query += "&" + query2; }
}
} }
}
if (isObject(path)) { const nextRoute = ROUTES_MAP[page]
page = path.url;
if (!isNil(path.params)) { // 未找到指定跳转页面
query = paramsToQuery(path.params); // 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
}
// 未找到指定跳转页面 url = page
// mark: 跳转404页 if (!isEmpty(query)) {
if (!nextRoute) { url += `?${query}`
console.log( }
`%c跳转路径参数错误<${page || "EMPTY"}>`,
"color:red;background:yellow"
);
return;
}
// 页面登录拦截 // 保存最新的页面地址
if (nextRoute.meta?.auth && !$store("user").isLogin) { $store('user').lastRoutePage = url
uni.redirectTo({
url: "/pages/index/login",
});
return;
}
url = page; // 跳转底部导航
if (!isEmpty(query)) { if (TABBAR.includes(page)) {
url += `?${query}`; uni.switchTab({
} url,
})
return
}
// 跳转底部导航 // 使用redirect跳转
if (TABBAR.includes(page)) { if (options.redirect) {
uni.switchTab({ uni.redirectTo({
url, url,
}); })
return; return
} }
// 使用redirect跳转 uni.navigateTo({
if (options.redirect) { url,
uni.redirectTo({ })
url, }
});
return;
}
uni.navigateTo({
url,
});
};
// 限流 防止重复点击跳转 // 限流 防止重复点击跳转
function go(...args) { function go(...args) {
throttle(() => { throttle(() => {
_go(...args); _go(...args)
}); })
} }
function paramsToQuery(params) { function paramsToQuery(params) {
if (isEmpty(params)) { if (isEmpty(params)) {
return ""; return ''
} }
// return new URLSearchParams(Object.entries(params)).toString(); // return new URLSearchParams(Object.entries(params)).toString();
let query = []; let query = []
for (let key in params) { for (let key in params) {
query.push(key + "=" + params[key]); query.push(key + '=' + params[key])
} }
return query.join("&"); return query.join('&')
} }
function back() { function back() {
// #ifdef H5 // #ifdef H5
history.back(); history.back()
// #endif // #endif
// #ifndef H5 // #ifndef H5
uni.navigateBack(); uni.navigateBack()
// #endif // #endif
} }
function redirect(path, params = {}) { function redirect(path, params = {}) {
go(path, params, { go(path, params, {
redirect: true, redirect: true,
}); })
} }
// 检测是否有浏览器历史 // 检测是否有浏览器历史
function hasHistory() { function hasHistory() {
// #ifndef H5 // #ifndef H5
const pages = getCurrentPages(); const pages = getCurrentPages()
if (pages.length > 1) { if (pages.length > 1) {
return true; return true
} }
return false; return false
// #endif // #endif
// #ifdef H5 // #ifdef H5
return !!history.back; return !!history.back
// #endif // #endif
} }
function getCurrentRoute(field = "") { function getCurrentRoute(field = '') {
let currentPage = getCurrentPage(); let currentPage = getCurrentPage()
// #ifdef MP // #ifdef MP
currentPage.$page["route"] = currentPage.route; currentPage.$page['route'] = currentPage.route
currentPage.$page["options"] = currentPage.options; currentPage.$page['options'] = currentPage.options
// #endif // #endif
if (field !== "") { if (field !== '') {
return currentPage.$page[field]; return currentPage.$page[field]
} else { } else {
return currentPage.$page; return currentPage.$page
} }
} }
function getCurrentPage() { function getCurrentPage() {
let pages = getCurrentPages(); let pages = getCurrentPages()
return pages[pages.length - 1]; return pages[pages.length - 1]
} }
function handleAction(path) { function handleAction(path) {
const action = path.split(":"); const action = path.split(':')
switch (action[1]) { switch (action[1]) {
case "showShareModal": case 'showShareModal':
showShareModal(); showShareModal()
break; break
} }
} }
function error(errCode, errMsg = "") { function error(errCode, errMsg = '') {
redirect("/pages/public/error", { redirect('/pages/public/error', {
errCode, errCode,
errMsg, errMsg,
}); })
} }
export default { export default {
go, go,
back, back,
hasHistory, hasHistory,
redirect, redirect,
getCurrentPage, getCurrentPage,
getCurrentRoute, getCurrentRoute,
error, error,
}; }

View File

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

View File

@ -1,110 +1,124 @@
import { ref } from "vue"; import { ref } from 'vue'
import { defineStore } from "pinia"; import { defineStore } from 'pinia'
import $share from "@/peach/platform/share"; import $share from '@/peach/platform/share'
import { isEmpty, cloneDeep, clone } from "lodash"; import $router from '@/peach/router'
import UserUtil from '@/peach/api/member/user'
import { isEmpty, cloneDeep, clone } from 'lodash'
// 默认用户信息 // 默认用户信息
const defaultUserInfo = { const defaultUserInfo = {
avatar: "", // 头像 avatar: '', // 头像
nickname: "", // 昵称 nickname: '', // 昵称
gender: 0, // 性别 mobile: '', // 手机号
mobile: "", // 手机号 point: 0, // 积分
point: 0, // 积分 particulars: null,
}; }
// 默认钱包信息 // 默认钱包信息
const defaultWallet = { const defaultWallet = {
balance: 0, // 余额 balance: 0, // 余额
}; totalExpense: 0, // 总消费
totalRecharge: 0, // 总充值
}
// 默认订单信息 // 默认订单信息
const defaultNumData = { const defaultNumData = {
unusedCouponCount: 0, unusedCouponCount: 0,
orderCount: { orderCount: {
allCount: 0, allCount: 0,
unpaidCount: 0, unpaidCount: 0,
undeliveredCount: 0, undeliveredCount: 0,
deliveredCount: 0, deliveredCount: 0,
uncommentedCount: 0, uncommentedCount: 0,
afterSaleCount: 0, afterSaleCount: 0,
}, },
}; }
const useUserStore = defineStore( const useUserStore = defineStore(
"user", 'user',
() => { () => {
const userInfo = ref(clone(defaultUserInfo)); const userInfo = ref(cloneDeep(defaultUserInfo))
const userWallet = ref(clone(defaultWallet)); const userWallet = ref(clone(defaultWallet))
const userNumData = ref(cloneDeep(defaultNumData)); const userNumData = ref(cloneDeep(defaultNumData))
const isLogin = ref(!!uni.getStorageSync("token")); const isLogin = ref(!!uni.getStorageSync('token'))
const lastUpdateTime = ref(0); 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) { function setToken(token, refreshToken) {
if (token === "") { if (token === '') {
isLogin.value = false; isLogin.value = false
uni.removeStorageSync("token"); uni.removeStorageSync('token')
uni.removeStorageSync("refresh-token"); uni.removeStorageSync('refresh-token')
} else { } else {
isLogin.value = true; isLogin.value = true
uni.setStorageSync("token", token); uni.setStorageSync('token', token)
uni.setStorageSync("refresh-token", refreshToken); uni.setStorageSync('refresh-token', refreshToken)
// 成功后处理 // 成功后处理
loginAfter(); loginAfter()
} }
return isLogin.value; 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() { export default useUserStore
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;

View File

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

BIN
static/default_avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB