注册相关内容

This commit is contained in:
王兴凯 2026-04-28 18:28:44 +08:00
parent 10c3c33ad0
commit 1963dbbba1
12 changed files with 2514 additions and 510 deletions

View File

@ -34,3 +34,113 @@ export function getCaptcha(): AxiosPromise<VerifyCode> {
method: 'get' method: 'get'
}); });
} }
/**
*
*/
export function registerUser(data: any): AxiosPromise<any> {
return request({
url: '/sms/register',
method: 'post',
data
});
}
/**
*
* @param companyId ID(),
*/
export function getBasinList(): AxiosPromise<any[]> {
return request({
url: '/env/hbrv/regDropdown',
method: 'get',
// params: baseid ? { baseid } : {}
});
}
/**
* (,)
*/
export function getGroupList(): AxiosPromise<any[]> {
return request({
url: '/env/hycd/regDropdown',
method: 'get',
params:{lx:1}
});
}
/**
*
*/
export function getCompanyList(): AxiosPromise<any[]> {
return request({
url: '/env/hycd/regDropdown',
method: 'get',
params: { lx:2 }
});
}
/**
*
*/
export function getStationList(params:any): AxiosPromise<any[]> {
return request({
url: '/env/engInfo/regDropdown',
method: 'post',
data: params
});
}
/**
*
* @param phone
* @param type 1- 2-
*/
export function sendSmsCode(phone: string, type: number = 1): AxiosPromise<any> {
return request({
url: '/sms/sendCode',
method: 'post',
data: { phone, type }
});
}
/**
*
* @param phone
* @param code
* @param type 1- 2-
*/
export function verifySmsCode(phone: string, code: string, type: number = 1): AxiosPromise<any> {
return request({
url: '/sms/verifyCode',
method: 'post',
data: { phone, code, type }
});
}
/**
*
* @param phone
* @param code
*/
export function smsLoginApi(phone: string, code: string): AxiosPromise<TokenResult> {
return request({
url: '/sms/login',
method: 'post',
data: { phone, code }
});
}
/**
*
* @param phone
* @param code
* @param password
*/
export function resetPassword(phone: string, code: string, password: string): AxiosPromise<any> {
return request({
url: '/sms/resetPassword',
method: 'post',
data: { phone, code, password }
});
}

View File

