mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-22 13:05:18 +00:00
feat: 更新应用名称和页面路由结构
refactor: 重构用户信息管理和登录流程 fix: 修复头像上传和密码修改功能 style: 优化登录页面和主题设置界面 docs: 更新主题系统文档说明 chore: 移除无用配置文件和更新依赖项
This commit is contained in:
@@ -18,7 +18,7 @@ const FileAPI = {
|
||||
*
|
||||
* @param filePath
|
||||
*/
|
||||
upload(filePath: string): Promise<UploadFilePath> {
|
||||
upload(filePath: string): Promise<UploadFileResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: this.uploadUrl,
|
||||
@@ -29,7 +29,7 @@ const FileAPI = {
|
||||
},
|
||||
formData: {},
|
||||
success: (response) => {
|
||||
const resData = JSON.parse(response.data) as ApiResponse<UploadFilePath>;
|
||||
const resData = JSON.parse(response.data) as ApiResponse<UploadFileResult>;
|
||||
// 业务状态码 00000 表示成功
|
||||
if (resData.code === ApiCode.SUCCESS) {
|
||||
resolve(resData.data);
|
||||
|
||||
+14
-50
@@ -21,8 +21,8 @@ const UserAPI = {
|
||||
* @param body
|
||||
* @returns 上传后的文件路径
|
||||
*/
|
||||
uploadCurrentUserAvatar(body: any): Promise<UploadFilePath> {
|
||||
return request<UploadFilePath>({
|
||||
uploadCurrentUserAvatar(body: any): Promise<UploadFileResult> {
|
||||
return request<UploadFileResult>({
|
||||
url: `${USER_BASE_URL}/current/avatar/upload`,
|
||||
method: "POST",
|
||||
data: body,
|
||||
@@ -145,7 +145,7 @@ const UserAPI = {
|
||||
*/
|
||||
deleteUser(ids: number[]): Promise<ApiResponse> {
|
||||
return request<ApiResponse>({
|
||||
url: `/system/user/delete`,
|
||||
url: `${USER_BASE_URL}/delete`,
|
||||
method: "DELETE",
|
||||
data: ids,
|
||||
});
|
||||
@@ -213,7 +213,7 @@ export interface UserInfo {
|
||||
avatar?: string;
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
gender?: string;
|
||||
gender?: number;
|
||||
password?: string;
|
||||
menus?: MenuTable[];
|
||||
dept?: deptTreeType;
|
||||
@@ -289,16 +289,16 @@ export interface positionSelectorType {
|
||||
/* 个人中心用户信息表单 */
|
||||
export interface UserProfileForm {
|
||||
id?: number;
|
||||
name: string;
|
||||
gender: number;
|
||||
mobile: string;
|
||||
email: string;
|
||||
username: string;
|
||||
dept_name: string;
|
||||
positions: positionSelectorType[];
|
||||
roles: roleSelectorType[];
|
||||
avatar: string;
|
||||
created_at: string;
|
||||
name?: string;
|
||||
gender?: number;
|
||||
mobile?: string;
|
||||
email?: string;
|
||||
username?: string;
|
||||
dept_name?: string;
|
||||
positions?: positionSelectorType[];
|
||||
roles?: roleSelectorType[];
|
||||
avatar?: string;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
/* 修改密码表单 */
|
||||
@@ -313,39 +313,3 @@ export interface ResetPasswordForm {
|
||||
id: number;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/* 修改手机表单 */
|
||||
export interface MobileBindingForm {
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
/** 验证码 */
|
||||
code?: string;
|
||||
}
|
||||
|
||||
/* 修改邮箱表单 */
|
||||
export interface EmailBindingForm {
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
/** 验证码 */
|
||||
code?: string;
|
||||
}
|
||||
|
||||
/* 微信手机号授权数据 */
|
||||
export interface WechatPhoneData {
|
||||
/** 微信授权码 */
|
||||
code: string;
|
||||
/** 加密数据 */
|
||||
encryptedData?: string;
|
||||
/** 初始向量 */
|
||||
iv?: string;
|
||||
}
|
||||
|
||||
/* 手机号获取结果 */
|
||||
export interface PhoneNumberResult {
|
||||
/** 手机号 */
|
||||
phoneNumber: string;
|
||||
/** 纯手机号(去除+86) */
|
||||
purePhoneNumber?: string;
|
||||
/** 国家代码 */
|
||||
countryCode?: string;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ export interface TabbarItem {
|
||||
}
|
||||
|
||||
const tabbarItems = ref<TabbarItem[]>([
|
||||
{ name: "home", value: null, active: true, title: "首页", icon: "home" },
|
||||
{ name: "work", value: null, active: true, title: "工作台", icon: "work" },
|
||||
{ name: "index", value: null, active: true, title: "首页", icon: "home" },
|
||||
{ name: "work", value: null, active: false, title: "工作台", icon: "app" },
|
||||
{ name: "mine", value: null, active: false, title: "我的", icon: "user" },
|
||||
]);
|
||||
|
||||
|
||||
@@ -25,6 +25,24 @@ export function useTheme() {
|
||||
const currentThemeColor = ref<ThemeColorOption>(themeColorOptions[0]);
|
||||
const showThemeColorSheet = ref(false);
|
||||
|
||||
const colorColumns = [
|
||||
{ value: "#165DFF", label: "蓝色" },
|
||||
{ value: "#0FC6C2", label: "青绿色" },
|
||||
{ value: "#722ED1", label: "紫色" },
|
||||
{ value: "#F5222D", label: "红色" },
|
||||
{ value: "#FA8C16", label: "橙色" },
|
||||
{ value: "#FADB14", label: "黄色" },
|
||||
{ value: "#52C41A", label: "绿色" },
|
||||
{ value: "#EB2F96", label: "粉色" },
|
||||
{ value: "#13C2C2", label: "青色" },
|
||||
{ value: "#1890FF", label: "天蓝色" },
|
||||
{ value: "#CD5C5C", label: "经典红" },
|
||||
{ value: "#228B22", label: "自然绿" },
|
||||
{ value: "#FF6B6B", label: "珊瑚红" },
|
||||
{ value: "#4ECDC4", label: "薄荷绿" },
|
||||
{ value: "#FFD166", label: "黄金黄色" },
|
||||
];
|
||||
|
||||
const themeVars = reactive<ConfigProviderThemeVars>({
|
||||
darkBackground: "#0f0f0f",
|
||||
darkBackground2: "#1a1a1a",
|
||||
@@ -114,7 +132,6 @@ export function useTheme() {
|
||||
theme.value = systemTheme;
|
||||
if (!hasUserSet.value) {
|
||||
followSystem.value = true;
|
||||
console.log("首次启动,使用系统主题:", theme.value);
|
||||
} else {
|
||||
console.log("跟随系统主题:", theme.value);
|
||||
}
|
||||
@@ -162,6 +179,7 @@ export function useTheme() {
|
||||
return {
|
||||
theme: computed(() => theme.value),
|
||||
isDark,
|
||||
colorColumns,
|
||||
followSystem: computed(() => followSystem.value),
|
||||
hasUserSet: computed(() => hasUserSet.value),
|
||||
currentThemeColor: computed(() => currentThemeColor.value),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "vue-uniapp-template",
|
||||
"name": "FastApp",
|
||||
"appid": "",
|
||||
"description": "FastApp",
|
||||
"versionName": "2.0.0",
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
{
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue"
|
||||
}
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"type": "home",
|
||||
"name": "home",
|
||||
"name": "index",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
},
|
||||
@@ -31,7 +25,7 @@
|
||||
{
|
||||
"path": "pages/work/index",
|
||||
"type": "page",
|
||||
"name": "work",
|
||||
"name": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台"
|
||||
},
|
||||
@@ -106,6 +100,9 @@
|
||||
{
|
||||
"pagePath": "pages/index/index"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/work/index"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/index"
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<!-- 通知公告 -->
|
||||
<wd-notice-bar
|
||||
text="vue-uniapp-template 是一个基于 Vue3 + UniApp 的前端模板项目,提供了一套完整的前端解决方案,包括登录、权限、字典、接口请求、状态管理、页面布局、组件封装等功能。"
|
||||
text="fastapp 是一个基于 Vue3 + UniApp 的前端模板项目,提供了一套完整的前端解决方案,包括登录、权限、字典、接口请求、状态管理、页面布局、组件封装等功能。"
|
||||
color="#34D19D"
|
||||
type="info"
|
||||
>
|
||||
@@ -80,7 +80,7 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="w-full h-300px mb-40rpx">
|
||||
<view class="w-full h-240px mb-40rpx">
|
||||
<qiun-data-charts type="area" :chartData="chartData" :opts="chartOpts" />
|
||||
</view>
|
||||
</wd-card>
|
||||
@@ -89,6 +89,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dayjs } from "wot-design-uni";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
// 定义访问统计数据类型
|
||||
interface VisitStatsVO {
|
||||
@@ -140,7 +141,7 @@ const chartOpts = ref({
|
||||
// 日期范围
|
||||
const recentDaysRange = ref(7);
|
||||
|
||||
const swiperList = ref(["https://www.youlai.tech/storage/blog/banner9.png"]);
|
||||
const swiperList = ref(["/static/images/banner.png"]);
|
||||
|
||||
// 快捷导航列表
|
||||
const navList = reactive([
|
||||
@@ -248,7 +249,6 @@ const loadVisitTrendData = () => {
|
||||
|
||||
// 数据范围变化
|
||||
const handleDataRangeChange = ({ value }: { value: number }) => {
|
||||
console.log("handleDataRangeChange", value);
|
||||
recentDaysRange.value = value;
|
||||
loadVisitTrendData();
|
||||
};
|
||||
@@ -273,7 +273,7 @@ onShow(() => {
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"name": "home",
|
||||
"name": "index",
|
||||
"style": { "navigationStyle": "custom" },
|
||||
"layout": "tabbar"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
<image src="/static/images/login-bg.svg" mode="aspectFill" class="login-bg" />
|
||||
|
||||
<!-- Logo和标题区域 -->
|
||||
<view class="header"></view>
|
||||
<view class="header">
|
||||
<image src="/static/logo.png" class="logo" mode="aspectFit" />
|
||||
<text class="title">FastApp管理系统</text>
|
||||
<text class="subtitle">欢迎使用移动端管理平台</text>
|
||||
</view>
|
||||
|
||||
<view class="login-card">
|
||||
<view class="form-wrap">
|
||||
@@ -23,6 +27,7 @@
|
||||
class="form-input input-transparent"
|
||||
placeholder="请输入用户名"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="handleAccountLogin"
|
||||
/>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
@@ -38,9 +43,11 @@
|
||||
<input
|
||||
v-model="loginFormData.password"
|
||||
class="form-input input-transparent"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
type="text"
|
||||
:password="showPassword ? false : true"
|
||||
placeholder="请输入密码"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="handleAccountLogin"
|
||||
/>
|
||||
<wd-icon
|
||||
:name="showPassword ? 'eye-open' : 'eye-close'"
|
||||
@@ -64,9 +71,10 @@
|
||||
type="text"
|
||||
placeholder="请输入验证码"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="handleAccountLogin"
|
||||
/>
|
||||
<view class="captcha-img">
|
||||
<image :src="captchaImg" class="captcha-img" />
|
||||
<view class="captcha-img" @click="getLoginCaptcha">
|
||||
<image :src="captchaImg" class="captcha-img" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
@@ -75,9 +83,10 @@
|
||||
<button
|
||||
class="login-btn"
|
||||
:disabled="loading"
|
||||
:style="loading ? 'opacity: 0.7;' : ''"
|
||||
:style="{ opacity: loading ? 0.7 : 1 }"
|
||||
@click="handleAccountLogin"
|
||||
>
|
||||
<wd-loading v-if="loading" size="20" color="#fff" />
|
||||
{{ loading ? "登录中..." : "账号登录" }}
|
||||
</button>
|
||||
|
||||
@@ -145,7 +154,8 @@ import { useUserStore } from "@/store/modules/user.store";
|
||||
import { useToast } from "wot-design-uni";
|
||||
import { useWechat } from "@/composables/useWechat";
|
||||
import { useTheme } from "@/composables/useTheme";
|
||||
import { computed, onMounted } from "vue";
|
||||
import { debounce } from "@/utils";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import AuthAPI, { type LoginFormData } from "@/api/auth";
|
||||
|
||||
const loginFormRef = ref();
|
||||
@@ -166,9 +176,9 @@ const isDarkMode = computed(() => theme.value === "dark");
|
||||
const loginFormData = ref<LoginFormData>({
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
captcha_key: "",
|
||||
captcha: "",
|
||||
remember: true,
|
||||
captcha_key: "",
|
||||
remember: true, // 添加 remember 属性,默认值设为 false
|
||||
});
|
||||
|
||||
// 获取重定向参数
|
||||
@@ -183,10 +193,15 @@ onLoad((options) => {
|
||||
const getLoginCaptcha = async () => {
|
||||
try {
|
||||
const result = await AuthAPI.getCaptcha();
|
||||
captchaImg.value = result.captcha_key;
|
||||
toast.success("验证码发送成功");
|
||||
captchaImg.value = result.img_base;
|
||||
loginFormData.value.captcha_key = result.key;
|
||||
// 只有在需要验证码时才清空输入框
|
||||
if (result.enable) {
|
||||
loginFormData.value.captcha = "";
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error?.message || "验证码发送失败");
|
||||
toast.error(error?.message || "验证码获取失败");
|
||||
captchaImg.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -203,12 +218,29 @@ onMounted(() => {
|
||||
getLoginCaptcha();
|
||||
});
|
||||
|
||||
// 账号密码登录处理
|
||||
const handleAccountLogin = () => {
|
||||
// 统一的登录成功处理
|
||||
const handleLoginSuccess = async (result: any) => {
|
||||
// 记住用户名
|
||||
await userStore.getInfo();
|
||||
toast.success("登录成功");
|
||||
|
||||
setTimeout(() => {
|
||||
if (!userStore.isUserInfoComplete() || result.isNewUser) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mine/profile/complete-profile?redirect=${encodeURIComponent(redirect.value)}`,
|
||||
});
|
||||
} else {
|
||||
uni.reLaunch({ url: redirect.value });
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// 账号密码登录处理(防抖处理)
|
||||
const handleAccountLogin = debounce(() => {
|
||||
if (loading.value) return;
|
||||
|
||||
// 表单验证
|
||||
if (!loginFormData.value.username) {
|
||||
if (!loginFormData.value.username.trim()) {
|
||||
toast.error("请输入用户名");
|
||||
return;
|
||||
}
|
||||
@@ -216,37 +248,28 @@ const handleAccountLogin = () => {
|
||||
toast.error("请输入密码");
|
||||
return;
|
||||
}
|
||||
if (loginFormData.value.captcha_key && !loginFormData.value.captcha) {
|
||||
if (loginFormData.value.captcha_key && !loginFormData.value.captcha.trim()) {
|
||||
toast.error("请输入验证码");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loading.value = true;
|
||||
|
||||
userStore
|
||||
.login(loginFormData.value)
|
||||
.then(() => userStore.getInfo())
|
||||
.then(() => {
|
||||
toast.success("登录成功");
|
||||
|
||||
// 账号密码登录直接跳转到重定向页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
}, 1000);
|
||||
})
|
||||
.then(handleLoginSuccess)
|
||||
.catch((error) => {
|
||||
toast.error(error?.message || "登录失败");
|
||||
// 登录失败后刷新验证码
|
||||
getLoginCaptcha();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
}, 500);
|
||||
|
||||
// 微信一键登录(通过手机号)
|
||||
const handleWechatPhoneLogin = async (e: any) => {
|
||||
// 微信一键登录(通过手机号)- 防抖处理
|
||||
const handleWechatPhoneLogin = debounce(async (e: any) => {
|
||||
if (loading.value || authState.value.isLogining) return;
|
||||
loading.value = true;
|
||||
|
||||
@@ -257,26 +280,8 @@ const handleWechatPhoneLogin = async (e: any) => {
|
||||
// 调用登录接口
|
||||
const result: any = await userStore.loginWithWxPhone(phoneData);
|
||||
|
||||
// 获取用户信息
|
||||
await userStore.getInfo();
|
||||
toast.success("登录成功");
|
||||
|
||||
// 检查是否为新用户或信息不完整
|
||||
if (result.isNewUser || !userStore.isUserInfoComplete()) {
|
||||
// 跳转到完善信息页面
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mine/profile/complete-profile?redirect=${encodeURIComponent(redirect.value)}`,
|
||||
});
|
||||
}, 1000);
|
||||
} else {
|
||||
// 跳转到重定向页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
// 使用统一的成功处理
|
||||
await handleLoginSuccess(result);
|
||||
} catch (error: any) {
|
||||
if (error.message === "用户拒绝授权") {
|
||||
toast.error("您已拒绝授权获取手机号");
|
||||
@@ -287,10 +292,10 @@ const handleWechatPhoneLogin = async (e: any) => {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
}, 500);
|
||||
|
||||
// 微信授权登录处理
|
||||
const handleWechatLogin = async () => {
|
||||
// 微信授权登录处理 - 防抖处理
|
||||
const handleWechatLogin = debounce(async () => {
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
|
||||
@@ -301,27 +306,8 @@ const handleWechatLogin = async () => {
|
||||
|
||||
// 尝试使用微信授权登录接口
|
||||
const result: any = await userStore.loginWithWxCode(code);
|
||||
|
||||
// 获取用户信息
|
||||
await userStore.getInfo();
|
||||
toast.success("登录成功");
|
||||
|
||||
// 检查用户信息是否完整
|
||||
if (result.isNewUser || !userStore.isUserInfoComplete()) {
|
||||
// 如果信息不完整,跳转到完善信息页面
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mine/profile/complete-profile?redirect=${encodeURIComponent(redirect.value)}`,
|
||||
});
|
||||
}, 1000);
|
||||
} else {
|
||||
// 否则直接跳转到重定向页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
// 使用统一的成功处理
|
||||
await handleLoginSuccess(result);
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
@@ -332,7 +318,7 @@ const handleWechatLogin = async () => {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
}, 500);
|
||||
|
||||
// 跳转到用户协议页面
|
||||
const navigateToUserAgreement = () => {
|
||||
@@ -365,10 +351,35 @@ const navigateToPrivacy = () => {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
}
|
||||
.header {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
@@ -478,6 +489,7 @@ input:-webkit-autofill:active {
|
||||
input.form-input,
|
||||
input.input-transparent {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: none !important;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
@@ -661,6 +673,7 @@ input.input-transparent {
|
||||
@supports (-webkit-appearance: none) {
|
||||
input {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
@@ -159,15 +159,15 @@ const handleSubmit = async () => {
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} catch (_error) {
|
||||
toast.error("提交失败,请重试");
|
||||
} catch (error: any) {
|
||||
toast.error(error?.message || "提交失败,请重试");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
} catch (_error) {
|
||||
} catch (error: any) {
|
||||
// 表单验证失败
|
||||
console.log("表单验证失败");
|
||||
console.log("表单验证失败", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<block v-if="isLogin">
|
||||
<view class="nickname">{{ userInfo!.nickname || "匿名用户" }}</view>
|
||||
<view class="name">{{ userInfo!.name || "匿名用户" }}</view>
|
||||
<view class="user-id">ID: {{ userInfo?.username || "0000000" }}</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<view class="login-prompt">立即登录获取更多功能</view>
|
||||
<view class="login-prompt">立即登录</view>
|
||||
<wd-button
|
||||
custom-class="btn-login"
|
||||
size="small"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<view class="nickname-section">
|
||||
<view class="section-title">昵称</view>
|
||||
<input
|
||||
v-model="profileForm.nickname"
|
||||
v-model="profileForm.name"
|
||||
type="nickname"
|
||||
class="nickname-input"
|
||||
placeholder="请输入昵称"
|
||||
@@ -80,10 +80,10 @@
|
||||
<text class="required">*</text>
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="profileForm.nickname"
|
||||
v-model="profileForm.name"
|
||||
placeholder="请输入昵称"
|
||||
prop="nickname"
|
||||
:rules="rules.nickname"
|
||||
prop="name"
|
||||
:rules="rules.name"
|
||||
custom-class="nickname-input"
|
||||
/>
|
||||
</view>
|
||||
@@ -98,28 +98,16 @@
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 手机号授权 -->
|
||||
<!-- 手机号输入 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">
|
||||
手机号
|
||||
<text class="required">*</text>
|
||||
</view>
|
||||
<view v-if="!profileForm.mobile" class="phone-auth">
|
||||
<button
|
||||
class="phone-auth-btn"
|
||||
open-type="getPhoneNumber"
|
||||
:disabled="phoneAuthLoading"
|
||||
@getphonenumber="onGetPhoneNumber"
|
||||
>
|
||||
<wd-icon name="phone" size="20" color="#165DFF" />
|
||||
<text class="auth-text">{{ phoneAuthLoading ? "授权中..." : "授权获取手机号" }}</text>
|
||||
</button>
|
||||
</view>
|
||||
<view v-else class="phone-display">
|
||||
<wd-icon name="phone" size="20" color="#52c41a" />
|
||||
<text class="phone-number">{{ formatPhoneNumber(profileForm.mobile) }}</text>
|
||||
<text class="change-phone" @click="changePhone">更换</text>
|
||||
</view>
|
||||
<view class="section-title">手机号</view>
|
||||
<wd-input
|
||||
v-model="profileForm.mobile"
|
||||
placeholder="请输入手机号"
|
||||
type="number"
|
||||
:maxlength="11"
|
||||
custom-class="mobile-input"
|
||||
/>
|
||||
</view>
|
||||
</wd-form>
|
||||
</view>
|
||||
@@ -157,9 +145,10 @@
|
||||
import { ref, reactive, computed } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useToast } from "wot-design-uni";
|
||||
import type { FormRules } from "wot-design-uni/components/wd-form/types";
|
||||
import { useUserStore } from "@/store/modules/user.store";
|
||||
import UserAPI, { type UserProfileForm } from "@/api/user";
|
||||
import FileAPI, { type FileInfo } from "@/api/file";
|
||||
import UserAPI, { type UserInfo } from "@/api/user";
|
||||
import FileAPI from "@/api/file";
|
||||
|
||||
const toast = useToast();
|
||||
const userStore = useUserStore();
|
||||
@@ -168,31 +157,33 @@ const userStore = useUserStore();
|
||||
const redirect = ref("/pages/index/index");
|
||||
|
||||
// 表单数据
|
||||
const profileForm = reactive<UserProfileForm & { mobile?: string }>({
|
||||
nickname: "",
|
||||
avatar: "",
|
||||
const profileForm = reactive({
|
||||
name: "",
|
||||
gender: 1,
|
||||
mobile: "",
|
||||
email: "",
|
||||
username: "",
|
||||
dept_name: "",
|
||||
positions: [] as UserInfo["positions"],
|
||||
roles: [] as UserInfo["roles"],
|
||||
avatar: "",
|
||||
created_at: "",
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
nickname: [
|
||||
{ required: true, message: "请输入昵称" },
|
||||
{ required: true, min: 2, max: 20, message: "昵称长度为2-20个字符" },
|
||||
],
|
||||
const rules: FormRules = {
|
||||
name: [{ required: true, message: "请输入昵称" }],
|
||||
};
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false);
|
||||
const phoneAuthLoading = ref(false);
|
||||
const cropperVisible = ref(false);
|
||||
const originalImageSrc = ref("");
|
||||
const profileFormRef = ref();
|
||||
|
||||
// 计算属性
|
||||
const canComplete = computed(() => {
|
||||
return profileForm.nickname && profileForm.mobile;
|
||||
return profileForm.name && profileForm.name.length >= 2;
|
||||
});
|
||||
|
||||
// 页面加载
|
||||
@@ -204,10 +195,15 @@ onLoad((options: any) => {
|
||||
// 如果用户已有部分信息,预填充
|
||||
const userInfo = userStore.userInfo;
|
||||
if (userInfo) {
|
||||
profileForm.nickname = userInfo.nickname || "";
|
||||
profileForm.name = userInfo.name || "";
|
||||
profileForm.avatar = userInfo.avatar || "";
|
||||
// 注意:UserInfo接口可能没有gender字段,这里需要安全访问
|
||||
profileForm.gender = (userInfo as any).gender || 1;
|
||||
profileForm.gender = userInfo.gender || 1;
|
||||
profileForm.email = userInfo.email || "";
|
||||
profileForm.username = userInfo.username || "";
|
||||
profileForm.dept_name = userInfo.dept_name || "";
|
||||
profileForm.positions = userInfo.positions || [];
|
||||
profileForm.roles = userInfo.roles || [];
|
||||
profileForm.created_at = userInfo.created_at || "";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -219,15 +215,14 @@ const onChooseAvatar = async (e: any) => {
|
||||
// 上传头像到服务器
|
||||
uni.showLoading({ title: "上传中..." });
|
||||
|
||||
const fileInfo: FileInfo = await FileAPI.upload(avatarUrl);
|
||||
profileForm.avatar = fileInfo.url;
|
||||
|
||||
const fileInfo: UploadFileResult = await FileAPI.upload(avatarUrl);
|
||||
profileForm.avatar = fileInfo.file_url;
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "头像上传成功", icon: "success" });
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
uni.hideLoading();
|
||||
console.error("头像上传失败:", error);
|
||||
uni.showToast({ title: "头像上传失败", icon: "error" });
|
||||
uni.showToast({ title: error?.message || "头像上传失败", icon: "error" });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -272,8 +267,8 @@ const handleAvatarConfirm = async (event: any) => {
|
||||
const { tempFilePath } = event;
|
||||
toast.loading("上传中...");
|
||||
|
||||
const fileInfo: FileInfo = await FileAPI.upload(tempFilePath);
|
||||
profileForm.avatar = fileInfo.url;
|
||||
const fileInfo: UploadFileResult = await FileAPI.upload(tempFilePath);
|
||||
profileForm.avatar = fileInfo.file_url;
|
||||
|
||||
toast.success("头像上传成功");
|
||||
} catch (error) {
|
||||
@@ -282,45 +277,6 @@ const handleAvatarConfirm = async (event: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取手机号授权
|
||||
const onGetPhoneNumber = async (e: any) => {
|
||||
console.log("手机号授权回调:", e);
|
||||
|
||||
if (e.detail.errMsg === "getPhoneNumber:ok") {
|
||||
phoneAuthLoading.value = true;
|
||||
|
||||
try {
|
||||
// 调用后端接口解析手机号
|
||||
const phoneData = await UserAPI.getPhoneNumber({
|
||||
code: e.detail.code,
|
||||
encryptedData: e.detail.encryptedData,
|
||||
iv: e.detail.iv,
|
||||
});
|
||||
|
||||
profileForm.mobile = phoneData.phoneNumber;
|
||||
toast.success("手机号授权成功");
|
||||
} catch (error: any) {
|
||||
console.error("手机号授权失败:", error);
|
||||
toast.error(error?.message || "手机号授权失败");
|
||||
} finally {
|
||||
phoneAuthLoading.value = false;
|
||||
}
|
||||
} else {
|
||||
toast.error("手机号授权失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 更换手机号
|
||||
const changePhone = () => {
|
||||
profileForm.mobile = "";
|
||||
};
|
||||
|
||||
// 格式化手机号显示
|
||||
const formatPhoneNumber = (phone: string) => {
|
||||
if (!phone) return "";
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
|
||||
};
|
||||
|
||||
// 完成信息填写
|
||||
const handleComplete = async () => {
|
||||
try {
|
||||
@@ -328,27 +284,22 @@ const handleComplete = async () => {
|
||||
const { valid } = await profileFormRef.value.validate();
|
||||
if (!valid) return;
|
||||
|
||||
if (!profileForm.mobile) {
|
||||
toast.error("请先授权获取手机号");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
// 更新用户信息
|
||||
await UserAPI.updateProfile({
|
||||
nickname: profileForm.nickname,
|
||||
avatar: profileForm.avatar,
|
||||
await UserAPI.updateCurrentUserInfo({
|
||||
name: profileForm.name,
|
||||
gender: profileForm.gender,
|
||||
mobile: profileForm.mobile || "",
|
||||
email: profileForm.email || "",
|
||||
username: profileForm.username || "",
|
||||
dept_name: profileForm.dept_name || "",
|
||||
positions: profileForm.positions || [],
|
||||
roles: profileForm.roles || [],
|
||||
avatar: profileForm.avatar || "",
|
||||
created_at: profileForm.created_at || "",
|
||||
});
|
||||
|
||||
// 如果有手机号,绑定手机号
|
||||
if (profileForm.mobile) {
|
||||
await UserAPI.bindMobile({
|
||||
mobile: profileForm.mobile,
|
||||
});
|
||||
}
|
||||
|
||||
// 重新获取用户信息
|
||||
await userStore.getInfo();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
/>
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-cell title="昵称" :value="userProfile.nickname" is-link @click="handleOpenDialog()" />
|
||||
<wd-cell title="昵称" :value="userProfile.name" is-link @click="handleOpenDialog()" />
|
||||
<wd-cell
|
||||
title="性别"
|
||||
:value="userProfile.gender === 1 ? '男' : userProfile.gender === 2 ? '女' : '未知'"
|
||||
@@ -29,9 +29,9 @@
|
||||
@click="handleOpenDialog()"
|
||||
/>
|
||||
<wd-cell title="用户名" :value="userProfile.username" />
|
||||
<wd-cell title="部门" :value="userProfile.deptName" />
|
||||
<wd-cell title="角色" :value="userProfile.roleNames" />
|
||||
<wd-cell title="创建日期" :value="userProfile.createTime" />
|
||||
<wd-cell title="部门" :value="userProfile.dept_name" />
|
||||
<wd-cell title="角色" :value="userProfile.roleNames?.join(', ')" />
|
||||
<wd-cell title="创建日期" :value="userProfile.created_at" />
|
||||
</wd-cell-group>
|
||||
</wd-card>
|
||||
|
||||
@@ -43,12 +43,12 @@
|
||||
<wd-form ref="userProfileFormRef" :model="userProfileForm" custom-class="edit-form">
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
v-model="userProfileForm.nickname"
|
||||
v-model="userProfileForm.name"
|
||||
label="昵称"
|
||||
label-width="160rpx"
|
||||
placeholder="请输入昵称"
|
||||
prop="nickname"
|
||||
:rules="rules.nickname"
|
||||
prop="name"
|
||||
:rules="rules.name"
|
||||
/>
|
||||
<wd-cell title="性别" title-width="160rpx" center prop="gender" :rules="rules.gender">
|
||||
<wd-radio-group v-model="userProfileForm.gender" shape="button" class="ef-radio-group">
|
||||
@@ -65,17 +65,18 @@
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import UserAPI, { type UserProfileVO, UserProfileForm } from "@/api/user";
|
||||
import FileAPI, { type FileInfo } from "@/api/file";
|
||||
import UserAPI, { type UserInfo } from "@/api/user";
|
||||
import FileAPI from "@/api/file";
|
||||
import { checkLogin } from "@/utils/auth";
|
||||
import type { FormRules } from "wot-design-uni/components/wd-form/types";
|
||||
|
||||
const originalSrc = ref<string>(""); //选取的原图路径
|
||||
const avatarShow = ref<boolean>(false); //显示头像裁剪
|
||||
const userProfile = ref<UserProfileVO>(); //用户信息
|
||||
const userProfile = ref<UserInfo>(); //用户信息
|
||||
|
||||
/** 加载用户信息 */
|
||||
const loadUserProfile = async () => {
|
||||
userProfile.value = await UserAPI.getProfile();
|
||||
userProfile.value = await UserAPI.getCurrentUserInfo();
|
||||
};
|
||||
|
||||
// 头像选择
|
||||
@@ -91,12 +92,21 @@ function avatarUpload() {
|
||||
// 头像裁剪完成
|
||||
function handleAvatarConfirm(event: any) {
|
||||
const { tempFilePath } = event;
|
||||
FileAPI.upload(tempFilePath).then((fileInfo: FileInfo) => {
|
||||
const avatarForm: UserProfileForm = {
|
||||
avatar: fileInfo.url,
|
||||
FileAPI.upload(tempFilePath).then((fileInfo: UploadFileResult) => {
|
||||
const avatarForm = {
|
||||
name: userProfile.value?.name || "",
|
||||
gender: userProfile.value?.gender || 1,
|
||||
mobile: userProfile.value?.mobile || "",
|
||||
email: userProfile.value?.email || "",
|
||||
username: userProfile.value?.username || "",
|
||||
dept_name: userProfile.value?.dept_name || "",
|
||||
positions: userProfile.value?.positions || [],
|
||||
roles: userProfile.value?.roles || [],
|
||||
avatar: fileInfo.file_url,
|
||||
created_at: userProfile.value?.created_at || "",
|
||||
};
|
||||
// 头像路径保存至后端
|
||||
UserAPI.updateProfile(avatarForm).then(() => {
|
||||
UserAPI.updateCurrentUserInfo(avatarForm).then(() => {
|
||||
uni.showToast({ title: "头像上传成功", icon: "none" });
|
||||
loadUserProfile();
|
||||
});
|
||||
@@ -104,16 +114,19 @@ function handleAvatarConfirm(event: any) {
|
||||
}
|
||||
|
||||
// 本页面中所有的校验规则
|
||||
const rules = reactive({
|
||||
nickname: [{ required: true, message: "请填写昵称" }],
|
||||
const rules: FormRules = {
|
||||
name: [{ required: true, message: "请填写昵称" }],
|
||||
gender: [{ required: true, message: "请选择性别" }],
|
||||
});
|
||||
};
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const userProfileForm = reactive<UserProfileForm>({});
|
||||
const userProfileForm = reactive<{
|
||||
name?: string;
|
||||
gender?: number;
|
||||
}>({});
|
||||
const userProfileFormRef = ref();
|
||||
|
||||
/**
|
||||
@@ -123,15 +136,26 @@ const userProfileFormRef = ref();
|
||||
const handleOpenDialog = () => {
|
||||
dialog.visible = true;
|
||||
// 初始化表单数据
|
||||
userProfileForm.nickname = userProfile.value?.nickname;
|
||||
userProfileForm.gender = userProfile.value?.gender;
|
||||
userProfileForm.name = userProfile.value?.name || "";
|
||||
userProfileForm.gender = userProfile.value?.gender || 1;
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
function handleSubmit() {
|
||||
userProfileFormRef.value.validate().then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
UserAPI.updateProfile(userProfileForm).then(() => {
|
||||
UserAPI.updateCurrentUserInfo({
|
||||
name: userProfileForm.name,
|
||||
gender: userProfileForm.gender,
|
||||
mobile: userProfile.value?.mobile || "",
|
||||
email: userProfile.value?.email || "",
|
||||
username: userProfile.value?.username || "",
|
||||
dept_name: userProfile.value?.dept_name || "",
|
||||
positions: userProfile.value?.positions || [],
|
||||
roles: userProfile.value?.roles || [],
|
||||
avatar: userProfile.value?.avatar || "",
|
||||
created_at: userProfile.value?.created_at || "",
|
||||
}).then(() => {
|
||||
uni.showToast({ title: "账号资料修改成功", icon: "none" });
|
||||
dialog.visible = false;
|
||||
loadUserProfile();
|
||||
|
||||
@@ -11,18 +11,6 @@
|
||||
is-link
|
||||
@click="handleOpenDialog(DialogType.PASSWORD)"
|
||||
/>
|
||||
<wd-cell
|
||||
title="绑定手机"
|
||||
:value="userProfile?.mobile"
|
||||
is-link
|
||||
@click="handleOpenDialog(DialogType.MOBILE)"
|
||||
/>
|
||||
<wd-cell
|
||||
title="绑定邮箱"
|
||||
:value="userProfile?.email ? userProfile.email : '未绑定邮箱'"
|
||||
is-link
|
||||
@click="handleOpenDialog(DialogType.EMAIL)"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-card>
|
||||
|
||||
@@ -36,33 +24,33 @@
|
||||
>
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
v-model="passwordChangeForm.oldPassword"
|
||||
v-model="passwordChangeForm.old_password"
|
||||
label="原密码"
|
||||
label-width="160rpx"
|
||||
show-password
|
||||
clearable
|
||||
placeholder="请输入原密码"
|
||||
prop="oldPassword"
|
||||
prop="old_password"
|
||||
:rules="rules.oldPassword"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="passwordChangeForm.newPassword"
|
||||
v-model="passwordChangeForm.new_password"
|
||||
label="新密码"
|
||||
label-width="160rpx"
|
||||
show-password
|
||||
clearable
|
||||
placeholder="请输入新密码"
|
||||
prop="newPassword"
|
||||
prop="new_password"
|
||||
:rules="rules.newPassword"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="passwordChangeForm.confirmPassword"
|
||||
v-model="passwordChangeForm.confirm_password"
|
||||
label="确认密码"
|
||||
label-width="160rpx"
|
||||
show-password
|
||||
clearable
|
||||
placeholder="请确认新密码"
|
||||
prop="confirmPassword"
|
||||
prop="confirm_password"
|
||||
:rules="rules.confirmPassword"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
@@ -70,102 +58,17 @@
|
||||
<wd-button type="primary" size="large" block @click="handleSubmit">提交</wd-button>
|
||||
</view>
|
||||
</wd-form>
|
||||
<wd-form
|
||||
v-if="dialog.type === DialogType.MOBILE"
|
||||
ref="mobileBindingFormRef"
|
||||
:model="mobileBindingForm"
|
||||
custom-class="edit-form"
|
||||
>
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
v-model="mobileBindingForm.mobile"
|
||||
label="手机号码"
|
||||
label-width="160rpx"
|
||||
clearable
|
||||
placeholder="请输入手机号码"
|
||||
prop="mobile"
|
||||
:rules="rules.mobile"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="mobileBindingForm.code"
|
||||
label="验证码"
|
||||
label-width="160rpx"
|
||||
clearable
|
||||
placeholder="请输入验证码"
|
||||
prop="code"
|
||||
:rules="rules.code"
|
||||
>
|
||||
<template #suffix>
|
||||
<wd-button
|
||||
plain
|
||||
:disabled="mobileCountdown > 0"
|
||||
@click="handleSendVerificationCode('MOBILE')"
|
||||
>
|
||||
{{ mobileCountdown > 0 ? `${mobileCountdown}s后重新发送` : "发送验证码" }}
|
||||
</wd-button>
|
||||
</template>
|
||||
</wd-input>
|
||||
</wd-cell-group>
|
||||
<view class="p-6">
|
||||
<wd-button type="primary" size="large" block @click="handleSubmit">提交</wd-button>
|
||||
</view>
|
||||
</wd-form>
|
||||
<wd-form
|
||||
v-if="dialog.type === DialogType.EMAIL"
|
||||
ref="emailBindingFormRef"
|
||||
:model="emailBindingForm"
|
||||
custom-class="edit-form"
|
||||
>
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
v-model="emailBindingForm.email"
|
||||
label="邮箱"
|
||||
label-width="160rpx"
|
||||
clearable
|
||||
placeholder="请输入邮箱"
|
||||
prop="email"
|
||||
:rules="rules.email"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="emailBindingForm.code"
|
||||
label="验证码"
|
||||
label-width="160rpx"
|
||||
clearable
|
||||
placeholder="请输入验证码"
|
||||
prop="code"
|
||||
:rules="rules.code"
|
||||
>
|
||||
<template #suffix>
|
||||
<wd-button
|
||||
plain
|
||||
:disabled="emailCountdown > 0"
|
||||
@click="handleSendVerificationCode('EMAIL')"
|
||||
>
|
||||
{{ emailCountdown > 0 ? `${emailCountdown}s后重新发送` : "发送验证码" }}
|
||||
</wd-button>
|
||||
</template>
|
||||
</wd-input>
|
||||
</wd-cell-group>
|
||||
<view class="p-6">
|
||||
<wd-button type="primary" size="large" block @click="handleSubmit">提交</wd-button>
|
||||
</view>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import UserAPI, {
|
||||
PasswordChangeForm,
|
||||
MobileBindingForm,
|
||||
EmailBindingForm,
|
||||
UserProfileVO,
|
||||
} from "@/api/user";
|
||||
import UserAPI, { PasswordChangeForm, UserInfo } from "@/api/user";
|
||||
|
||||
const validatorConfirmPassword = (value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject("请确认密码");
|
||||
} else {
|
||||
if (value !== passwordChangeForm.newPassword) {
|
||||
if (value !== passwordChangeForm.new_password) {
|
||||
return Promise.reject("两次输入的密码不一致");
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
@@ -190,8 +93,6 @@ const rules = reactive({
|
||||
|
||||
enum DialogType {
|
||||
PASSWORD = "password",
|
||||
MOBILE = "mobile",
|
||||
EMAIL = "email",
|
||||
}
|
||||
|
||||
const dialog = reactive({
|
||||
@@ -199,19 +100,13 @@ const dialog = reactive({
|
||||
type: "" as DialogType, // 修改账号资料,修改密码、绑定手机、绑定邮箱
|
||||
});
|
||||
|
||||
const userProfile = ref<UserProfileVO>(); //用户信息
|
||||
const passwordChangeForm = reactive<PasswordChangeForm>({});
|
||||
const mobileBindingForm = reactive<MobileBindingForm>({});
|
||||
const emailBindingForm = reactive<EmailBindingForm>({});
|
||||
const userProfile = ref<UserInfo>(); //用户信息
|
||||
const passwordChangeForm = reactive<PasswordChangeForm>({
|
||||
old_password: "",
|
||||
new_password: "",
|
||||
confirm_password: "",
|
||||
});
|
||||
const passwordChangeFormRef = ref();
|
||||
const mobileBindingFormRef = ref();
|
||||
const emailBindingFormRef = ref();
|
||||
|
||||
const mobileCountdown = ref(0);
|
||||
const mobileTimer = ref<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
const emailCountdown = ref(0);
|
||||
const emailTimer = ref<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
// 处理返回按钮点击
|
||||
const handleBack = () => {
|
||||
@@ -220,7 +115,7 @@ const handleBack = () => {
|
||||
|
||||
/** 加载用户信息 */
|
||||
const loadUserProfile = async () => {
|
||||
userProfile.value = await UserAPI.getProfile();
|
||||
userProfile.value = await UserAPI.getCurrentUserInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -232,59 +127,10 @@ const handleOpenDialog = (type: DialogType) => {
|
||||
dialog.visible = true;
|
||||
switch (type) {
|
||||
case DialogType.PASSWORD:
|
||||
passwordChangeForm.oldPassword = "";
|
||||
passwordChangeForm.newPassword = "";
|
||||
passwordChangeForm.confirmPassword = "";
|
||||
passwordChangeForm.old_password = "";
|
||||
passwordChangeForm.new_password = "";
|
||||
passwordChangeForm.confirm_password = "";
|
||||
break;
|
||||
case DialogType.MOBILE:
|
||||
mobileBindingForm.mobile = "";
|
||||
mobileBindingForm.code = "";
|
||||
break;
|
||||
case DialogType.EMAIL:
|
||||
emailBindingForm.email = "";
|
||||
emailBindingForm.code = "";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
*
|
||||
* @param contactType 联系方式类型 MOBILE: 手机号码 EMAIL: 邮箱
|
||||
*/
|
||||
const handleSendVerificationCode = async (contactType: string) => {
|
||||
if (contactType === "MOBILE") {
|
||||
mobileBindingFormRef.value.validate("mobile").then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
UserAPI.sendVerificationCode(mobileBindingForm.mobile!, "MOBILE").then(() => {
|
||||
uni.showToast({ title: "验证码已发送", icon: "none" });
|
||||
mobileCountdown.value = 60;
|
||||
mobileTimer.value = setInterval(() => {
|
||||
if (mobileCountdown.value > 0) {
|
||||
mobileCountdown.value -= 1;
|
||||
} else {
|
||||
clearInterval(mobileTimer.value!);
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (contactType === "EMAIL") {
|
||||
emailBindingFormRef.value.validate("email").then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
UserAPI.sendVerificationCode(emailBindingForm.email!, "EMAIL").then(() => {
|
||||
uni.showToast({ title: "验证码已发送", icon: "none" });
|
||||
emailCountdown.value = 60;
|
||||
emailTimer.value = setInterval(() => {
|
||||
if (emailCountdown.value > 0) {
|
||||
emailCountdown.value -= 1;
|
||||
} else {
|
||||
clearInterval(emailTimer.value!);
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -293,32 +139,12 @@ function handleSubmit() {
|
||||
if (dialog.type === DialogType.PASSWORD) {
|
||||
passwordChangeFormRef.value.validate().then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
UserAPI.changePassword(passwordChangeForm).then(() => {
|
||||
UserAPI.changeCurrentUserPassword(passwordChangeForm).then(() => {
|
||||
uni.showToast({ title: "密码修改成功", icon: "none" });
|
||||
dialog.visible = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (dialog.type === DialogType.MOBILE) {
|
||||
mobileBindingFormRef.value.validate().then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
UserAPI.bindMobile(mobileBindingForm).then(() => {
|
||||
uni.showToast({ title: "手机号绑定成功", icon: "none" });
|
||||
dialog.visible = false;
|
||||
loadUserProfile();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (dialog.type === DialogType.EMAIL) {
|
||||
emailBindingFormRef.value.validate().then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
UserAPI.bindEmail(emailBindingForm).then(() => {
|
||||
uni.showToast({ title: "邮箱绑定成功", icon: "none" });
|
||||
dialog.visible = false;
|
||||
loadUserProfile();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@ const startTest = async () => {
|
||||
// #ifndef H5
|
||||
await request({
|
||||
url: "/api/v1/auth/captcha",
|
||||
method: "GET",
|
||||
timeout: 5000,
|
||||
});
|
||||
// #endif
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"name": "work",
|
||||
"name": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台"
|
||||
},
|
||||
|
||||
-23
File diff suppressed because one or more lines are too long
Vendored
-23
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 243 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 2.5 MiB |
@@ -6,7 +6,7 @@ import AuthAPI, {
|
||||
type LogoutBody,
|
||||
} from "@/api/auth";
|
||||
import UserAPI, { type UserInfo } from "@/api/user";
|
||||
import { setAccessToken, clearTokens } from "@/utils/auth";
|
||||
import { getAccessToken, setAccessToken, clearTokens } from "@/utils/auth";
|
||||
import { getUserInfo, setUserInfo } from "@/utils/storage";
|
||||
import { USER_INFO_KEY } from "@/constants";
|
||||
import { Storage } from "@/utils/storage";
|
||||
@@ -76,9 +76,12 @@ export const useUserStore = defineStore("user", () => {
|
||||
};
|
||||
|
||||
// 登出
|
||||
const logout = async (data: LogoutBody) => {
|
||||
const logout = async () => {
|
||||
try {
|
||||
await AuthAPI.logout(data); // 调用后台注销接口
|
||||
const logoutBody: LogoutBody = {
|
||||
token: getAccessToken(),
|
||||
};
|
||||
await AuthAPI.logout(logoutBody); // 调用后台注销接口
|
||||
} catch (error) {
|
||||
console.error("登出失败", error);
|
||||
} finally {
|
||||
|
||||
@@ -7,5 +7,5 @@ body,
|
||||
}
|
||||
|
||||
.app-container {
|
||||
padding: 0 10rpx;
|
||||
padding: 10rpx 10rpx;
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -56,7 +56,7 @@ declare global {
|
||||
/**
|
||||
* 上传文件返回
|
||||
*/
|
||||
interface UploadFilePath {
|
||||
interface UploadFileResult {
|
||||
file_path: string;
|
||||
file_name: string;
|
||||
origin_name: string;
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// Type definitions for vite-plugin-uni-pages
|
||||
// This file provides type definitions for the uni-pages plugin
|
||||
|
||||
declare module '@uni-helper/vite-plugin-uni-pages' {
|
||||
import { Plugin } from 'vite'
|
||||
|
||||
export interface UniPagesOptions {
|
||||
/**
|
||||
* Subpackage directory
|
||||
* @default 'src/pages'
|
||||
*/
|
||||
subPackages?: string[]
|
||||
|
||||
/**
|
||||
* Global style file
|
||||
*/
|
||||
globalStyle?: Record<string, any>
|
||||
|
||||
/**
|
||||
* Tabbar configuration
|
||||
*/
|
||||
tabBar?: Record<string, any>
|
||||
|
||||
/**
|
||||
* Whether to merge pages.json
|
||||
* @default true
|
||||
*/
|
||||
mergePages?: boolean
|
||||
|
||||
/**
|
||||
* Whether to use uni-cloud-router
|
||||
* @default false
|
||||
*/
|
||||
uniCloudRouter?: boolean
|
||||
}
|
||||
|
||||
export default function UniPages(options?: UniPagesOptions): Plugin
|
||||
}
|
||||
|
||||
// Augment Uni namespace for type-safe routing
|
||||
declare global {
|
||||
namespace UniNamespace {
|
||||
interface NavigateToOptions {
|
||||
url: string
|
||||
}
|
||||
|
||||
interface RedirectToOptions {
|
||||
url: string
|
||||
}
|
||||
|
||||
interface SwitchTabOptions {
|
||||
url: string
|
||||
}
|
||||
|
||||
interface ReLaunchOptions {
|
||||
url: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ import { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY } from "@/constants";
|
||||
* 获取访问令牌
|
||||
* @returns 返回访问令牌,如果不存在则返回null
|
||||
*/
|
||||
export function getAccessToken(): string | null {
|
||||
return Storage.get<string>(ACCESS_TOKEN_KEY) || null;
|
||||
export function getAccessToken(): string {
|
||||
return Storage.get<string>(ACCESS_TOKEN_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +132,7 @@ export function requireLogin(): void {
|
||||
if (!accessToken || !userStore.userInfo) {
|
||||
// 清除可能存在的无效状态
|
||||
clearTokens();
|
||||
userStore.logout({ token: accessToken || "" });
|
||||
userStore.logout();
|
||||
|
||||
// 跳转到登录页
|
||||
uni.reLaunch({
|
||||
|
||||
Reference in New Issue
Block a user