9 changed files with 36 additions and 491 deletions
@ -1,361 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<PageWrapper dense |
|
||||||
fixedHeight |
|
||||||
contentFullHeight |
|
||||||
contentClass="flex" |
|
||||||
> |
|
||||||
<ACard class="base-info w-2/5 xl:w-2/5"> |
|
||||||
<template #title> |
|
||||||
<UserOutlined/> 基本信息 |
|
||||||
</template> |
|
||||||
<div class="user-info-top"> |
|
||||||
<AAvatar draggable |
|
||||||
width="32px" |
|
||||||
:src="getAvatarUrl" |
|
||||||
:size="80" |
|
||||||
/> |
|
||||||
<h2>{{ getUserInfo.nickName }}</h2> |
|
||||||
<p>{{ getUserInfo.remarks }}</p> |
|
||||||
<a-button preIcon="fa6-solid:id-badge" |
|
||||||
type="primary" |
|
||||||
shape="round" |
|
||||||
>{{ getUserInfo.userName }}</a-button> |
|
||||||
</div> |
|
||||||
<ADivider/> |
|
||||||
<div class="user-info-main"> |
|
||||||
<ADescriptions :labelStyle="getLabelStyle" |
|
||||||
:contentStyle="getDescriptionsStyle" |
|
||||||
:colon="false" |
|
||||||
> |
|
||||||
<ADescriptionsItem span="24"> |
|
||||||
<template #label> |
|
||||||
<Icon icon="fa6-solid:building"/> |
|
||||||
</template> |
|
||||||
{{ getUserInfo.deptName }} |
|
||||||
</ADescriptionsItem> |
|
||||||
<ADescriptionsItem span="24"> |
|
||||||
<template #label> |
|
||||||
<Icon icon="fa6-solid:envelope"/> |
|
||||||
</template> |
|
||||||
{{ getUserInfo.email }} |
|
||||||
</ADescriptionsItem> |
|
||||||
<ADescriptionsItem span="24"> |
|
||||||
<template #label> |
|
||||||
<Icon icon="fa6-solid:mobile"/> |
|
||||||
</template> |
|
||||||
{{ getUserInfo.phone }} |
|
||||||
</ADescriptionsItem> |
|
||||||
<ADescriptionsItem span="24"> |
|
||||||
<template #label> |
|
||||||
<Icon icon="fa6-solid:venus-double"/> |
|
||||||
</template> |
|
||||||
{{ ~~getUserInfo.sex === 0 ? '男' : '女' }} |
|
||||||
</ADescriptionsItem> |
|
||||||
<ADescriptionsItem span="24"> |
|
||||||
<template #label> |
|
||||||
<Icon icon="fa6-solid:city"/> |
|
||||||
</template> |
|
||||||
长沙康来公司多租户 |
|
||||||
</ADescriptionsItem> |
|
||||||
<ADescriptionsItem span="24"> |
|
||||||
<template #label> |
|
||||||
<Icon icon="fa6-solid:robot"/> |
|
||||||
</template> |
|
||||||
<h1 style="margin-right: 5px">企业认证:</h1> |
|
||||||
<a v-if="getPushThirdParty.status == '0'">申请中</a> |
|
||||||
<a v-else-if="getPushThirdParty.status == '1'">已认证</a> |
|
||||||
<a v-else @click="handleCertification">申请</a> |
|
||||||
</ADescriptionsItem> |
|
||||||
</ADescriptions> |
|
||||||
</div> |
|
||||||
</ACard> |
|
||||||
<ACard class="info-modify w-3/5 xl:w-3/5"> |
|
||||||
<template #title> |
|
||||||
<CreditCardOutlined/> 资料修改 |
|
||||||
</template> |
|
||||||
<ACard :bordered="false" |
|
||||||
:tab-list="state.tabList" |
|
||||||
:active-tab-key="state.currentCardKey" |
|
||||||
@tabChange="key => state.currentCardKey = key" |
|
||||||
> |
|
||||||
<BasicForm v-show="state.currentCardKey === 'baseInfo'" @register="registerForm"/> |
|
||||||
<AForm v-show="state.currentCardKey === 'uploadAvatar'" |
|
||||||
ref="formElRef" |
|
||||||
class="upload-avatar-form" |
|
||||||
:scrollToFirstError="true" |
|
||||||
@keypress.enter="handleSubmit" |
|
||||||
> |
|
||||||
<AFormItem> |
|
||||||
<AUpload list-type="picture-card" |
|
||||||
class="avatar-uploader" |
|
||||||
accept="image/*" |
|
||||||
:multiple="false" |
|
||||||
:show-upload-list="false" |
|
||||||
:customRequest="handleUploadAvatar" |
|
||||||
:before-upload="handleBeforeUpload" |
|
||||||
> |
|
||||||
<img v-if="getImageUrl" :src="getImageUrl"> |
|
||||||
<div v-else> |
|
||||||
<LoadingOutlined v-if="state.uploadAvatarLoading"/> |
|
||||||
<PlusOutlined v-else/> |
|
||||||
<div class="ant-upload-text">上传头像</div> |
|
||||||
</div> |
|
||||||
</AUpload> |
|
||||||
</AFormItem> |
|
||||||
<AFormItem :wrapperCol="{ style: { 'text-align': 'left' }, class: 'avatar-submit-form-item' }"> |
|
||||||
<a-button preIcon="fa-regular:save" |
|
||||||
:loading="state.uploadAvatarBtnLoading" |
|
||||||
type="primary" |
|
||||||
@click="handleAvatarSubmit" |
|
||||||
>保存</a-button> |
|
||||||
<a-button preIcon="ant-design:delete-twotone" |
|
||||||
danger |
|
||||||
type="primary" |
|
||||||
:disabled="!state.imageUrl" |
|
||||||
@click="state.imageUrl = ''" |
|
||||||
>删除</a-button> |
|
||||||
</AFormItem> |
|
||||||
</AForm> |
|
||||||
</ACard> |
|
||||||
</ACard> |
|
||||||
</PageWrapper> |
|
||||||
<ThirdPartyModal @register="registerModal" @success="handleRefreshPushThirdParty"/> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script lang="ts" setup> |
|
||||||
import {Avatar, Card, Descriptions, Divider, Form, Upload} from 'ant-design-vue'; |
|
||||||
import {PageWrapper} from '/@/components/Page'; |
|
||||||
import {CreditCardOutlined, LoadingOutlined, PlusOutlined, UserOutlined} from '@ant-design/icons-vue'; |
|
||||||
import type {CSSProperties} from 'vue'; |
|
||||||
import {computed, reactive, unref} from 'vue'; |
|
||||||
import {Icon} from '/@/components/Icon'; |
|
||||||
import {BasicForm, useForm} from '/@/components/Form/index'; |
|
||||||
import {userFormSchema} from './userInfo.data'; |
|
||||||
import {useMessage} from '/@/hooks/web/useMessage'; |
|
||||||
import {editUser, getUser} from '/@/api/platform/system/controller/user'; |
|
||||||
import type {User} from '/@/api/platform/core/entity/user'; |
|
||||||
import type {PushThirdParty} from '/@/api/platform/common/entity/pushThirdParty'; |
|
||||||
import {useUserStore} from '/@/store/modules/user'; |
|
||||||
import {commonUpload} from '/@/api/platform/core/controller/upload'; |
|
||||||
import {useGlobSetting} from '/@/hooks/setting'; |
|
||||||
import {isUrl} from '/@/utils/is'; |
|
||||||
import {getPushThirdPartyByUserId} from '/@/api/platform/common/controller/pushThirdParty'; |
|
||||||
import {useModal} from '/@/components/Modal'; |
|
||||||
import ThirdPartyModal from '/@/views/common/push/pushThirdParty/ThirdPartyModal.vue'; |
|
||||||
|
|
||||||
interface InfoState { |
|
||||||
currentCardKey: string; |
|
||||||
baseInfoBtnLoading: boolean; |
|
||||||
uploadAvatarBtnLoading: boolean; |
|
||||||
uploadAvatarLoading: boolean; |
|
||||||
userInfo: User | any; |
|
||||||
pushThirdParty: PushThirdParty | any; |
|
||||||
imageUrl: string; |
|
||||||
tabList: Recordable[]; |
|
||||||
} |
|
||||||
|
|
||||||
const ACard = Card; |
|
||||||
const AAvatar = Avatar; |
|
||||||
const ADescriptions = Descriptions; |
|
||||||
const ADescriptionsItem = Descriptions.Item; |
|
||||||
const ADivider = Divider; |
|
||||||
const AForm = Form; |
|
||||||
const AFormItem = Form.Item; |
|
||||||
const AUpload = Upload; |
|
||||||
const { createMessage } = useMessage(); |
|
||||||
const userStore = useUserStore(); |
|
||||||
const userInfo = userStore.getUserInfo; |
|
||||||
const { apiUrl } = useGlobSetting(); |
|
||||||
const state = reactive<InfoState>({ |
|
||||||
currentCardKey: 'baseInfo', |
|
||||||
baseInfoBtnLoading: false, |
|
||||||
uploadAvatarLoading: false, |
|
||||||
uploadAvatarBtnLoading: false, |
|
||||||
userInfo: undefined, |
|
||||||
pushThirdParty: undefined, |
|
||||||
imageUrl: '', |
|
||||||
tabList: [ |
|
||||||
{ |
|
||||||
key: 'baseInfo', |
|
||||||
tab: '基本信息', |
|
||||||
}, |
|
||||||
{ |
|
||||||
key: 'uploadAvatar', |
|
||||||
tab: '上传头像', |
|
||||||
} |
|
||||||
] |
|
||||||
}); |
|
||||||
|
|
||||||
const [registerModal, { openModal }] = useModal(); |
|
||||||
const defaultAvatarUrl = 'https://godolphinx.org/dolphin1024x1024.png'; |
|
||||||
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate, clearValidate }] = useForm({ |
|
||||||
labelWidth: 100, |
|
||||||
schemas: userFormSchema, |
|
||||||
showSubmitButton: true, |
|
||||||
showResetButton: false, |
|
||||||
showAdvancedButton: false, |
|
||||||
submitButtonOptions: { |
|
||||||
text: '保存', |
|
||||||
preIcon: 'fa-regular:save', |
|
||||||
loading: state.baseInfoBtnLoading, |
|
||||||
onClick: handleSubmit |
|
||||||
}, |
|
||||||
actionColOptions: { span: 24 } |
|
||||||
}); |
|
||||||
getUser(userInfo.id).then(result => { |
|
||||||
state.userInfo = result.result; |
|
||||||
setFieldsValue(result.result); |
|
||||||
handleRefreshPushThirdParty(); |
|
||||||
}); |
|
||||||
|
|
||||||
function handleRefreshPushThirdParty() { |
|
||||||
getPushThirdPartyByUserId(userInfo.id).then(pushThirdParty => state.pushThirdParty = pushThirdParty); |
|
||||||
} |
|
||||||
|
|
||||||
async function handleSubmit() { |
|
||||||
try { |
|
||||||
const formData = await validate(); |
|
||||||
state.baseInfoBtnLoading = true; |
|
||||||
await editUser(formData); |
|
||||||
createMessage.success('保存成功!'); |
|
||||||
const result = await getUser(userInfo.id); |
|
||||||
state.userInfo = result.result; |
|
||||||
await setFieldsValue(result.result); |
|
||||||
} finally { |
|
||||||
state.baseInfoBtnLoading = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
async function handleAvatarSubmit() { |
|
||||||
try { |
|
||||||
state.uploadAvatarBtnLoading = true; |
|
||||||
await editUser({ |
|
||||||
id: userInfo.id, |
|
||||||
avatar: state.imageUrl |
|
||||||
}); |
|
||||||
createMessage.success('保存成功!'); |
|
||||||
const result = await getUser(userInfo.id); |
|
||||||
state.userInfo = result.result; |
|
||||||
await setFieldsValue(result.result); |
|
||||||
} finally { |
|
||||||
state.uploadAvatarBtnLoading = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function handleCertification() { |
|
||||||
openModal(true,{ _tag: 'add' }); |
|
||||||
} |
|
||||||
|
|
||||||
const getUserInfo = computed((): User & any => { |
|
||||||
let userInfo = state.userInfo || {}; |
|
||||||
userInfo.avatar || (userInfo.avatar = defaultAvatarUrl); |
|
||||||
state!.imageUrl = userInfo.avatar; |
|
||||||
return userInfo; |
|
||||||
}); |
|
||||||
|
|
||||||
const getPushThirdParty = computed((): PushThirdParty & any => state.pushThirdParty || {}); |
|
||||||
|
|
||||||
const getAvatarUrl = computed((): string => isUrl(unref(getUserInfo).avatar) ? unref(getUserInfo).avatar : apiUrl + unref(getUserInfo).avatar); |
|
||||||
|
|
||||||
const getImageUrl = computed((): string => (!state.imageUrl || isUrl(state.imageUrl)) ? state.imageUrl : apiUrl + state.imageUrl); |
|
||||||
|
|
||||||
const getLabelStyle = computed((): CSSProperties => ({ |
|
||||||
fontSize: '14px', |
|
||||||
display: 'inline', |
|
||||||
width: '40px' |
|
||||||
})); |
|
||||||
|
|
||||||
const getDescriptionsStyle = computed((): CSSProperties => ({ |
|
||||||
fontSize: '20px', |
|
||||||
color: '#606266', |
|
||||||
'text-align': 'left', |
|
||||||
'font-weight': 400, |
|
||||||
'line-height': '23px', |
|
||||||
'font-size': '14px' |
|
||||||
})); |
|
||||||
|
|
||||||
function handleBeforeUpload (file: File) { |
|
||||||
const isJpgOrPng = file.type?.includes('image/'), |
|
||||||
isLt2M = file.size / 1024 / 1024 < 10; |
|
||||||
|
|
||||||
!isJpgOrPng && createMessage.error('您只能上传 image/* 格式的文件!'); |
|
||||||
!isLt2M && createMessage.error('图片必须小于 10MB!'); |
|
||||||
return isJpgOrPng && isLt2M; |
|
||||||
} |
|
||||||
|
|
||||||
async function handleUploadAvatar(file) { |
|
||||||
try { |
|
||||||
state.uploadAvatarLoading = true; |
|
||||||
const { data } = await commonUpload({ |
|
||||||
file: file.file, |
|
||||||
name: 'file', |
|
||||||
}, () => {}); |
|
||||||
state.imageUrl = data.url; |
|
||||||
} finally { |
|
||||||
state.uploadAvatarLoading = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
</script> |
|
||||||
|
|
||||||
<style lang="less" scoped> |
|
||||||
|
|
||||||
.base-info { |
|
||||||
overflow-y: scroll; |
|
||||||
overflow-x: hidden; |
|
||||||
margin: 10px 15px 10px 10px; |
|
||||||
scrollbar-width: none; |
|
||||||
|
|
||||||
.user-info-top { |
|
||||||
padding: 20px 0px; |
|
||||||
text-align: center; |
|
||||||
|
|
||||||
h2 { |
|
||||||
color: #515a6e; |
|
||||||
margin-top: 10px; |
|
||||||
font-size: 24px; |
|
||||||
} |
|
||||||
|
|
||||||
p { |
|
||||||
color: #999; |
|
||||||
margin-top: 5px; |
|
||||||
font-size: 12px; |
|
||||||
font-weight: 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
.user-info-main { |
|
||||||
padding: 20px 100px; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
.base-info::-webkit-scrollbar { |
|
||||||
display: none; |
|
||||||
} |
|
||||||
|
|
||||||
.info-modify { |
|
||||||
margin: 10px 10px 10px 0px; |
|
||||||
|
|
||||||
::v-deep(.avatar-uploader) > .ant-upload { |
|
||||||
width: 180px; |
|
||||||
height: 180px; |
|
||||||
} |
|
||||||
|
|
||||||
::v-deep(.ant-upload-select-picture-card .ant-upload-text) { |
|
||||||
margin-top: 8px; |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
::v-deep(.upload-avatar-form) > .ant-row { |
|
||||||
margin-bottom: 0px; |
|
||||||
} |
|
||||||
|
|
||||||
::v-deep(.avatar-submit-form-item) button { |
|
||||||
margin-left: 6px; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
</style> |
|
@ -1,96 +0,0 @@ |
|||||||
import { FormSchema } from '/@/components/Table'; |
|
||||||
|
|
||||||
export const userFormSchema: FormSchema[] = [ |
|
||||||
{ |
|
||||||
field: 'id', |
|
||||||
label: 'ID', |
|
||||||
component: 'Input', |
|
||||||
show: false |
|
||||||
}, |
|
||||||
{ |
|
||||||
field: 'nickName', |
|
||||||
label: '用户昵称', |
|
||||||
component: 'Input', |
|
||||||
required: true, |
|
||||||
colProps: { |
|
||||||
span: 12 |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
field: 'deptName', |
|
||||||
label: '归属机构', |
|
||||||
component: 'Input', |
|
||||||
required: true, |
|
||||||
componentProps: { |
|
||||||
disabled: true |
|
||||||
}, |
|
||||||
colProps: { |
|
||||||
span: 12 |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
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' |
|
||||||
} |
|
||||||
], |
|
||||||
colProps: { |
|
||||||
span: 12 |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
field: 'email', |
|
||||||
label: '邮箱', |
|
||||||
component: 'Input', |
|
||||||
rules: [ |
|
||||||
{ |
|
||||||
required: true, |
|
||||||
message: '请输入邮箱!', |
|
||||||
}, |
|
||||||
{ |
|
||||||
type: 'email', |
|
||||||
message: '请输入正确的邮箱地址!', |
|
||||||
validateTrigger: ['blur', 'change'] |
|
||||||
} |
|
||||||
], |
|
||||||
colProps: { |
|
||||||
span: 12 |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
field: 'sex', |
|
||||||
label: '性别', |
|
||||||
component: 'RadioGroup', |
|
||||||
defaultValue: '0', |
|
||||||
required: true, |
|
||||||
componentProps: { |
|
||||||
options: [ |
|
||||||
{ label: '男', value: '0' }, |
|
||||||
{ label: '女', value: '1' } |
|
||||||
] |
|
||||||
}, |
|
||||||
colProps: { |
|
||||||
span: 12 |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
label: '备注', |
|
||||||
field: 'remarks', |
|
||||||
component: 'InputTextArea', |
|
||||||
componentProps: { |
|
||||||
rows: 6 |
|
||||||
}, |
|
||||||
colProps: { |
|
||||||
span: 24 |
|
||||||
} |
|
||||||
} |
|
||||||
]; |
|
Loading…
Reference in new issue