@ -17,6 +17,14 @@ export function gettableData(queryParams:any) {
params: queryParams params: queryParams
}); });
} }
// 审核用户列表
export function queryPendingAuditUsers(queryParams:any) {
return request({
url: '/system/user/queryPendingAuditUsers',
method: 'get',
params: queryParams
});
}
//用户-禁用,启用 //用户-禁用,启用
export function DataStatus (queryParams:any) { export function DataStatus (queryParams:any) {
return request({ return request({
@ -105,7 +113,7 @@ export function updatePassword (queryParams:any) {
//获取过鱼设施权限树 //获取过鱼设施权限树
export function getFishtree(queryParams:any) { export function getFishtree(queryParams:any) {
return request({ return request({
url: '/env/tree/rvcdEng', url: '/env/tree/hbrvEng',
method: 'get', method: 'get',
params: queryParams params: queryParams
}); });
@ -127,6 +135,15 @@ export function getuserdata(queryParams:any) {
}); });
} }
// 审计用户(审批通过/驳回)
export function auditUser(data: any) {
return request({
url: '/system/user/auditUser',
method: 'post',
data
});
}
/** /**
* *
*/ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View File

@ -12,7 +12,7 @@ const routeKey = computed(() => router.path + Math.random());
<template> <template>
<section class="app-main"> <section class="app-main">
<GisView /> <!-- <GisView /> -->
<div class="gi-panels"> <div class="gi-panels">
<router-view v-slot="{ Component, route }" :key="routeKey"> <router-view v-slot="{ Component, route }" :key="routeKey">
<transition name="router-fade" mode="out-in"> <transition name="router-fade" mode="out-in">

View File

@ -9,7 +9,7 @@ NProgress.configure({ showSpinner: false });
const permissionStore = usePermissionStoreHook(); const permissionStore = usePermissionStoreHook();
// 白名单路由 // 白名单路由
const whiteList = ['/login']; //login const whiteList = ['/login','/register']; //login
// 查找第一个可用路由 // 查找第一个可用路由
function findFirstAvailableRoute(routes: any[]): string | undefined { function findFirstAvailableRoute(routes: any[]): string | undefined {

View File

@ -26,6 +26,11 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import('@/views/login-sjtb/index.vue'), component: () => import('@/views/login-sjtb/index.vue'),
meta: { hidden: true } meta: { hidden: true }
}, },
{
path: '/register',
component: () => import('@/views/register/index.vue'),
meta: { hidden: true }
},
{ {
path: '/404', path: '/404',
component: () => import('@/views/error-page/404.vue'), component: () => import('@/views/error-page/404.vue'),

View File

@ -25,14 +25,9 @@
<!-- <h3 class="forgot-password-title">忘记密码</h3> --> <!-- <h3 class="forgot-password-title">忘记密码</h3> -->
</div> </div>
<a-form <a-form :model="forgotPasswordForm" :rules="forgotPasswordRules" layout="vertical" class="form-container">
:model="forgotPasswordForm"
:rules="forgotPasswordRules"
layout="vertical"
class="form-container"
>
<a-form-item label="" name="phone"> <a-form-item label="" name="phone">
<a-input placeholder="请输入手机号" size="large"> <a-input v-model:value="forgotPasswordForm.phone" placeholder="请输入手机号" size="large">
<template #prefix> <template #prefix>
<MobileOutlined /> <MobileOutlined />
</template> </template>
@ -41,23 +36,13 @@
<a-form-item label="" name="captcha"> <a-form-item label="" name="captcha">
<div class="captcha-row"> <div class="captcha-row">
<a-input <a-input v-model:value="forgotPasswordForm.captcha" placeholder="请输入验证码" size="large" class="captcha-input">
v-model="forgotPasswordForm.captcha"
placeholder="请输入验证码"
size="large"
class="captcha-input"
>
<template #prefix> <template #prefix>
<LockOutlined /> <LockOutlined />
</template> </template>
<template #suffix> <template #suffix>
<a-button <a-button type="text" size="small" @click="sendForgotPasswordSms" :disabled="smsButtonDisabled"
type="text" :loading="loading">
size="small"
@click="sendForgotPasswordSms"
:disabled="smsButtonDisabled"
:loading="loading"
>
{{ {{
smsCountdown > 0 ? `${smsCountdown}秒后重新获取` : "获取验证码" smsCountdown > 0 ? `${smsCountdown}秒后重新获取` : "获取验证码"
}} }}
@ -68,24 +53,22 @@
</a-form-item> </a-form-item>
<a-form-item label="" name="newPassword"> <a-form-item label="" name="newPassword">
<a-input-password <a-input-password v-model:value="forgotPasswordForm.newPassword" placeholder="请输入新密码" size="large">
v-model="forgotPasswordForm.newPassword"
placeholder="请输入新密码"
size="large"
>
<template #prefix> <template #prefix>
<LockOutlined /> <LockOutlined />
</template> </template>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<a-button <a-form-item label="" name="confirmPassword">
type="primary" <a-input-password v-model:value="forgotPasswordForm.confirmPassword" placeholder="请再次输入密码" size="large">
size="large" <template #prefix>
block <LockOutlined />
@click="handleResetPassword" </template>
:loading="loading" </a-input-password>
> </a-form-item>
<a-button type="primary" size="large" block @click="handleResetPassword" :loading="loading">
<span>提交</span> <span>提交</span>
</a-button> </a-button>
</a-form> </a-form>
@ -96,23 +79,12 @@
<!-- Tabs 切换账号登录 / 短信登录 --> <!-- Tabs 切换账号登录 / 短信登录 -->
<a-tabs v-model:activeKey="activeTab" class="login-tabs"> <a-tabs v-model:activeKey="activeTab" class="login-tabs">
<a-tab-pane key="account" tab="账号登录"> <a-tab-pane key="account" tab="账号登录">
<a-form <a-form :model="loginData" :rules="loginRules" layout="vertical" class="form-container"
:model="loginData" @finish="onFinish">
:rules="loginRules"
layout="vertical"
class="form-container"
@finish="onFinish"
>
<!-- 用户名/账号/手机号输入框 --> <!-- 用户名/账号/手机号输入框 -->
<a-form-item label="" name="username"> <a-form-item label="" name="username">
<a-input <a-input ref="username" v-model:value="loginData.username" clearable type="text"
ref="username" placeholder="请输入用户账号/身份证号/手机号" size="large">
v-model:value="loginData.username"
clearable
type="text"
placeholder="请输入用户账号/身份证号/手机号"
size="large"
>
<template #prefix> <template #prefix>
<UserOutlined /> <UserOutlined />
</template> </template>
@ -121,12 +93,7 @@
<!-- 密码输入框 --> <!-- 密码输入框 -->
<a-form-item label="" name="password"> <a-form-item label="" name="password">
<a-input-password <a-input-password type="password" v-model:value="loginData.password" placeholder="请输入密码" size="large">
type="password"
v-model:value="loginData.password"
placeholder="请输入密码"
size="large"
>
<template #prefix> <template #prefix>
<LockOutlined /> <LockOutlined />
</template> </template>
@ -136,65 +103,38 @@
<a-form-item label="" name="captcha"> <a-form-item label="" name="captcha">
<div class="captcha-row"> <div class="captcha-row">
<a-row :gutter="24" align="middle"> <a-row :gutter="24" align="middle">
<a-col :span="10" <a-col :span="10"><a-input v-model:value="loginData.code" placeholder="请输入验证码" size="large"
><a-input class="captcha-input" /></a-col>
v-model:value="loginData.code" <a-col :span="10"><img :src="codeUrl" alt="验证码" class="captcha-img" /></a-col>
placeholder="请输入验证码"
size="large"
class="captcha-input"
/></a-col>
<a-col :span="10"
><img :src="codeUrl" alt="验证码" class="captcha-img"
/></a-col>
<a-col :span="4"> <a @click="getCode">换一张</a></a-col> <a-col :span="4"> <a @click="getCode">换一张</a></a-col>
</a-row> </a-row>
</div> </div>
</a-form-item> </a-form-item>
<a-button <a-button type="primary" size="large" block htmlType="submit" :loading="loading">
type="primary"
size="large"
block
htmlType="submit"
:loading="loading"
>
<span>登录</span> <span>登录</span>
</a-button> </a-button>
<!-- <a-button <div style="width: 100%;display: flex;align-items: center;justify-content: space-between;">
type="link" <a-button type="link" size="mini" block @click="showForgotPasswordPage"
size="mini" :style="{ marginTop: '10px', border: 'none' }">
block
@click="showForgotPasswordPage"
:style="{ marginTop: '10px', border: 'none' }"
>
忘记密码 忘记密码
</a-button> --> </a-button>
<a-button <a-button type="link" size="mini" block @click="goRegister"
type="link" :style="{ marginTop: '10px', border: 'none' }">
size="mini"
block
:style="{ marginTop: '10px', border: 'none' }"
>
注册 注册
</a-button> </a-button>
</div>
<!-- 忘记密码 --> <!-- 忘记密码 -->
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
<!-- 短信登录 Tab (占位) --> <!-- 短信登录 Tab (占位) -->
<!-- <a-tab-pane key="sms" tab="短信登录"> <a-tab-pane key="sms" tab="短信登录">
<a-form <a-form :model="loginData" :rules="state.smsLoginRules" layout="vertical" class="form-container"
:model="loginData" @finish="onSmsLogin">
:rules="loginRules"
layout="vertical"
class="form-container"
>
<a-form-item label="" name="username"> <a-form-item label="" name="username">
<a-input <a-input v-model:value="loginData.username" placeholder="请输入手机号" size="large">
v-model:value="loginData.username"
placeholder="请输入手机号"
size="large"
>
<template #prefix> <template #prefix>
<MobileOutlined /> <MobileOutlined />
</template> </template>
@ -202,23 +142,13 @@
</a-form-item> </a-form-item>
<a-form-item label="" name="captcha"> <a-form-item label="" name="captcha">
<div class="captcha-row"> <div class="captcha-row">
<a-input <a-input v-model:value="loginData.code" placeholder="请输入验证码" size="large" class="captcha-input">
v-model="loginData.code"
placeholder="请输入验证码"
size="large"
class="captcha-input"
>
<template #prefix> <template #prefix>
<LockOutlined /> <LockOutlined />
</template> </template>
<template #suffix> <template #suffix>
<a-button <a-button type="text" size="small" @click="sendSms" :disabled="smsButtonDisabled"
type="text" :loading="loading">
size="small"
@click="sendSms"
:disabled="smsButtonDisabled"
:loading="loading"
>
{{ {{
smsCountdown > 0 smsCountdown > 0
? `${smsCountdown}秒后重新获取` ? `${smsCountdown}秒后重新获取`
@ -229,17 +159,21 @@
</a-input> </a-input>
</div> </div>
</a-form-item> </a-form-item>
<a-button <a-button type="primary" size="large" block htmlType="submit" :loading="loading">
type="primary"
size="large"
block
htmlType="submit"
:loading="loading"
>
<span>登录</span> <span>登录</span>
</a-button> </a-button>
<div style="width: 100%;display: flex;align-items: center;justify-content: space-between;">
<a-button type="link" size="mini" block @click="showForgotPasswordPage"
:style="{ marginTop: '10px', border: 'none' }">
忘记密码
</a-button>
<a-button type="link" size="mini" block @click="goRegister"
:style="{ marginTop: '10px', border: 'none' }">
注册
</a-button>
</div>
</a-form> </a-form>
</a-tab-pane> --> </a-tab-pane>
</a-tabs> </a-tabs>
</div> </div>
</div> </div>
@ -251,7 +185,7 @@
import { onMounted, reactive, ref, toRefs, watch } from "vue"; import { onMounted, reactive, ref, toRefs, watch } from "vue";
import loginImg from "@/assets/images/logo.png"; import loginImg from "@/assets/images/logo.png";
import { UserOutlined, LockOutlined, MobileOutlined } from "@ant-design/icons-vue"; import { UserOutlined, LockOutlined, MobileOutlined } from "@ant-design/icons-vue";
import { getCaptcha } from "@/api/auth"; import { getCaptcha, sendSmsCode, smsLoginApi, resetPassword } from "@/api/auth";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import { setPath } from "@/utils/auth"; import { setPath } from "@/utils/auth";
// //
@ -285,6 +219,7 @@ const forgotPasswordForm = ref({
phone: "", phone: "",
captcha: "", captcha: "",
newPassword: "", newPassword: "",
confirmPassword: "",
}); });
// //
const showForgotPassword = ref(false); const showForgotPassword = ref(false);
@ -304,6 +239,10 @@ const state = reactive({
password: [{ required: true, trigger: "blur", message: t("login.rulesPassword") }], password: [{ required: true, trigger: "blur", message: t("login.rulesPassword") }],
code: [{ required: true, trigger: "blur", message: "请输入验证码" }], code: [{ required: true, trigger: "blur", message: "请输入验证码" }],
}, },
smsLoginRules: {
username: [{ required: true, trigger: "blur", message: "请输入手机号" }],
code: [{ required: true, trigger: "blur", message: "请输入验证码" }]
},
loginImg: loginImg[0], loginImg: loginImg[0],
loading: false, loading: false,
passwordType: "password", passwordType: "password",
@ -325,7 +264,67 @@ const forgotPasswordRules = ref({
captcha: [{ required: true, message: "验证码不能为空", trigger: "blur" }], captcha: [{ required: true, message: "验证码不能为空", trigger: "blur" }],
newPassword: [ newPassword: [
{ required: true, message: "新密码不能为空", trigger: "blur" }, { required: true, message: "新密码不能为空", trigger: "blur" },
{ min: 6, message: "密码长度不能少于6位", trigger: "blur" }, { min: 10, message: "密码长度不能少于10位", trigger: "blur" },
{
validator: (rule: any, value: string) => {
if (!value) return Promise.resolve();
//
const hasUpperCase = /[A-Z]/.test(value);
const hasLowerCase = /[a-z]/.test(value);
const hasNumber = /\d/.test(value);
const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(value);
const typeCount = [hasUpperCase, hasLowerCase, hasNumber, hasSpecialChar].filter(Boolean).length;
if (typeCount < 3) {
return Promise.reject(new Error('密码必须包含大写字母、小写字母、数字、特殊字符中的至少三种'));
}
//
if (/(.)\1{1,}/.test(value)) {
return Promise.reject(new Error('密码不能包含连续重复字符'));
}
// /
for (let i = 0; i < value.length - 1; i++) {
if (/\d/.test(value[i]) && /\d/.test(value[i + 1])) {
const diff = value.charCodeAt(i + 1) - value.charCodeAt(i);
if (Math.abs(diff) === 1) {
return Promise.reject(new Error('密码不能包含连续递增或递减的数字'));
}
}
}
// /
for (let i = 0; i < value.length - 1; i++) {
if (/[a-zA-Z]/.test(value[i]) && /[a-zA-Z]/.test(value[i + 1])) {
const diff = value.charCodeAt(i + 1) - value.charCodeAt(i);
if (Math.abs(diff) === 1) {
return Promise.reject(new Error('密码不能包含连续递增或递减的字母'));
}
}
}
return Promise.resolve();
},
trigger: "blur"
}
],
confirmPassword: [
{ required: true, message: "请再次输入密码", trigger: "blur" },
{
validator: (rule: any, value: string) => {
if (!value) {
return Promise.reject(new Error('请再次输入密码'));
}
if (value !== forgotPasswordForm.value.newPassword) {
return Promise.reject(new Error('两次输入的密码不一致'));
}
return Promise.resolve();
},
trigger: "blur"
}
], ],
}); });
@ -382,6 +381,50 @@ function onFinish() {
}); });
} }
/**
* 短信验证码登录
*/
const onSmsLogin = async () => {
//
try {
//
const phoneRegex = /^1[3-9]\d{9}$/;
if (!loginData.value.username || !phoneRegex.test(loginData.value.username)) {
message.error("请输入正确的手机号");
return;
}
//
if (!loginData.value.code || loginData.value.code.trim() === '') {
message.error("请输入验证码");
return;
}
// console.log()
// debugger
state.loading = true;
//
await smsLoginApi(
loginData.value.username, //
loginData.value.code //
);
//
const res: any = setPath("/login-sjtb");
if (res.code == 0) {
message.success("登录成功");
//
router.push({ path: "/" });
}
} catch (error: any) {
// console.error("", error);
// message.error(error.message || "");
} finally {
state.loading = false;
}
};
watch( watch(
route, route,
() => { () => {
@ -426,9 +469,9 @@ const startCountdown = () => {
}; };
// //
// const showForgotPasswordPage = () => { const showForgotPasswordPage = () => {
// showForgotPassword.value = true; showForgotPassword.value = true;
// }; };
// //
const goRegister = () => { const goRegister = () => {
router.push({ path: "/register" }); router.push({ path: "/register" });
@ -441,49 +484,53 @@ const backToLogin = () => {
phone: "", phone: "",
captcha: "", captcha: "",
newPassword: "", newPassword: "",
confirmPassword: "",
}; };
}; };
// //
// const sendSms = async () => { const sendSms = async () => {
// // //
// if (!loginData.value.username) { if (!loginData.value.username) {
// message.error(""); message.error("请输入手机号");
// return; return;
// } }
// // //
// const phoneRegex = /^1[3-9]\d{9}$/; const phoneRegex = /^1[3-9]\d{9}$/;
// if (!phoneRegex.test(loginData.value.username)) { if (!phoneRegex.test(loginData.value.username)) {
// message.error(""); message.error("请输入正确的手机号");
// return; return;
// } }
// // //
// if (smsCountdown.value > 0) { if (smsCountdown.value > 0) {
// return; return;
// } }
// loading.value = true; loading.value = true;
// smsButtonDisabled.value = true; smsButtonDisabled.value = true;
// try { try {
// // // type1
// // await axios.post('/sms/send', { phone: loginForm.value.username }) const res: any = await sendSmsCode(loginData.value.username, 3);
if (res.code == 1) {
message.success(res.msg);
return
}
//
message.success("验证码发送成功");
// // //
// message.success(""); startCountdown();
} catch (error) {
// // console.error("发送验证码失败", error);
// startCountdown(); message.error("验证码发送失败,请重试");
// } catch (error) { smsButtonDisabled.value = false;
// console.error("", error); } finally {
// message.error(""); loading.value = false;
// smsButtonDisabled.value = false; }
// } finally { };
// loading.value = false;
// }
// };
// //
const sendForgotPasswordSms = async () => { const sendForgotPasswordSms = async () => {
// //
@ -508,17 +555,23 @@ const sendForgotPasswordSms = async () => {
smsButtonDisabled.value = true; smsButtonDisabled.value = true;
try { try {
// // type=3
// await axios.post('/sms/forgot-password', { phone: forgotPasswordForm.value.phone }) const res: any = await sendSmsCode(forgotPasswordForm.value.phone, 2);
// if (res.code == 1) {
message.error(res.msg);
smsButtonDisabled.value = false;
return;
}
//
message.success("验证码发送成功"); message.success("验证码发送成功");
// //
startCountdown(); startCountdown();
} catch (error) { } catch (error: any) {
console.error("发送验证码失败", error); // console.error("", error);
message.error("验证码发送失败,请重试"); // message.error(error.message || "");
smsButtonDisabled.value = false; smsButtonDisabled.value = false;
} finally { } finally {
loading.value = false; loading.value = false;
@ -526,23 +579,111 @@ const sendForgotPasswordSms = async () => {
}; };
// //
const handleResetPassword = async () => { const handleResetPassword = async () => {
loading.value = true; //
try { try {
// 2. () //
// await axios.post('/user/reset-password', { const phoneRegex = /^1[3-9]\d{9}$/;
// phone: forgotPasswordForm.value.phone, if (!forgotPasswordForm.value.phone || !phoneRegex.test(forgotPasswordForm.value.phone)) {
// captcha: forgotPasswordForm.value.captcha, message.error("请输入正确的手机号");
// newPassword: forgotPasswordForm.value.newPassword return;
// }) }
// 3. if (!forgotPasswordForm.value.captcha) {
message.success("密码重置成功"); message.error("请输入验证码");
return;
}
if (!forgotPasswordForm.value.newPassword) {
message.error("请输入新密码");
return;
}
//
if (!forgotPasswordForm.value.confirmPassword) {
message.error("请再次输入密码");
return;
}
//
if (forgotPasswordForm.value.newPassword !== forgotPasswordForm.value.confirmPassword) {
message.error("两次输入的密码不一致");
return;
}
//
const password = forgotPasswordForm.value.newPassword;
if (password.length < 10) {
message.error("密码长度不能少于10位");
return;
}
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password);
const typeCount = [hasUpperCase, hasLowerCase, hasNumber, hasSpecialChar].filter(Boolean).length;
if (typeCount < 3) {
message.error('密码必须包含大写字母、小写字母、数字、特殊字符中的至少三种');
return;
}
//
if (/(.)\1{1,}/.test(password)) {
message.error('密码不能包含连续重复字符');
return;
}
// /
for (let i = 0; i < password.length - 1; i++) {
if (/\d/.test(password[i]) && /\d/.test(password[i + 1])) {
const diff = password.charCodeAt(i + 1) - password.charCodeAt(i);
if (Math.abs(diff) === 1) {
message.error('密码不能包含连续递增或递减的数字');
return;
}
}
}
// /
for (let i = 0; i < password.length - 1; i++) {
if (/[a-zA-Z]/.test(password[i]) && /[a-zA-Z]/.test(password[i + 1])) {
const diff = password.charCodeAt(i + 1) - password.charCodeAt(i);
if (Math.abs(diff) === 1) {
message.error('密码不能包含连续递增或递减的字母');
return;
}
}
}
// 4.
backToLogin();
} catch (error) { } catch (error) {
console.error("密码重置失败", error); console.error("表单验证失败", error);
message.error("密码重置失败,请重试"); return;
}
loading.value = true;
try {
// RSA
const encryptedPassword = encrypt(forgotPasswordForm.value.newPassword);
// 使
const res: any = await resetPassword(
forgotPasswordForm.value.phone,
forgotPasswordForm.value.captcha,
encryptedPassword
);
if (res.code == 0 ) {
message.success("密码重置成功");
//
backToLogin();
} else {
message.error(res.msg || "密码重置失败");
}
} catch (error: any) {
// console.error("", error);
// message.error(error.message || "");
} finally { } finally {
loading.value = false; loading.value = false;
} }
@ -632,7 +773,7 @@ onMounted(() => {
left: 74%; left: 74%;
top: 30%; top: 30%;
width: 20%; width: 20%;
max-height: 402px; max-height: 420px;
max-width: 400px; max-width: 400px;
min-height: 362px; min-height: 362px;
border-radius: 3px; border-radius: 3px;
@ -647,10 +788,12 @@ onMounted(() => {
/* 验证码布局 */ /* 验证码布局 */
.captcha-row { .captcha-row {
display: flex; display: flex;
.ant-row { .ant-row {
display: flex; display: flex;
align-items: start; align-items: start;
} }
/* gap: 12px; /* gap: 12px;
align-items: center; */ align-items: center; */

View File

@ -12,133 +12,219 @@
<div class="right-section"> <div class="right-section">
<a-tabs v-model:activeKey="activeTab" class="register-tabs"> <a-tabs v-model:activeKey="activeTab" class="register-tabs">
<a-tab-pane key="register" tab="用户注册"> <a-tab-pane key="register" tab="用户注册">
<a-form <a-form :model="registerData" :rules="registerRules" layout="horizontal"
:model="registerData" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" class="form-container"
:rules="registerRules" @finish="handleFormFinish">
layout="vertical" <!-- 账号信息 -->
class="form-container" <div class="form-section">
@finish="onRegister" <div class="section-title">账号信息</div>
>
<!-- 登录账号 -->
<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-form-item name="username" label="账&nbsp;&nbsp;号">
<a-input <a-input v-model:value="registerData.username" placeholder="请输入登录账号4-20个字符" />
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>
<!-- 密码 --> <!-- 密码 -->
<a-form-item name="password" label="密码"> <a-form-item name="password" label="密&nbsp;&nbsp;码">
<a-input-password <a-input-password v-model:value="registerData.password"
v-model:value="registerData.password" placeholder="请设置密码6-20个字符" />
placeholder="请设置密码6-20个字符"
:prefix="h(LockOutlined)"
/>
</a-form-item> </a-form-item>
<!-- 确认密码 --> <!-- 确认密码 -->
<a-form-item name="confirmPassword" label="确认密码"> <a-form-item name="confirmPassword" label="确认密码">
<a-input-password <a-input-password v-model:value="registerData.confirmPassword"
v-model:value="registerData.confirmPassword" placeholder="请再次输入密码" />
placeholder="请再次输入密码" </a-form-item>
:prefix="h(LockOutlined)" </div>
/>
<!-- 个人信息 -->
<div class="form-section">
<div class="section-title">个人信息</div>
<!-- 真实姓名 -->
<a-form-item name="realName" label="真实姓名">
<a-input v-model:value="registerData.realName" placeholder="请输入真实姓名" />
</a-form-item> </a-form-item>
<!-- 验证码 --> <!-- 所属单位 -->
<a-form-item name="code" label="验证码"> <a-form-item name="belongingUnit" label="所属单位">
<a-input v-model:value="registerData.belongingUnit" placeholder="请输入所属单位(选填)" />
</a-form-item>
<!-- 手机号 -->
<a-form-item name="phone" label="手 机 号">
<a-row :gutter="8"> <a-row :gutter="8">
<a-col :span="16"> <a-col :span="16">
<a-input <a-input v-model:value="registerData.phone" placeholder="请输入11位手机号" />
v-model:value="registerData.code"
placeholder="请输入验证码"
/>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<img <a-button type="primary" :disabled="smsCountdown > 0" @click="handleSendSms"
v-if="captchaImg" style="width: 100%">
:src="captchaImg" {{ smsCountdown > 0 ? `${smsCountdown}s` : '获取验证码' }}
@click="refreshCaptcha" </a-button>
style="cursor: pointer; width: 100%; height: 36px;"
/>
</a-col> </a-col>
</a-row> </a-row>
</a-form-item> </a-form-item>
<!-- 短信验证码 -->
<a-form-item name="smsCode" label="验证码">
<a-input v-model:value="registerData.smsCode" placeholder="请输入短信验证码" />
</a-form-item>
</div>
<!-- 注册按钮 --> <!-- 注册按钮 -->
<a-button <a-form-item style="display: flex;align-items: center;justify-content: center;width: 100%;">
type="primary" <a-button type="primary" size="large" block htmlType="submit" :loading="loading"
size="large" style="margin-left: 20px;">
block
htmlType="submit"
:loading="loading"
>
<span>立即注册</span> <span>立即注册</span>
</a-button> </a-button>
</a-form-item>
<!-- 返回登录 --> <!-- 返回登录 -->
<a-button <a-form-item style="display: flex;align-items: center;justify-content: center;width: 100%;">
type="link"
size="small" <a-button type="link" size="small" block @click="backToLogin"
block style="margin-left: 20px;">
@click="backToLogin"
:style="{ marginTop: '10px' }"
>
已有账号返回登录 已有账号返回登录
</a-button> </a-button>
</a-form-item>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
<!-- 注册确认弹框 -->
<a-modal v-model:open="modalVisible" title="选择所属组织" width="600px" :confirm-loading="loading"
@ok="onRegister" @cancel="handleModalCancel">
<a-form :model="organizationData" :rules="organizationRules" layout="horizontal"
ref="organizationFormRef" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
<!-- 集团单选字符串 -->
<a-form-item name="groupCode" label="集 团">
<a-select v-model:value="organizationData.groupCode" placeholder="请选择集团" style="width: 100%"
show-search :filter-option="filterOption">
<a-select-option v-for="item in groupList" :key="item.hycd" :value="item.hycd" :label="item.hynm">
{{ item.hynm }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 公司单选字符串 -->
<a-form-item name="companyCode" label="公 司">
<a-select v-model:value="organizationData.companyCode" placeholder="请选择公司"
style="width: 100%" show-search :filter-option="filterOption" allow-clear>
<a-select-option v-for="item in companyList" :key="item.hycd" :value="item.hycd"
:label="item.hynm">
{{ item.hynm }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 流域多选数组必填 -->
<a-form-item name="rvcdCode" label="流 域" required>
<a-select v-model:value="organizationData.rvcdCode" mode="multiple" placeholder="请选择流域"
style="width: 100%" show-search :filter-option="filterOption" @change="onRvcdChange">
<a-select-option v-for="item in basinList" :key="item.hbrvcd" :value="item.hbrvcd"
:label="item.hbrvnm">
{{ item.hbrvnm }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 电站多选数组必填依赖流域 -->
<a-form-item name="stationCode" label="电 站" required>
<a-select v-model:value="organizationData.stationCode" mode="multiple" placeholder="请先选择流域"
:disabled="organizationData.rvcdCode.length === 0" style="width: 100%" show-search
:filter-option="filterOption">
<a-select-option v-for="item in stationList" :key="item.stcd" :value="item.stcd"
:label="item.ennm">
{{ item.ennm }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</a-modal>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, h } from "vue"; import { reactive, ref, onMounted, onUnmounted } from "vue";
import { UserOutlined, LockOutlined, MobileOutlined } from "@ant-design/icons-vue"; import {
import { getCaptcha, getBasinList,
// registerUser getGroupList,
} from "@/api/auth"; getCompanyList,
getStationList,
sendSmsCode,
verifySmsCode,
registerUser
} from "@/api/auth";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import router from "@/router"; import router from "@/router";
import { encrypt } from "@/utils/rsaEncrypt"; import { encrypt } from "@/utils/rsaEncrypt";
// //
const registerData = reactive({ const registerData = reactive({
//
//
username: "", username: "",
realName: "",
phone: "",
password: "", password: "",
confirmPassword: "", confirmPassword: "",
code: "", realName: "",
uuid: "", belongingUnit: "",
phone: "",
// smsCode: "",
userType: 1,
status: 1,
regStatus: 0
}); });
//
const basinList = ref<any[]>([]);
const groupList = ref<any[]>([]);
const companyList = ref<any[]>([]);
const stationList = ref<any[]>([]);
//
const smsCountdown = ref(0);
let smsTimer: any = null;
// ==================== ====================
const organizationData = reactive({
groupCode: '', //
companyCode: '', //
rvcdCode: [], //
stationCode: [] //
});
//
const organizationFormRef = ref();
// ==================== ====================
const organizationRules = {
rvcdCode: [
{
validator: (rule: any, value: any[]) => {
if (!value || value.length === 0) {
return Promise.reject("请至少选择一个流域");
}
return Promise.resolve();
},
trigger: "change"
}
],
stationCode: [
{
validator: (rule: any, value: any[]) => {
if (!value || value.length === 0) {
return Promise.reject("请至少选择一个电站");
}
return Promise.resolve();
},
trigger: "change"
}
]
};
// //
const registerRules = { const registerRules = {
// //
@ -148,22 +234,96 @@ const registerRules = {
{ pattern: /^[a-zA-Z0-9_]+$/, message: "只能包含字母、数字和下划线", 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: [ password: [
{ required: true, message: "请输入密码", trigger: "blur" }, { required: true, message: "请输入密码", trigger: "blur" },
{ min: 6, max: 20, message: "密码长度6-20个字符", trigger: "blur" } {
validator: (rule: any, value: string) => {
if (!value) {
return Promise.reject("请输入密码");
}
// 1. 10
if (value.length < 10) {
return Promise.reject("密码长度不能少于10位");
}
// 2.
const hasUpperCase = /[A-Z]/.test(value);
const hasLowerCase = /[a-z]/.test(value);
const hasNumber = /[0-9]/.test(value);
const hasSpecial = /[^a-zA-Z0-9]/.test(value);
const typeCount = [hasUpperCase, hasLowerCase, hasNumber, hasSpecial].filter(Boolean).length;
if (typeCount < 3) {
return Promise.reject("密码必须包含大写字母、小写字母、数字、特殊字符中的至少三类");
}
// 3.
// 3.1 2
for (let i = 0; i <= value.length - 2; i++) {
if (value[i] === value[i + 1]) {
return Promise.reject("密码不能包含2位及以上相同字符的连续重复如11、aa");
}
}
// 3.2 2/
for (let i = 0; i <= value.length - 2; i++) {
const char1 = value.charCodeAt(i);
const char2 = value.charCodeAt(i + 1);
//
if (/\d/.test(value[i]) && /\d/.test(value[i + 1])) {
//
if (Math.abs(char2 - char1) === 1) {
return Promise.reject("密码不能包含2位及以上连续递增或递减的数字如12、21");
}
}
}
// 3.3 2/
for (let i = 0; i <= value.length - 2; i++) {
const char1 = value.charCodeAt(i);
const char2 = value.charCodeAt(i + 1);
//
if (/[a-zA-Z]/.test(value[i]) && /[a-zA-Z]/.test(value[i + 1])) {
//
if (Math.abs(char2 - char1) === 1) {
return Promise.reject("密码不能包含2位及以上连续递增或递减的字母如ab、ba");
}
}
}
// 4.
const username = registerData.username.toLowerCase();
const passwordLower = value.toLowerCase();
// 4.1
if (passwordLower.includes(username)) {
return Promise.reject("密码不能包含用户名");
}
// 4.2
const passwordLetters = passwordLower.replace(/[^a-z]/g, '');
// 4.3 >= 3
if (passwordLetters.length >= 3 && username.includes(passwordLetters)) {
return Promise.reject("密码的字母部分不能是用户名的子串");
}
// 4.4 3
for (let i = 0; i <= passwordLetters.length - 3; i++) {
const substring = passwordLetters.substring(i, i + 3);
if (username.includes(substring)) {
return Promise.reject("密码不能与用户名存在明显关联");
}
}
return Promise.resolve();
},
trigger: "blur"
}
], ],
// //
@ -180,66 +340,220 @@ const registerRules = {
} }
], ],
// //
code: [ realName: [
{ required: true, message: "请输入验证码", trigger: "blur" } { 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" }
],
//
smsCode: [
{ required: true, message: "请输入短信验证码", trigger: "blur" },
{ len: 6, message: "验证码为6位数字", trigger: "blur" }
] ]
}; };
const loading = ref(false); const loading = ref(false);
const captchaImg = ref("");
const activeTab = ref("register"); const activeTab = ref("register");
const modalVisible = ref(false);
// //
const refreshCaptcha = async () => { const loadGroupList = async () => {
try { try {
const res = await getCaptcha(); const res = await getGroupList();
registerData.uuid = res.data.verifyCodeKey; groupList.value = res.data || [];
captchaImg.value = res.data.verifyCodeImg; // base64
} catch (error) { } catch (error) {
message.error("获取验证码失败"); console.error("加载集团列表失败", error);
} }
}; };
//
const onGroupChange = async () => {
//
// if (value) {
try {
const res = await getCompanyList();
companyList.value = res.data || [];
} catch (error) {
message.error("加载公司列表失败");
}
// }
};
//
const onCompanyChange = async () => {
//
// if (value) {
try {
const res = await getBasinList();
basinList.value = res.data || [];
} catch (error) {
message.error("加载流域列表失败");
}
// }
};
//
const onBasinChange = async (ids: any) => {
//
// if (value) {
try {
const res = await getStationList(ids);
stationList.value = res.data || [];
} catch (error) {
message.error("加载电站列表失败");
}
// }
};
// ==================== ====================
const onRvcdChange = () => {
//
organizationData.stationCode = [];
// ID
if (organizationData.rvcdCode && organizationData.rvcdCode.length > 0) {
onBasinChange({ hbrvcds: organizationData.rvcdCode });
} else {
//
stationList.value = [];
}
};
//
const handleSendSms = async () => {
//
if (!/^1[3-9]\d{9}$/.test(registerData.phone)) {
message.error("请输入正确的手机号");
return;
}
try {
await sendSmsCode(registerData.phone, 1); // type=1
message.success("验证码已发送");
// 60
smsCountdown.value = 60;
smsTimer = setInterval(() => {
smsCountdown.value--;
if (smsCountdown.value <= 0) {
clearInterval(smsTimer);
smsTimer = null;
}
}, 1000);
} catch (error: any) {
message.error(error.message || "发送失败,请重试");
}
};
//
const handleFormFinish = async () => {
//
// const res: any = await verifySmsCode(registerData.phone, registerData.smsCode, 1);
// if (res.code == 1) {
// message.error('')
// return
// }
//
modalVisible.value = true;
};
//
const handleModalCancel = () => {
//
modalVisible.value = false;
//
organizationData.groupCode = '';
organizationData.companyCode = '';
organizationData.rvcdCode = [];
organizationData.stationCode = [];
//
organizationFormRef.value?.clearValidate();
};
// //
const onRegister = async () => { const onRegister = async () => {
//
try {
await organizationFormRef.value.validate();
} catch (error) {
message.error("请完善组织信息");
return; //
}
loading.value = true; loading.value = true;
try { try {
//
//
const encryptedPassword = encrypt(registerData.password); const encryptedPassword = encrypt(registerData.password);
// //
const registerParams = { const registerParams: any = {
username: registerData.username, type: 1, //
realName: registerData.realName, phone: registerData.phone, //
phone: registerData.phone, code: registerData.smsCode, //
password: encryptedPassword, username: registerData.username, //
password: encryptedPassword, //
// realName: registerData.realName, //
userType: 1,
status: 1,
regStatus: 0,
//
code: registerData.code,
uuid: registerData.uuid
}; };
// //
// await registerUser(registerParams); if (registerData.belongingUnit) {
registerParams.belongingUnit = registerData.belongingUnit;
}
//
if (organizationData.groupCode) {
registerParams.groupCode = organizationData.groupCode;
}
//
if (organizationData.companyCode) {
registerParams.companyCode = organizationData.companyCode;
}
//
if (organizationData.rvcdCode && organizationData.rvcdCode.length > 0) {
registerParams.rvcdCode = organizationData.rvcdCode.join(',');
}
//
if (organizationData.stationCode && organizationData.stationCode.length > 0) {
registerParams.stationCode = organizationData.stationCode.join(',');
}
//
const res1: any = await registerUser(registerParams);
if (res1.code == 1) {
message.error('注册失败,请重试')
return
}
message.success("注册成功,等待管理员审核"); message.success("注册成功,等待管理员审核");
//
modalVisible.value = false;
// //
setTimeout(() => { setTimeout(() => {
router.push({ path: "/login" }); router.push({ path: "/login-sjtb" });
}, 1500); }, 1500);
} catch (error: any) { } catch (error: any) {
message.error(error.message || "注册失败,请重试"); //
// // if (error.message && error.message.includes('')) {
refreshCaptcha(); // message.error("");
// } else {
// message.error(error.message || "");
// }
} finally { } finally {
loading.value = false; loading.value = false;
} }
@ -247,13 +561,28 @@ const onRegister = async () => {
// //
const backToLogin = () => { const backToLogin = () => {
router.push({ path: "/login" }); router.push({ path: "/login-sjtb" });
}; };
// //
onMounted(() => { onMounted(() => {
refreshCaptcha(); loadGroupList()
onGroupChange()
onCompanyChange()
}); });
//
onUnmounted(() => {
if (smsTimer) {
clearInterval(smsTimer);
smsTimer = null;
}
});
const filterOption = (inputValue: string, option: any) => {
if (!option.label) return false;
return option.label.indexOf(inputValue) !== -1;
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -262,7 +591,7 @@ onMounted(() => {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
min-width: 1500px; min-width: 1200px; //
background-color: #fff; background-color: #fff;
.register-wrapper { .register-wrapper {
@ -272,7 +601,8 @@ onMounted(() => {
min-height: 600px; min-height: 600px;
background: url("@/assets/images/bg_sjtb.png"); background: url("@/assets/images/bg_sjtb.png");
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 100% 100%; background-size: cover; // cover
background-position: center; //
} }
// //
@ -280,45 +610,217 @@ onMounted(() => {
.slogan { .slogan {
position: absolute; position: absolute;
top: 20%; top: 20%;
left: 18%; left: 5%; //
width: 700px; width: 40vw; // 使
height: 112px; max-width: 700px; //
min-width: 300px; //
color: #040504; color: #040504;
font-size: 40px; font-size: clamp(24px, 3vw, 40px); //
line-height: 1.5;
p {
margin: 0;
word-wrap: break-word; //
}
} }
} }
// //
.right-section { .right-section {
position: absolute; position: absolute;
left: 70%; right: 5%; // right
top: 15%; top: 50%; //
width: 25%; transform: translateY(-50%); //
// max-height: 650px; width: clamp(380px, 30vw, 500px); //
// max-width: 400px; min-height: 600px; //
min-height: 650px; max-height: 90vh; // 90%
border-radius: 3px; border-radius: 8px; //
padding: 20px 24px 24px; padding: clamp(15px, 2vw, 24px) clamp(15px, 2vw, 24px); //
background-color: #fff; background-color: rgba(255, 255, 255, 0.98); //
overflow-y: auto; overflow-y: auto;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); //
//
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
:deep(.ant-tabs-nav) {
height: auto; //
margin-bottom: 16px;
}
//
.form-section {
margin-bottom: clamp(12px, 1.5vh, 20px); //
.section-title {
font-size: clamp(13px, 1.2vw, 15px); //
font-weight: 600;
color: #333;
margin-bottom: clamp(8px, 1vh, 12px);
padding-bottom: clamp(6px, 0.8vh, 10px);
border-bottom: 1px solid #e8e8e8;
}
}
:deep(.ant-form-item) { :deep(.ant-form-item) {
margin-bottom: 14px; margin-bottom: clamp(10px, 1.2vh, 16px); //
} }
:deep(.ant-form-item-label > label) { :deep(.ant-form-item-label) {
text-align: right !important;
>label {
font-size: clamp(12px, 1vw, 14px); //
color: #333;
white-space: nowrap;
display: inline-block;
&::after {
content: ':';
}
}
}
:deep(.ant-input),
:deep(.ant-input-password) {
font-size: clamp(12px, 1vw, 14px); //
}
:deep(.ant-btn) {
font-size: clamp(12px, 1vw, 14px); //
}
:deep(.ant-radio-group) {
display: flex;
gap: clamp(12px, 1.5vw, 20px); //
}
:deep(.ant-form-item-control-input-content) {
display: flex;
align-items: center;
}
}
}
// -
@media (max-width: 1400px) {
.register-container {
.left-section {
.slogan {
left: 3%;
width: 45vw;
font-size: clamp(20px, 2.5vw, 32px);
}
}
.right-section {
right: 3%;
width: clamp(350px, 35vw, 450px);
}
}
}
//
@media (min-width: 1401px) and (max-width: 1920px) {
.register-container {
.left-section {
.slogan {
left: 5%;
width: 40vw;
}
}
.right-section {
right: 5%;
width: clamp(380px, 30vw, 480px);
}
}
}
//
@media (min-width: 1921px) {
.register-container {
.left-section {
.slogan {
left: 8%;
width: 35vw;
font-size: clamp(32px, 3.5vw, 48px);
}
}
.right-section {
right: 8%;
width: clamp(420px, 28vw, 550px);
padding: 24px 32px;
}
}
}
//
@media (max-width: 1200px) {
.register-container {
.left-section {
.slogan {
display: none; //
}
}
.right-section {
left: 50%;
right: auto;
transform: translate(-50%, -50%); //
width: clamp(320px, 90vw, 420px);
}
}
}
// 125%150%
@media (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
.register-container {
.right-section {
padding: clamp(12px, 1.5vw, 20px);
.form-section {
margin-bottom: clamp(10px, 1.2vh, 16px);
}
:deep(.ant-form-item) {
margin-bottom: clamp(8px, 1vh, 14px);
}
}
}
}
//
@media (orientation: landscape) and (max-height: 700px) {
.register-container {
.right-section {
max-height: 95vh;
padding: 10px 15px;
.form-section {
margin-bottom: 8px;
.section-title {
margin-bottom: 6px;
padding-bottom: 6px;
font-size: 13px; font-size: 13px;
} }
}
:deep(.ant-form-item) {
:deep(.ant-input-prefix) { margin-bottom: 8px;
display: flex;
width: 26px;
svg {
width: 18px;
height: 18px;
margin-right: 4px;
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -282,57 +282,80 @@ const userId = ref('')
// ID // ID
const fishhui = ref<any[]>([]) const fishhui = ref<any[]>([])
const fishTreeDialog = ref(false) const fishTreeDialog = ref(false)
function openFishway(row: any) {
//
const isFishDataLoaded = ref(false)
async function openFishway(row: any) {
fishway.value = true fishway.value = true
userId.value = row.id userId.value = row.id
//
getFishTree() //
// watch
fishhui.value = [] fishhui.value = []
tableDatafish.value = [] tableDatafish.value = []
isFishDataLoaded.value = false
try {
// 使 Promise.all
await Promise.all([
getFishTree(), //
getuserdata({ userId: userId.value }).then((res: any) => { getuserdata({ userId: userId.value }).then((res: any) => {
console.log(res) console.log('用户权限数据:', res)
if (res.code == 0) { if (res.code == 0) {
res.data.forEach((item: any) => { res.data.forEach((item: any) => {
fishhui.value.push(item.orgId) fishhui.value.push(item.orgId)
tableDatafish.value.push({ tableDatafish.value.push({
name: item.orgName, name: item.orgName,
type: item.orgType, // '' type: item.orgType,
id: item.orgId.toString(), // 使codeid id: item.orgId.toString(),
code: item.orgId, code: item.orgId,
path: item.path, path: item.path,
userId: item.userId, userId: item.userId,
parentId: item.parentId, parentId: item.parentId,
orgLevel: item.orgLevel, orgLevel: item.orgLevel,
permissionType: item.permissionType // permissionType: item.permissionType
}) })
}) })
} }
}) })
])
//
isFishDataLoaded.value = true
console.log('所有数据加载完成,准备回显')
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败,请重试')
}
} }
// fishway, // fishway,
watch(fishway, (newVal) => { watch([fishway, isFishDataLoaded], ([newFishway, newDataLoaded]) => {
if (newVal) { if (newFishway && newDataLoaded) {
// dialog,tree //
setTimeout(() => {
console.log('开始回显, fishTreeRef:', fishTreeRef.value); console.log('开始回显, fishTreeRef:', fishTreeRef.value);
console.log('回显ID:', fishhui.value); console.log('回显ID:', fishhui.value);
console.log('树数据:', fishData.value);
// 使 nextTick DOM
nextTick(() => {
if (fishTreeRef.value && fishhui.value.length > 0) { if (fishTreeRef.value && fishhui.value.length > 0) {
// //
fishTreeRef.value.setCheckedKeys([], false); fishTreeRef.value.setCheckedKeys([], false);
// , //
setTimeout(() => { setTimeout(() => {
fishTreeRef.value.setCheckedKeys(fishhui.value, false); fishTreeRef.value.setCheckedKeys(fishhui.value, false);
// //
const checkedKeys = fishTreeRef.value.getCheckedKeys(); const checkedKeys = fishTreeRef.value.getCheckedKeys();
const halfCheckedKeys = fishTreeRef.value.getHalfCheckedKeys(); const halfCheckedKeys = fishTreeRef.value.getHalfCheckedKeys();
console.log('回显完成 - 全选节点:', checkedKeys); console.log('回显完成 - 全选节点:', checkedKeys);
console.log('回显完成 - 半选节点:', halfCheckedKeys); console.log('回显完成 - 半选节点:', halfCheckedKeys);
//
fishTableData.value = tableDatafish.value
}, 100); }, 100);
} else if (fishTreeRef.value) { } else if (fishTreeRef.value) {
// fishhui.value // fishhui.value
@ -341,20 +364,12 @@ watch(fishway, (newVal) => {
fishTableData.value = []; fishTableData.value = [];
}, 100); }, 100);
} }
fishTableData.value = tableDatafish.value });
// if (fishhui.value.length > 0 && activeName.value == '1') { } else if (!newFishway) {
// isCompanyDisabled.value = true
// } else if (fishhui.value.length > 0 && activeName.value == '2') {
// isBaseDisabled.value = true
// } else {
// isCompanyDisabled.value = false
// isBaseDisabled.value = false
// }
}, 500);
} else {
// //
console.log('关闭对话框,清理数据'); console.log('关闭对话框,清理数据');
fishhui.value = []; fishhui.value = [];
isFishDataLoaded.value = false;
// tree // tree
setTimeout(() => { setTimeout(() => {
if (fishTreeRef.value) { if (fishTreeRef.value) {
@ -367,6 +382,7 @@ watch(fishway, (newVal) => {
function fishHandleClose() { function fishHandleClose() {
fishway.value = false fishway.value = false
isFishDataLoaded.value = false
fishTableData.value = [] fishTableData.value = []
fishData.value = [] fishData.value = []
} }
@ -406,19 +422,21 @@ const fishData: any = ref([])
// //
// const huixiandata = ref([]) // const huixiandata = ref([])
let tableDatafish: any = [] let tableDatafish: any = []
// Promise便
function getFishTree() { function getFishTree() {
fishTreeDialog.value = true fishTreeDialog.value = true
const params = { const params = {
// type:'1', // type:'1',
engName: treeInput.value engName: treeInput.value
} }
getFishtree(params).then((res: any) => { return getFishtree(params).then((res: any) => {
if (res.code == 0) { if (res.code == 0) {
fishData.value = res.data fishData.value = res.data
}
})
fishTreeDialog.value = false fishTreeDialog.value = false
}
}).catch(() => {
fishTreeDialog.value = false
})
} }
// - ID // - ID
@ -1000,7 +1018,7 @@ function handleClearSelection() {
<!-- 过鱼设施权限维护 --> <!-- 过鱼设施权限维护 -->
<el-dialog v-model="fishway" :close-on-click-modal="false" :before-close="fishHandleClose" title="过鱼设施权限维护" <el-dialog v-model="fishway" :close-on-click-modal="false" :before-close="fishHandleClose" title="过鱼设施权限维护"
append-to-body width="1600px" draggable> append-to-body width="1600px" draggable>
<el-scrollbar height="610px"> <!-- <el-scrollbar height="610px"> -->
<!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"> <!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="基地" name="1" :disabled="isBaseDisabled"></el-tab-pane> <el-tab-pane label="基地" name="1" :disabled="isBaseDisabled"></el-tab-pane>
<el-tab-pane label="公司" name="2" :disabled="isCompanyDisabled"></el-tab-pane> <el-tab-pane label="公司" name="2" :disabled="isCompanyDisabled"></el-tab-pane>
@ -1021,9 +1039,9 @@ function handleClearSelection() {
</div> </div>
</div> </div>
<el-scrollbar height="460px"> <el-scrollbar height="450px" style="margin-top: 10px;">
<el-tree ref="fishTreeRef" style="max-width: 300px" :data="fishData" show-checkbox default-expand-all <el-tree ref="fishTreeRef" style="max-width: 300px" :data="fishData" show-checkbox default-expand-all
:v-loading="fishTreeDialog" node-key="code" highlight-current :props="fishProps" v-loading="fishTreeDialog" node-key="code" highlight-current :props="fishProps"
@check="handleFishCheckChange" /> @check="handleFishCheckChange" />
</el-scrollbar> </el-scrollbar>
</div> </div>
@ -1053,7 +1071,7 @@ function handleClearSelection() {
</el-table> </el-table>
</div> </div>
</div> </div>
</el-scrollbar> <!-- </el-scrollbar> -->
<span class="dialog-footer" <span class="dialog-footer"
style="display: flex;display: -webkit-flex; justify-content: flex-end;-webkit-justify-content: flex-end;margin-top: 20px;"> style="display: flex;display: -webkit-flex; justify-content: flex-end;-webkit-justify-content: flex-end;margin-top: 20px;">
<el-button @click="fishHandleClose"> </el-button> <el-button @click="fishHandleClose"> </el-button>