This commit is contained in:
那些花儿
2025-04-30 13:34:37 +08:00
parent ad9a7bc7b8
commit baa6f7284a
12 changed files with 567 additions and 284 deletions
@@ -15,6 +15,8 @@ import com.budwk.app.web.commons.slog.SLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
@@ -89,7 +91,7 @@ public class ClientAuthController {
}
}
@At("/info")
@At(value = {"/info", "/user-info"})
@Ok("json")
@SaCheckLogin(type = StpClientUtil.TYPE)
public Result info() {
@@ -97,6 +99,17 @@ public class ClientAuthController {
return Result.success(user);
}
@At("/update-user-info")
@Ok("json")
@SaCheckLogin(type = StpClientUtil.TYPE)
@ApiOperation(value = "修改用户信息", nickname = "/update-user-info", httpMethod = "POST")
@AdaptBy(type = JsonAdaptor.class)
public Result updateUserInfo(Client_user user) {
user.setId(StpClientUtil.STP.getLoginIdAsLong());
user.setAvatar("https://tdesign.gtimg.com/mobile/demos/avatar1.png");
dao.updateIgnoreNull(user);
return Result.success(user);
}
/**
* 退出登录
@@ -38,6 +38,11 @@ public class Client_user extends BaseModel implements Serializable {
@Comment("身份证")
private String idCard;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
@Comment("头像")
private String avatar;
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
@Comment("openId")
+136
View File
@@ -26,6 +26,7 @@
"@dcloudio/uni-quickapp-webview": "3.0.0-4050720250324001",
"html5-qrcode": "^2.3.8",
"moment": "^2.30.1",
"pinia": "^3.0.2",
"tdesign-mobile-vue": "^1.8.3",
"vue": "^3.4.21",
"vue-i18n": "^9.1.9"
@@ -4609,6 +4610,30 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/@vue/devtools-kit": {
"version": "7.7.6",
"resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz",
"integrity": "sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.6",
"birpc": "^2.3.0",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
"perfect-debounce": "^1.0.0",
"speakingurl": "^14.0.1",
"superjson": "^2.2.2"
}
},
"node_modules/@vue/devtools-shared": {
"version": "7.7.6",
"resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz",
"integrity": "sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==",
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.13",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.13.tgz",
@@ -5219,6 +5244,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/birpc": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.3.0.tgz",
"integrity": "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/bmp-js": {
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/bmp-js/-/bmp-js-0.1.0.tgz",
@@ -6931,6 +6965,12 @@
"node": ">= 0.4"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/html-encoding-sniffer": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
@@ -8751,6 +8791,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
@@ -9185,6 +9231,12 @@
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"license": "MIT"
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"license": "MIT"
},
"node_modules/phin": {
"version": "2.9.3",
"resolved": "https://registry.npmmirror.com/phin/-/phin-2.9.3.tgz",
@@ -9219,6 +9271,36 @@
"node": ">=0.10.0"
}
},
"node_modules/pinia": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.2.tgz",
"integrity": "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.2"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/@vue/devtools-api": {
"version": "7.7.6",
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.6.tgz",
"integrity": "sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==",
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.7.6"
}
},
"node_modules/pirates": {
"version": "4.0.7",
"resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz",
@@ -9886,6 +9968,12 @@
"node": ">=0.10.0"
}
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
@@ -10277,6 +10365,15 @@
"source-map": "^0.6.0"
}
},
"node_modules/speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -10413,6 +10510,45 @@
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"license": "MIT"
},
"node_modules/superjson": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
"integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
"license": "MIT",
"dependencies": {
"copy-anything": "^3.0.2"
},
"engines": {
"node": ">=16"
}
},
"node_modules/superjson/node_modules/copy-anything": {
"version": "3.0.5",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
"integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
"license": "MIT",
"dependencies": {
"is-what": "^4.1.8"
},
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/superjson/node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
"integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
"license": "MIT",
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+1
View File
@@ -54,6 +54,7 @@
"@dcloudio/uni-quickapp-webview": "3.0.0-4050720250324001",
"html5-qrcode": "^2.3.8",
"moment": "^2.30.1",
"pinia": "^3.0.2",
"tdesign-mobile-vue": "^1.8.3",
"vue": "^3.4.21",
"vue-i18n": "^9.1.9"
+1 -2
View File
@@ -5,9 +5,8 @@ export default {
globalData: {
user: null
},
onLaunch: function () {
onLaunch: async function () {
console.log('App Launch')
console.log(process.env.VUE_APP_PLATFORM)
// 检查访问页面是否需要登录(不走拦截器 直接从地址栏进入)
checkAuth()
// 初始化路由拦截器
+9 -1
View File
@@ -1,4 +1,5 @@
import {createSSRApp} from "vue";
import * as Pinia from 'pinia';
import App from "./App.vue";
// 引入TDesign组件库
@@ -14,10 +15,17 @@ import 'moment/dist/locale/zh-cn'
moment.locale('zh-cn')
uni.$on('beforeRouterEnter', async (to, from, next) => {
console.log('beforeRouterEnter', to, from)
})
export function createApp() {
const app = createSSRApp(App);
app.use(TDesign); // 使用TDesign组件库
app.use(TDesign);
app.use(Pinia.createPinia())
return {
app,
Pinia
};
}
+1 -2
View File
@@ -152,11 +152,10 @@ const getListData = async (isRefresh = false) => {
hasMore.value = dataList.value.length < totalCount;
pageForm.pageNumber++;
} else {
Toast({message: '获取活动列表失败', theme: 'error'});
Toast({message: response.msg, theme: 'error'});
}
} catch (error) {
console.error('获取活动列表失败', error);
Toast({message: '获取活动列表失败', theme: 'error'});
} finally {
loading.value = false;
+69 -51
View File
@@ -3,13 +3,15 @@
<view class="title-area">
<div class="tech-animation">
<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 300 100">
<pattern id="pattern-hex" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="scale(0.8)">
<pattern id="pattern-hex" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"
patternTransform="scale(0.8)">
<rect x="0" y="0" width="10" height="10" fill="none"/>
<path d="M0,5 L2.5,0 L7.5,0 L10,5 L7.5,10 L2.5,10 Z" stroke-width="0.5" stroke="rgba(244, 90, 90, 0.2)" fill="rgba(244, 90, 90, 0.1)">
<animate attributeName="stroke-opacity" values="0.1;0.3;0.1" dur="3s" repeatCount="indefinite" />
<path d="M0,5 L2.5,0 L7.5,0 L10,5 L7.5,10 L2.5,10 Z" stroke-width="0.5" stroke="rgba(244, 90, 90, 0.2)"
fill="rgba(244, 90, 90, 0.1)">
<animate attributeName="stroke-opacity" values="0.1;0.3;0.1" dur="3s" repeatCount="indefinite"/>
</path>
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-hex)" />
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-hex)"/>
<g fill="none" stroke-width="0.5">
<path d="M20,50 Q60,20 100,50 T180,50 T260,50" stroke="rgba(244, 90, 90, 0.6)">
@@ -17,7 +19,7 @@
values="M20,50 Q60,20 100,50 T180,50 T260,50;
M20,50 Q60,80 100,50 T180,50 T260,50;
M20,50 Q60,20 100,50 T180,50 T260,50"
dur="10s" repeatCount="indefinite" />
dur="10s" repeatCount="indefinite"/>
</path>
<path d="M20,70 Q60,40 100,70 T180,70 T260,70" stroke="rgba(244, 90, 90, 0.4)">
@@ -25,26 +27,26 @@
values="M20,70 Q60,40 100,70 T180,70 T260,70;
M20,70 Q60,100 100,70 T180,70 T260,70;
M20,70 Q60,40 100,70 T180,70 T260,70"
dur="12s" repeatCount="indefinite" />
dur="12s" repeatCount="indefinite"/>
</path>
</g>
<circle cx="50" cy="50" r="1" fill="rgba(244, 90, 90, 0.8)">
<animate attributeName="cx" values="50;250;50" dur="20s" repeatCount="indefinite" />
<animate attributeName="cy" values="50;30;80;50" dur="16s" repeatCount="indefinite" />
<animate attributeName="r" values="1;1.5;1" dur="5s" repeatCount="indefinite" />
<animate attributeName="cx" values="50;250;50" dur="20s" repeatCount="indefinite"/>
<animate attributeName="cy" values="50;30;80;50" dur="16s" repeatCount="indefinite"/>
<animate attributeName="r" values="1;1.5;1" dur="5s" repeatCount="indefinite"/>
</circle>
<circle cx="150" cy="30" r="1" fill="rgba(244, 90, 90, 0.8)">
<animate attributeName="cx" values="150;100;200;150" dur="25s" repeatCount="indefinite" />
<animate attributeName="cy" values="30;60;20;30" dur="18s" repeatCount="indefinite" />
<animate attributeName="r" values="1;1.8;1" dur="6s" repeatCount="indefinite" />
<animate attributeName="cx" values="150;100;200;150" dur="25s" repeatCount="indefinite"/>
<animate attributeName="cy" values="30;60;20;30" dur="18s" repeatCount="indefinite"/>
<animate attributeName="r" values="1;1.8;1" dur="6s" repeatCount="indefinite"/>
</circle>
<circle cx="250" cy="70" r="1" fill="rgba(244, 90, 90, 0.8)">
<animate attributeName="cx" values="250;150;250" dur="22s" repeatCount="indefinite" />
<animate attributeName="cy" values="70;40;70" dur="15s" repeatCount="indefinite" />
<animate attributeName="r" values="1;1.2;1" dur="4s" repeatCount="indefinite" />
<animate attributeName="cx" values="250;150;250" dur="22s" repeatCount="indefinite"/>
<animate attributeName="cy" values="70;40;70" dur="15s" repeatCount="indefinite"/>
<animate attributeName="r" values="1;1.2;1" dur="4s" repeatCount="indefinite"/>
</circle>
</svg>
</div>
@@ -77,7 +79,7 @@
<view class="form-container">
<!-- 手机号码输入框 -->
<view class="input-box">
<t-icon name="mobile" size="20px" class="input-icon" />
<t-icon name="mobile" size="20px" class="input-icon"/>
<input
class="input"
type="tel"
@@ -89,7 +91,7 @@
<!-- 验证码输入框 -->
<view class="input-box">
<t-icon name="shield-error" size="20px" class="input-icon" />
<t-icon name="shield-error" size="20px" class="input-icon"/>
<input
class="input"
type="number"
@@ -107,11 +109,11 @@
立即登录
</view>
<!-- &lt;!&ndash; 注册链接 &ndash;&gt;-->
<!-- <view class="register-link">-->
<!-- <text>没有账号?</text>-->
<!-- <text class="link">去注册</text>-->
<!-- </view>-->
<!-- &lt;!&ndash; 注册链接 &ndash;&gt;-->
<!-- <view class="register-link">-->
<!-- <text>没有账号?</text>-->
<!-- <text class="link">去注册</text>-->
<!-- </view>-->
</view>
</view>
</view>
@@ -121,8 +123,10 @@
import {ref, reactive, computed, onMounted, onBeforeUnmount} from 'vue'
import {Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import {useUserStore} from "@/store";
const request = useRequest()
const userStore = useUserStore()
const activeMethod = ref('phone')
const loginForm = reactive({
@@ -167,15 +171,15 @@ const saveCountdownState = (seconds, phone) => {
const restoreCountdownState = () => {
const endTime = uni.getStorageSync(SMS_COUNTDOWN_KEY)
const savedPhone = uni.getStorageSync(SMS_PHONE_KEY)
if (endTime && savedPhone) {
const now = Date.now()
const remainingSeconds = Math.floor((endTime - now) / 1000)
if (remainingSeconds > 0) {
countdown.value = remainingSeconds
startCountDownTimer() // 重新启动定时器
// 如果用户还没输入手机号,填入保存的手机号
if (!loginForm.phone) {
loginForm.phone = savedPhone
@@ -267,33 +271,47 @@ async function handleLogin() {
}
try {
const {data, code, msg} = await request.post('/client/auth/sms-login', loginForm)
if (code === 0) {
Toast({message: '登录成功', theme: 'success'})
// 保存token
uni.setStorageSync('token', data)
userStore.smsLogin(loginForm).then(() => {
userStore.getUserInfo().then(() => {
Toast({message: '登录成功', theme: 'success'})
uni.switchTab({
url: '/pages/index/index'
})
}).catch(e => {
Toast({message: e.message, theme: 'error'})
})
}).catch(e => {
console.log(e)
Toast({message: e.message, theme: 'error'})
})
// 获取之前保存的重定向URL
const redirectUrl = uni.getStorageSync('REDIRECT_URL') || '/pages/index/index'
uni.removeStorageSync('REDIRECT_URL') // 使用后删除
// 延迟跳转,让用户看到成功提示
setTimeout(() => {
if (redirectUrl.startsWith('/pages/index/') || redirectUrl.startsWith('/pages/profile/')) {
// 主页和个人中心,使用switchTab
uni.switchTab({
url: redirectUrl
})
} else {
// 其他页面使用reLaunch
uni.reLaunch({
url: redirectUrl
})
}
}, 1000)
} else {
Toast({message: msg, theme: 'error'})
}
// const {data, code, msg} = await request.post('/client/auth/sms-login', loginForm)
// if (code === 0) {
// Toast({message: '登录成功', theme: 'success'})
// // 保存token
// uni.setStorageSync('token', data)
//
// // 获取之前保存的重定向URL
// const redirectUrl = uni.getStorageSync('REDIRECT_URL') || '/pages/index/index'
// uni.removeStorageSync('REDIRECT_URL') // 使用后删除
//
// // 延迟跳转,让用户看到成功提示
// setTimeout(() => {
// if (redirectUrl.startsWith('/pages/index/') || redirectUrl.startsWith('/pages/profile/')) {
// // 主页和个人中心,使用switchTab
// uni.switchTab({
// url: redirectUrl
// })
// } else {
// // 其他页面使用reLaunch
// uni.reLaunch({
// url: redirectUrl
// })
// }
// }, 1000)
// } else {
// Toast({message: msg, theme: 'error'})
// }
} catch (e) {
console.error(e)
Toast({message: e.message, theme: 'error'})
+51 -46
View File
@@ -1,21 +1,16 @@
<script setup>
import {ref, onMounted} from 'vue';
import {ref, onMounted, computed} from 'vue';
import {Toast, Dialog} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import {useUserStore} from "@/store";
// 用户资料数据
const userInfo = ref({
id: '--',
name: '--',
avatar: '/static/avatar.png',
phone: '--',
company: '--'
});
const userStore = useUserStore()
// 我的活动数据
const activities = ref({
enrolled: 6
});
// 用户信息
const userInfo = computed(() => {
return userStore.userInfo
})
console.log('userInfo', userInfo)
// 退出登录
const handleLogout = () => {
@@ -25,18 +20,11 @@ const handleLogout = () => {
confirmBtn: '确定',
cancelBtn: '取消',
onConfirm: () => {
useRequest().post('/client/auth/logout').then(res=>{
if(res.code===0){
uni.clearStorageSync()
Toast({message: '退出登录成功', theme: 'success'});
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index'
});
}, 1000);
}else{
Toast({message: '退出登录失败', theme: 'error'});
}
userStore.logout().then(() => {
Toast({message: '退出登录成功', theme: 'success'});
uni.reLaunch({
url: '/pages/login/index'
});
})
}
});
@@ -47,22 +35,41 @@ const navigateTo = (path) => {
uni.navigateTo({url: path});
};
// 处理编辑资料
const handleEditProfile = () => {
navigateTo('/pages/profile/edit/index');
};
// 选择头像
function changeAvatar() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
success: async (res) => {
try {
const {code, data, msg} = await useRequest().upload(res.tempFiles[0])
if (code === 0) {
updateAvatar(data)
} else {
Toast({message: msg, theme: 'error'});
}
} catch (e) {
Toast({message: e, theme: 'error'});
}
}
})
}
onMounted(() => {
// 获取用户信息
console.log('个人资料页面已加载');
userInfo.value={
id: getApp().globalData.user.id,
name: getApp().globalData.user.userName,
avatar: '/static/avatar.png',
phone: getApp().globalData.user.phone,
company: getApp().globalData.user.unitName
// 修改头像
async function updateAvatar(avatar) {
try {
await userStore.updateUserInfo({
...userStore.userInfo,
avatar: avatar
})
Toast({message: '修改头像成功', theme: 'success'});
} catch (e) {
Toast({message: e, theme: 'error'});
}
});
}
</script>
<template>
@@ -74,14 +81,14 @@ onMounted(() => {
<view class="user-profile-section">
<view class="user-info-container">
<view class="avatar-wrapper">
<t-avatar :image="userInfo.avatar" size="large"/>
<t-avatar :image="userInfo.avatar" size="large" @click="changeAvatar"/>
</view>
<view class="user-info">
<view class="user-name-row">
<text class="user-name">{{ userInfo.name }}</text>
<view class="edit-profile" @click="handleEditProfile">
<t-icon name="edit" size="20"/>
</view>
<text class="user-name">{{ userInfo.userName }}</text>
<!-- <view class="edit-profile" @click="handleEditProfile">-->
<!-- <t-icon name="edit" size="20"/>-->
<!-- </view>-->
</view>
<view class="user-company">{{ userInfo.phone }}</view>
</view>
@@ -115,12 +122,10 @@ onMounted(() => {
</t-cell-group>
</view>
<!-- 退出登录按钮 -->
<view class="logout-btn">
<t-button theme="danger" size="large" block @click="handleLogout">退出登录</t-button>
</view>
<!-- 底部版权信息 -->
<view class="footer">
<t-divider>活动报名系统 v1.0.0</t-divider>
</view>
+86
View File
@@ -0,0 +1,86 @@
import {defineStore} from "pinia";
import {useRequest} from "@/utils/request";
const request = useRequest()
export const useUserStore = defineStore("user", {
state: () => ({
userInfo: uni.getStorageSync('userInfo') || null,
token: uni.getStorageSync('token')
}),
actions: {
setToken(token) {
this.token = token
uni.setStorageSync('token', token)
},
setUserInfo(userInfo) {
this.userInfo = userInfo
uni.setStorageSync('userInfo', userInfo)
},
setUserInfoKV(key, value) {
this.userInfo[key] = value
},
async smsLogin(loginForm) {
try {
const {data, code, msg} = await request.post('/client/auth/sms-login', loginForm)
if (code === 0) {
this.setToken(data)
} else {
throw new Error(msg)
}
} catch (e) {
console.log(e)
throw e
}
},
async getUserInfo() {
try {
const {data, code, msg} = await request.get('/client/auth/user-info')
if (code === 0) {
this.setUserInfo(data)
} else {
throw new Error(msg)
}
} catch (e) {
console.log(e)
throw e
}
},
async updateUserInfo(userInfo) {
try {
const {data, code, msg} = await request.post('/client/auth/update-user-info', userInfo)
if (code === 0) {
this.setUserInfo(data)
} else {
throw new Error(msg)
}
} catch (e) {
console.log(e)
throw e
}
},
async logout() {
try {
const {code, msg} = await request.post('/client/auth/logout')
if (code === 0) {
this.setToken(null)
this.setUserInfo(null)
await uni.reLaunch({
url: '/pages/login/index'
});
} else {
throw new Error(msg)
}
} catch (e) {
console.log(e)
throw e
}
}
}
})
+185 -161
View File
@@ -1,200 +1,224 @@
import { reactive } from "vue";
import {reactive} from "vue";
/**
* 全局请求状态管理
*/
export const requestState = reactive({
// 所有正在加载的请求ID映射表
loadingRequests: {},
// 是否有任何请求正在加载中
get isLoading() {
return Object.keys(this.loadingRequests).length > 0;
},
// 当前正在加载的请求数量
get loadingCount() {
return Object.keys(this.loadingRequests).length;
}
// 所有正在加载的请求ID映射表
loadingRequests: {},
// 是否有任何请求正在加载中
get isLoading() {
return Object.keys(this.loadingRequests).length > 0;
},
// 当前正在加载的请求数量
get loadingCount() {
return Object.keys(this.loadingRequests).length;
}
});
/**
* 生成唯一的请求ID
*/
function generateRequestId() {
return Date.now() + "-" + Math.random().toString(36).substr(2, 9);
return Date.now() + "-" + Math.random().toString(36).substr(2, 9);
}
/**
* 请求开始处理
*/
function handleRequestStart(requestId) {
requestState.loadingRequests[requestId] = true;
requestState.loadingRequests[requestId] = true;
}
/**
* 请求结束处理
*/
function handleRequestEnd(requestId) {
delete requestState.loadingRequests[requestId];
delete requestState.loadingRequests[requestId];
}
/**
* 处理未登录状态
*/
function handleUnauthorized() {
// Token失效,清除存储并跳转登录
uni.removeStorageSync("token");
uni.removeStorageSync("userInfo");
uni.showToast({
title: "登录已过期,请重新登录",
icon: "none",
duration: 2000
});
setTimeout(() => {
uni.navigateTo({ url: "/pages/login/index" });
}, 2000);
// Token失效,清除存储并跳转登录
uni.removeStorageSync("token");
uni.removeStorageSync("userInfo");
uni.showToast({
title: "登录已过期,请重新登录",
icon: "none",
duration: 2000
});
setTimeout(() => {
uni.navigateTo({url: "/pages/login/index"});
}, 2000);
}
/**
* 使用简化的请求功能
*/
export function useRequest(options = {}) {
const defaultOptions = {
baseUrl: "/api",
timeout: 60000,
...options
};
/**
* 执行HTTP请求
*/
const request = (config) => {
const requestId = generateRequestId();
// 合并配置
const mergedConfig = {
...defaultOptions,
...config,
header: {
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
...defaultOptions.header,
...config.header
}
const defaultOptions = {
baseUrl: "/api",
timeout: 60000,
...options
};
// 构建完整URL
if (mergedConfig.url && !mergedConfig.url.startsWith("http")) {
mergedConfig.url = `${mergedConfig.baseUrl}${mergedConfig.url}`;
}
handleRequestStart(requestId);
return new Promise((resolve, reject) => {
uni.request({
...mergedConfig,
success: (response) => {
const { statusCode, data } = response;
// HTTP状态码成功
if (statusCode >= 200 && statusCode < 300) {
if (data.code === 600098) {
handleUnauthorized();
reject(data);
}else{
resolve(data);
}
// // 业务逻辑成功
// if (data.code === 0) {
// resolve(data);
// }
// // 未登录
// else if (data.code === 600098) {
// handleUnauthorized();
// reject(data);
// }
// // 其他业务错误,直接返回,由业务代码处理
// else {
// reject(data);
// }
}
// HTTP错误
else {
reject({
code: statusCode,
message: `HTTP错误: ${statusCode}`,
data: data
});
}
},
fail: (error) => {
reject(error);
},
complete: () => {
handleRequestEnd(requestId);
}
});
});
};
/**
* GET请求
*/
const get = (url, params = {}, config = {}) => {
return request({
method: "GET",
url,
data: params,
...config
});
};
/**
* POST请求
*/
const post = (url, data = {}, config = {}) => {
return request({
method: "POST",
url,
data,
...config
});
};
/**
* PUT请求
*/
const put = (url, data = {}, config = {}) => {
return request({
method: "PUT",
url,
data,
...config
});
};
/**
* DELETE请求
*/
const del = (url, data = {}, config = {}) => {
return request({
method: "DELETE",
url,
data,
...config
});
};
// 返回所有HTTP方法
return {
request,
get,
post,
put,
delete: del
};
/**
* 执行HTTP请求
*/
const request = (config) => {
const requestId = generateRequestId();
// 合并配置
const mergedConfig = {
...defaultOptions,
...config,
header: {
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
...defaultOptions.header,
...config.header
}
};
// 构建完整URL
if (mergedConfig.url && !mergedConfig.url.startsWith("http")) {
mergedConfig.url = `${mergedConfig.baseUrl}${mergedConfig.url}`;
}
handleRequestStart(requestId);
return new Promise((resolve, reject) => {
uni.request({
...mergedConfig,
success: (response) => {
const {statusCode, data} = response;
// HTTP状态码成功
if (statusCode >= 200 && statusCode < 300) {
if (data.code === 600098) {
handleUnauthorized();
reject(data);
} else {
resolve(data);
}
// // 业务逻辑成功
// if (data.code === 0) {
// resolve(data);
// }
// // 未登录
// else if (data.code === 600098) {
// handleUnauthorized();
// reject(data);
// }
// // 其他业务错误,直接返回,由业务代码处理
// else {
// reject(data);
// }
}
// HTTP错误
else {
reject({
code: statusCode,
message: `HTTP错误: ${statusCode}`,
data: data
});
}
},
fail: (error) => {
reject(error);
},
complete: () => {
handleRequestEnd(requestId);
}
});
});
};
/**
* GET请求
*/
const get = (url, params = {}, config = {}) => {
return request({
method: "GET",
url,
data: params,
...config
});
};
/**
* POST请求
*/
const post = (url, data = {}, config = {}) => {
return request({
method: "POST",
url,
data,
...config
});
};
/**
* PUT请求
*/
const put = (url, data = {}, config = {}) => {
return request({
method: "PUT",
url,
data,
...config
});
};
/**
* DELETE请求
*/
const del = (url, data = {}, config = {}) => {
return request({
method: "DELETE",
url,
data,
...config
});
};
/**
* 上传文件
*/
const upload = (file) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: '/api/open/file/upload/image',
file: file,
name: 'file',
success: (res) => {
if (res.statusCode === 200) {
resolve(JSON.parse(res.data))
} else {
reject(res)
}
},
fail: (err) => {
reject(err)
}
})
})
}
// 返回所有HTTP方法
return {
request,
get,
post,
put,
delete: del,
upload
};
}
+9 -20
View File
@@ -1,5 +1,6 @@
import {Dialog} from "tdesign-mobile-vue";
import {useRequest} from "@/utils/request";
import {useUserStore} from "@/store";
/**
* 路由拦截器配置
@@ -10,24 +11,15 @@ export const setupRouterInterceptor = (whiteList = []) => {
addNavigationInterceptors(whiteList)
}
export const checkAuth = () => {
debugger
export const checkAuth = async () => {
if (process.env.VUE_APP_PLATFORM === 'h5' && window.location.pathname !== '/') {
const path = window.location.pathname.replace(/^\//, '')
if (!getDefaultWhiteList().includes(path)) {
const token = uni.getStorageSync('token')
if(token){
// 获取用户信息
const {user} = getApp().globalData
if(user == null){
return useRequest().get('/client/auth/info').then(({code, data, msg}) => {
if(code === 0){
getApp().globalData.user = data
}
})
}
}else{
uni.redirectTo({
if (token) {
await useUserStore().getUserInfo()
} else {
await uni.redirectTo({
url: '/pages/login/index'
})
}
@@ -84,17 +76,14 @@ async function routeInterceptor(args, whiteList) {
}
console.log('路由拦截检查:', {url: targetUrl, isInWhiteList, hasToken: !!token})
// 如果不在白名单且未登录,则跳转到登录页
if (!isInWhiteList) {
if (token) {
// 获取用户信息
const {user} = getApp().globalData
const user = useUserStore().userInfo
if (user == null) {
const {code, data, msg} = await useRequest().get('/client/auth/info')
if (code === 0) {
getApp().globalData.user = data
return args
}
await userStore().getUserInfo()
}
return args
} else {