Files
tiantian-system/app/pages/admin/account.vue
赵忠林 f9e1286ab1 refactor(developer-config): 移除开发者配置页面相关代码和文档
- 删除应用配置页面及相关组件,重构路由为 /developer/config/[id].vue
- 移除开发者文档页面及其导航与样式实现
- 清理开发者侧功能完善工作日志文件
- 删除全局.gitignore配置文件,清理无用忽略规则
- 优化应用配置页面的参数读取和路由结构,解决刷新404问题
- 解决数据库配置唯一键冲突,调整保存逻辑避免重复插入
- 移除对后端配置加密字段的 secret 标记,修正加密异常问题
2026-04-09 07:35:34 +08:00

248 lines
8.0 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
definePageMeta({ layout: 'admin' })
const { user } = useUser()
// 个人信息数据
const profile = reactive({
username: 'admin',
nickname: '系统管理员',
email: 'admin@company.com',
phone: '138****8888',
department: '信息技术部',
position: '系统管理员',
joinDate: '2024-01-15',
lastLogin: '2026-04-09 07:00',
avatar: '',
})
// 安全设置
const securitySettings = reactive({
emailVerified: true,
phoneVerified: true,
twoFactorEnabled: false,
loginPwdChanged: '2026-03-15',
})
// 修改密码表单
const passwordForm = reactive({
oldPassword: '',
newPassword: '',
confirmPassword: '',
})
const passwordRules = {
newPassword: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
confirmPassword: [
{ required: true, message: '请确认新密码', trigger: 'blur' },
{
validator: (_rule: any, value: string) => {
if (value !== passwordForm.newPassword) {
return Promise.reject('两次输入的密码不一致')
}
return Promise.resolve()
},
trigger: 'blur',
},
],
}
// 操作日志
const loginHistory = ref([
{ time: '2026-04-09 07:00:00', ip: '192.168.1.100', device: 'Chrome / Windows 11', location: '广东深圳', status: '成功' },
{ time: '2026-04-08 18:30:00', ip: '192.168.1.100', device: 'Chrome / macOS', location: '广东深圳', status: '成功' },
{ time: '2026-04-08 09:15:00', ip: '192.168.1.101', device: 'Safari / iOS', location: '广东广州', status: '成功' },
{ time: '2026-04-07 16:45:00', ip: '10.0.0.1', device: 'Firefox / Ubuntu', location: '广东深圳', status: '成功' },
{ time: '2026-04-07 08:00:00', ip: '192.168.1.100', device: 'Chrome / Windows 11', location: '广东深圳', status: '成功' },
])
const activeTab = ref('profile')
</script>
<template>
<div class="account-page">
<a-tabs v-model:activeKey="activeTab" class="account-tabs">
<!-- 基本信息 -->
<a-tab-pane key="profile" tab="基本信息">
<a-row :gutter="24">
<a-col :xs="24" :lg="16">
<a-card title="个人信息" class="info-card">
<a-descriptions :column="{ xs: 1, sm: 2 }" bordered>
<a-descriptions-item label="用户名">{{ profile.username }}</a-descriptions-item>
<a-descriptions-item label="昵称">{{ profile.nickname }}</a-descriptions-item>
<a-descriptions-item label="邮箱">
{{ profile.email }}
<a-tag v-if="securitySettings.emailVerified" color="success" size="small">已认证</a-tag>
</a-descriptions-item>
<a-descriptions-item label="手机号">
{{ profile.phone }}
<a-tag v-if="securitySettings.phoneVerified" color="success" size="small">已认证</a-tag>
</a-descriptions-item>
<a-descriptions-item label="部门">{{ profile.department }}</a-descriptions-item>
<a-descriptions-item label="职位">{{ profile.position }}</a-descriptions-item>
<a-descriptions-item label="入职日期">{{ profile.joinDate }}</a-descriptions-item>
<a-descriptions-item label="上次登录">{{ profile.lastLogin }}</a-descriptions-item>
</a-descriptions>
<div class="mt-4">
<a-button type="primary">编辑资料</a-button>
</div>
</a-card>
</a-col>
<a-col :xs="24" :lg="8">
<a-card title="头像设置" class="avatar-card">
<div class="avatar-upload">
<a-avatar :size="100" :src="profile.avatar">
<template #icon><UserOutlined /></template>
</a-avatar>
<div class="mt-4">
<a-button size="small">更换头像</a-button>
<p class="text-xs text-gray-400 mt-2">支持 JPGPNG 格式文件小于 2MB</p>
</div>
</div>
</a-card>
</a-col>
</a-row>
</a-tab-pane>
<!-- 账号安全 -->
<a-tab-pane key="security" tab="账号安全">
<a-row :gutter="24">
<a-col :xs="24" :lg="16">
<!-- 登录密码 -->
<a-card title="登录密码" class="security-card mb-4">
<div class="security-item">
<div class="security-info">
<div class="security-title">登录密码</div>
<div class="security-desc">上次修改于 {{ securitySettings.loginPwdChanged }}</div>
</div>
<a-button>修改密码</a-button>
</div>
</a-card>
<!-- 邮箱绑定 -->
<a-card title="邮箱绑定" class="security-card mb-4">
<div class="security-item">
<div class="security-info">
<div class="security-title">{{ profile.email }}</div>
<div class="security-desc">
<a-tag v-if="securitySettings.emailVerified" color="success" size="small">已验证</a-tag>
<span v-else class="text-orange-500">未验证</span>
</div>
</div>
<a-button type="primary" ghost>更换邮箱</a-button>
</div>
</a-card>
<!-- 手机绑定 -->
<a-card title="手机绑定" class="security-card mb-4">
<div class="security-item">
<div class="security-info">
<div class="security-title">{{ profile.phone }}</div>
<div class="security-desc">
<a-tag v-if="securitySettings.phoneVerified" color="success" size="small">已验证</a-tag>
</div>
</div>
<a-button type="primary" ghost>更换手机</a-button>
</div>
</a-card>
<!-- 两步验证 -->
<a-card title="两步验证" class="security-card">
<div class="security-item">
<div class="security-info">
<div class="security-title">开启两步验证</div>
<div class="security-desc">启用后登录需输入手机验证码提升账号安全</div>
</div>
<a-switch v-model:checked="securitySettings.twoFactorEnabled" />
</div>
</a-card>
</a-col>
</a-row>
</a-tab-pane>
<!-- 登录历史 -->
<a-tab-pane key="history" tab="登录历史">
<a-card title="最近登录记录">
<a-table :dataSource="loginHistory" :pagination="false" rowKey="time" size="small">
<a-table-column title="登录时间" dataIndex="time" width="180" />
<a-table-column title="IP 地址" dataIndex="ip" width="140" />
<a-table-column title="设备" dataIndex="device" />
<a-table-column title="位置" dataIndex="location" width="100" />
<a-table-column title="状态" dataIndex="status" width="80" align="center">
<template #default="{ text }">
<a-tag :color="text === '成功' ? 'success' : 'error'">{{ text }}</a-tag>
</template>
</a-table-column>
</a-table>
</a-card>
</a-tab-pane>
</a-tabs>
</div>
</template>
<style scoped>
.account-page {
max-width: 1200px;
}
.account-tabs :deep(.ant-tabs-nav) {
margin-bottom: 20px;
}
.info-card,
.security-card,
.avatar-card {
border-radius: 8px;
}
.avatar-upload {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.security-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
}
.security-info {
flex: 1;
}
.security-title {
font-size: 15px;
font-weight: 500;
color: #1f2937;
margin-bottom: 4px;
}
.security-desc {
font-size: 13px;
color: #6b7280;
}
.text-xs {
font-size: 12px;
}
.text-gray-400 {
color: #9ca3af;
}
.text-orange-500 {
color: #f97316;
}
.mt-4 {
margin-top: 16px;
}
.mb-4 {
margin-bottom: 16px;
}
</style>