refactor: 优化安全设置
@@ -7,7 +7,6 @@ export interface UserInfo {
 | 
				
			|||||||
  email: string
 | 
					  email: string
 | 
				
			||||||
  phone: string
 | 
					  phone: string
 | 
				
			||||||
  avatar: string
 | 
					  avatar: string
 | 
				
			||||||
  pwdResetTime: string
 | 
					 | 
				
			||||||
  registrationDate: string
 | 
					  registrationDate: string
 | 
				
			||||||
  deptName: string
 | 
					  deptName: string
 | 
				
			||||||
  roles: string[]
 | 
					  roles: string[]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								src/assets/icons/email-color.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					<rect x="4" y="8" width="40" height="32" fill="#4785FF"/>
 | 
				
			||||||
 | 
					<path d="M4 8.00098H44V14.001C32.63 24.2339 15.37 24.2339 4 14.001V8.00098Z" fill="#94C2FF"/>
 | 
				
			||||||
 | 
					<circle cx="24" cy="23" r="4" fill="white"/>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 300 B  | 
@@ -1,6 +0,0 @@
 | 
				
			|||||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
					 | 
				
			||||||
<rect width="48" height="48" rx="24" fill="#F6F7FB"/>
 | 
					 | 
				
			||||||
<rect x="12.334" y="14.667" width="23.3333" height="18.6667" fill="#86909C"/>
 | 
					 | 
				
			||||||
<path d="M12.334 14.667H35.6673V18.167C29.0348 24.1362 18.9665 24.1362 12.334 18.167V14.667Z" fill="#C3C7CE"/>
 | 
					 | 
				
			||||||
<circle cx="23.9993" cy="23.4163" r="2.33333" fill="white"/>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 407 B  | 
@@ -1,6 +0,0 @@
 | 
				
			|||||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
					 | 
				
			||||||
<rect width="48" height="48" rx="24" fill="#F6F7FB"/>
 | 
					 | 
				
			||||||
<rect x="12.334" y="14.667" width="23.3333" height="18.6667" fill="#4785FF"/>
 | 
					 | 
				
			||||||
<path d="M12.334 14.667H35.6673V18.167C29.0348 24.1362 18.9665 24.1362 12.334 18.167V14.667Z" fill="#94C2FF"/>
 | 
					 | 
				
			||||||
<circle cx="23.9993" cy="23.4163" r="2.33333" fill="white"/>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 407 B  | 
| 
		 Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B  | 
							
								
								
									
										6
									
								
								src/assets/icons/phone-color.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					<path d="M9 3H39V45H9V3Z" fill="#4785FF"/>
 | 
				
			||||||
 | 
					<path d="M9 31H39V45H9V31Z" fill="#94C2FF"/>
 | 
				
			||||||
 | 
					<circle cx="24" cy="38" r="4" fill="white"/>
 | 
				
			||||||
 | 
					<path d="M18 7H30V10H18V7Z" fill="white"/>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 279 B  | 
@@ -1 +0,0 @@
 | 
				
			|||||||
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="butt" stroke-linejoin="miter"><path d="M6.707 34.284a1 1 0 010-1.414l5.657-5.657a1 1 0 011.414 0l4.95 4.95s3.535-1.414 7.778-5.657c4.243-4.243 5.657-7.778 5.657-7.778l-4.95-4.95a1 1 0 010-1.414l5.657-5.657a1 1 0 011.414 0l6.01 6.01s3.183 7.425-8.485 19.092c-11.667 11.668-19.092 8.485-19.092 8.485l-6.01-6.01z" /></svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 411 B  | 
| 
		 Before Width: | Height: | Size: 491 B After Width: | Height: | Size: 491 B  | 
@@ -40,8 +40,8 @@ export const constantRoutes: RouteRecordRaw[] = [
 | 
				
			|||||||
    children: [
 | 
					    children: [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        path: '/home',
 | 
					        path: '/home',
 | 
				
			||||||
        component: () => import('@/views/home/index.vue'),
 | 
					 | 
				
			||||||
        name: 'Home',
 | 
					        name: 'Home',
 | 
				
			||||||
 | 
					        component: () => import('@/views/home/index.vue'),
 | 
				
			||||||
        meta: { title: '首页', icon: 'dashboard', affix: true, hidden: false }
 | 
					        meta: { title: '首页', icon: 'dashboard', affix: true, hidden: false }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
@@ -56,29 +56,19 @@ export const constantRoutes: RouteRecordRaw[] = [
 | 
				
			|||||||
    path: '/setting',
 | 
					    path: '/setting',
 | 
				
			||||||
    name: 'Setting',
 | 
					    name: 'Setting',
 | 
				
			||||||
    component: Layout,
 | 
					    component: Layout,
 | 
				
			||||||
    redirect: '/setting/profile',
 | 
					 | 
				
			||||||
    meta: { hidden: true },
 | 
					    meta: { hidden: true },
 | 
				
			||||||
    children: [
 | 
					    children: [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        path: '/setting',
 | 
					        path: '/setting/profile',
 | 
				
			||||||
        name: 'Setting',
 | 
					        name: 'SettingProfile',
 | 
				
			||||||
        component: () => import('@/views/setting/index.vue'),
 | 
					        component: () => import('@/views/setting/profile/index.vue'),
 | 
				
			||||||
        redirect: '',
 | 
					        meta: { title: '账号管理', showInTabs: false }
 | 
				
			||||||
        meta: { hidden: true },
 | 
					      },
 | 
				
			||||||
        children: [
 | 
					      {
 | 
				
			||||||
          {
 | 
					        path: '/setting/security',
 | 
				
			||||||
            path: '/setting/profile',
 | 
					        name: 'SettingSecurity',
 | 
				
			||||||
            component: () => import('@/views/setting/profile/index.vue'),
 | 
					        component: () => import('@/views/setting/security/index.vue'),
 | 
				
			||||||
            name: 'Profile',
 | 
					        meta: { title: '安全设置', showInTabs: false }
 | 
				
			||||||
            meta: { title: '账号管理', hidden: false, showInTabs: false }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            path: '/setting/security',
 | 
					 | 
				
			||||||
            component: () => import('@/views/setting/security/index.vue'),
 | 
					 | 
				
			||||||
            name: 'Security',
 | 
					 | 
				
			||||||
            meta: { title: '安全设置', hidden: false, showInTabs: false }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,13 +18,18 @@ import { resetHasRouteFlag } from '@/router/permission'
 | 
				
			|||||||
import getAvatar from '@/utils/avatar'
 | 
					import getAvatar from '@/utils/avatar'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const storeSetup = () => {
 | 
					const storeSetup = () => {
 | 
				
			||||||
  const userInfo = reactive<Pick<UserInfo, 'id' | 'nickname' | 'avatar' | 'email' | 'phone' | 'registrationDate'>>({
 | 
					  const userInfo = reactive<UserInfo>({
 | 
				
			||||||
    id: '',
 | 
					    id: '',
 | 
				
			||||||
 | 
					    username: '',
 | 
				
			||||||
    nickname: '',
 | 
					    nickname: '',
 | 
				
			||||||
    avatar: '',
 | 
					    gender: 0,
 | 
				
			||||||
    email: '',
 | 
					    email: '',
 | 
				
			||||||
    phone: '',
 | 
					    phone: '',
 | 
				
			||||||
    registrationDate: ''
 | 
					    avatar: '',
 | 
				
			||||||
 | 
					    registrationDate: '',
 | 
				
			||||||
 | 
					    deptName: '',
 | 
				
			||||||
 | 
					    roles: [],
 | 
				
			||||||
 | 
					    permissions: []
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  const name = computed(() => userInfo.nickname)
 | 
					  const name = computed(() => userInfo.nickname)
 | 
				
			||||||
  const avatar = computed(() => userInfo.avatar)
 | 
					  const avatar = computed(() => userInfo.avatar)
 | 
				
			||||||
@@ -90,12 +95,8 @@ const storeSetup = () => {
 | 
				
			|||||||
  // 获取用户信息
 | 
					  // 获取用户信息
 | 
				
			||||||
  const getInfo = async () => {
 | 
					  const getInfo = async () => {
 | 
				
			||||||
    const res = await getUserInfoApi()
 | 
					    const res = await getUserInfoApi()
 | 
				
			||||||
    userInfo.id = res.data.id
 | 
					    Object.assign(userInfo, res.data)
 | 
				
			||||||
    userInfo.nickname = res.data.nickname
 | 
					 | 
				
			||||||
    userInfo.avatar = getAvatar(res.data.avatar, res.data.gender)
 | 
					    userInfo.avatar = getAvatar(res.data.avatar, res.data.gender)
 | 
				
			||||||
    userInfo.email = res.data.email
 | 
					 | 
				
			||||||
    userInfo.phone = res.data.phone
 | 
					 | 
				
			||||||
    userInfo.registrationDate = res.data.registrationDate
 | 
					 | 
				
			||||||
    if (res.data.roles && res.data.roles.length) {
 | 
					    if (res.data.roles && res.data.roles.length) {
 | 
				
			||||||
      roles.value = res.data.roles
 | 
					      roles.value = res.data.roles
 | 
				
			||||||
      permissions.value = res.data.permissions
 | 
					      permissions.value = res.data.permissions
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -269,6 +269,81 @@
 | 
				
			|||||||
    border: none;
 | 
					    border: none;
 | 
				
			||||||
    background: linear-gradient(180deg, rgba(232, 244, 255, 0.5), hsla(0, 0%, 100%, 0));
 | 
					    background: linear-gradient(180deg, rgba(232, 244, 255, 0.5), hsla(0, 0%, 100%, 0));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  .item {
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    margin-bottom: 20px;
 | 
				
			||||||
 | 
					    .icon-wrapper {
 | 
				
			||||||
 | 
					      align-items: center;
 | 
				
			||||||
 | 
					      background: var(--color-neutral-2);
 | 
				
			||||||
 | 
					      border-radius: 50%;
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      height: 48px;
 | 
				
			||||||
 | 
					      justify-content: center;
 | 
				
			||||||
 | 
					      width: 48px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .info {
 | 
				
			||||||
 | 
					      flex: 1 1;
 | 
				
			||||||
 | 
					      margin: 0 16px;
 | 
				
			||||||
 | 
					      &-top {
 | 
				
			||||||
 | 
					        margin-bottom: 4px;
 | 
				
			||||||
 | 
					        .label {
 | 
				
			||||||
 | 
					          font-weight: 500;
 | 
				
			||||||
 | 
					          line-height: 22px;
 | 
				
			||||||
 | 
					          margin-right: 12px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .bind {
 | 
				
			||||||
 | 
					          font-size: 12px;
 | 
				
			||||||
 | 
					          font-weight: 500;
 | 
				
			||||||
 | 
					          line-height: 20px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &-desc {
 | 
				
			||||||
 | 
					        color: #86909c;
 | 
				
			||||||
 | 
					        font-size: 12px;
 | 
				
			||||||
 | 
					        font-weight: 400;
 | 
				
			||||||
 | 
					        line-height: 20px;
 | 
				
			||||||
 | 
					        .value {
 | 
				
			||||||
 | 
					          color: #4e5969;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .btn-wrapper {
 | 
				
			||||||
 | 
					      align-self: flex-start;
 | 
				
			||||||
 | 
					      .btn {
 | 
				
			||||||
 | 
					        height: 28px;
 | 
				
			||||||
 | 
					        width: 56px;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .detail {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    justify-content: flex-start;
 | 
				
			||||||
 | 
					    margin: -5px 0 0 64px;
 | 
				
			||||||
 | 
					    .sub-text-wrapper {
 | 
				
			||||||
 | 
					      width: 100%;
 | 
				
			||||||
 | 
					      border-left: 2px solid var(--color-fill-4);
 | 
				
			||||||
 | 
					      padding-left: 12px;
 | 
				
			||||||
 | 
					      .sub-text {
 | 
				
			||||||
 | 
					        color: #4e5969;
 | 
				
			||||||
 | 
					        font-weight: 400;
 | 
				
			||||||
 | 
					        line-height: 20px;
 | 
				
			||||||
 | 
					        margin-bottom: 8px;
 | 
				
			||||||
 | 
					        &-value {
 | 
				
			||||||
 | 
					          background: var(--color-neutral-2);
 | 
				
			||||||
 | 
					          padding: 1px 5px;
 | 
				
			||||||
 | 
					          margin: 0 5px;
 | 
				
			||||||
 | 
					          border-radius: 3px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      .arco-link.link {
 | 
				
			||||||
 | 
					        font-size: 12px;
 | 
				
			||||||
 | 
					        padding: 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 通用描述
 | 
					// 通用描述
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts"></script>
 | 
					<script lang="ts" setup></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
.login-bg {
 | 
					.login-bg {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,45 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
  <div class="setting" :class="{ 'setting--h5': !isDesktop }">
 | 
					 | 
				
			||||||
    <div class="setting__main">
 | 
					 | 
				
			||||||
      <div class="setting__main__content">
 | 
					 | 
				
			||||||
        <ParentView></ParentView>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script setup lang="ts">
 | 
					 | 
				
			||||||
import { useDevice } from '@/hooks'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
defineOptions({ name: 'Setting' })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const { isDesktop } = useDevice()
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="scss" scoped>
 | 
					 | 
				
			||||||
.setting {
 | 
					 | 
				
			||||||
  flex: 1;
 | 
					 | 
				
			||||||
  box-sizing: border-box;
 | 
					 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  &__main {
 | 
					 | 
				
			||||||
    flex: 1;
 | 
					 | 
				
			||||||
    overflow: hidden;
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    &__content {
 | 
					 | 
				
			||||||
      flex: 1;
 | 
					 | 
				
			||||||
      height: 100%;
 | 
					 | 
				
			||||||
      padding: $margin;
 | 
					 | 
				
			||||||
      box-sizing: border-box;
 | 
					 | 
				
			||||||
      overflow: hidden;
 | 
					 | 
				
			||||||
      overflow-y: auto;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.setting--h5 {
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -16,8 +16,11 @@
 | 
				
			|||||||
      </section>
 | 
					      </section>
 | 
				
			||||||
      <footer>
 | 
					      <footer>
 | 
				
			||||||
        <a-descriptions column="4" size="large">
 | 
					        <a-descriptions column="4" size="large">
 | 
				
			||||||
          <a-descriptions-item label="性别" :span="4">
 | 
					          <a-descriptions-item :span="4">
 | 
				
			||||||
            {{ userInfo.nickname }}
 | 
					            <template #label> <icon-user /><span style="margin-left: 5px">用户名</span></template>
 | 
				
			||||||
 | 
					            {{ userInfo.username }}
 | 
				
			||||||
 | 
					            <icon-man v-if="userInfo.gender === 1" style="color: #19bbf1" />
 | 
				
			||||||
 | 
					            <icon-woman v-else-if="userInfo.gender === 2" style="color: #fa7fa9" />
 | 
				
			||||||
          </a-descriptions-item>
 | 
					          </a-descriptions-item>
 | 
				
			||||||
          <a-descriptions-item :span="4">
 | 
					          <a-descriptions-item :span="4">
 | 
				
			||||||
            <template #label> <icon-phone /><span style="margin-left: 5px">手机</span></template>
 | 
					            <template #label> <icon-phone /><span style="margin-left: 5px">手机</span></template>
 | 
				
			||||||
@@ -29,11 +32,11 @@
 | 
				
			|||||||
          </a-descriptions-item>
 | 
					          </a-descriptions-item>
 | 
				
			||||||
          <a-descriptions-item :span="4">
 | 
					          <a-descriptions-item :span="4">
 | 
				
			||||||
            <template #label> <icon-mind-mapping /><span style="margin-left: 5px">部门</span></template>
 | 
					            <template #label> <icon-mind-mapping /><span style="margin-left: 5px">部门</span></template>
 | 
				
			||||||
            {{ userInfo.nickname }}
 | 
					            {{ userInfo.deptName }}
 | 
				
			||||||
          </a-descriptions-item>
 | 
					          </a-descriptions-item>
 | 
				
			||||||
          <a-descriptions-item :span="4">
 | 
					          <a-descriptions-item :span="4">
 | 
				
			||||||
            <template #label> <icon-user-group /><span style="margin-left: 5px">角色</span></template>
 | 
					            <template #label> <icon-user-group /><span style="margin-left: 5px">角色</span></template>
 | 
				
			||||||
            {{ userInfo.nickname }}
 | 
					            {{ userInfo.roles.join(',') }}
 | 
				
			||||||
          </a-descriptions-item>
 | 
					          </a-descriptions-item>
 | 
				
			||||||
        </a-descriptions>
 | 
					        </a-descriptions>
 | 
				
			||||||
      </footer>
 | 
					      </footer>
 | 
				
			||||||
@@ -47,6 +50,7 @@
 | 
				
			|||||||
import { updateUserBaseInfo } from '@/apis'
 | 
					import { updateUserBaseInfo } from '@/apis'
 | 
				
			||||||
import VerifyModel from '../components/VerifyModel.vue'
 | 
					import VerifyModel from '../components/VerifyModel.vue'
 | 
				
			||||||
import { useUserStore } from '@/stores'
 | 
					import { useUserStore } from '@/stores'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const userStore = useUserStore()
 | 
					const userStore = useUserStore()
 | 
				
			||||||
const userInfo = computed(() => userStore.userInfo)
 | 
					const userInfo = computed(() => userStore.userInfo)
 | 
				
			||||||
const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
 | 
					const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,25 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <a-card title="登录方式" bordered class="gradient-card">
 | 
					  <a-card title="登录方式" bordered class="gradient-card">
 | 
				
			||||||
    <div class="mode-list">
 | 
					    <div v-for="item in modeList" :key="item.title">
 | 
				
			||||||
      <div v-for="item in modeList" :key="item.title" class="mode-item">
 | 
					      <div class="item">
 | 
				
			||||||
        <div class="mode-item-box">
 | 
					        <div class="icon-wrapper"><GiSvgIcon :name="item.icon" :size="26" /></div>
 | 
				
			||||||
          <div class="mode-item-box__icon">
 | 
					        <div class="info">
 | 
				
			||||||
            <GiSvgIcon :name="item.icon" :size="48" />
 | 
					          <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.status ? '已绑定' : '未绑定'
 | 
				
			||||||
 | 
					              }}</span>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div class="mode-item-box__content">
 | 
					          <div class="info-desc">
 | 
				
			||||||
            <div class="title">
 | 
					            <span class="value">{{ item.value }}</span>
 | 
				
			||||||
              <div>{{ item.title }}</div>
 | 
					            {{ item.subtitle }}
 | 
				
			||||||
              <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>
 | 
					        </div>
 | 
				
			||||||
        <div>
 | 
					        <div class="btn-wrapper">
 | 
				
			||||||
          <a-button
 | 
					          <a-button
 | 
				
			||||||
            v-if="item.jumpMode == 'modal'"
 | 
					            v-if="item.jumpMode == 'modal'"
 | 
				
			||||||
            class="btn"
 | 
					            class="btn"
 | 
				
			||||||
@@ -46,7 +45,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { socialAuth, getSocialAccount, unbindSocialAccount } from '@/apis'
 | 
					import { socialAuth, getSocialAccount, unbindSocialAccount } from '@/apis'
 | 
				
			||||||
import type { ModeItem } from './type'
 | 
					import type { ModeItem } from '../type'
 | 
				
			||||||
import { useUserStore } from '@/stores'
 | 
					import { useUserStore } from '@/stores'
 | 
				
			||||||
import VerifyModel from '../components/VerifyModel.vue'
 | 
					import VerifyModel from '../components/VerifyModel.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,16 +61,18 @@ const modeList = ref<ModeItem[]>([])
 | 
				
			|||||||
modeList.value = [
 | 
					modeList.value = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    title: '绑定手机',
 | 
					    title: '绑定手机',
 | 
				
			||||||
    icon: userInfo.value.phone ? 'tel' : 'tel-unbind',
 | 
					    icon: 'phone-color',
 | 
				
			||||||
    subtitle: `${userInfo.value.phone || '绑定后'},可通过手机验证码快捷登录`,
 | 
					    value: `${userInfo.value.phone + ' ' || '绑定后,'}`,
 | 
				
			||||||
 | 
					    subtitle: `可通过手机验证码快捷登录`,
 | 
				
			||||||
    type: 'phone',
 | 
					    type: 'phone',
 | 
				
			||||||
    jumpMode: 'modal',
 | 
					    jumpMode: 'modal',
 | 
				
			||||||
    status: !!userInfo.value.phone
 | 
					    status: !!userInfo.value.phone
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    title: '绑定邮箱',
 | 
					    title: '绑定邮箱',
 | 
				
			||||||
    icon: userInfo.value.email ? 'mail' : 'mail-unbind',
 | 
					    icon: 'email-color',
 | 
				
			||||||
    subtitle: `${userInfo.value.email || '绑定后'},可通过邮箱验证码进行登录`,
 | 
					    value: `${userInfo.value.email + ' ' || '绑定后,'}`,
 | 
				
			||||||
 | 
					    subtitle: `可通过邮箱验证码进行登录`,
 | 
				
			||||||
    type: 'email',
 | 
					    type: 'email',
 | 
				
			||||||
    jumpMode: 'modal',
 | 
					    jumpMode: 'modal',
 | 
				
			||||||
    status: !!userInfo.value.email
 | 
					    status: !!userInfo.value.email
 | 
				
			||||||
@@ -79,7 +80,7 @@ modeList.value = [
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    title: '绑定 Gitee',
 | 
					    title: '绑定 Gitee',
 | 
				
			||||||
    icon: 'gitee',
 | 
					    icon: 'gitee',
 | 
				
			||||||
    subtitle: '绑定后,可通过 Gitee 进行登录',
 | 
					    subtitle: `${socialList.value.some((el) => el == 'gitee') ? '' : '绑定后,'}可通过 Gitee 进行登录`,
 | 
				
			||||||
    jumpMode: 'link',
 | 
					    jumpMode: 'link',
 | 
				
			||||||
    type: 'gitee',
 | 
					    type: 'gitee',
 | 
				
			||||||
    status: socialList.value.some((el) => el == 'gitee')
 | 
					    status: socialList.value.some((el) => el == 'gitee')
 | 
				
			||||||
@@ -87,7 +88,7 @@ modeList.value = [
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    title: '绑定 GitHub',
 | 
					    title: '绑定 GitHub',
 | 
				
			||||||
    icon: 'github',
 | 
					    icon: 'github',
 | 
				
			||||||
    subtitle: '绑定后,可通过 GitHub 进行登录',
 | 
					    subtitle: `${socialList.value.some((el) => el == 'gitee') ? '' : '绑定后,'}可通过 GitHub 进行登录`,
 | 
				
			||||||
    type: 'github',
 | 
					    type: 'github',
 | 
				
			||||||
    jumpMode: 'link',
 | 
					    jumpMode: 'link',
 | 
				
			||||||
    status: socialList.value.some((el) => el == 'github')
 | 
					    status: socialList.value.some((el) => el == 'github')
 | 
				
			||||||
@@ -118,34 +119,4 @@ onMounted(() => {
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
.mode-list {
 | 
					 | 
				
			||||||
  .mode-item {
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    justify-content: space-between;
 | 
					 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
    margin-bottom: 20px;
 | 
					 | 
				
			||||||
    &-box {
 | 
					 | 
				
			||||||
      display: flex;
 | 
					 | 
				
			||||||
      align-items: center;
 | 
					 | 
				
			||||||
      &__icon {
 | 
					 | 
				
			||||||
        margin-right: 10px;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      &__content {
 | 
					 | 
				
			||||||
        div {
 | 
					 | 
				
			||||||
          line-height: 26px;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        .title {
 | 
					 | 
				
			||||||
          display: flex;
 | 
					 | 
				
			||||||
          align-items: center;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    .btn {
 | 
					 | 
				
			||||||
      height: 28px;
 | 
					 | 
				
			||||||
      margin-left: 10px;
 | 
					 | 
				
			||||||
      width: 56px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@
 | 
				
			|||||||
import LeftBox from './LeftBox.vue'
 | 
					import LeftBox from './LeftBox.vue'
 | 
				
			||||||
import RightBox from './RightBox.vue'
 | 
					import RightBox from './RightBox.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Profile' })
 | 
					defineOptions({ name: 'SettingProfile' })
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,99 +1,49 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Card>
 | 
					  <a-card title="账号保护" bordered class="gradient-card">
 | 
				
			||||||
    <template #header> 账号保护 </template>
 | 
					    <div v-for="item in modeList" :key="item.title">
 | 
				
			||||||
    <template #body>
 | 
					      <div class="item">
 | 
				
			||||||
      <div class="mode-item" v-for="item in modeList" :key="item.title">
 | 
					        <div class="icon-wrapper"><GiSvgIcon :name="item.icon" :size="26" /></div>
 | 
				
			||||||
        <div class="mode-item-content">
 | 
					        <div class="info">
 | 
				
			||||||
          <div class="icon"><GiSvgIcon :name="item.icon" :size="36" /></div>
 | 
					          <div class="info-top">
 | 
				
			||||||
          <div>
 | 
					            <span class="label">{{ item.title }}</span>
 | 
				
			||||||
            <div style="font-size: 14px; font-weight: 500; line-height: 28px; display: flex; align-items: center">
 | 
					            <span class="bind">
 | 
				
			||||||
              <span>{{ item.title }}</span>
 | 
					              <icon-check-circle-fill v-if="item.status" :size="14" class="success" />
 | 
				
			||||||
              <div style="margin-left: 10px">
 | 
					              <icon-exclamation-circle-fill v-else :size="14" class="warning" />
 | 
				
			||||||
                <GiSvgIcon :name="item.status ? 'success' : 'warning'" :size="14" /><span
 | 
					              <span style="font-size: 12px" :class="item.status ? 'success' : 'warning'">{{
 | 
				
			||||||
                  style="margin-left: 5px; font-size: 12px"
 | 
					                item.status ? '已开启' : '未开启'
 | 
				
			||||||
                  >{{ item.status ? '已开启' : '未开启' }}</span
 | 
					              }}</span>
 | 
				
			||||||
                >
 | 
					            </span>
 | 
				
			||||||
              </div>
 | 
					          </div>
 | 
				
			||||||
            </div>
 | 
					          <div class="info-desc">
 | 
				
			||||||
            <div style="font-size: 12px">{{ item.subtitle }}</div>
 | 
					            {{ item.subtitle }}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div>
 | 
					        <div class="btn-wrapper">
 | 
				
			||||||
          <a-button disabled>未开放</a-button>
 | 
					          <a-switch disabled />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div>
 | 
					    </div>
 | 
				
			||||||
        <div class="content_title">
 | 
					  </a-card>
 | 
				
			||||||
          <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>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="content_Box">
 | 
					 | 
				
			||||||
          <p>可使用<span class="subTitle">手机号</span>进行二次身份验证</p>
 | 
					 | 
				
			||||||
          <p>未设置密码有效期</p>
 | 
					 | 
				
			||||||
          <p>敏感操作二次身份验证后<span class="subTitle">10</span>分钟内不需要再次进行验证</p>
 | 
					 | 
				
			||||||
          <p class="link_btn">修改规则(未开发)</p>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </template>
 | 
					 | 
				
			||||||
  </Card>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import Card from '../components/Card.vue'
 | 
					import type { ModeItem } from '../type'
 | 
				
			||||||
interface ModeItem {
 | 
					
 | 
				
			||||||
  title: string
 | 
					 | 
				
			||||||
  icon: string
 | 
					 | 
				
			||||||
  subtitle: string
 | 
					 | 
				
			||||||
  status: boolean
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
const modeList = ref<ModeItem[]>([])
 | 
					const modeList = ref<ModeItem[]>([])
 | 
				
			||||||
modeList.value = [
 | 
					modeList.value = [
 | 
				
			||||||
  { title: '登录保护', icon: 'login-protect', subtitle: '开启登录保护后,账号登录需进行二次身份验证', status: false }
 | 
					  {
 | 
				
			||||||
 | 
					    title: '登录保护',
 | 
				
			||||||
 | 
					    icon: 'protect',
 | 
				
			||||||
 | 
					    subtitle: '开启登录保护后,账号登录需进行二次身份验证',
 | 
				
			||||||
 | 
					    status: false
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    title: '操作保护',
 | 
				
			||||||
 | 
					    icon: 'protect',
 | 
				
			||||||
 | 
					    subtitle: '进行敏感操作时需进行二次身份校验',
 | 
				
			||||||
 | 
					    status: false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style scoped lang="scss">
 | 
					
 | 
				
			||||||
.mode-item {
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  justify-content: space-between;
 | 
					 | 
				
			||||||
  margin-bottom: 20px;
 | 
					 | 
				
			||||||
  .mode-item-content {
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
    .icon {
 | 
					 | 
				
			||||||
      margin-right: 10px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.content_title {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  .icon {
 | 
					 | 
				
			||||||
    margin-right: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.content_Box {
 | 
					 | 
				
			||||||
  border-left: 1px solid #ccc;
 | 
					 | 
				
			||||||
  margin-left: 40px;
 | 
					 | 
				
			||||||
  padding-left: 10px;
 | 
					 | 
				
			||||||
  font-size: 12px;
 | 
					 | 
				
			||||||
  margin-top: 20px;
 | 
					 | 
				
			||||||
  & > p {
 | 
					 | 
				
			||||||
    margin: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.link_btn {
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
  color: #007aff;
 | 
					 | 
				
			||||||
  &:hover {
 | 
					 | 
				
			||||||
    color: rgba($color: #007aff, $alpha: 0.8);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.subTitle {
 | 
					 | 
				
			||||||
  background: var(--color-neutral-2);
 | 
					 | 
				
			||||||
  padding: 1px 5px;
 | 
					 | 
				
			||||||
  margin: 0px 5px;
 | 
					 | 
				
			||||||
  border-radius: 3px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,59 +1,78 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Card style="height: 100%">
 | 
					  <a-card title="基本设置" bordered class="gradient-card">
 | 
				
			||||||
    <template #header>基本设置</template>
 | 
					    <div v-for="item in modeList" :key="item.title">
 | 
				
			||||||
    <template #body>
 | 
					      <div class="item">
 | 
				
			||||||
      <div class="mode-item" v-for="item in modeList" :key="item.title">
 | 
					        <div class="icon-wrapper"><GiSvgIcon :name="item.icon" :size="26" /></div>
 | 
				
			||||||
        <div class="mode-item-content">
 | 
					        <div class="info">
 | 
				
			||||||
          <div class="icon"><GiSvgIcon :name="item.icon" :size="36" /></div>
 | 
					          <div class="info-top">
 | 
				
			||||||
          <div>
 | 
					            <span class="label">{{ item.title }}</span>
 | 
				
			||||||
            <div style="font-size: 14px; font-weight: 500; line-height: 28px; display: flex; align-items: center">
 | 
					            <span class="bind">
 | 
				
			||||||
              <span>{{ item.title }}</span>
 | 
					              <icon-check-circle-fill v-if="item.status" :size="14" class="success" />
 | 
				
			||||||
              <div style="margin-left: 10px">
 | 
					              <icon-exclamation-circle-fill v-else :size="14" class="warning" />
 | 
				
			||||||
                <GiSvgIcon :name="item.status ? 'success' : 'warning'" :size="14" /><span
 | 
					              <span style="font-size: 12px" :class="item.status ? 'success' : 'warning'">{{
 | 
				
			||||||
                  style="margin-left: 5px; font-size: 12px"
 | 
					                item.status ? '已绑定' : '未绑定'
 | 
				
			||||||
                  >{{ item.status ? '已开启' : '未开启' }}</span
 | 
					              }}</span>
 | 
				
			||||||
                >
 | 
					            </span>
 | 
				
			||||||
              </div>
 | 
					          </div>
 | 
				
			||||||
            </div>
 | 
					          <div class="info-desc">
 | 
				
			||||||
            <div style="font-size: 12px">{{ item.subtitle }}</div>
 | 
					            <span class="value">{{ item.value }}</span>
 | 
				
			||||||
 | 
					            {{ item.subtitle }}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div>
 | 
					        <div class="btn-wrapper">
 | 
				
			||||||
          <a-button @click="openVerifyModel(item.type)">修改</a-button>
 | 
					          <a-button class="btn" :type="item.status ? 'secondary' : 'primary'" @click="onUpdate(item.type)">
 | 
				
			||||||
 | 
					            {{ item.status ? '修改' : '绑定' }}
 | 
				
			||||||
 | 
					          </a-button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </div>
 | 
				
			||||||
  </Card>
 | 
					  </a-card>
 | 
				
			||||||
  <VerifyModel ref="verifyModelRef" />
 | 
					  <VerifyModel ref="verifyModelRef" />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import Card from '../components/Card.vue'
 | 
					import type { ModeItem } from '../type'
 | 
				
			||||||
import VerifyModel from '../components/VerifyModel.vue'
 | 
					import VerifyModel from '../components/VerifyModel.vue'
 | 
				
			||||||
interface ModeItem {
 | 
					import { useUserStore } from '@/stores'
 | 
				
			||||||
  title: string
 | 
					
 | 
				
			||||||
  icon: string
 | 
					const userStore = useUserStore()
 | 
				
			||||||
  subtitle: string
 | 
					const userInfo = computed(() => userStore.userInfo)
 | 
				
			||||||
  type: 'phone' | 'email'
 | 
					
 | 
				
			||||||
  status: boolean
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
const modeList = ref<ModeItem[]>([])
 | 
					const modeList = ref<ModeItem[]>([])
 | 
				
			||||||
modeList.value = [
 | 
					modeList.value = [
 | 
				
			||||||
  { title: '绑定手机号', icon: 'tel', subtitle: '+86******88888可通过手机验证码快捷登录', type: 'phone', status: true },
 | 
					  {
 | 
				
			||||||
  { title: '绑定邮箱', icon: 'mail', subtitle: '邮箱可用于身份验证、密码找回、通知接收', type: 'email', status: true }
 | 
					    title: '安全手机',
 | 
				
			||||||
 | 
					    icon: 'phone-color',
 | 
				
			||||||
 | 
					    value: `${userInfo.value.phone + ' ' || '手机号'}`,
 | 
				
			||||||
 | 
					    subtitle: `可用于身份验证、密码找回、通知接收`,
 | 
				
			||||||
 | 
					    type: 'phone',
 | 
				
			||||||
 | 
					    status: !!userInfo.value.phone
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    title: '安全邮箱',
 | 
				
			||||||
 | 
					    icon: 'email-color',
 | 
				
			||||||
 | 
					    value: `${userInfo.value.email + ' ' || '邮箱'}`,
 | 
				
			||||||
 | 
					    subtitle: `可用于身份验证、密码找回、通知接收`,
 | 
				
			||||||
 | 
					    type: 'email',
 | 
				
			||||||
 | 
					    status: !!userInfo.value.email
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
 | 
					const verifyModelRef = ref<InstanceType<typeof VerifyModel>>()
 | 
				
			||||||
const openVerifyModel = (type: 'phone' | 'email') => {
 | 
					// 修改
 | 
				
			||||||
 | 
					const onUpdate = (type: string) => {
 | 
				
			||||||
  verifyModelRef.value?.open(type)
 | 
					  verifyModelRef.value?.open(type)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
.mode-item {
 | 
					.mode-item {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  justify-content: space-between;
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
  margin-bottom: 20px;
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
  .mode-item-content {
 | 
					  .mode-item-content {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
    .icon {
 | 
					    .icon {
 | 
				
			||||||
      margin-right: 10px;
 | 
					      margin-right: 10px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,61 +1,38 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Card>
 | 
					  <a-card title="密码策略" bordered class="gradient-card">
 | 
				
			||||||
    <template #header> 密码策略 </template>
 | 
					    <div class="item">
 | 
				
			||||||
    <template #body>
 | 
					      <div class="icon-wrapper"><GiSvgIcon name="password" :size="26" /></div>
 | 
				
			||||||
      <div class="content_title">
 | 
					      <div class="info">
 | 
				
			||||||
        <div class="icon"><GiSvgIcon name="password" :size="36" /></div>
 | 
					        <div class="info-top">
 | 
				
			||||||
        <div>
 | 
					          <span class="label">登录密码</span>
 | 
				
			||||||
          <div style="font-size: 14px; font-weight: 500; line-height: 28px">登录密码</div>
 | 
					 | 
				
			||||||
          <div style="font-size: 12px">为了您的账号安全,建议定期修改密码</div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="info-desc">为了您的账号安全,建议定期修改密码</div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="content_Box">
 | 
					      <div class="btn-wrapper">
 | 
				
			||||||
        <p>
 | 
					        <a-button class="btn">修改</a-button>
 | 
				
			||||||
          密码至少包含 <span class="subTitle">大写字母</span><span class="subTitle">小写字母</span
 | 
					 | 
				
			||||||
          ><span class="subTitle">数字</span><span class="subTitle">特殊字符</span>3种
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
        <p>限制密码长度至少为<span class="subTitle">8</span>位</p>
 | 
					 | 
				
			||||||
        <p>未设置密码有效期</p>
 | 
					 | 
				
			||||||
        <p>新密码不能与历史前<span class="subTitle">3</span>次密码重复</p>
 | 
					 | 
				
			||||||
        <p>1小时内密码错误可重试 <span class="subTitle">5</span>次</p>
 | 
					 | 
				
			||||||
        <p>超过错误密码重试次数账号将被锁定<span class="subTitle">60</span>分钟</p>
 | 
					 | 
				
			||||||
        <p class="link_btn">修改规则(未开发)</p>
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </div>
 | 
				
			||||||
  </Card>
 | 
					    <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">数字</span>
 | 
				
			||||||
 | 
					          <span class="sub-text-value">特殊字符</span>3种
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="sub-text">限制密码长度至少为<span class="sub-text-value">6</span>位</div>
 | 
				
			||||||
 | 
					        <div class="sub-text">未设置密码有效期</div>
 | 
				
			||||||
 | 
					        <div class="sub-text">新密码不能与历史前<span class="sub-text-value">N</span>次密码重复</div>
 | 
				
			||||||
 | 
					        <div class="sub-text">1小时内密码错误可重试<span class="sub-text-value">N</span>次</div>
 | 
				
			||||||
 | 
					        <div class="sub-text">超过错误密码重试次数账号将被锁定<span class="sub-text-value">N</span>分钟</div>
 | 
				
			||||||
 | 
					        <a-link class="link">修改规则(未开放)</a-link>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </a-card>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script setup lang="ts">
 | 
					
 | 
				
			||||||
import Card from '../components/Card.vue'
 | 
					<script lang="ts" setup></script>
 | 
				
			||||||
</script>
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
.content_title {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  .icon {
 | 
					 | 
				
			||||||
    margin-right: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.content_Box {
 | 
					 | 
				
			||||||
  border-left: 1px solid #ccc;
 | 
					 | 
				
			||||||
  margin-left: 40px;
 | 
					 | 
				
			||||||
  padding-left: 10px;
 | 
					 | 
				
			||||||
  font-size: 12px;
 | 
					 | 
				
			||||||
  margin-top: 20px;
 | 
					 | 
				
			||||||
  & > p {
 | 
					 | 
				
			||||||
    margin: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.link_btn {
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
  color: #007aff;
 | 
					 | 
				
			||||||
  &:hover {
 | 
					 | 
				
			||||||
    color: rgba($color: #007aff, $alpha: 0.8);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.subTitle {
 | 
					 | 
				
			||||||
  background: var(--color-neutral-2);
 | 
					 | 
				
			||||||
  padding: 1px 5px;
 | 
					 | 
				
			||||||
  margin: 0px 5px;
 | 
					 | 
				
			||||||
  border-radius: 3px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,48 +1,24 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Card style="height: 100%">
 | 
					  <a-card title="登录会话设置" bordered class="gradient-card">
 | 
				
			||||||
    <template #header> 登录会话设置 </template>
 | 
					    <div class="item">
 | 
				
			||||||
    <template #body>
 | 
					      <div class="icon-wrapper"><GiSvgIcon name="message-color" :size="26" /></div>
 | 
				
			||||||
      <div class="content_title">
 | 
					      <div class="info">
 | 
				
			||||||
        <div class="icon"><GiSvgIcon name="login-status" :size="36" /></div>
 | 
					        <div class="info-top">
 | 
				
			||||||
        <div>
 | 
					          <span class="label">登录态保持时间设置</span>
 | 
				
			||||||
          <div style="font-size: 14px; font-weight: 500; line-height: 28px">登录态保持时间设置</div>
 | 
					 | 
				
			||||||
          <div style="font-size: 12px">保持登录状态的限制</div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="info-desc">保持登录状态的限制</div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="content_Box">
 | 
					    </div>
 | 
				
			||||||
        <p>操作登录会话保持120分钟,超时登录会话将失效</p>
 | 
					    <div class="detail">
 | 
				
			||||||
        <p>登录会话最大保持0天,超时登录会话将失效</p>
 | 
					      <div class="sub-text-wrapper">
 | 
				
			||||||
        <p class="link_btn">修改规则(未开发)</p>
 | 
					        <div class="sub-text">无操作登录会话保持<span class="sub-text-value">30</span>分钟,超时登录会话将失效</div>
 | 
				
			||||||
 | 
					        <div class="sub-text">登录会话最大保持<span class="sub-text-value">0</span>天,超时登录会话将失效</div>
 | 
				
			||||||
 | 
					        <a-link class="link">修改规则(未开放)</a-link class="link">
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </div>
 | 
				
			||||||
  </Card>
 | 
					  </a-card>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script setup lang="ts">
 | 
					
 | 
				
			||||||
import Card from '../components/Card.vue'
 | 
					<script lang="ts" setup></script>
 | 
				
			||||||
</script>
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
.content_title {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  .icon {
 | 
					 | 
				
			||||||
    margin-right: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.content_Box {
 | 
					 | 
				
			||||||
  border-left: 1px solid #ccc;
 | 
					 | 
				
			||||||
  margin-left: 40px;
 | 
					 | 
				
			||||||
  padding-left: 10px;
 | 
					 | 
				
			||||||
  font-size: 12px;
 | 
					 | 
				
			||||||
  margin-top: 20px;
 | 
					 | 
				
			||||||
  & > p {
 | 
					 | 
				
			||||||
    margin: 10px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.link_btn {
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
  color: #007aff;
 | 
					 | 
				
			||||||
  &:hover {
 | 
					 | 
				
			||||||
    color: rgba($color: #007aff, $alpha: 0.8);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,21 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="page">
 | 
					  <div class="gi_page">
 | 
				
			||||||
    <div class="flex_box">
 | 
					    <a-row wrap :gutter="16">
 | 
				
			||||||
      <div class="flex_item_container">
 | 
					      <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="12">
 | 
				
			||||||
        <BasicsSetting />
 | 
					        <BasicsSetting />
 | 
				
			||||||
      </div>
 | 
					      </a-col>
 | 
				
			||||||
      <div class="flex_item_container">
 | 
					      <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="12">
 | 
				
			||||||
        <SessionSetting />
 | 
					        <SessionSetting />
 | 
				
			||||||
      </div>
 | 
					      </a-col>
 | 
				
			||||||
    </div>
 | 
					    </a-row>
 | 
				
			||||||
    <div class="flex_box">
 | 
					    <a-row wrap :gutter="16" style="margin-top: 16px">
 | 
				
			||||||
      <div class="flex_item_container">
 | 
					      <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="12">
 | 
				
			||||||
        <PasswordPolicy />
 | 
					        <PasswordPolicy />
 | 
				
			||||||
      </div>
 | 
					      </a-col>
 | 
				
			||||||
      <div class="flex_item_container">
 | 
					      <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="12">
 | 
				
			||||||
        <AccountProtection />
 | 
					        <AccountProtection />
 | 
				
			||||||
      </div>
 | 
					      </a-col>
 | 
				
			||||||
    </div>
 | 
					    </a-row>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,20 +25,19 @@ import BasicsSetting from './BasicsSetting.vue'
 | 
				
			|||||||
import SessionSetting from './SessionSetting.vue'
 | 
					import SessionSetting from './SessionSetting.vue'
 | 
				
			||||||
import PasswordPolicy from './PasswordPolicy.vue'
 | 
					import PasswordPolicy from './PasswordPolicy.vue'
 | 
				
			||||||
import AccountProtection from './AccountProtection.vue'
 | 
					import AccountProtection from './AccountProtection.vue'
 | 
				
			||||||
defineOptions({ name: 'Security' })
 | 
					
 | 
				
			||||||
 | 
					defineOptions({ name: 'SettingSecurity' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const route = useRoute()
 | 
					const route = useRoute()
 | 
				
			||||||
const form = reactive({ name: '' })
 | 
					const form = reactive({ name: '' })
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
.page {
 | 
					.gi_page {
 | 
				
			||||||
  padding: $padding;
 | 
					 | 
				
			||||||
  background-color: var(--color-bg-1);
 | 
					  background-color: var(--color-bg-1);
 | 
				
			||||||
  .flex_box {
 | 
					  .flex_box {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    margin-bottom: 20px;
 | 
					    margin-bottom: 20px;
 | 
				
			||||||
    height: 100%;
 | 
					 | 
				
			||||||
    .flex_item_container {
 | 
					    .flex_item_container {
 | 
				
			||||||
      width: 100%;
 | 
					      width: 100%;
 | 
				
			||||||
      height: 100%;
 | 
					      height: 100%;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,8 @@ export interface ModeItem {
 | 
				
			|||||||
  title: string
 | 
					  title: string
 | 
				
			||||||
  icon: string
 | 
					  icon: string
 | 
				
			||||||
  subtitle: string
 | 
					  subtitle: string
 | 
				
			||||||
 | 
					  value?: string
 | 
				
			||||||
  type: 'phone' | 'email' | 'gitee' | 'github'
 | 
					  type: 'phone' | 'email' | 'gitee' | 'github'
 | 
				
			||||||
  jumpMode: 'link' | 'modal'
 | 
					  jumpMode?: 'link' | 'modal'
 | 
				
			||||||
  status: boolean
 | 
					  status: boolean
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||