mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-08 22:57:11 +08:00
refactor: 优化个人中心部分代码
This commit is contained in:
@@ -8,7 +8,7 @@ export interface UserInfo {
|
||||
phone: string
|
||||
avatar: string
|
||||
pwdResetTime: string
|
||||
passwordExpired: boolean
|
||||
pwdExpired: boolean
|
||||
registrationDate: string
|
||||
deptName: string
|
||||
roles: string[]
|
||||
|
@@ -19,12 +19,12 @@ export function updateUserPassword(data: { oldPassword: string; newPassword: str
|
||||
}
|
||||
|
||||
/** @desc 修改手机号 */
|
||||
export function updateUserPhone(data: { newPhone: string; captcha: string; currentPassword: string }) {
|
||||
export function updateUserPhone(data: { phone: string; captcha: string; oldPassword: string }) {
|
||||
return http.patch(`${BASE_URL}/phone`, data)
|
||||
}
|
||||
|
||||
/** @desc 修改邮箱 */
|
||||
export function updateUserEmail(data: { newEmail: string; captcha: string; currentPassword: string }) {
|
||||
export function updateUserEmail(data: { email: string; captcha: string; oldPassword: string }) {
|
||||
return http.patch(`${BASE_URL}/email`, data)
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 846 B |
@@ -51,7 +51,7 @@
|
||||
</a-row>
|
||||
<template #content>
|
||||
<a-doption @click="router.push('/setting/profile')">
|
||||
<span>账号管理</span>
|
||||
<span>个人中心</span>
|
||||
</a-doption>
|
||||
<a-divider :margin="0" />
|
||||
<a-doption @click="logout">
|
||||
@@ -98,16 +98,14 @@ const logout = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
checkPasswordExpired()
|
||||
})
|
||||
|
||||
const checkPasswordExpired = () => {
|
||||
if (!userStore.passwordExpiredShow || !userStore.userInfo.passwordExpired) {
|
||||
if (!userStore.pwdExpiredShow || !userStore.userInfo.pwdExpired) {
|
||||
return
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '密码已过期,是否去修改?',
|
||||
content: '密码已过期,需要跳转到修改密码页面?',
|
||||
hideCancel: false,
|
||||
closable: true,
|
||||
onBeforeOk: async () => {
|
||||
@@ -120,10 +118,14 @@ const checkPasswordExpired = () => {
|
||||
},
|
||||
onCancel: () => {
|
||||
// 当前登录会话不再提示
|
||||
userStore.passwordExpiredShow = false
|
||||
userStore.pwdExpiredShow = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkPasswordExpired()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -61,7 +61,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: '/setting/profile',
|
||||
name: 'SettingProfile',
|
||||
component: () => import('@/views/setting/profile/index.vue'),
|
||||
meta: { title: '账号管理', showInTabs: false }
|
||||
meta: { title: '个人中心', showInTabs: false }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ const storeSetup = () => {
|
||||
phone: '',
|
||||
avatar: '',
|
||||
pwdResetTime: '',
|
||||
passwordExpired: false,
|
||||
pwdExpired: false,
|
||||
registrationDate: '',
|
||||
deptName: '',
|
||||
roles: [],
|
||||
@@ -37,7 +37,7 @@ const storeSetup = () => {
|
||||
const avatar = computed(() => userInfo.avatar)
|
||||
|
||||
const token = ref(getToken() || '')
|
||||
const passwordExpiredShow = ref<boolean>(true)
|
||||
const pwdExpiredShow = ref<boolean>(true)
|
||||
const roles = ref<string[]>([]) // 当前用户角色
|
||||
const permissions = ref<string[]>([]) // 当前角色权限标识集合
|
||||
|
||||
@@ -91,7 +91,7 @@ const storeSetup = () => {
|
||||
const logoutCallBack = async () => {
|
||||
roles.value = []
|
||||
permissions.value = []
|
||||
passwordExpiredShow.value = true
|
||||
pwdExpiredShow.value = true
|
||||
resetToken()
|
||||
resetRouter()
|
||||
}
|
||||
@@ -114,7 +114,7 @@ const storeSetup = () => {
|
||||
token,
|
||||
roles,
|
||||
permissions,
|
||||
passwordExpiredShow,
|
||||
pwdExpiredShow,
|
||||
accountLogin,
|
||||
emailLogin,
|
||||
phoneLogin,
|
||||
@@ -127,5 +127,5 @@ const storeSetup = () => {
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore('user', storeSetup, {
|
||||
persist: { paths: ['token', 'roles', 'permissions', 'passwordExpiredShow'], storage: localStorage }
|
||||
persist: { paths: ['token', 'roles', 'permissions', 'pwdExpiredShow'], storage: localStorage }
|
||||
})
|
||||
|
@@ -19,15 +19,21 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// import { getSmsCaptcha, getEmailCaptcha, updateUserEmail, updateUserPhone } from '@/apis'
|
||||
import { updateUserPassword } from '@/apis'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
// import { encryptByRsa } from '@/utils/encrypt'
|
||||
import { encryptByRsa } from '@/utils/encrypt'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { type Columns, GiForm } from '@/components/GiForm'
|
||||
import { useForm } from '@/hooks'
|
||||
import * as Regexp from '@/utils/regexp'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
|
||||
const verifyType = ref()
|
||||
const title = computed(() => (verifyType.value === 'phone' ? '修改手机号' : '修改邮箱'))
|
||||
const title = computed(
|
||||
() => `修改${verifyType.value === 'phone' ? '手机号' : verifyType.value === 'email' ? '邮箱' : '密码'}`
|
||||
)
|
||||
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||
|
||||
const options: Options = {
|
||||
@@ -39,7 +45,7 @@ const options: Options = {
|
||||
const columns: Columns = [
|
||||
{
|
||||
label: '手机号',
|
||||
field: 'newPhone',
|
||||
field: 'phone',
|
||||
type: 'input',
|
||||
rules: [
|
||||
{ required: true, message: '请输入手机号' },
|
||||
@@ -65,21 +71,50 @@ const columns: Columns = [
|
||||
label: '验证码',
|
||||
field: 'captcha',
|
||||
type: 'input',
|
||||
rules: [{ required: true, message: '请输入验证码' }]
|
||||
rules: [{ required: true, message: '请输入验证码' }],
|
||||
hide: () => {
|
||||
return !['phone', 'email'].includes(verifyType.value)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '当前密码',
|
||||
field: 'currentPassword',
|
||||
type: 'input',
|
||||
rules: [{ required: true, message: '请输入当前密码' }]
|
||||
field: 'oldPassword',
|
||||
type: 'input-password',
|
||||
rules: [{ required: true, message: '请输入当前密码' }],
|
||||
hide: () => {
|
||||
return !userInfo.value.pwdResetTime
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '新密码',
|
||||
field: 'newPassword',
|
||||
type: 'input-password',
|
||||
rules: [{ required: true, message: '请输入新密码' }],
|
||||
hide: () => {
|
||||
return verifyType.value !== 'password'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '确认新密码',
|
||||
field: 'rePassword',
|
||||
type: 'input-password',
|
||||
rules: [{ required: true, message: '请再次输入新密码' }],
|
||||
props: {
|
||||
placeholder: '请再次输入新密码'
|
||||
},
|
||||
hide: () => {
|
||||
return verifyType.value !== 'password'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const { form, resetForm } = useForm({
|
||||
newPhone: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
captcha: '',
|
||||
currentPassword: '',
|
||||
email: ''
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
rePassword: ''
|
||||
})
|
||||
|
||||
// 重置
|
||||
@@ -112,7 +147,7 @@ const onCaptcha = async () => {
|
||||
captchaBtnName.value = '发送中...'
|
||||
if (verifyType.value === 'phone') {
|
||||
// await getSmsCaptcha({
|
||||
// phone: form.newPhone
|
||||
// phone: form.phone
|
||||
// })
|
||||
} else if (verifyType.value === 'email') {
|
||||
// await getEmailCaptcha({
|
||||
@@ -138,24 +173,36 @@ const onCaptcha = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
// 保存
|
||||
const save = async () => {
|
||||
const isInvalid = await formRef.value?.formRef?.validate()
|
||||
if (isInvalid) return false
|
||||
try {
|
||||
if (verifyType.value === 'phone') {
|
||||
// await updateUserEmail({
|
||||
// newEmail: form.email,
|
||||
// await updateUserPhone({
|
||||
// phone: form.phone,
|
||||
// captcha: form.captcha,
|
||||
// currentPassword: encryptByRsa(form.currentPassword) as string
|
||||
// oldPassword: encryptByRsa(form.oldPassword) as string
|
||||
// })
|
||||
} else if (verifyType.value === 'email') {
|
||||
// await updateUserPhone({
|
||||
// newPhone: form.email,
|
||||
// await updateUserEmail({
|
||||
// email: form.email,
|
||||
// captcha: form.captcha,
|
||||
// currentPassword: encryptByRsa(form.currentPassword) as string
|
||||
// oldPassword: encryptByRsa(form.oldPassword) as string
|
||||
// })
|
||||
} else if (verifyType.value === 'password') {
|
||||
if (form.newPassword !== form.rePassword) {
|
||||
Message.error('两次新密码不一致')
|
||||
return false
|
||||
}
|
||||
if (form.newPassword === form.oldPassword) {
|
||||
Message.error('新密码与旧密码不能相同')
|
||||
return false
|
||||
}
|
||||
await updateUserPassword({
|
||||
oldPassword: encryptByRsa(form.oldPassword) || '',
|
||||
newPassword: encryptByRsa(form.newPassword) || ''
|
||||
})
|
||||
}
|
||||
Message.success('修改成功')
|
||||
// 修改成功后,重新获取用户信息
|
||||
|
@@ -1,178 +0,0 @@
|
||||
<template>
|
||||
<a-card title="密码策略" bordered class="gradient-card">
|
||||
<div class="item">
|
||||
<div class="icon-wrapper"><GiSvgIcon name="password" :size="26" /></div>
|
||||
<div class="info">
|
||||
<div class="info-top">
|
||||
<span class="label">登录密码</span>
|
||||
</div>
|
||||
<div class="info-desc">为了您的账号安全,建议定期修改密码</div>
|
||||
</div>
|
||||
<div class="btn-wrapper">
|
||||
<a-button class="btn" @click="onUpdate">修改</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="sub-text-wrapper">
|
||||
<div class="sub-text">
|
||||
密码至少包含
|
||||
<span class="sub-text-value">大写字母</span>
|
||||
<span class="sub-text-value">小写字母</span>
|
||||
<span class="sub-text-value">数字</span>
|
||||
<span class="sub-text-value" v-if="securityConfig.password_special_char.value == 1">特殊字符</span>
|
||||
</div>
|
||||
<div class="sub-text" v-if="securityConfig.password_contain_name.value == 1">
|
||||
密码不能包含<span class="sub-text-value">正反序用户名</span>
|
||||
</div>
|
||||
<div class="sub-text">
|
||||
密码长度至少
|
||||
<span class="sub-text-value">
|
||||
{{ securityConfig.password_min_length.value }}
|
||||
</span>
|
||||
位
|
||||
</div>
|
||||
<div class="sub-text">
|
||||
<div v-if="securityConfig.password_expiration_days.value == 0">未设置密码有效期</div>
|
||||
<div v-else>
|
||||
密码有效期
|
||||
<span class="sub-text-value">
|
||||
{{ securityConfig.password_expiration_days.value }}
|
||||
</span>
|
||||
天
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-text">
|
||||
连续密码错误可重试
|
||||
<span class="sub-text-value">
|
||||
{{ securityConfig.password_error_count.value }}
|
||||
</span>
|
||||
次
|
||||
</div>
|
||||
<div class="sub-text">
|
||||
超过错误密码重试次数账号将被锁定
|
||||
<span class="sub-text-value">
|
||||
{{ securityConfig.password_lock_minutes.value }}
|
||||
</span>
|
||||
分钟
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-modal v-model:visible="visible" title="修改密码" @before-ok="save" @close="reset">
|
||||
<GiForm ref="formRef" v-model="form" :options="options" :columns="columns" />
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { listOption, type OptionResp, type SecurityConfigResp, updateUserPassword } from '@/apis'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { encryptByRsa } from '@/utils/encrypt'
|
||||
import { type Columns, GiForm } from '@/components/GiForm'
|
||||
import { useForm } from '@/hooks'
|
||||
import { useUserStore } from '@/stores'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||
|
||||
const options: Options = {
|
||||
form: {},
|
||||
col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
|
||||
btns: { hide: true }
|
||||
}
|
||||
|
||||
const columns: Columns = [
|
||||
{
|
||||
label: '当前密码',
|
||||
field: 'oldPassword',
|
||||
type: 'input-password',
|
||||
rules: [{ required: true, message: '密码长度不正确', maxLength: 32, minLength: 6 }],
|
||||
hide: () => {
|
||||
return userInfo.pwdResetTime
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '新密码',
|
||||
field: 'newPassword',
|
||||
type: 'input-password',
|
||||
rules: [{ required: true, message: '密码长度不正确', maxLength: 32, minLength: 6 }]
|
||||
},
|
||||
{
|
||||
label: '确认新密码',
|
||||
field: 'rePassword',
|
||||
type: 'input-password',
|
||||
rules: [{ required: true, message: '密码长度不正确', maxLength: 32, minLength: 6 }],
|
||||
props: {
|
||||
placeholder: '请再次输入新密码'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const { form, resetForm } = useForm({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
rePassword: ''
|
||||
})
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
formRef.value?.formRef?.resetFields()
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const visible = ref(false)
|
||||
// 修改
|
||||
const onUpdate = async () => {
|
||||
reset()
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 保存
|
||||
const save = async () => {
|
||||
const isInvalid = await formRef.value?.formRef?.validate()
|
||||
if (isInvalid) return false
|
||||
if (form.newPassword !== form.rePassword) {
|
||||
Message.error('两次新密码不一致')
|
||||
return false
|
||||
}
|
||||
if (form.newPassword === form.oldPassword) {
|
||||
Message.error('新密码与旧密码不能相同')
|
||||
return false
|
||||
}
|
||||
try {
|
||||
await updateUserPassword({
|
||||
oldPassword: encryptByRsa(form.oldPassword) || '',
|
||||
newPassword: encryptByRsa(form.newPassword) || ''
|
||||
})
|
||||
Message.success('修改成功')
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const securityConfig = ref<SecurityConfigResp>({
|
||||
password_contain_name: {},
|
||||
password_error_count: {},
|
||||
password_expiration_days: {},
|
||||
password_lock_minutes: {},
|
||||
password_min_length: {},
|
||||
password_special_char: {},
|
||||
password_update_interval: {}
|
||||
})
|
||||
|
||||
// 查询列表数据
|
||||
const getDataList = async () => {
|
||||
const { data } = await listOption({ code: Object.keys(securityConfig.value) })
|
||||
securityConfig.value = data.reduce((obj: SecurityConfigResp, option: OptionResp) => {
|
||||
obj[option.code] = { ...option, value: parseInt(option.value) }
|
||||
return obj
|
||||
}, {})
|
||||
}
|
||||
onMounted(() => {
|
||||
getDataList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
109
src/views/setting/profile/Security.vue
Normal file
109
src/views/setting/profile/Security.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<a-card title="安全设置" bordered class="gradient-card">
|
||||
<div v-for="item in modeList" :key="item.title">
|
||||
<div class="item">
|
||||
<div class="icon-wrapper"><GiSvgIcon :name="item.icon" :size="26" /></div>
|
||||
<div class="info">
|
||||
<div class="info-top">
|
||||
<span class="label">{{ item.title }}</span>
|
||||
<span class="bind">
|
||||
<icon-check-circle-fill v-if="item.status" :size="14" class="success" />
|
||||
<icon-exclamation-circle-fill v-else :size="14" class="warning" />
|
||||
<span style="font-size: 12px" :class="item.status ? 'success' : 'warning'">{{ item.statusString }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-desc">
|
||||
<span class="value">{{ item.value }}</span>
|
||||
{{ item.subtitle }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-wrapper">
|
||||
<a-button
|
||||
v-if="item.jumpMode == 'modal'"
|
||||
class="btn"
|
||||
:type="item.status ? 'secondary' : 'primary'"
|
||||
@click="onUpdate(item.type, item.status)"
|
||||
>
|
||||
{{ ['password'].includes(item.type) || item.status ? '修改' : '绑定' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<VerifyModel ref="verifyModelRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { listOption, type OptionResp, type SecurityConfigResp } from '@/apis'
|
||||
import type { ModeItem } from '../type'
|
||||
import VerifyModel from '../components/VerifyModel.vue'
|
||||
import { useUserStore } from '@/stores'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
|
||||
const modeList = ref<ModeItem[]>([])
|
||||
modeList.value = [
|
||||
{
|
||||
title: '安全手机',
|
||||
icon: 'phone-color',
|
||||
value: `${userInfo.value.phone + ' ' || '手机号'}`,
|
||||
subtitle: `可用于身份验证、密码找回、通知接收`,
|
||||
type: 'phone',
|
||||
jumpMode: 'modal',
|
||||
status: !!userInfo.value.phone,
|
||||
statusString: userInfo.value.phone ? '已绑定' : '未绑定'
|
||||
},
|
||||
{
|
||||
title: '安全邮箱',
|
||||
icon: 'email-color',
|
||||
value: `${userInfo.value.email + ' ' || '邮箱'}`,
|
||||
subtitle: `可用于身份验证、密码找回、通知接收`,
|
||||
type: 'email',
|
||||
jumpMode: 'modal',
|
||||
status: !!userInfo.value.email,
|
||||
statusString: userInfo.value.email ? '已绑定' : '未绑定'
|
||||
},
|
||||
{
|
||||
title: '登录密码',
|
||||
icon: 'password-color',
|
||||
subtitle: userInfo.value.pwdResetTime ? `为了您的账号安全,建议定期修改密码` : '请设置密码,可通过账号+密码登录',
|
||||
type: 'password',
|
||||
jumpMode: 'modal',
|
||||
status: !!userInfo.value.pwdResetTime,
|
||||
statusString: userInfo.value.pwdResetTime ? '已设置' : '未设置'
|
||||
}
|
||||
]
|
||||
|
||||
const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
|
||||
// 修改
|
||||
const onUpdate = (type: string) => {
|
||||
verifyModelRef.value?.open(type)
|
||||
}
|
||||
|
||||
const securityConfig = ref<SecurityConfigResp>({
|
||||
password_contain_name: {},
|
||||
password_error_count: {},
|
||||
password_expiration_days: {},
|
||||
password_lock_minutes: {},
|
||||
password_min_length: {},
|
||||
password_special_char: {},
|
||||
password_update_interval: {}
|
||||
})
|
||||
|
||||
// 查询列表数据
|
||||
const getDataList = async () => {
|
||||
const { data } = await listOption({ code: Object.keys(securityConfig.value) })
|
||||
securityConfig.value = data.reduce((obj: SecurityConfigResp, option: OptionResp) => {
|
||||
obj[option.code] = { ...option, value: parseInt(option.value) }
|
||||
return obj
|
||||
}, {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-card title="登录方式" bordered class="gradient-card">
|
||||
<a-card title="第三方账号" bordered class="gradient-card">
|
||||
<div v-for="item in modeList" :key="item.title">
|
||||
<div class="item">
|
||||
<div class="icon-wrapper"><GiSvgIcon :name="item.icon" :size="26" /></div>
|
||||
@@ -55,24 +55,6 @@ const userInfo = computed(() => userStore.userInfo)
|
||||
const socialList = ref<any>([])
|
||||
const modeList = ref<ModeItem[]>([])
|
||||
modeList.value = [
|
||||
{
|
||||
title: '绑定手机',
|
||||
icon: 'phone-color',
|
||||
value: `${userInfo.value.phone + ' ' || '绑定后,'}`,
|
||||
subtitle: `可通过手机验证码快捷登录`,
|
||||
type: 'phone',
|
||||
jumpMode: 'modal',
|
||||
status: !!userInfo.value.phone
|
||||
},
|
||||
{
|
||||
title: '绑定邮箱',
|
||||
icon: 'email-color',
|
||||
value: `${userInfo.value.email + ' ' || '绑定后,'}`,
|
||||
subtitle: `可通过邮箱验证码进行登录`,
|
||||
type: 'email',
|
||||
jumpMode: 'modal',
|
||||
status: !!userInfo.value.email
|
||||
},
|
||||
{
|
||||
title: '绑定 Gitee',
|
||||
icon: 'gitee',
|
@@ -17,9 +17,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LeftBox from './LeftBox.vue'
|
||||
import RightBox from './RightBox.vue'
|
||||
import PasswordPolicy from './PasswordPolicy.vue'
|
||||
import LeftBox from './BasicInfo.vue'
|
||||
import RightBox from './Social.vue'
|
||||
import PasswordPolicy from './Security.vue'
|
||||
|
||||
defineOptions({ name: 'SettingProfile' })
|
||||
</script>
|
||||
|
@@ -6,4 +6,5 @@ export interface ModeItem {
|
||||
type: 'phone' | 'email' | 'gitee' | 'github'
|
||||
jumpMode?: 'link' | 'modal'
|
||||
status: boolean
|
||||
statusString: string
|
||||
}
|
||||
|
Reference in New Issue
Block a user