feat(admin): 模板精简+请求层适配 Django JWT
- 克隆 art-design-pro v3.0.2(GitHub depth1),rm .git,pnpm clean:dev 精简演示页(92 文件) - .env.development/.env.production 均设 VITE_API_URL=/api,代理目标 127.0.0.1:8000(移除 Apifox Mock) - http 封装信封适配:code===200 解包 data;msg/message 字段兼容(拦截器/error/类型) - /api/admin/auth/token 登录适配(access->Bearer token, refresh->refreshToken);/api/admin/auth/me 映射 UserInfo(userName/roles/is_admin) - 登录页演示账号替换为真实 admin;logOut 按路由名判登录页修复 redirect 嵌套 - 根 .gitignore 补 !admin/.env(模板公共 env 非密钥,需入库) - 验证:Playwright 全链路(错误密码 401 留登页/正确登录进工作台/登出/受保护路由重定向/redirect 回跳)+ pnpm build(vue-tsc 通过,产物无 mock 域名) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
<!-- 个人中心页面 -->
|
||||
<template>
|
||||
<div class="w-full h-full p-0 bg-transparent border-none shadow-none">
|
||||
<div class="relative flex-b mt-2.5 max-md:block max-md:mt-1">
|
||||
<div class="w-112 mr-5 max-md:w-full max-md:mr-0">
|
||||
<div class="art-card-sm relative p-9 pb-6 overflow-hidden text-center">
|
||||
<img class="absolute top-0 left-0 w-full h-50 object-cover" src="@imgs/user/bg.webp" />
|
||||
<img
|
||||
class="relative z-10 w-20 h-20 mt-30 mx-auto object-cover border-2 border-white rounded-full"
|
||||
src="@imgs/user/avatar.webp"
|
||||
/>
|
||||
<h2 class="mt-5 text-xl font-normal">{{ userInfo.userName }}</h2>
|
||||
<p class="mt-5 text-sm">专注于用户体验跟视觉设计</p>
|
||||
|
||||
<div class="w-75 mx-auto mt-7.5 text-left">
|
||||
<div class="mt-2.5">
|
||||
<ArtSvgIcon icon="ri:mail-line" class="text-g-700" />
|
||||
<span class="ml-2 text-sm">jdkjjfnndf@mall.com</span>
|
||||
</div>
|
||||
<div class="mt-2.5">
|
||||
<ArtSvgIcon icon="ri:user-3-line" class="text-g-700" />
|
||||
<span class="ml-2 text-sm">交互专家</span>
|
||||
</div>
|
||||
<div class="mt-2.5">
|
||||
<ArtSvgIcon icon="ri:map-pin-line" class="text-g-700" />
|
||||
<span class="ml-2 text-sm">广东省深圳市</span>
|
||||
</div>
|
||||
<div class="mt-2.5">
|
||||
<ArtSvgIcon icon="ri:dribbble-fill" class="text-g-700" />
|
||||
<span class="ml-2 text-sm">字节跳动-某某平台部-UED</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-10">
|
||||
<h3 class="text-sm font-medium">标签</h3>
|
||||
<div class="flex flex-wrap justify-center mt-3.5">
|
||||
<div
|
||||
v-for="item in lableList"
|
||||
:key="item"
|
||||
class="py-1 px-1.5 mr-2.5 mb-2.5 text-xs border border-g-300 rounded"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden max-md:w-full max-md:mt-3.5">
|
||||
<div class="art-card-sm">
|
||||
<h1 class="p-4 text-xl font-normal border-b border-g-300">基本设置</h1>
|
||||
|
||||
<ElForm
|
||||
:model="form"
|
||||
class="box-border p-5 [&>.el-row_.el-form-item]:w-[calc(50%-10px)] [&>.el-row_.el-input]:w-full [&>.el-row_.el-select]:w-full"
|
||||
ref="ruleFormRef"
|
||||
:rules="rules"
|
||||
label-width="86px"
|
||||
label-position="top"
|
||||
>
|
||||
<ElRow>
|
||||
<ElFormItem label="姓名" prop="realName">
|
||||
<ElInput v-model="form.realName" :disabled="!isEdit" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex" class="ml-5">
|
||||
<ElSelect v-model="form.sex" placeholder="Select" :disabled="!isEdit">
|
||||
<ElOption
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElRow>
|
||||
|
||||
<ElRow>
|
||||
<ElFormItem label="昵称" prop="nikeName">
|
||||
<ElInput v-model="form.nikeName" :disabled="!isEdit" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="邮箱" prop="email" class="ml-5">
|
||||
<ElInput v-model="form.email" :disabled="!isEdit" />
|
||||
</ElFormItem>
|
||||
</ElRow>
|
||||
|
||||
<ElRow>
|
||||
<ElFormItem label="手机" prop="mobile">
|
||||
<ElInput v-model="form.mobile" :disabled="!isEdit" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="地址" prop="address" class="ml-5">
|
||||
<ElInput v-model="form.address" :disabled="!isEdit" />
|
||||
</ElFormItem>
|
||||
</ElRow>
|
||||
|
||||
<ElFormItem label="个人介绍" prop="des" class="h-32">
|
||||
<ElInput type="textarea" :rows="4" v-model="form.des" :disabled="!isEdit" />
|
||||
</ElFormItem>
|
||||
|
||||
<div class="flex-c justify-end [&_.el-button]:!w-27.5">
|
||||
<ElButton type="primary" class="w-22.5" v-ripple @click="edit">
|
||||
{{ isEdit ? '保存' : '编辑' }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</ElForm>
|
||||
</div>
|
||||
|
||||
<div class="art-card-sm my-5">
|
||||
<h1 class="p-4 text-xl font-normal border-b border-g-300">更改密码</h1>
|
||||
|
||||
<ElForm :model="pwdForm" class="box-border p-5" label-width="86px" label-position="top">
|
||||
<ElFormItem label="当前密码" prop="password">
|
||||
<ElInput
|
||||
v-model="pwdForm.password"
|
||||
type="password"
|
||||
:disabled="!isEditPwd"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="新密码" prop="newPassword">
|
||||
<ElInput
|
||||
v-model="pwdForm.newPassword"
|
||||
type="password"
|
||||
:disabled="!isEditPwd"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="确认新密码" prop="confirmPassword">
|
||||
<ElInput
|
||||
v-model="pwdForm.confirmPassword"
|
||||
type="password"
|
||||
:disabled="!isEditPwd"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<div class="flex-c justify-end [&_.el-button]:!w-27.5">
|
||||
<ElButton type="primary" class="w-22.5" v-ripple @click="editPwd">
|
||||
{{ isEditPwd ? '保存' : '编辑' }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</ElForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'UserCenter' })
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.getUserInfo)
|
||||
|
||||
const isEdit = ref(false)
|
||||
const isEditPwd = ref(false)
|
||||
const date = ref('')
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 用户信息表单
|
||||
*/
|
||||
const form = reactive({
|
||||
realName: 'John Snow',
|
||||
nikeName: '皮卡丘',
|
||||
email: '59301283@mall.com',
|
||||
mobile: '18888888888',
|
||||
address: '广东省深圳市宝安区西乡街道101栋201',
|
||||
sex: '2',
|
||||
des: 'Art Design Pro 是一款兼具设计美学与高效开发的后台系统.'
|
||||
})
|
||||
|
||||
/**
|
||||
* 密码修改表单
|
||||
*/
|
||||
const pwdForm = reactive({
|
||||
password: '123456',
|
||||
newPassword: '123456',
|
||||
confirmPassword: '123456'
|
||||
})
|
||||
|
||||
/**
|
||||
* 表单验证规则
|
||||
*/
|
||||
const rules = reactive<FormRules>({
|
||||
realName: [
|
||||
{ required: true, message: '请输入姓名', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
nikeName: [
|
||||
{ required: true, message: '请输入昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
|
||||
mobile: [{ required: true, message: '请输入手机号码', trigger: 'blur' }],
|
||||
address: [{ required: true, message: '请输入地址', trigger: 'blur' }],
|
||||
sex: [{ required: true, message: '请选择性别', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/**
|
||||
* 性别选项
|
||||
*/
|
||||
const options = [
|
||||
{ value: '1', label: '男' },
|
||||
{ value: '2', label: '女' }
|
||||
]
|
||||
|
||||
/**
|
||||
* 用户标签列表
|
||||
*/
|
||||
const lableList: Array<string> = ['专注设计', '很有想法', '辣~', '大长腿', '川妹子', '海纳百川']
|
||||
|
||||
onMounted(() => {
|
||||
getDate()
|
||||
})
|
||||
|
||||
/**
|
||||
* 根据当前时间获取问候语
|
||||
*/
|
||||
const getDate = () => {
|
||||
const h = new Date().getHours()
|
||||
|
||||
if (h >= 6 && h < 9) date.value = '早上好'
|
||||
else if (h >= 9 && h < 11) date.value = '上午好'
|
||||
else if (h >= 11 && h < 13) date.value = '中午好'
|
||||
else if (h >= 13 && h < 18) date.value = '下午好'
|
||||
else if (h >= 18 && h < 24) date.value = '晚上好'
|
||||
else date.value = '很晚了,早点睡'
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换用户信息编辑状态
|
||||
*/
|
||||
const edit = () => {
|
||||
isEdit.value = !isEdit.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换密码编辑状态
|
||||
*/
|
||||
const editPwd = () => {
|
||||
isEditPwd.value = !isEditPwd.value
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user