陈薪名
9 months ago
7 changed files with 0 additions and 468 deletions
@ -1,92 +0,0 @@ |
|||||
<template> |
|
||||
<Form ref="formRef" :labelWidth="200" :rules="rules" :schema="schema"> |
|
||||
<template #sex="form"> |
|
||||
<el-radio-group v-model="form['sex']"> |
|
||||
<el-radio :label="1">{{ t('profile.user.man') }}</el-radio> |
|
||||
<el-radio :label="2">{{ t('profile.user.woman') }}</el-radio> |
|
||||
</el-radio-group> |
|
||||
</template> |
|
||||
</Form> |
|
||||
<div style="text-align: center"> |
|
||||
<XButton :title="t('common.save')" type="primary" @click="submit()" /> |
|
||||
<XButton :title="t('common.reset')" type="danger" @click="init()" /> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script lang="ts" setup> |
|
||||
import type { FormRules } from 'element-plus' |
|
||||
import { FormSchema } from '@/types/form' |
|
||||
import type { FormExpose } from '@/components/Form' |
|
||||
import { |
|
||||
getUserProfile, |
|
||||
updateUserProfile, |
|
||||
UserProfileUpdateReqVO |
|
||||
} from '@/api/system/user/profile' |
|
||||
|
|
||||
defineOptions({ name: 'BasicInfo' }) |
|
||||
|
|
||||
const { t } = useI18n() |
|
||||
const message = useMessage() // 消息弹窗 |
|
||||
// 表单校验 |
|
||||
const rules = reactive<FormRules>({ |
|
||||
nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }], |
|
||||
email: [ |
|
||||
{ required: true, message: t('profile.rules.mail'), trigger: 'blur' }, |
|
||||
{ |
|
||||
type: 'email', |
|
||||
message: t('profile.rules.truemail'), |
|
||||
trigger: ['blur', 'change'] |
|
||||
} |
|
||||
], |
|
||||
mobile: [ |
|
||||
{ required: true, message: t('profile.rules.phone'), trigger: 'blur' }, |
|
||||
{ |
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, |
|
||||
message: t('profile.rules.truephone'), |
|
||||
trigger: 'blur' |
|
||||
} |
|
||||
] |
|
||||
}) |
|
||||
const schema = reactive<FormSchema[]>([ |
|
||||
{ |
|
||||
field: 'nickname', |
|
||||
label: t('profile.user.nickname'), |
|
||||
component: 'Input' |
|
||||
}, |
|
||||
{ |
|
||||
field: 'mobile', |
|
||||
label: t('profile.user.mobile'), |
|
||||
component: 'Input' |
|
||||
}, |
|
||||
{ |
|
||||
field: 'email', |
|
||||
label: t('profile.user.email'), |
|
||||
component: 'Input' |
|
||||
}, |
|
||||
{ |
|
||||
field: 'sex', |
|
||||
label: t('profile.user.sex'), |
|
||||
component: 'InputNumber', |
|
||||
value: 0 |
|
||||
} |
|
||||
]) |
|
||||
const formRef = ref<FormExpose>() // 表单 Ref |
|
||||
const submit = () => { |
|
||||
const elForm = unref(formRef)?.getElFormRef() |
|
||||
if (!elForm) return |
|
||||
elForm.validate(async (valid) => { |
|
||||
if (valid) { |
|
||||
const data = unref(formRef)?.formModel as UserProfileUpdateReqVO |
|
||||
await updateUserProfile(data) |
|
||||
message.success(t('common.updateSuccess')) |
|
||||
await init() |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
const init = async () => { |
|
||||
const res = await getUserProfile() |
|
||||
unref(formRef)?.setValues(res) |
|
||||
} |
|
||||
onMounted(async () => { |
|
||||
await init() |
|
||||
}) |
|
||||
</script> |
|
@ -1,99 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<div class="text-center"> |
|
||||
<UserAvatar :img="userInfo?.avatar" /> |
|
||||
</div> |
|
||||
<ul class="list-group list-group-striped"> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="ep:user" /> |
|
||||
{{ t('profile.user.username') }} |
|
||||
<div class="pull-right">{{ userInfo?.username }}</div> |
|
||||
</li> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="ep:phone" /> |
|
||||
{{ t('profile.user.mobile') }} |
|
||||
<div class="pull-right">{{ userInfo?.mobile }}</div> |
|
||||
</li> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="fontisto:email" /> |
|
||||
{{ t('profile.user.email') }} |
|
||||
<div class="pull-right">{{ userInfo?.email }}</div> |
|
||||
</li> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="carbon:tree-view-alt" /> |
|
||||
{{ t('profile.user.dept') }} |
|
||||
<div v-if="userInfo?.dept" class="pull-right">{{ userInfo?.dept.name }}</div> |
|
||||
</li> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="ep:suitcase" /> |
|
||||
{{ t('profile.user.posts') }} |
|
||||
<div v-if="userInfo?.posts" class="pull-right"> |
|
||||
{{ userInfo?.posts.map((post) => post.name).join(',') }} |
|
||||
</div> |
|
||||
</li> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="icon-park-outline:peoples" /> |
|
||||
{{ t('profile.user.roles') }} |
|
||||
<div v-if="userInfo?.roles" class="pull-right"> |
|
||||
{{ userInfo?.roles.map((role) => role.name).join(',') }} |
|
||||
</div> |
|
||||
</li> |
|
||||
<li class="list-group-item"> |
|
||||
<Icon class="mr-5px" icon="ep:calendar" /> |
|
||||
{{ t('profile.user.createTime') }} |
|
||||
<div class="pull-right">{{ formatDate(userInfo?.createTime) }}</div> |
|
||||
</li> |
|
||||
</ul> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script lang="ts" setup> |
|
||||
import { formatDate } from '@/utils/formatTime' |
|
||||
import UserAvatar from './UserAvatar.vue' |
|
||||
|
|
||||
import { getUserProfile, ProfileVO } from '@/api/system/user/profile' |
|
||||
|
|
||||
defineOptions({ name: 'ProfileUser' }) |
|
||||
|
|
||||
const { t } = useI18n() |
|
||||
const userInfo = ref<ProfileVO>() |
|
||||
const getUserInfo = async () => { |
|
||||
const users = await getUserProfile() |
|
||||
userInfo.value = users |
|
||||
} |
|
||||
onMounted(async () => { |
|
||||
await getUserInfo() |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.text-center { |
|
||||
position: relative; |
|
||||
height: 120px; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.list-group-striped > .list-group-item { |
|
||||
padding-right: 0; |
|
||||
padding-left: 0; |
|
||||
border-right: 0; |
|
||||
border-left: 0; |
|
||||
border-radius: 0; |
|
||||
} |
|
||||
|
|
||||
.list-group { |
|
||||
padding-left: 0; |
|
||||
list-style: none; |
|
||||
} |
|
||||
|
|
||||
.list-group-item { |
|
||||
padding: 11px 0; |
|
||||
margin-bottom: -1px; |
|
||||
font-size: 13px; |
|
||||
border-top: 1px solid #e7eaec; |
|
||||
border-bottom: 1px solid #e7eaec; |
|
||||
} |
|
||||
|
|
||||
.pull-right { |
|
||||
float: right !important; |
|
||||
} |
|
||||
</style> |
|
@ -1,73 +0,0 @@ |
|||||
<template> |
|
||||
<el-form ref="formRef" :model="password" :rules="rules" :label-width="200"> |
|
||||
<el-form-item :label="t('profile.password.oldPassword')" prop="oldPassword"> |
|
||||
<InputPassword v-model="password.oldPassword" /> |
|
||||
</el-form-item> |
|
||||
<el-form-item :label="t('profile.password.newPassword')" prop="newPassword"> |
|
||||
<InputPassword v-model="password.newPassword" strength /> |
|
||||
</el-form-item> |
|
||||
<el-form-item :label="t('profile.password.confirmPassword')" prop="confirmPassword"> |
|
||||
<InputPassword v-model="password.confirmPassword" strength /> |
|
||||
</el-form-item> |
|
||||
<el-form-item> |
|
||||
<XButton :title="t('common.save')" type="primary" @click="submit(formRef)" /> |
|
||||
<XButton :title="t('common.reset')" type="danger" @click="reset(formRef)" /> |
|
||||
</el-form-item> |
|
||||
</el-form> |
|
||||
</template> |
|
||||
<script lang="ts" setup> |
|
||||
import type { FormInstance, FormRules } from 'element-plus' |
|
||||
|
|
||||
import { InputPassword } from '@/components/InputPassword' |
|
||||
import { updateUserPassword } from '@/api/system/user/profile' |
|
||||
|
|
||||
defineOptions({ name: 'ResetPwd' }) |
|
||||
|
|
||||
const { t } = useI18n() |
|
||||
const message = useMessage() |
|
||||
const formRef = ref<FormInstance>() |
|
||||
const password = reactive({ |
|
||||
oldPassword: '', |
|
||||
newPassword: '', |
|
||||
confirmPassword: '' |
|
||||
}) |
|
||||
|
|
||||
// 表单校验 |
|
||||
const equalToPassword = (_rule, value, callback) => { |
|
||||
if (password.newPassword !== value) { |
|
||||
callback(new Error(t('profile.password.diffPwd'))) |
|
||||
} else { |
|
||||
callback() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const rules = reactive<FormRules>({ |
|
||||
oldPassword: [ |
|
||||
{ required: true, message: t('profile.password.oldPwdMsg'), trigger: 'blur' }, |
|
||||
{ min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' } |
|
||||
], |
|
||||
newPassword: [ |
|
||||
{ required: true, message: t('profile.password.newPwdMsg'), trigger: 'blur' }, |
|
||||
{ min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' } |
|
||||
], |
|
||||
confirmPassword: [ |
|
||||
{ required: true, message: t('profile.password.cfPwdMsg'), trigger: 'blur' }, |
|
||||
{ required: true, validator: equalToPassword, trigger: 'blur' } |
|
||||
] |
|
||||
}) |
|
||||
|
|
||||
const submit = (formEl: FormInstance | undefined) => { |
|
||||
if (!formEl) return |
|
||||
formEl.validate(async (valid) => { |
|
||||
if (valid) { |
|
||||
await updateUserPassword(password.oldPassword, password.newPassword) |
|
||||
message.success(t('common.updateSuccess')) |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const reset = (formEl: FormInstance | undefined) => { |
|
||||
if (!formEl) return |
|
||||
formEl.resetFields() |
|
||||
} |
|
||||
</script> |
|
@ -1,39 +0,0 @@ |
|||||
<template> |
|
||||
<div class="change-avatar"> |
|
||||
<CropperAvatar |
|
||||
ref="cropperRef" |
|
||||
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }" |
|
||||
:showBtn="false" |
|
||||
:value="img" |
|
||||
width="120px" |
|
||||
@change="handelUpload" |
|
||||
/> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script lang="ts" setup> |
|
||||
import { propTypes } from '@/utils/propTypes' |
|
||||
import { uploadAvatar } from '@/api/system/user/profile' |
|
||||
import { CropperAvatar } from '@/components/Cropper' |
|
||||
|
|
||||
defineOptions({ name: 'UserAvatar' }) |
|
||||
|
|
||||
defineProps({ |
|
||||
img: propTypes.string.def('') |
|
||||
}) |
|
||||
|
|
||||
const cropperRef = ref() |
|
||||
const handelUpload = async ({ data }) => { |
|
||||
await uploadAvatar({ avatarFile: data }) |
|
||||
cropperRef.value.close() |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.change-avatar { |
|
||||
img { |
|
||||
display: block; |
|
||||
margin-bottom: 15px; |
|
||||
border-radius: 50%; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
@ -1,94 +0,0 @@ |
|||||
<template> |
|
||||
<el-table :data="socialUsers" :show-header="false"> |
|
||||
<el-table-column fixed="left" title="序号" type="seq" width="60" /> |
|
||||
<el-table-column align="left" label="社交平台" width="120"> |
|
||||
<template #default="{ row }"> |
|
||||
<img :src="row.img" alt="" class="h-5 align-middle" /> |
|
||||
<p class="mr-5">{{ row.title }}</p> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
<el-table-column align="center" label="操作"> |
|
||||
<template #default="{ row }"> |
|
||||
<template v-if="row.openid"> |
|
||||
已绑定 |
|
||||
<XTextButton class="mr-5" title="(解绑)" type="primary" @click="unbind(row)" /> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
未绑定 |
|
||||
<XTextButton class="mr-5" title="(绑定)" type="primary" @click="bind(row)" /> |
|
||||
</template> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
</el-table> |
|
||||
</template> |
|
||||
<script lang="ts" setup> |
|
||||
import { SystemUserSocialTypeEnum } from '@/utils/constants' |
|
||||
import { getUserProfile, ProfileVO } from '@/api/system/user/profile' |
|
||||
import { socialAuthRedirect, socialBind, socialUnbind } from '@/api/system/user/socialUser' |
|
||||
|
|
||||
defineOptions({ name: 'UserSocial' }) |
|
||||
|
|
||||
const message = useMessage() |
|
||||
const socialUsers = ref<any[]>([]) |
|
||||
const userInfo = ref<ProfileVO>() |
|
||||
|
|
||||
const initSocial = async () => { |
|
||||
const res = await getUserProfile() |
|
||||
userInfo.value = res |
|
||||
for (const i in SystemUserSocialTypeEnum) { |
|
||||
const socialUser = { ...SystemUserSocialTypeEnum[i] } |
|
||||
socialUsers.value.push(socialUser) |
|
||||
if (userInfo.value?.socialUsers) { |
|
||||
for (const j in userInfo.value.socialUsers) { |
|
||||
if (socialUser.type === userInfo.value.socialUsers[j].type) { |
|
||||
socialUser.openid = userInfo.value.socialUsers[j].openid |
|
||||
break |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
const route = useRoute() |
|
||||
const bindSocial = () => { |
|
||||
// 社交绑定 |
|
||||
const type = route.query.type |
|
||||
const code = route.query.code |
|
||||
const state = route.query.state |
|
||||
if (!code) { |
|
||||
return |
|
||||
} |
|
||||
socialBind(type, code, state).then(() => { |
|
||||
message.success('绑定成功') |
|
||||
initSocial() |
|
||||
}) |
|
||||
} |
|
||||
const bind = (row) => { |
|
||||
const redirectUri = location.origin + '/user/profile?type=' + row.type |
|
||||
// 进行跳转 |
|
||||
socialAuthRedirect(row.type, encodeURIComponent(redirectUri)).then((res) => { |
|
||||
window.location.href = res |
|
||||
}) |
|
||||
} |
|
||||
const unbind = async (row) => { |
|
||||
const res = await socialUnbind(row.type, row.openid) |
|
||||
if (res) { |
|
||||
row.openid = undefined |
|
||||
} |
|
||||
message.success('解绑成功') |
|
||||
} |
|
||||
|
|
||||
onMounted(async () => { |
|
||||
await initSocial() |
|
||||
}) |
|
||||
|
|
||||
watch( |
|
||||
() => route, |
|
||||
(newRoute) => { |
|
||||
bindSocial() |
|
||||
console.log(newRoute) |
|
||||
}, |
|
||||
{ |
|
||||
immediate: true |
|
||||
} |
|
||||
) |
|
||||
</script> |
|
@ -1,7 +0,0 @@ |
|||||
import BasicInfo from './BasicInfo.vue' |
|
||||
import ProfileUser from './ProfileUser.vue' |
|
||||
import ResetPwd from './ResetPwd.vue' |
|
||||
import UserAvatarVue from './UserAvatar.vue' |
|
||||
import UserSocial from './UserSocial.vue' |
|
||||
|
|
||||
export { BasicInfo, ProfileUser, ResetPwd, UserAvatarVue, UserSocial } |
|
@ -1,64 +0,0 @@ |
|||||
<template> |
|
||||
<div class="flex"> |
|
||||
<el-card class="user w-1/3" shadow="hover"> |
|
||||
<template #header> |
|
||||
<div class="card-header"> |
|
||||
<span>{{ t('profile.user.title') }}</span> |
|
||||
</div> |
|
||||
</template> |
|
||||
<ProfileUser /> |
|
||||
</el-card> |
|
||||
<el-card class="user ml-3 w-2/3" shadow="hover"> |
|
||||
<template #header> |
|
||||
<div class="card-header"> |
|
||||
<span>{{ t('profile.info.title') }}</span> |
|
||||
</div> |
|
||||
</template> |
|
||||
<div> |
|
||||
<el-tabs v-model="activeName" tab-position="top" style="height: 400px" class="profile-tabs"> |
|
||||
<el-tab-pane :label="t('profile.info.basicInfo')" name="basicInfo"> |
|
||||
<BasicInfo /> |
|
||||
</el-tab-pane> |
|
||||
<el-tab-pane :label="t('profile.info.resetPwd')" name="resetPwd"> |
|
||||
<ResetPwd /> |
|
||||
</el-tab-pane> |
|
||||
<!-- <el-tab-pane :label="t('profile.info.userSocial')" name="userSocial"> |
|
||||
<UserSocial /> |
|
||||
</el-tab-pane> --> |
|
||||
</el-tabs> |
|
||||
</div> |
|
||||
</el-card> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts" name="Profile"> |
|
||||
import { BasicInfo, ProfileUser, ResetPwd, UserSocial } from './components/' |
|
||||
const { t } = useI18n() |
|
||||
|
|
||||
const activeName = ref('basicInfo') |
|
||||
</script> |
|
||||
<style scoped> |
|
||||
.user { |
|
||||
max-height: 960px; |
|
||||
padding: 15px 20px 20px; |
|
||||
} |
|
||||
|
|
||||
.card-header { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
:deep(.el-card .el-card__header, .el-card .el-card__body) { |
|
||||
padding: 15px !important; |
|
||||
} |
|
||||
|
|
||||
.profile-tabs > .el-tabs__content { |
|
||||
padding: 32px; |
|
||||
font-weight: 600; |
|
||||
color: #6b778c; |
|
||||
} |
|
||||
|
|
||||
.el-tabs--left .el-tabs__content { |
|
||||
height: 100%; |
|
||||
} |
|
||||
</style> |
|
Loading…
Reference in new issue