16 changed files with 931 additions and 10 deletions
@ -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> |
@ -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> |
@ -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> |
@ -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; |
||||||
|
})(); |
@ -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> |
@ -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> |
@ -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> |
@ -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 |
||||||
|
} |
||||||
|
} |
||||||
|
]; |
@ -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…
Reference in new issue