mirror of https://github.com/certd/certd
perf: 添加用户资料编辑功能
- 新增用户资料编辑对话框组件 - 添加后端更新用户资料接口 - 在用户信息页面添加编辑按钮 - 新增中英文翻译字段 - 实现头像上传和昵称修改功能pull/453/head
parent
c1bccb970f
commit
7c0f43c8a3
|
@ -81,4 +81,5 @@ export default {
|
|||
nickName: "Nickname",
|
||||
phoneNumber: "Phone Number",
|
||||
changePassword: "Change Password",
|
||||
updateProfile: "Update Profile",
|
||||
};
|
||||
|
|
|
@ -19,4 +19,5 @@ export default {
|
|||
create: "Create",
|
||||
yes: "Yes",
|
||||
no: "No",
|
||||
handle: "Handle",
|
||||
};
|
||||
|
|
|
@ -82,4 +82,5 @@ export default {
|
|||
nickName: "昵称",
|
||||
phoneNumber: "手机号",
|
||||
changePassword: "修改密码",
|
||||
updateProfile: "修改个人信息",
|
||||
};
|
||||
|
|
|
@ -19,4 +19,5 @@ export default {
|
|||
create: "新增",
|
||||
yes: "是",
|
||||
no: "否",
|
||||
handle: "操作",
|
||||
};
|
||||
|
|
|
@ -14,3 +14,11 @@ export async function changePassword(form: any) {
|
|||
data: form,
|
||||
});
|
||||
}
|
||||
|
||||
export async function UpdateProfile(form: any) {
|
||||
return await request({
|
||||
url: "/mine/updateProfile",
|
||||
method: "POST",
|
||||
data: form,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
// useUserProfile, 获取 openEditProfileDialog ,参考 useTemplate方法
|
||||
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import { ref } from "vue";
|
||||
import { cloneDeep, merge } from "lodash-es";
|
||||
|
||||
// 假设的 API 导入
|
||||
import * as userProfileApi from "./api";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { useI18n } from "/src/locales";
|
||||
|
||||
/**
|
||||
* 获取用户资料编辑相关功能
|
||||
* @returns {{openEditProfileDialog: openEditProfileDialog}}
|
||||
*/
|
||||
export function useUserProfile() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const wrapperRef = ref();
|
||||
async function openEditProfileDialog(req: { onUpdated?: (ctx: any) => void }) {
|
||||
const detail = await userProfileApi.getMineInfo();
|
||||
if (!detail) {
|
||||
throw new Error("用户资料不存在");
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const userProfileFormRef = ref();
|
||||
async function doSubmit(opts: { form: any }) {
|
||||
const form = opts.form;
|
||||
const { id } = await userProfileApi.UpdateProfile(form);
|
||||
if (req.onUpdated) {
|
||||
req.onUpdated({ id });
|
||||
}
|
||||
}
|
||||
|
||||
const crudOptions: any = {
|
||||
form: {
|
||||
doSubmit,
|
||||
wrapper: {
|
||||
title: `编辑用户资料`,
|
||||
width: 1100,
|
||||
onOpened(opts: { form: any }) {
|
||||
merge(opts.form, detail);
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
nickName: {
|
||||
title: t("certd.nickName"),
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
placeholder: t("certd.nickName"),
|
||||
},
|
||||
rules: [{ required: true, message: t("certd.nickName") }],
|
||||
},
|
||||
},
|
||||
avatar: {
|
||||
title: t("certd.avatar"),
|
||||
type: "cropper-uploader",
|
||||
column: {
|
||||
width: 70,
|
||||
component: {
|
||||
style: {
|
||||
height: "30px",
|
||||
width: "auto",
|
||||
},
|
||||
buildUrl(key: string) {
|
||||
return `api/basic/file/download?&key=` + key;
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
vModel: "modelValue",
|
||||
valueType: "key",
|
||||
cropper: {
|
||||
aspectRatio: 1,
|
||||
autoCropArea: 1,
|
||||
viewMode: 0,
|
||||
},
|
||||
onReady: null,
|
||||
uploader: {
|
||||
type: "form",
|
||||
action: "/basic/file/upload",
|
||||
name: "file",
|
||||
headers: {
|
||||
Authorization: "Bearer " + userStore.getToken,
|
||||
},
|
||||
successHandle(res: any) {
|
||||
return res;
|
||||
},
|
||||
},
|
||||
buildUrl(key: string) {
|
||||
return `api/basic/file/download?&key=` + key;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = await openCrudFormDialog({ crudOptions });
|
||||
wrapperRef.value = wrapper;
|
||||
}
|
||||
|
||||
return {
|
||||
openEditProfileDialog,
|
||||
};
|
||||
}
|
|
@ -4,19 +4,21 @@
|
|||
<div class="title">{{ t("certd.myInfo") }}</div>
|
||||
</template>
|
||||
<div class="p-10">
|
||||
<a-descriptions title="" bordered :column="1">
|
||||
<a-descriptions title="" bordered :column="2">
|
||||
<a-descriptions-item :label="t('authentication.username')">{{ userInfo.username }}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('authentication.nickName')">{{ userInfo.nickName }}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('authentication.avatar')">
|
||||
<a-avatar v-if="userInfo.avatar" size="large" :src="'api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
||||
<a-avatar v-else size="large" style="background-color: #00b4f5">
|
||||
{{ userInfo.username }}
|
||||
</a-avatar>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('authentication.nickName')">{{ userInfo.nickName }}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('authentication.email')">{{ userInfo.email }}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('authentication.phoneNumber')">{{ userInfo.phoneCode }}{{ userInfo.mobile }}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('authentication.changePassword')">
|
||||
<change-password-button :show-button="true"> </change-password-button>
|
||||
<a-descriptions-item></a-descriptions-item>
|
||||
<a-descriptions-item :label="t('common.handle')">
|
||||
<a-button type="primary" @click="doUpdate">{{ t("authentication.updateProfile") }}</a-button>
|
||||
<change-password-button class="ml-10" :show-button="true"> </change-password-button>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
|
@ -28,6 +30,7 @@ import * as api from "./api";
|
|||
import { Ref, ref } from "vue";
|
||||
import ChangePasswordButton from "/@/views/certd/mine/change-password-button.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useUserProfile } from "./use";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -41,4 +44,14 @@ const getUserInfo = async () => {
|
|||
userInfo.value = await api.getMineInfo();
|
||||
};
|
||||
getUserInfo();
|
||||
|
||||
const { openEditProfileDialog } = useUserProfile();
|
||||
|
||||
function doUpdate() {
|
||||
openEditProfileDialog({
|
||||
onUpdated: async () => {
|
||||
await getUserInfo();
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -32,4 +32,15 @@ export class MineController extends BaseController {
|
|||
await this.userService.changePassword(userId, body);
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/updateProfile', { summary: Constants.per.authOnly })
|
||||
public async updateProfile(@Body(ALL) body: any) {
|
||||
const userId = this.getUserId();
|
||||
|
||||
await this.userService.updateProfile(userId, {
|
||||
avatar: body.avatar,
|
||||
nickName: body.nickName,
|
||||
});
|
||||
return this.ok({});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ export const AdminRoleId = 1
|
|||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class UserService extends BaseService<UserEntity> {
|
||||
|
||||
@InjectEntityModel(UserEntity)
|
||||
repository: Repository<UserEntity>;
|
||||
@Inject()
|
||||
|
@ -335,4 +336,14 @@ export class UserService extends BaseService<UserEntity> {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
async updateProfile(userId: any, body: any) {
|
||||
|
||||
await this.update({
|
||||
id: userId,
|
||||
...body,
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue