mps-platform/cds-fontend-2025.V1/src/views/_builtin/login/modules/register.vue
xiaocp2009 5ae10027cd 1、用户注册V1
2、修复营销编号校验bug
3、其他优化
2025-08-15 11:00:00 +08:00

276 lines
8.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import type { SelectOption } from 'naive-ui';
import { useLoading } from '@sa/hooks';
import { fetchCaptchaCode, fetchRegister, fetchTenantList } from '@/service/api';
import { useRouterPush } from '@/hooks/common/router';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
import { fetchGetRegDeptTree} from "@/service/api/system";
import {useDict} from "@/hooks/business/dict";
defineOptions({
name: 'Register'
});
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useNaiveForm();
const { loading: codeLoading, startLoading: startCodeLoading, endLoading: endCodeLoading } = useLoading();
const { loading: registerLoading, startLoading: startRegisterLoading, endLoading: endRegisterLoading } = useLoading();
const { loading: deptLoading, startLoading: startDeptLoading, endLoading: endDeptLoading } = useLoading();
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
const codeUrl = ref<string>();
const captchaEnabled = ref<boolean>(false);
const tenantEnabled = ref<boolean>(false);
const tenantOption = ref<SelectOption[]>([]);
const deptData = ref<Api.Common.CommonTreeRecord>([]);
const {options: mpsUserCategory} = useDict('mps_user_category');
const model: Api.Auth.RegisterForm = reactive({
/*tenantId: '000000',*/
tenantId: '069291',//注意要和部署法人保持一致
username: '',
nickName: '',
idCard: '',
userCategory: '1',//注意跟字典保持一致
mktNo: '',
code: '',
password: '',
confirmPassword: '',
userType: 'sys_user',
deptId: null,
});
type RuleKey = Extract<keyof Api.Auth.RegisterForm, 'username' | 'password' | 'confirmPassword' | 'code' | 'tenantId'>;
const rules = computed<Record<RuleKey, App.Global.FormRule[]>>(() => {
const { formRules, createConfirmPwdRule, createRequiredRule } = useFormRules();
return {
tenantId: tenantEnabled.value ? formRules.tenantId : [],
username: [...formRules.userName, { required: true }],
nickName: [...formRules.nickName, { required: true }],
idCard: [...formRules.idCard, { required: true }],
mktNo: [...formRules.mktNo, { required: true }],
password: [...formRules.pwd, { required: true }],
confirmPassword: createConfirmPwdRule(model.password!),
code: captchaEnabled.value ? [createRequiredRule($t('form.code.required'))] : []
};
});
async function getTreeData() {
startTreeLoading();
const { data: tree, error } = await fetchGetRegDeptTree(model.tenantId);
if (!error) {
deptData.value = tree;
}
endTreeLoading();
}
getTreeData();
async function handleSubmit() {
try {
await validate();
startRegisterLoading();
const { error } = await fetchRegister({
tenantId: model.tenantId,
username: model.username,
password: model.password,
code: model.code,
uuid: model.uuid,
grantType: 'password',
userType: model.userType,
clientId: import.meta.env.VITE_APP_CLIENT_ID,
/** 姓名 */
nickName: model.nickName,
/** 身份证号 */
idCard: model.idCard,
/*用户种类(0-营销 1-其他)*/
userCategory: model.userCategory,//注意跟字典保持一致
/** 营销编号 */
mktNo: model.mktNo,
/** 部门编号 */
deptId: model.deptId,
});
if (error) {
handleFetchCaptchaCode();
return;
}
window.$message?.success('注册成功');
// 注册成功后跳转到登录页
toggleLoginModule('pwd-login');
} catch {
handleFetchCaptchaCode();
} finally {
endRegisterLoading();
}
}
async function handleFetchTenantList() {
const { data, error } = await fetchTenantList();
if (error) return;
tenantEnabled.value = data.tenantEnabled;
tenantOption.value = data.voList.map(tenant => {
return {
label: tenant.companyName,
value: tenant.tenantId
};
});
}
handleFetchTenantList();
async function handleFetchCaptchaCode() {
startCodeLoading();
const { data, error } = await fetchCaptchaCode();
if (!error) {
captchaEnabled.value = data.captchaEnabled;
if (data.captchaEnabled) {
model.uuid = data.uuid;
codeUrl.value = `data:image/gif;base64,${data.img}`;
}
}
endCodeLoading();
}
handleFetchCaptchaCode();
</script>
<template>
<div>
<div class="mb-12px text-24px text-black font-500 sm:text-30px dark:text-white">注册新账户</div>
<div class="pb-24px text-18px text-#858585">欢迎注册请输入您的账户信息</div>
<NForm
ref="formRef"
:model="model"
:rules="rules"
size="large"
:show-label="true"
label-placement="left"
:label-width="100"
@keyup.enter="() => !registerLoading && handleSubmit()"
>
<!-- 法人、部门 -->
<NFormItem label="所属法人" v-if="tenantEnabled" path="tenantId">
<NSelect v-model:value="model.tenantId" :options="tenantOption" :enabled="tenantEnabled" />
</NFormItem>
<NFormItem required :label="$t('page.system.user.deptName')" path="deptId">
<NTreeSelect
v-model:value="model.deptId"
:loading="deptLoading"
clearable
:options="deptData as []"
label-field="label"
key-field="id"
:default-expanded-keys="deptData?.length ? [deptData[0].id] : []"
:placeholder="$t('page.system.user.form.deptId.required')"
:default-value="deptData?.length ? [deptData[0].id] : null"
/>
</NFormItem>
<!-- 营销 -->
<NFormItem required label="用户类别" path="userCategory">
<NSelect
v-model:value="model.userCategory"
placeholder="请选择用户类别"
:options="mpsUserCategory"
filterable
/>
</NFormItem>
<NFormItem v-if="model.userCategory === '0'" label="营销编号" path="mktNo" required>
<NInput v-model:value="model.mktNo" :placeholder="$t('page.system.user.form.mktNo.required')" />
</NFormItem>
<!-- 真实用户信息 -->
<NFormItem required label="姓名" path="nickName">
<NInput v-model:value="model.nickName" :placeholder="$t('form.nickName.required')" />
</NFormItem>
<NFormItem required label="身份证号" path="idCard">
<NInput v-model:value="model.idCard" :placeholder="$t('form.idCard.required')" />
</NFormItem>
<!-- 登录用户信息 -->
<NFormItem required label="登录名" path="username">
<NInput v-model:value="model.username" :placeholder="$t('page.login.common.userNamePlaceholder')" />
</NFormItem>
<NFormItem required label="登陆密码" path="password">
<NInput
v-model:value="model.password"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</NFormItem>
<NFormItem required label="登录密码" path="confirmPassword">
<NInput
v-model:value="model.confirmPassword"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
/>
</NFormItem>
<NFormItem required label="验证码" v-if="captchaEnabled" path="code">
<div class="w-full flex-y-center gap-16px">
<NInput v-model:value="model.code" :placeholder="$t('page.login.common.codePlaceholder')" />
<NSpin :show="codeLoading" :size="28" class="h-52px">
<NButton :focusable="false" class="login-code h-52px w-136px" @click="handleFetchCaptchaCode">
<img v-if="codeUrl" :src="codeUrl" />
<NEmpty v-else :show-icon="false" description="暂无验证码" />
</NButton>
</NSpin>
</div>
</NFormItem>
<NSpace vertical :size="18" class="w-full pt-6px">
<NButton type="primary" size="large" block :loading="registerLoading" @click="handleSubmit">
{{ $t('page.login.common.register') }}
</NButton>
</NSpace>
</NForm>
<div class="mt-32px w-full text-center text-18px text-#858585">
您已有账户?
<NA type="primary" class="text-18px" @click="toggleLoginModule('pwd-login')">
{{ $t('common.login') }}
</NA>
</div>
</div>
</template>
<style scoped lang="scss">
.login-code {
&.n-button {
--n-padding: 0 8px !important;
background-color: #c0c0c0;
}
img {
height: 40px;
}
}
:deep(.n-base-selection),
:deep(.n-input) {
--n-height: 52px !important;
--n-font-size: 16px !important;
--n-border-radius: 8px !important;
}
:deep(.n-base-selection-label) {
padding: 0 6px !important;
}
:deep(.n-button) {
--n-height: 52px !important;
--n-font-size: 18px !important;
--n-border-radius: 8px !important;
}
</style>