Browse Source

🚀 重构用户上传界面

master
wangxiang 2 years ago
parent
commit
0bbfcfdc8c
  1. 1
      build/vite/plugin/styleImport.ts
  2. 4
      src/components/Cropper/src/CropperAvatar.vue
  3. 10
      src/layouts/default/header/components/user-dropdown/index.vue
  4. 2
      src/locales/lang/en/layout.ts
  5. 2
      src/locales/lang/zh-CN/layout.ts
  6. 3
      src/store/modules/user.ts
  7. 5
      src/utils/is.ts
  8. 92
      src/views/system/user/account/center/Application.vue
  9. 97
      src/views/system/user/account/center/Article.vue
  10. 71
      src/views/system/user/account/center/Project.vue
  11. 132
      src/views/system/user/account/center/data.tsx
  12. 155
      src/views/system/user/account/center/index.vue
  13. 34
      src/views/system/user/account/setting/EntCertification.vue
  14. 152
      src/views/system/user/account/setting/UserInfo.vue
  15. 93
      src/views/system/user/account/setting/data.ts
  16. 88
      src/views/system/user/account/setting/index.vue

1
build/vite/plugin/styleImport.ts

@ -8,7 +8,6 @@ @@ -8,7 +8,6 @@
*/
import styleImport from 'vite-plugin-style-import';
import {createStyleImportPlugin} from 'vite-plugin-style-import';
export function configStyleImportPlugin() {
const styleImportPlugin = styleImport({
libs: [

4
src/components/Cropper/src/CropperAvatar.vue

@ -91,9 +91,9 @@ @@ -91,9 +91,9 @@
}
);
function handleUploadSuccess({ source }) {
function handleUploadSuccess({ source, data }) {
sourceValue.value = source;
emit('change', source);
emit('change', {source, data});
createMessage.success(t('component.cropper.uploadSuccess'));
}

10
src/layouts/default/header/components/user-dropdown/index.vue

@ -43,10 +43,9 @@ @@ -43,10 +43,9 @@
import { propTypes } from '/@/utils/propTypes';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import { useRouter } from 'vue-router';
import { isUrl } from '/@/utils/is';
import { isUrl, isBase64image } from '/@/utils/is';
import { useGlobSetting } from '/@/hooks/setting';
type MenuEvent = 'logout' | 'doc' | 'lock' | 'userInfo';
import defaultAvatar from '/@/assets/images/defaultAvatar.svg';
import type { User } from '/@/api/platform/core/entity/user';
export default defineComponent({
@ -81,14 +80,15 @@ @@ -81,14 +80,15 @@
}
const getAvatarUrl = computed((): string => {
if (!unref(getUserInfo).avatar) return defaultAvatar;
return isUrl(unref(getUserInfo).avatar) ? unref(getUserInfo).avatar : apiUrl + unref(getUserInfo).avatar;
return (isUrl(unref(getUserInfo).avatar) || isBase64image(unref(getUserInfo).avatar))
? unref(getUserInfo).avatar
: apiUrl + unref(getUserInfo).avatar;
});
function handleMenuClick(e: { key: MenuEvent }) {
switch (e.key) {
case 'userInfo':
push('/system/userInfo');
push('/system/accountSetting');
break;
case 'logout':
handleLoginOut();

2
src/locales/lang/en/layout.ts

@ -3,7 +3,7 @@ export default { @@ -3,7 +3,7 @@ export default {
header: {
// 用户下拉
dropdownItemLoginOut: 'Login Out',
dropdownItemUserInfo: 'User Info',
dropdownItemUserInfo: 'User Settings',
tooltipErrorLog: 'Error log',
tooltipLock: 'Lock screen',

2
src/locales/lang/zh-CN/layout.ts

@ -3,7 +3,7 @@ export default { @@ -3,7 +3,7 @@ export default {
header: {
// 用户下拉
dropdownItemLoginOut: '退出系统',
dropdownItemUserInfo: '个人中心',
dropdownItemUserInfo: '个人设置',
// 提示信息
tooltipErrorLog: '错误日志',

3
src/store/modules/user.ts

@ -17,6 +17,8 @@ import { useMessage } from '/@/hooks/web/useMessage'; @@ -17,6 +17,8 @@ import { useMessage } from '/@/hooks/web/useMessage';
import { router } from '/@/router';
import { usePermissionStore } from '/@/store/modules/permission';
import { RouteRecordRaw } from 'vue-router';
import defaultAvatar from '/@/assets/images/defaultAvatar.svg';
import { urlToBase64 } from '/@/utils/file/base64Conver';
interface UserState {
userInfo: Nullable<User>;
@ -133,6 +135,7 @@ export const useUserStore = defineStore({ @@ -133,6 +135,7 @@ export const useUserStore = defineStore({
/** 获取用户信息 */
async getUserInfoAction(): Promise<User> {
const userInfo = await getUserInfo().catch(()=> this.resetState());
userInfo.avatar || (userInfo.avatar = await urlToBase64(defaultAvatar));
userInfo.tenantIds = String(userInfo.tenantId).split(',');
// 存储用户扩展信息,便于鉴权
this.setUserInfo(userInfo);

5
src/utils/is.ts

@ -104,3 +104,8 @@ export function isUrl(path: string): boolean { @@ -104,3 +104,8 @@ export function isUrl(path: string): boolean {
/(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
return reg.test(path);
}
export function isBase64image(base64: string): boolean {
const reg = /data:image\/([^;]+).*/;
return reg.test(base64);
}

92
src/views/system/user/account/center/Application.vue

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
<template>
<List :class="prefixCls">
<a-row :gutter="16">
<template v-for="item in list" :key="item.title">
<a-col :span="6">
<ListItem>
<Card :hoverable="true" :class="`${prefixCls}__card`">
<div :class="`${prefixCls}__card-title`">
<Icon class="icon" v-if="item.icon" :icon="item.icon" :color="item.color" />
{{ item.title }}
</div>
<div :class="`${prefixCls}__card-num`">
活跃用户<span>{{ item.active }}</span>
</div>
<div :class="`${prefixCls}__card-num`">
新增用户<span>{{ item.new }}</span>
</div>
<Icon
:class="`${prefixCls}__card-download`"
v-if="item.download"
:icon="item.download"
/>
</Card>
</ListItem>
</a-col>
</template>
</a-row>
</List>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { List, Card, Row, Col } from 'ant-design-vue';
import Icon from '/@/components/Icon/index';
import { applicationList } from './data';
export default defineComponent({
components: {
List,
ListItem: List.Item,
Card,
Icon,
[Row.name]: Row,
[Col.name]: Col,
},
setup() {
return {
prefixCls: 'account-center-application',
list: applicationList,
};
},
});
</script>
<style lang="less">
.account-center-application {
&__card {
width: 100%;
margin-bottom: -12px;
.ant-card-body {
padding: 16px;
}
&-title {
margin-bottom: 5px;
font-size: 16px;
font-weight: 500;
.icon {
margin-top: -5px;
font-size: 22px;
}
}
&-num {
margin-left: 24px;
line-height: 36px;
color: @text-color-secondary;
span {
margin-left: 5px;
font-size: 18px;
}
}
&-download {
float: right;
font-size: 20px !important;
color: @primary-color;
}
}
}
</style>

97
src/views/system/user/account/center/Article.vue

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
<template>
<List item-layout="vertical" :class="prefixCls">
<template v-for="item in list" :key="item.title">
<ListItem>
<ListItemMeta>
<template #description>
<div :class="`${prefixCls}__content`">
{{ item.content }}
</div>
</template>
<template #title>
<p :class="`${prefixCls}__title`">
{{ item.title }}
</p>
<div>
<template v-for="tag in item.description" :key="tag">
<Tag class="mb-2">
{{ tag }}
</Tag>
</template>
</div>
</template>
</ListItemMeta>
<div>
<template v-for="action in actions" :key="action.text">
<div :class="`${prefixCls}__action`">
<Icon
v-if="action.icon"
:class="`${prefixCls}__action-icon`"
:icon="action.icon"
:color="action.color"
/>
{{ action.text }}
</div>
</template>
<span :class="`${prefixCls}__time`">{{ item.time }}</span>
</div>
</ListItem>
</template>
</List>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { List, Tag } from 'ant-design-vue';
import Icon from '/@/components/Icon/index';
import { actions, articleList } from './data';
export default defineComponent({
components: {
List,
ListItem: List.Item,
ListItemMeta: List.Item.Meta,
Tag,
Icon,
},
setup() {
return {
prefixCls: 'account-center-article',
list: articleList,
actions,
};
},
});
</script>
<style lang="less" scoped>
.account-center-article {
&__title {
margin-bottom: 12px;
font-size: 18px;
}
&__content {
color: rgb(0 0 0 / 65%);
}
&__action {
display: inline-block;
padding: 0 16px;
color: rgb(0 0 0 / 45%);
&:nth-child(1),
&:nth-child(2) {
border-right: 1px solid rgb(206 206 206 / 40%);
}
&-icon {
margin-right: 3px;
}
}
&__time {
position: absolute;
right: 20px;
color: rgb(0 0 0 / 45%);
}
}
</style>

71
src/views/system/user/account/center/Project.vue

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
<template>
<List :class="prefixCls">
<a-row :gutter="16">
<template v-for="item in list" :key="item.title">
<a-col :span="6">
<ListItem>
<Card :hoverable="true" :class="`${prefixCls}__card`">
<img :src="demoImg" />
<div :class="`${prefixCls}__card-title`">
{{ item.title }}
</div>
<div :class="`${prefixCls}__card-content`">
{{ item.content }}
</div>
</Card>
</ListItem>
</a-col>
</template>
</a-row>
</List>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { List, Card, Row, Col } from 'ant-design-vue';
import demoImg from '/@/assets/images/demo.png';
import { projectList } from './data';
export default defineComponent({
components: {
List,
ListItem: List.Item,
Card,
[Row.name]: Row,
[Col.name]: Col,
},
setup() {
return {
prefixCls: 'account-center-project',
list: projectList,
demoImg,
};
},
});
</script>
<style lang="less">
.account-center-project {
&__card {
width: 100%;
.ant-card-body {
padding: 0 0 24px;
}
img {
width: 100%;
height: 130px;
}
&-title {
margin: 5px 10px;
font-size: 16px;
font-weight: 500;
color: rgb(0 0 0 / 85%);
}
&-content {
margin: 5px 10px;
}
}
}
</style>

132
src/views/system/user/account/center/data.tsx

@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
export interface ListItem {
title: string;
icon: string;
color?: string;
}
export interface TabItem {
key: string;
name: string;
component: string;
}
export const tags: string[] = [
'很有想法的',
'专注设计',
'川妹子',
'大长腿',
'海纳百川',
'前端开发',
'vue3',
];
export const teams: ListItem[] = [
{
icon: 'ri:alipay-fill',
title: '科学搬砖组',
color: '#ff4000',
},
{
icon: 'emojione-monotone:letter-a',
title: '中二少年团',
color: '#7c51b8',
},
{
icon: 'ri:alipay-fill',
title: '高逼格设计',
color: '#00adf7',
},
{
icon: 'jam:codepen-circle',
title: '程序员日常',
color: '#00adf7',
},
{
icon: 'fa:behance-square',
title: '科学搬砖组',
color: '#7c51b8',
},
{
icon: 'jam:codepen-circle',
title: '程序员日常',
color: '#ff4000',
},
];
export const details: ListItem[] = [
{
icon: 'ic:outline-contacts',
title: '交互专家',
},
{
icon: 'grommet-icons:cluster',
title: '某某某事业群',
},
{
icon: 'bx:bx-home-circle',
title: '福建省厦门市',
},
];
export const achieveList: TabItem[] = [
{
key: '1',
name: '文章',
component: 'Article',
},
{
key: '2',
name: '应用',
component: 'Application',
},
{
key: '3',
name: '项目',
component: 'Project',
},
];
export const actions: any[] = [
{ icon: 'clarity:star-line', text: '156', color: '#018ffb' },
{ icon: 'bx:bxs-like', text: '156', color: '#459ae8' },
{ icon: 'bx:bxs-message-dots', text: '2', color: '#42d27d' },
];
export const articleList = (() => {
const result: any[] = [];
for (let i = 0; i < 4; i++) {
result.push({
title: 'Vben Admin',
description: ['Vben', '设计语言', 'Typescript'],
content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。',
time: '2020-11-14 11:20',
});
}
return result;
})();
export const applicationList = (() => {
const result: any[] = [];
for (let i = 0; i < 8; i++) {
result.push({
title: 'Vben Admin',
icon: 'emojione-monotone:letter-a',
color: '#1890ff',
active: '100',
new: '1,799',
download: 'bx:bx-download',
});
}
return result;
})();
export const projectList = (() => {
const result: any[] = [];
for (let i = 0; i < 8; i++) {
result.push({
title: 'Vben Admin',
content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。',
});
}
return result;
})();

155
src/views/system/user/account/center/index.vue

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
<template>
<div :class="prefixCls">
<a-row :class="`${prefixCls}-top`">
<a-col :span="9" :class="`${prefixCls}-col`">
<a-row>
<a-col :span="8">
<div :class="`${prefixCls}-top__avatar`">
<img width="70" :src="avatar" />
<span>Vben</span>
<div>海纳百川有容乃大</div>
</div>
</a-col>
<a-col :span="16">
<div :class="`${prefixCls}-top__detail`">
<template v-for="detail in details" :key="detail.title">
<p>
<Icon :icon="detail.icon" />
{{ detail.title }}
</p>
</template>
</div>
</a-col>
</a-row>
</a-col>
<a-col :span="7" :class="`${prefixCls}-col`">
<CollapseContainer title="标签" :canExpan="false">
<template v-for="tag in tags" :key="tag">
<Tag class="mb-2">
{{ tag }}
</Tag>
</template>
</CollapseContainer>
</a-col>
<a-col :span="8" :class="`${prefixCls}-col`">
<CollapseContainer :class="`${prefixCls}-top__team`" title="团队" :canExpan="false">
<div v-for="(team, index) in teams" :key="index" :class="`${prefixCls}-top__team-item`">
<Icon :icon="team.icon" :color="team.color" />
<span>{{ team.title }}</span>
</div>
</CollapseContainer>
</a-col>
</a-row>
<div :class="`${prefixCls}-bottom`">
<Tabs>
<template v-for="item in achieveList" :key="item.key">
<TabPane :tab="item.name">
<component :is="item.component" />
</TabPane>
</template>
</Tabs>
</div>
</div>
</template>
<script lang="ts">
import { Tag, Tabs, Row, Col } from 'ant-design-vue';
import { defineComponent, computed } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import Icon from '/@/components/Icon/index';
import Article from './Article.vue';
import Application from './Application.vue';
import Project from './Project.vue';
import headerImg from '/@/assets/images/header.jpg';
import { tags, teams, details, achieveList } from './data';
import { useUserStore } from '/@/store/modules/user';
export default defineComponent({
components: {
CollapseContainer,
Icon,
Tag,
Tabs,
TabPane: Tabs.TabPane,
Article,
Application,
Project,
[Row.name]: Row,
[Col.name]: Col,
},
setup() {
const userStore = useUserStore();
const avatar = computed(() => userStore.getUserInfo.avatar || headerImg);
return {
prefixCls: 'account-center',
avatar,
tags,
teams,
details,
achieveList,
};
},
});
</script>
<style lang="less" scoped>
.account-center {
&-col:not(:last-child) {
padding: 0 10px;
&:not(:last-child) {
border-right: 1px dashed rgb(206 206 206 / 50%);
}
}
&-top {
padding: 10px;
margin: 16px 16px 12px;
background-color: @component-background;
border-radius: 3px;
&__avatar {
text-align: center;
img {
margin: auto;
border-radius: 50%;
}
span {
display: block;
font-size: 20px;
font-weight: 500;
}
div {
margin-top: 3px;
font-size: 12px;
}
}
&__detail {
padding-left: 20px;
margin-top: 15px;
}
&__team {
&-item {
display: inline-block;
padding: 4px 24px;
}
span {
margin-left: 3px;
}
}
}
&-bottom {
padding: 10px;
margin: 0 16px 16px;
background-color: @component-background;
border-radius: 3px;
}
}
</style>

34
src/views/system/user/account/setting/EntCertification.vue

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
<template>
<CollapseContainer title="企业认证" :canExpan="false">
</CollapseContainer>
</template>
<script lang="ts">
import { List } from 'ant-design-vue';
import { defineComponent } from 'vue';
import { CollapseContainer } from '/@/components/Container/index';
import Icon from '/@/components/Icon/index';
export default defineComponent({
components: {
CollapseContainer
},
setup() {
return {
};
},
});
</script>
<style lang="less" scoped>
.avatar {
font-size: 40px !important;
}
.extra {
float: right;
margin-top: 10px;
margin-right: 30px;
cursor: pointer;
}
</style>

152
src/views/system/user/account/setting/UserInfo.vue

@ -0,0 +1,152 @@ @@ -0,0 +1,152 @@
<template>
<div ref="wrapperRef"
class="user-info"
:style="getContentStyle"
>
<ARow :gutter="24">
<ACol :span="14">
<BasicForm @register="register"/>
</ACol>
<ACol :span="10">
<div class="change-avatar">
<CropperAvatar
:uploadApi="commonUpload"
:value="getAvatarUrl"
btnText="更换头像"
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
:width="150"
@change="handleAvatarChange"
/>
</div>
</ACol>
</ARow>
</div>
</template>
<script lang="ts">
import { Row, Col } from 'ant-design-vue';
import { defineComponent, onMounted, computed, ref, CSSProperties, unref, reactive } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { CropperAvatar } from '/@/components/Cropper';
import { useMessage } from '/@/hooks/web/useMessage';
import { editUser, getUser } from '/@/api/platform/system/controller/user';
import { userFormSchema } from './data';
import { useUserStore } from '/@/store/modules/user';
import { commonUpload } from '/@/api/platform/core/controller/upload';
import { useContentHeight } from '/@/hooks/web/useContentHeight';
import { User } from '/@/api/platform/core/entity/user';
import { isEmpty, isUrl } from '/@/utils/is';
import { assignWith } from 'lodash-es';
import { useGlobSetting } from '/@/hooks/setting';
const { apiUrl } = useGlobSetting();
interface State {
baseInfoBtnLoading: boolean;
userInfo: User | any;
}
export default defineComponent({
components: {
BasicForm,
ARow: Row,
ACol: Col,
CropperAvatar,
},
setup() {
const wrapperRef = ref(null);
const { createMessage } = useMessage();
const userStore = useUserStore();
const userInfoStore = userStore.getUserInfo;
const state = reactive<State>({
baseInfoBtnLoading: false,
userInfo: undefined
});
const [register, { setFieldsValue, validate }] = useForm({
labelWidth: 120,
schemas: userFormSchema,
showSubmitButton: true,
showResetButton: false,
showAdvancedButton: false,
submitButtonOptions: {
text: '保存',
preIcon: 'fa-regular:save',
loading: state.baseInfoBtnLoading,
onClick: handleSubmit
},
actionColOptions: { span: 24 }
});
onMounted(async () => {
const result = await getUser(userInfoStore.id);
state.userInfo = result.result;
await setFieldsValue(result.result);
});
//
const { redoHeight, contentHeight } = useContentHeight(
computed(() => true),
wrapperRef,
[],
[],
ref(30));
const getContentStyle = computed((): CSSProperties => ({ minHeight: `${unref(contentHeight)}px` }));
async function handleSubmit() {
try {
const formData = await validate();
formData.avatar = state.userInfo.avatar;
state.baseInfoBtnLoading = true;
await editUser(formData);
createMessage.success('保存成功!');
const result = await getUser(userInfoStore.id);
state.userInfo = result.result;
//
const userinfo = userStore.getUserInfo;
assignWith(userinfo, state.userInfo, (objValue, srcValue)=> !isEmpty(srcValue) ? srcValue : objValue);
userStore.setUserInfo(userinfo);
await setFieldsValue(result.result);
} finally {
state.baseInfoBtnLoading = false;
}
}
const getUserInfo = computed((): User & any => state.userInfo || {});
const getAvatarUrl = computed((): string => {
if (!unref(getUserInfo).avatar) return userInfoStore.avatar;
return isUrl(unref(getUserInfo).avatar) ? unref(getUserInfo).avatar : apiUrl + unref(getUserInfo).avatar;
});
function handleAvatarChange({ src, data }) {
state.userInfo.avatar = data.url;
}
return {
register,
wrapperRef,
getUserInfo,
commonUpload,
handleAvatarChange,
getContentStyle,
getAvatarUrl,
handleSubmit
};
}
});
</script>
<style lang="less" scoped>
.user-info {
background: @component-background;
> .ant-row:first-child {
.ant-col {
margin-top: 45px;
}
}
.change-avatar {
margin-top: 35px;
text-align: center;
}
}
</style>

93
src/views/system/user/account/setting/data.ts

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
import { FormSchema } from '/@/components/Form/index';
export const settingList = [
{
key: '1',
name: '个人信息',
component: 'UserInfo',
prefixIcon: 'fa:user'
},
{
key: '2',
name: '企业认证',
component: 'EntCertification',
prefixIcon: 'fa6-solid:city'
}
];
export const userFormSchema: FormSchema[] = [
{
field: 'id',
label: 'ID',
component: 'Input',
show: false
},
{
field: 'nickName',
label: '用户昵称',
component: 'Input',
required: true
},
{
field: 'deptName',
label: '归属机构',
component: 'Input',
required: true,
componentProps: {
disabled: true
}
},
{
field: 'phone',
label: '手机号',
component: 'Input',
rules: [
{
required: true,
message: '请输入手机号!',
},
{
pattern: new RegExp('^1[3|4|5|6|7|8|9][0-9]\\d{8}$'),
message: '请输入正确的手机号码!',
validateTrigger: 'blur'
}
]
},
{
field: 'email',
label: '邮箱',
component: 'Input',
rules: [
{
required: true,
message: '请输入邮箱!',
},
{
type: 'email',
message: '请输入正确的邮箱地址!',
validateTrigger: ['blur', 'change']
}
]
},
{
field: 'sex',
label: '性别',
component: 'RadioGroup',
defaultValue: '0',
required: true,
componentProps: {
options: [
{ label: '男', value: '0' },
{ label: '女', value: '1' }
]
}
},
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
componentProps: {
rows: 6
}
}
];

88
src/views/system/user/account/setting/index.vue

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
<template>
<ScrollContainer>
<div class="account-setting">
<Tabs tab-position="left"
:animated="false"
:tabBarStyle="tabBarStyle"
>
<template v-for="item in settingList" :key="item.key">
<TabPane>
<template #tab>
<span>
<Icon :icon="item.prefixIcon"/>
{{ item.name }}
</span>
</template>
<component :is="item.component"/>
</TabPane>
</template>
</Tabs>
</div>
</ScrollContainer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Tabs } from 'ant-design-vue';
import { ScrollContainer } from '/@/components/Container/index';
import { settingList } from './data';
import UserInfo from './UserInfo.vue';
import EntCertification from './EntCertification.vue';
import { Icon } from '/@/components/Icon';
export default defineComponent({
components: {
ScrollContainer,
Tabs,
TabPane: Tabs.TabPane,
UserInfo,
EntCertification,
Icon
},
setup() {
return {
settingList,
tabBarStyle: {
width: '220px',
'margin-bottom': '200px'
},
};
},
});
</script>
<style lang="less" scoped>
.account-setting {
margin: 12px;
::v-deep(.ant-tabs-left) > .ant-tabs-left-bar {
background-color: @component-background;
height: 260px;
.ant-tabs-tab {
padding: 8px 24px;
text-align: left;
}
.ant-tabs-tab-active {
border-radius: 0 20px 20px 0;
background-color: @primary-color !important;
color: #fff!important;
}
.ant-tabs-nav {
padding-top: 14px;
padding-right: 14px;
padding-bottom: 14px;
}
.ant-tabs-ink-bar {
height: 0px !important;
}
}
::v-deep(.ant-tabs) > .ant-tabs-left-content {
padding-left: 10px;
}
}
</style>
Loading…
Cancel
Save