mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-11 16:57:09 +08:00
refactor: 优化账号管理
This commit is contained in:
@@ -1,19 +1,6 @@
|
||||
<template>
|
||||
<div class="setting" :class="{ 'setting--h5': !isDesktop }">
|
||||
<div class="setting__tabs">
|
||||
<a-tabs hide-content size="medium" :active-key="selectedKeys" @change="(key) => toPage(String(key))">
|
||||
<a-tab-pane v-for="item in list" :key="item.path" :title="item.name"> </a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
|
||||
<div class="setting__main">
|
||||
<div class="setting__main__menu">
|
||||
<a-menu :selected-keys="selectedKeys">
|
||||
<a-menu-item v-for="item in list" :key="item.path" @click="toPage(item.path)">
|
||||
<span>{{ item.name }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<div class="setting__main__content">
|
||||
<ParentView></ParentView>
|
||||
</div>
|
||||
@@ -26,29 +13,7 @@ import { useDevice } from '@/hooks'
|
||||
|
||||
defineOptions({ name: 'Setting' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { isDesktop } = useDevice()
|
||||
|
||||
const selectedKeys = ref('')
|
||||
watch(
|
||||
() => route.path,
|
||||
(newPath) => {
|
||||
selectedKeys.value = newPath
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const list = [
|
||||
{ name: '基本信息', value: 1, path: '/setting/profile' },
|
||||
{ name: '安全设置', value: 2, path: '/setting/security' },
|
||||
{ name: '消息中心', value: 3, path: '/setting/notice' }
|
||||
]
|
||||
|
||||
const toPage = (path: string) => {
|
||||
router.push({ path: path })
|
||||
selectedKeys.value = path
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -58,20 +23,11 @@ const toPage = (path: string) => {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__tabs {
|
||||
display: none;
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
|
||||
&__main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
&__menu {
|
||||
width: 200px;
|
||||
margin-top: $margin;
|
||||
margin-left: $margin;
|
||||
}
|
||||
&__content {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
@@ -85,15 +41,5 @@ const toPage = (path: string) => {
|
||||
|
||||
.setting--h5 {
|
||||
flex-direction: column;
|
||||
.setting__tabs {
|
||||
display: block;
|
||||
}
|
||||
.setting__main__menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-menu-vertical .arco-menu-inner) {
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div class="page"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
defineOptions({ name: 'Notification' })
|
||||
|
||||
const route = useRoute()
|
||||
const form = reactive({ name: '' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding: $padding;
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
</style>
|
@@ -1,65 +1,49 @@
|
||||
<template>
|
||||
<Card style="height: 600px">
|
||||
<template #header> 主账号信息 </template>
|
||||
<template #body>
|
||||
<div class="body">
|
||||
<section>
|
||||
<div class="avatar">
|
||||
<img :src="userStore.avatar" alt="avatar" />
|
||||
</div>
|
||||
<div class="name">
|
||||
<span style="margin-right: 10px">{{ userInfo.nickname }}</span>
|
||||
<icon-edit :size="16" class="btn" @click="onEditNickName" />
|
||||
</div>
|
||||
<div class="id">
|
||||
<GiSvgIcon name="id" :size="16" />
|
||||
<span style="margin-left: 10px">{{ userInfo.id }}</span>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="footer_item">
|
||||
<div>
|
||||
<span style="margin-right: 10px">安全手机</span>
|
||||
<span>{{ userInfo.phone }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
><GiSvgIcon :name="userInfo.email ? 'success' : 'warning'" :size="14" /><span
|
||||
style="margin-left: 5px; font-size: 12px"
|
||||
>{{ userInfo.phone ? '已绑定' : '未绑定' }}</span
|
||||
></span
|
||||
>
|
||||
<a-divider direction="vertical" />
|
||||
<icon-edit :size="16" class="btn" @click="openVerifyModel('phone')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_item">
|
||||
<div>
|
||||
<span style="margin-right: 10px">安全邮箱</span>
|
||||
<span>{{ userInfo.email }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
><GiSvgIcon :name="userInfo.email ? 'success' : 'warning'" :size="14" /><span
|
||||
style="margin-left: 5px; font-size: 12px"
|
||||
>{{ userInfo.email ? '已绑定' : '未绑定' }}</span
|
||||
></span
|
||||
>
|
||||
<a-divider direction="vertical" />
|
||||
<icon-edit :size="16" class="btn" @click="openVerifyModel('email')" />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="footer">注册于 {{ userInfo.registrationDate }}</div>
|
||||
</template>
|
||||
</Card>
|
||||
<a-card title="基本信息" bordered class="gradient-card">
|
||||
<div class="body">
|
||||
<section>
|
||||
<div class="avatar">
|
||||
<img :src="userStore.avatar" alt="avatar" />
|
||||
</div>
|
||||
<div class="name">
|
||||
<span style="margin-right: 10px">{{ userInfo.nickname }}</span>
|
||||
<icon-edit :size="16" class="btn" @click="onEditNickName" />
|
||||
</div>
|
||||
<div class="id">
|
||||
<GiSvgIcon name="id" :size="16" />
|
||||
<span>{{ userInfo.id }}</span>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<a-descriptions column="4" size="large">
|
||||
<a-descriptions-item label="性别" :span="4">
|
||||
{{ userInfo.nickname }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-phone /><span style="margin-left: 5px">手机</span></template>
|
||||
{{ userInfo.phone }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-email /><span style="margin-left: 5px">邮箱</span></template>
|
||||
{{ userInfo.email }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-mind-mapping /><span style="margin-left: 5px">部门</span></template>
|
||||
{{ userInfo.nickname }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-user-group /><span style="margin-left: 5px">角色</span></template>
|
||||
{{ userInfo.nickname }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="footer">注册于 {{ userInfo.registrationDate }}</div>
|
||||
</a-card>
|
||||
<VerifyModel ref="verifyModelRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Card from '../components/Card.vue'
|
||||
import { updateUserBaseInfo } from '@/apis'
|
||||
import VerifyModel from '../components/VerifyModel.vue'
|
||||
import { useUserStore } from '@/stores'
|
||||
@@ -73,20 +57,23 @@ const openVerifyModel = (type: 'phone' | 'email') => {
|
||||
verifyModelRef.value?.open(type)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 28px 10px 20px 10px;
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& > section {
|
||||
flex: 1;
|
||||
flex: 1 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32px 0 50px;
|
||||
.avatar > img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
@@ -96,18 +83,30 @@ const openVerifyModel = (type: 'phone' | 'email') => {
|
||||
font-size: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.id {
|
||||
span {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
padding: 0 6px;
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > footer .footer_item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 15px 28px;
|
||||
margin: 0 -16px;
|
||||
padding-top: 16px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--color-neutral-2);
|
||||
border-top: 1px solid var(--color-border-2);
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,54 +1,55 @@
|
||||
<template>
|
||||
<Card>
|
||||
<template #header> 登录方式 </template>
|
||||
<template #body>
|
||||
<div class="mode-list">
|
||||
<div v-for="item in modeList" :key="item.title" class="mode-item">
|
||||
<div class="mode-item-box">
|
||||
<div class="mode-item-icon">
|
||||
<GiSvgIcon :name="item.icon" :size="50" />
|
||||
</div>
|
||||
<div class="mode-item-content">
|
||||
<div class="mode-item-title">
|
||||
<div>{{ item.title }}</div>
|
||||
<div style="margin-left: 10px">
|
||||
<GiSvgIcon :name="item.status ? 'success' : 'warning'" :size="14" /><span
|
||||
style="margin-left: 5px; font-size: 12px"
|
||||
>{{ item.status ? '已绑定' : '未绑定' }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-item-subtitle">{{ item.subtitle }}</div>
|
||||
</div>
|
||||
<a-card title="登录方式" bordered class="gradient-card">
|
||||
<div class="mode-list">
|
||||
<div v-for="item in modeList" :key="item.title" class="mode-item">
|
||||
<div class="mode-item-box">
|
||||
<div class="mode-item-box__icon">
|
||||
<GiSvgIcon :name="item.icon" :size="48" />
|
||||
</div>
|
||||
<div class="model-item-btn">
|
||||
<a-button @click="openVerifyModel(item.type, item.status)" v-if="item.jumpMode == 'modal'">{{
|
||||
item.status ? '修改' : '绑定'
|
||||
}}</a-button>
|
||||
<a-button @click="onBinding(item.type, item.status)" v-else-if="item.jumpMode == 'link'">{{
|
||||
item.status ? '解绑' : '绑定'
|
||||
}}</a-button>
|
||||
<div class="mode-item-box__content">
|
||||
<div class="title">
|
||||
<div>{{ item.title }}</div>
|
||||
<div style="margin-left: 10px">
|
||||
<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.status ? '已绑定' : '未绑定'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-item-box__subtitle">{{ item.subtitle }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a-button
|
||||
v-if="item.jumpMode == 'modal'"
|
||||
class="btn"
|
||||
:type="item.status ? 'secondary' : 'primary'"
|
||||
@click="openVerifyModel(item.type, item.status)"
|
||||
>
|
||||
{{ item.status ? '修改' : '绑定' }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.jumpMode == 'link'"
|
||||
class="btn"
|
||||
:type="item.status ? 'secondary' : 'primary'"
|
||||
@click="onBinding(item.type, item.status)"
|
||||
>
|
||||
{{ item.status ? '解绑' : '绑定' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</a-card>
|
||||
<VerifyModel ref="verifyModelRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/stores'
|
||||
import Card from '../components/Card.vue'
|
||||
import VerifyModel from '../components/VerifyModel.vue'
|
||||
import { socialAuth, getSocialAccount, unbindSocialAccount } from '@/apis'
|
||||
interface ModeItem {
|
||||
title: string
|
||||
icon: string
|
||||
subtitle: string
|
||||
type: 'phone' | 'email' | 'gitee' | 'github'
|
||||
jumpMode: 'link' | 'modal'
|
||||
status: boolean
|
||||
}
|
||||
import type { ModeItem } from './type'
|
||||
import { useUserStore } from '@/stores'
|
||||
import VerifyModel from '../components/VerifyModel.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
|
||||
@@ -56,36 +57,37 @@ const openVerifyModel = (type: 'phone' | 'email') => {
|
||||
verifyModelRef.value?.open(type)
|
||||
}
|
||||
const socialList = ref<any>([])
|
||||
|
||||
const modeList = ref<ModeItem[]>([])
|
||||
modeList.value = [
|
||||
{
|
||||
title: '绑定手机号',
|
||||
icon: 'Tel',
|
||||
subtitle: `${userInfo.value.phone || '绑定后'},可通过手机验证码快捷登录`,
|
||||
title: '绑定手机',
|
||||
icon: userInfo.value.phone ? 'tel' : 'tel-unbind',
|
||||
subtitle: `${userInfo.value.phone || '绑定后'},可通过手机验证码快捷登录`,
|
||||
type: 'phone',
|
||||
jumpMode: 'modal',
|
||||
status: userInfo.value.phone ? true : false
|
||||
status: !!userInfo.value.phone
|
||||
},
|
||||
{
|
||||
title: '绑定邮箱',
|
||||
icon: 'Mail',
|
||||
subtitle: `${userInfo.value.email || '绑定后'},可通过邮箱验证码进行登录`,
|
||||
icon: userInfo.value.email ? 'mail' : 'mail-unbind',
|
||||
subtitle: `${userInfo.value.email || '绑定后'},可通过邮箱验证码进行登录`,
|
||||
type: 'email',
|
||||
jumpMode: 'modal',
|
||||
status: userInfo.value.email ? true : false
|
||||
status: !!userInfo.value.email
|
||||
},
|
||||
{
|
||||
title: '绑定Gitee',
|
||||
title: '绑定 Gitee',
|
||||
icon: 'gitee',
|
||||
subtitle: '绑定后,可通过Gitee进行登录',
|
||||
subtitle: '绑定后,可通过 Gitee 进行登录',
|
||||
jumpMode: 'link',
|
||||
type: 'gitee',
|
||||
status: socialList.value.some((el) => el == 'gitee')
|
||||
},
|
||||
{
|
||||
title: '绑定GitHub',
|
||||
title: '绑定 GitHub',
|
||||
icon: 'github',
|
||||
subtitle: '绑定后,可通过github进行登录',
|
||||
subtitle: '绑定后,可通过 GitHub 进行登录',
|
||||
type: 'github',
|
||||
jumpMode: 'link',
|
||||
status: socialList.value.some((el) => el == 'github')
|
||||
@@ -96,9 +98,7 @@ const initData = () => {
|
||||
socialList.value = res.data.map((el) => el.source)
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
|
||||
const onBinding = (type: string, status: boolean) => {
|
||||
if (!status) {
|
||||
socialAuth(type).then((res) => {
|
||||
@@ -112,6 +112,10 @@ const onBinding = (type: string, status: boolean) => {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -119,21 +123,29 @@ const onBinding = (type: string, status: boolean) => {
|
||||
.mode-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
.mode-item-box {
|
||||
&-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.mode-item-icon {
|
||||
&__icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.mode-item-content > div {
|
||||
line-height: 26px;
|
||||
}
|
||||
.mode-item-content .mode-item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&__content {
|
||||
div {
|
||||
line-height: 26px;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
width: 56px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,40 +1,25 @@
|
||||
<template>
|
||||
<div class="user">
|
||||
<div class="user__info">
|
||||
<a-row justify="space-between">
|
||||
<a-col :span="7" style="padding-right: 20px">
|
||||
<LeftBox />
|
||||
</a-col>
|
||||
<a-col :span="17">
|
||||
<RightBox />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<div class="gi_page">
|
||||
<a-row wrap :gutter="16">
|
||||
<a-col :xs="24" :sm="24" :md="10" :lg="10" :xl="7" :xxl="7">
|
||||
<LeftBox />
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :md="14" :lg="14" :xl="17" :xxl="17">
|
||||
<RightBox />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { useUserStore } from '@/stores'
|
||||
import RightBox from './RightBox.vue'
|
||||
import LeftBox from './LeftBox.vue'
|
||||
defineOptions({ name: 'Profile' })
|
||||
import RightBox from './RightBox.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
defineOptions({ name: 'Profile' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user {
|
||||
// background-color: var(--color-bg-1);
|
||||
&__alert {
|
||||
padding: $padding;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
&__info {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
// display: flex;
|
||||
}
|
||||
.gi_page {
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
</style>
|
||||
|
8
src/views/setting/profile/type.ts
Normal file
8
src/views/setting/profile/type.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export interface ModeItem {
|
||||
title: string
|
||||
icon: string
|
||||
subtitle: string
|
||||
type: 'phone' | 'email' | 'gitee' | 'github'
|
||||
jumpMode: 'link' | 'modal'
|
||||
status: boolean
|
||||
}
|
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_title">
|
||||
<div class="icon"><GiSvgIcon name="loginProtect" :size="36" /></div>
|
||||
<div class="icon"><GiSvgIcon name="login-protect" :size="36" /></div>
|
||||
<div>
|
||||
<div style="font-size: 14px; font-weight: 500; line-height: 28px">操作保护</div>
|
||||
<div style="font-size: 12px">进行敏感操作时需进行二次身份校验</div>
|
||||
@@ -50,7 +50,7 @@ interface ModeItem {
|
||||
}
|
||||
const modeList = ref<ModeItem[]>([])
|
||||
modeList.value = [
|
||||
{ title: '登录保护', icon: 'loginProtect', subtitle: '开启登录保护后,账号登录需进行二次身份验证', status: false }
|
||||
{ title: '登录保护', icon: 'login-protect', subtitle: '开启登录保护后,账号登录需进行二次身份验证', status: false }
|
||||
]
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
@@ -38,8 +38,8 @@ interface ModeItem {
|
||||
}
|
||||
const modeList = ref<ModeItem[]>([])
|
||||
modeList.value = [
|
||||
{ title: '绑定手机号', icon: 'Tel', subtitle: '+86******88888可通过手机验证码快捷登录', type: 'phone', status: true },
|
||||
{ title: '绑定邮箱', icon: 'Mail', subtitle: '邮箱可用于身份验证、密码找回、通知接收', type: 'email', status: true }
|
||||
{ title: '绑定手机号', icon: 'tel', subtitle: '+86******88888可通过手机验证码快捷登录', type: 'phone', status: true },
|
||||
{ title: '绑定邮箱', icon: 'mail', subtitle: '邮箱可用于身份验证、密码找回、通知接收', type: 'email', status: true }
|
||||
]
|
||||
const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
|
||||
const openVerifyModel = (type: 'phone' | 'email') => {
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<template #header> 登录会话设置 </template>
|
||||
<template #body>
|
||||
<div class="content_title">
|
||||
<div class="icon"><GiSvgIcon name="loginStatus" :size="36" /></div>
|
||||
<div class="icon"><GiSvgIcon name="login-status" :size="36" /></div>
|
||||
<div>
|
||||
<div style="font-size: 14px; font-weight: 500; line-height: 28px">登录态保持时间设置</div>
|
||||
<div style="font-size: 12px">保持登录状态的限制</div>
|
||||
|
Reference in New Issue
Block a user