Translate user account info page

pull/436/head
Lorenzo 2025-06-26 00:36:22 +02:00
parent dfddfc3e06
commit b50121ad0b
8 changed files with 303 additions and 258 deletions

View File

@ -77,4 +77,8 @@ export default {
newPasswordNotSameOld: "The new password cannot be the same as the old password", newPasswordNotSameOld: "The new password cannot be the same as the old password",
enterPasswordAgain: "Please enter the password again", enterPasswordAgain: "Please enter the password again",
passwordsNotMatch: "The two passwords do not match!", passwordsNotMatch: "The two passwords do not match!",
avatar: "Avatar",
nickName: "Nickname",
phoneNumber: "Phone Number",
changePassword: "Change Password",
} }

View File

@ -289,4 +289,15 @@ export default {
step3: "3. Enter the verification code", step3: "3. Enter the verification code",
inputVerifyCode: "Please enter the verification code", inputVerifyCode: "Please enter the verification code",
cancel: "Cancel", cancel: "Cancel",
authorizationManagement: "Authorization Management",
manageThirdPartyAuth: "Manage third-party system authorization information",
name: "Name",
pleaseEnterName: "Please enter the name",
nameHelper: "Fill in as you like, useful to distinguish when multiple authorizations of the same type exist",
level: "Level",
system: "System",
usera: "User",
nickName: "Nickname",
max50Chars: "Maximum 50 characters",
myInfo: "My Information",
}; };

View File

@ -78,4 +78,8 @@ export default {
newPasswordNotSameOld: "新密码不能和旧密码相同", newPasswordNotSameOld: "新密码不能和旧密码相同",
enterPasswordAgain: "请再次输入密码", enterPasswordAgain: "请再次输入密码",
passwordsNotMatch: "两次输入密码不一致!", passwordsNotMatch: "两次输入密码不一致!",
avatar: "头像",
nickName: "昵称",
phoneNumber: "手机号",
changePassword: "修改密码",
} }

View File

@ -295,4 +295,15 @@ export default {
step3: "3. 输入验证码", step3: "3. 输入验证码",
inputVerifyCode: "请输入验证码", inputVerifyCode: "请输入验证码",
cancel: "取消", cancel: "取消",
authorizationManagement: "授权管理",
manageThirdPartyAuth: "管理第三方系统授权信息",
name: "名称",
pleaseEnterName: "请填写名称",
nameHelper: "随便填,当多个相同类型的授权时,便于区分",
level: "级别",
system: "系统",
usera: "用户",
nickName: "昵称",
max50Chars: "最大50个字符",
myInfo: "我的信息",
}; };

View File

@ -2,141 +2,143 @@
import { ref } from "vue"; import { ref } from "vue";
import { getCommonColumnDefine } from "/@/views/certd/access/common"; import { getCommonColumnDefine } from "/@/views/certd/access/common";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { useI18n } from "vue-i18n";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding } = crudExpose; const { t } = useI18n();
const { props, ctx, api } = context; const { crudBinding } = crudExpose;
const lastResRef = ref(); const { props, ctx, api } = context;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const lastResRef = ref();
return await context.api.GetList(query); const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
}; return await context.api.GetList(query);
const editRequest = async (req: EditReq) => { };
const { form, row } = req; const editRequest = async (req: EditReq) => {
form.id = row.id; const { form, row } = req;
form.type = props.type; form.id = row.id;
const res = await context.api.UpdateObj(form); form.type = props.type;
lastResRef.value = res; const res = await context.api.UpdateObj(form);
return res; lastResRef.value = res;
}; return res;
const delRequest = async (req: DelReq) => { };
const { row } = req; const delRequest = async (req: DelReq) => {
return await context.api.DelObj(row.id); const { row } = req;
}; return await context.api.DelObj(row.id);
};
const addRequest = async (req: AddReq) => { const addRequest = async (req: AddReq) => {
const { form } = req; const { form } = req;
form.type = props.type; form.type = props.type;
const res = await context.api.AddObj(form); const res = await context.api.AddObj(form);
lastResRef.value = res; lastResRef.value = res;
return res; return res;
}; };
const selectedRowKey = ref([props.modelValue]); const selectedRowKey = ref([props.modelValue]);
const onSelectChange = (changed: any) => { const onSelectChange = (changed: any) => {
selectedRowKey.value = changed; selectedRowKey.value = changed;
ctx.emit("update:modelValue", changed[0]); ctx.emit("update:modelValue", changed[0]);
}; };
const typeRef = ref("aliyun"); const typeRef = ref("aliyun");
context.typeRef = typeRef; context.typeRef = typeRef;
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api); const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api);
commonColumnsDefine.type.form.component.disabled = true; commonColumnsDefine.type.form.component.disabled = true;
return { return {
typeRef, typeRef,
crudOptions: { crudOptions: {
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest, delRequest,
}, },
toolbar: { toolbar: {
show: false, show: false,
}, },
search: { search: {
show: false, show: false,
}, },
form: { form: {
wrapper: { wrapper: {
width: "1050px", width: "1050px",
}, },
}, },
rowHandle: { rowHandle: {
width: 200, width: 200,
}, },
table: { table: {
scroll: { scroll: {
x: 800, x: 800,
}, },
rowSelection: { rowSelection: {
type: "radio", type: "radio",
selectedRowKeys: selectedRowKey, selectedRowKeys: selectedRowKey,
onChange: onSelectChange, onChange: onSelectChange,
}, },
customRow: (record: any) => { customRow: (record: any) => {
return { return {
onClick: () => { onClick: () => {
onSelectChange([record.id]); onSelectChange([record.id]);
}, // 点击行 }, // 点击行
}; };
}, },
}, },
columns: { columns: {
id: { id: {
title: "ID", title: "ID",
key: "id", key: "id",
type: "number", type: "number",
column: { column: {
width: 50, width: 50,
}, },
form: { form: {
show: false, show: false,
}, },
}, },
name: { name: {
title: "名称", title: t("certd.name"),
search: { search: {
show: true, show: true,
}, },
type: ["text"], type: ["text"],
form: { form: {
rules: [{ required: true, message: "请填写名称" }], rules: [{ required: true, message: t("certd.pleaseEnterName") }],
helper: "随便填,当多个相同类型的授权时,便于区分", helper: t("certd.nameHelper"),
}, },
column: { column: {
width: 200, width: 200,
}, },
}, },
from: { from: {
title: "级别", title: t("certd.level"),
type: "dict-select", type: "dict-select",
dict: dict({ dict: dict({
data: [ data: [
{ label: "系统", value: "sys" }, { label: t("certd.system"), value: "sys" },
{ label: "用户", value: "user" }, { label: t("certd.usera"), value: "user" },
], ],
}), }),
search: { search: {
show: false, show: false,
}, },
form: { form: {
show: false, show: false,
}, },
column: { column: {
width: 100, width: 100,
align: "center", align: "center",
component: { component: {
color: "auto", color: "auto",
}, },
order: 10, order: 10,
}, },
valueBuilder: ({ row, key, value }) => { valueBuilder: ({ row, key, value }) => {
row[key] = row.userId > 0 ? "user" : "sys"; row[key] = row.userId > 0 ? "user" : "sys";
}, },
}, },
...commonColumnsDefine, ...commonColumnsDefine,
}, },
}, },
}; };
} }

View File

@ -1,13 +1,13 @@
<template> <template>
<fs-page> <fs-page>
<template #header> <template #header>
<div class="title"> <div class="title">
授权管理 {{ t("certd.authorizationManagement") }}
<span class="sub">管理第三方系统授权信息</span> <span class="sub">{{ t("certd.manageThirdPartyAuth") }}</span>
</div> </div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud> <fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page> </fs-page>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -15,25 +15,29 @@ import { defineComponent, onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { createAccessApi } from "/@/views/certd/access/api"; import { createAccessApi } from "/@/views/certd/access/api";
import { useI18n } from "vue-i18n";
export default defineComponent({ export default defineComponent({
name: "AccessManager", name: "AccessManager",
setup() { setup() {
const api = createAccessApi("user"); const { t } = useI18n();
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } }); const api = createAccessApi("user");
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
// //
onMounted(() => { onMounted(() => {
crudExpose.doRefresh(); crudExpose.doRefresh();
}); });
onActivated(() => { onActivated(() => {
crudExpose.doRefresh(); crudExpose.doRefresh();
}); });
return { return {
crudBinding, crudBinding,
crudRef, crudRef,
}; t
}, };
},
}); });
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<a-button v-if="showButton" type="primary" @click="open"> <a-button v-if="showButton" type="primary" @click="open">
{{ $t("passwordForm.changePasswordButton") }} {{ $t("authentication.changePasswordButton") }}
</a-button> </a-button>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -15,100 +15,100 @@ import { notification } from "ant-design-vue";
import { useUserStore } from "/@/store/user"; import { useUserStore } from "/@/store/user";
defineProps<{ defineProps<{
showButton: boolean; showButton: boolean;
}>(); }>();
let passwordFormRef = ref(); let passwordFormRef = ref();
const validatePass1 = async (rule: any, value: any) => { const validatePass1 = async (rule: any, value: any) => {
if (value === "") { if (value === "") {
throw new Error(t("passwordForm.enterPassword")); throw new Error(t("authentication.enterPassword"));
} }
const formData = passwordFormRef.value.getFormData(); const formData = passwordFormRef.value.getFormData();
if (formData.confirmNewPassword !== "") { if (formData.confirmNewPassword !== "") {
passwordFormRef.value.formRef.formRef.validateFields(["confirmNewPassword"]); passwordFormRef.value.formRef.formRef.validateFields(["confirmNewPassword"]);
} }
if (formData.password === formData.newPassword) { if (formData.password === formData.newPassword) {
throw new Error(t("passwordForm.newPasswordNotSameOld")); throw new Error(t("authentication.newPasswordNotSameOld"));
} }
}; };
const validatePass2 = async (rule: any, value: any) => { const validatePass2 = async (rule: any, value: any) => {
if (value === "") { if (value === "") {
throw new Error(t("passwordForm.enterPasswordAgain")); throw new Error(t("authentication.enterPasswordAgain"));
} else if (value !== passwordFormRef.value.getFormData().newPassword) { } else if (value !== passwordFormRef.value.getFormData().newPassword) {
throw new Error(t("passwordForm.passwordsNotMatch")); throw new Error(t("authentication.passwordsNotMatch"));
} }
}; };
const userStore = useUserStore(); const userStore = useUserStore();
const { openDialog } = useFormWrapper(); const { openDialog } = useFormWrapper();
const { buildFormOptions } = useColumns(); const { buildFormOptions } = useColumns();
const passwordFormOptions: CrudOptions = { const passwordFormOptions: CrudOptions = {
form: { form: {
col: { col: {
span: 24, span: 24,
}, },
wrapper: { wrapper: {
title: t("passwordForm.title"), title: t("authentication.title"),
width: "500px", width: "500px",
}, },
async doSubmit({ form }) { async doSubmit({ form }) {
await api.changePassword(form); await api.changePassword(form);
// //
await userStore.loadUserInfo(); await userStore.loadUserInfo();
}, },
async afterSubmit() { async afterSubmit() {
notification.success({ message: t("passwordForm.successMessage") }); notification.success({ message: t("authentication.successMessage") });
}, },
}, },
columns: { columns: {
password: { password: {
title: t("passwordForm.oldPassword"), title: t("authentication.oldPassword"),
type: "password", type: "password",
form: { form: {
rules: [{ required: true, message: t("passwordForm.oldPasswordRequired") }], rules: [{ required: true, message: t("authentication.oldPasswordRequired") }],
}, },
}, },
newPassword: { newPassword: {
title: t("passwordForm.newPassword"), title: t("authentication.newPassword"),
type: "password", type: "password",
form: { form: {
rules: [ rules: [
{ required: true, message: t("passwordForm.newPasswordRequired") }, { required: true, message: t("authentication.newPasswordRequired") },
//@ts-ignore //@ts-ignore
{ validator: validatePass1, trigger: "blur" }, { validator: validatePass1, trigger: "blur" },
], ],
}, },
}, },
confirmNewPassword: { confirmNewPassword: {
title: t("passwordForm.confirmNewPassword"), title: t("authentication.confirmNewPassword"),
type: "password", type: "password",
form: { form: {
rules: [ rules: [
{ {
required: true, required: true,
message: t("passwordForm.confirmNewPasswordRequired"), message: t("authentication.confirmNewPasswordRequired"),
}, },
//@ts-ignore //@ts-ignore
{ validator: validatePass2, trigger: "blur" }, { validator: validatePass2, trigger: "blur" },
], ],
}, },
}, },
}, },
}; };
async function open(opts: { password: "" }) { async function open(opts: { password: "" }) {
const formOptions = buildFormOptions(passwordFormOptions); const formOptions = buildFormOptions(passwordFormOptions);
formOptions.newInstance = true; // formOptions.newInstance = true; //
passwordFormRef.value = await openDialog(formOptions); passwordFormRef.value = await openDialog(formOptions);
passwordFormRef.value.setFormData({ passwordFormRef.value.setFormData({
password: opts.password, password: opts.password,
}); });
console.log(passwordFormRef.value); console.log(passwordFormRef.value);
} }
const scope = ref({ const scope = ref({
open: open, open: open,
}); });
defineExpose(scope.value); defineExpose(scope.value);

View File

@ -1,41 +1,50 @@
<template> <template>
<fs-page class="page-user-profile"> <fs-page class="page-user-profile">
<template #header> <template #header>
<div class="title">我的信息</div> <div class="title">{{ t("certd.myInfo") }}</div>
</template> </template>
<div class="p-10"> <div class="p-10">
<a-descriptions title="" bordered> <a-descriptions title="" bordered :column="1">
<a-descriptions-item label="用户名">{{ userInfo.username }}</a-descriptions-item> <a-descriptions-item :label="t('authentication.username')">{{ userInfo.username }}</a-descriptions-item>
<a-descriptions-item label="头像"> <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-if="userInfo.avatar" size="large"
<a-avatar v-else size="large" style="background-color: #00b4f5"> :src="'api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee">
{{ userInfo.username }} </a-avatar>
</a-avatar> <a-avatar v-else size="large" style="background-color: #00b4f5">
</a-descriptions-item> {{ userInfo.username }}
<a-descriptions-item label="昵称">{{ userInfo.nickName }}</a-descriptions-item> </a-avatar>
<a-descriptions-item label="邮箱">{{ userInfo.email }}</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="手机号">{{ userInfo.phoneCode }}{{ userInfo.mobile }}</a-descriptions-item> <a-descriptions-item :label="t('authentication.nickName')">{{ userInfo.nickName }}</a-descriptions-item>
<a-descriptions-item label="修改密码"> <a-descriptions-item :label="t('authentication.email')">{{ userInfo.email }}</a-descriptions-item>
<change-password-button :show-button="true"> </change-password-button> <a-descriptions-item :label="t('authentication.phoneNumber')">{{ userInfo.phoneCode }}{{ userInfo.mobile
</a-descriptions-item> }}</a-descriptions-item>
</a-descriptions> <a-descriptions-item :label="t('authentication.changePassword')">
</div> <change-password-button :show-button="true"> </change-password-button>
</fs-page> </a-descriptions-item>
</a-descriptions>
</div>
</fs-page>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as api from "./api"; import * as api from "./api";
import { Ref, ref } from "vue"; import { Ref, ref } from "vue";
import ChangePasswordButton from "/@/views/certd/mine/change-password-button.vue"; import ChangePasswordButton from "/@/views/certd/mine/change-password-button.vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
defineOptions({ defineOptions({
name: "UserProfile" name: "UserProfile"
}); });
const userInfo: Ref = ref({}); const userInfo: Ref = ref({});
const getUserInfo = async () => { const getUserInfo = async () => {
userInfo.value = await api.getMineInfo(); userInfo.value = await api.getMineInfo();
}; };
getUserInfo(); getUserInfo();
</script> </script>