2026-04-27 11:57:26 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="register-container">
|
|
|
|
|
|
<div class="register-wrapper">
|
|
|
|
|
|
<!-- 左侧:背景图区域 -->
|
|
|
|
|
|
<div class="left-section">
|
|
|
|
|
|
<div class="slogan">
|
|
|
|
|
|
<p>{{ $t("login.titleSjtb") }}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧:注册表单区域 -->
|
|
|
|
|
|
<div class="right-section">
|
|
|
|
|
|
<a-tabs v-model:activeKey="activeTab" class="register-tabs">
|
|
|
|
|
|
<a-tab-pane key="register" tab="用户注册">
|
|
|
|
|
|
<a-form
|
|
|
|
|
|
:model="registerData"
|
|
|
|
|
|
:rules="registerRules"
|
|
|
|
|
|
layout="vertical"
|
|
|
|
|
|
class="form-container"
|
|
|
|
|
|
@finish="onRegister"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 登录账号 -->
|
|
|
|
|
|
<a-form-item name="username" label="登录账号">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="registerData.username"
|
|
|
|
|
|
placeholder="请输入登录账号(4-20个字符)"
|
|
|
|
|
|
:prefix="h(UserOutlined)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 真实姓名 -->
|
|
|
|
|
|
<a-form-item name="realName" label="真实姓名">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="registerData.realName"
|
|
|
|
|
|
placeholder="请输入真实姓名"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 手机号 -->
|
|
|
|
|
|
<a-form-item name="phone" label="手机号">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="registerData.phone"
|
|
|
|
|
|
placeholder="请输入11位手机号"
|
|
|
|
|
|
:prefix="h(MobileOutlined)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 密码 -->
|
|
|
|
|
|
<a-form-item name="password" label="密码">
|
|
|
|
|
|
<a-input-password
|
|
|
|
|
|
v-model:value="registerData.password"
|
|
|
|
|
|
placeholder="请设置密码(6-20个字符)"
|
|
|
|
|
|
:prefix="h(LockOutlined)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 确认密码 -->
|
|
|
|
|
|
<a-form-item name="confirmPassword" label="确认密码">
|
|
|
|
|
|
<a-input-password
|
|
|
|
|
|
v-model:value="registerData.confirmPassword"
|
|
|
|
|
|
placeholder="请再次输入密码"
|
|
|
|
|
|
:prefix="h(LockOutlined)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 验证码 -->
|
|
|
|
|
|
<a-form-item name="code" label="验证码">
|
|
|
|
|
|
<a-row :gutter="8">
|
|
|
|
|
|
<a-col :span="16">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="registerData.code"
|
|
|
|
|
|
placeholder="请输入验证码"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :span="8">
|
|
|
|
|
|
<img
|
|
|
|
|
|
v-if="captchaImg"
|
|
|
|
|
|
:src="captchaImg"
|
|
|
|
|
|
@click="refreshCaptcha"
|
|
|
|
|
|
style="cursor: pointer; width: 100%; height: 36px;"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 注册按钮 -->
|
|
|
|
|
|
<a-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
block
|
|
|
|
|
|
htmlType="submit"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span>立即注册</span>
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 返回登录 -->
|
|
|
|
|
|
<a-button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
block
|
|
|
|
|
|
@click="backToLogin"
|
|
|
|
|
|
:style="{ marginTop: '10px' }"
|
|
|
|
|
|
>
|
|
|
|
|
|
已有账号?返回登录
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
</a-form>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
</a-tabs>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { reactive, ref, onMounted, h } from "vue";
|
|
|
|
|
|
import { UserOutlined, LockOutlined, MobileOutlined } from "@ant-design/icons-vue";
|
2026-04-27 13:43:34 +08:00
|
|
|
|
import { getCaptcha,
|
|
|
|
|
|
// registerUser
|
|
|
|
|
|
} from "@/api/auth";
|
2026-04-27 11:57:26 +08:00
|
|
|
|
import { message } from "ant-design-vue";
|
|
|
|
|
|
import router from "@/router";
|
|
|
|
|
|
import { encrypt } from "@/utils/rsaEncrypt";
|
|
|
|
|
|
|
|
|
|
|
|
// 注册表单数据
|
|
|
|
|
|
const registerData = reactive({
|
|
|
|
|
|
// 必填字段
|
|
|
|
|
|
username: "",
|
|
|
|
|
|
realName: "",
|
|
|
|
|
|
phone: "",
|
|
|
|
|
|
password: "",
|
|
|
|
|
|
confirmPassword: "",
|
|
|
|
|
|
code: "",
|
|
|
|
|
|
uuid: "",
|
|
|
|
|
|
|
|
|
|
|
|
// 系统字段
|
|
|
|
|
|
userType: 1,
|
|
|
|
|
|
status: 1,
|
|
|
|
|
|
regStatus: 0
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 表单验证规则
|
|
|
|
|
|
const registerRules = {
|
|
|
|
|
|
// 登录账号
|
|
|
|
|
|
username: [
|
|
|
|
|
|
{ required: true, message: "请输入登录账号", trigger: "blur" },
|
|
|
|
|
|
{ min: 4, max: 20, message: "账号长度4-20个字符", trigger: "blur" },
|
|
|
|
|
|
{ pattern: /^[a-zA-Z0-9_]+$/, message: "只能包含字母、数字和下划线", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 真实姓名
|
|
|
|
|
|
realName: [
|
|
|
|
|
|
{ required: true, message: "请输入真实姓名", trigger: "blur" },
|
|
|
|
|
|
{ min: 2, max: 20, message: "姓名长度2-20个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 手机号
|
|
|
|
|
|
phone: [
|
|
|
|
|
|
{ required: true, message: "请输入手机号", trigger: "blur" },
|
|
|
|
|
|
{ pattern: /^1[3-9]\d{9}$/, message: "请输入正确的11位手机号", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 密码
|
|
|
|
|
|
password: [
|
|
|
|
|
|
{ required: true, message: "请输入密码", trigger: "blur" },
|
|
|
|
|
|
{ min: 6, max: 20, message: "密码长度6-20个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 确认密码
|
|
|
|
|
|
confirmPassword: [
|
|
|
|
|
|
{ required: true, message: "请再次输入密码", trigger: "blur" },
|
|
|
|
|
|
{
|
|
|
|
|
|
validator: (rule: any, value: string) => {
|
|
|
|
|
|
if (value && value !== registerData.password) {
|
|
|
|
|
|
return Promise.reject("两次输入的密码不一致");
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
},
|
|
|
|
|
|
trigger: "blur"
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 验证码
|
|
|
|
|
|
code: [
|
|
|
|
|
|
{ required: true, message: "请输入验证码", trigger: "blur" }
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
const captchaImg = ref("");
|
|
|
|
|
|
const activeTab = ref("register");
|
|
|
|
|
|
|
|
|
|
|
|
// 获取验证码
|
|
|
|
|
|
const refreshCaptcha = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await getCaptcha();
|
|
|
|
|
|
registerData.uuid = res.data.verifyCodeKey;
|
|
|
|
|
|
captchaImg.value = res.data.verifyCodeImg; // base64图片
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
message.error("获取验证码失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 注册提交
|
|
|
|
|
|
const onRegister = async () => {
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 密码加密
|
|
|
|
|
|
const encryptedPassword = encrypt(registerData.password);
|
|
|
|
|
|
|
|
|
|
|
|
// 构造注册数据
|
|
|
|
|
|
const registerParams = {
|
|
|
|
|
|
username: registerData.username,
|
|
|
|
|
|
realName: registerData.realName,
|
|
|
|
|
|
phone: registerData.phone,
|
|
|
|
|
|
password: encryptedPassword,
|
|
|
|
|
|
|
|
|
|
|
|
// 系统字段
|
|
|
|
|
|
userType: 1,
|
|
|
|
|
|
status: 1,
|
|
|
|
|
|
regStatus: 0,
|
|
|
|
|
|
|
|
|
|
|
|
// 验证码相关
|
|
|
|
|
|
code: registerData.code,
|
|
|
|
|
|
uuid: registerData.uuid
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 调用注册接口
|
2026-04-27 13:43:34 +08:00
|
|
|
|
// await registerUser(registerParams);
|
2026-04-27 11:57:26 +08:00
|
|
|
|
|
|
|
|
|
|
message.success("注册成功,等待管理员审核");
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟跳转到登录页
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
router.push({ path: "/login" });
|
|
|
|
|
|
}, 1500);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
message.error(error.message || "注册失败,请重试");
|
|
|
|
|
|
// 刷新验证码
|
|
|
|
|
|
refreshCaptcha();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 返回登录
|
|
|
|
|
|
const backToLogin = () => {
|
|
|
|
|
|
router.push({ path: "/login" });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
refreshCaptcha();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.register-container {
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
min-width: 1500px;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
|
|
|
|
|
|
.register-wrapper {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
min-height: 600px;
|
|
|
|
|
|
background: url("@/assets/images/bg_sjtb.png");
|
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
|
background-size: 100% 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 左侧背景区域
|
|
|
|
|
|
.left-section {
|
|
|
|
|
|
.slogan {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 20%;
|
|
|
|
|
|
left: 18%;
|
|
|
|
|
|
width: 700px;
|
|
|
|
|
|
height: 112px;
|
|
|
|
|
|
color: #040504;
|
|
|
|
|
|
font-size: 40px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 右侧注册卡片区域
|
|
|
|
|
|
.right-section {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 70%;
|
|
|
|
|
|
top: 15%;
|
|
|
|
|
|
width: 25%;
|
|
|
|
|
|
// max-height: 650px;
|
|
|
|
|
|
// max-width: 400px;
|
|
|
|
|
|
min-height: 650px;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
padding: 20px 24px 24px;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.ant-form-item) {
|
|
|
|
|
|
margin-bottom: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.ant-form-item-label > label) {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.ant-input-prefix) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 26px;
|
|
|
|
|
|
|
|
|
|
|
|
svg {
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 18px;
|
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|