From 85c54d88f139096614aea4b06f2166cae27729d7 Mon Sep 17 00:00:00 2001 From: zhangwei <1504152376@qq.com> Date: 星期五, 22 八月 2025 10:11:19 +0800 Subject: [PATCH] 用户管理 --- src/views/login/index.vue | 469 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 400 insertions(+), 69 deletions(-) diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 732463a..f79d52d 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -1,24 +1,42 @@ -<script setup lang="ts"> +<script setup lang="tsx"> import Motion from "./utils/motion"; import { useRouter } from "vue-router"; import { message } from "@/utils/message"; import { loginRules } from "./utils/rule"; -import { ref, reactive, toRaw } from "vue"; -import { debounce } from "@pureadmin/utils"; +import { + reactive, + computed, + ref, + Ref, + h, + onMounted, + defineAsyncComponent, + onUnmounted, + watch +} from "vue"; +import { debounce, storageLocal } from "@pureadmin/utils"; import { useNav } from "@/layout/hooks/useNav"; import { useEventListener } from "@vueuse/core"; import type { FormInstance } from "element-plus"; +import { + ElForm, + ElFormItem, + ElRadioGroup, + ElRadio, + ElRow, + ElTag, + ElCol +} from "element-plus"; import { useLayout } from "@/layout/hooks/useLayout"; import { useUserStoreHook } from "@/store/modules/user"; import { initRouter, getTopMenu } from "@/router/utils"; -import { bg, avatar, illustration } from "./utils/static"; -import { useRenderIcon } from "@/components/ReIcon/src/hooks"; +import { bg, avatar, logo1 } from "./utils/static"; +// import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; +import { useRoute } from "vue-router"; -import dayIcon from "@/assets/svg/day.svg?component"; -import darkIcon from "@/assets/svg/dark.svg?component"; -import Lock from "~icons/ri/lock-fill"; -import User from "~icons/ri/user-3-fill"; +const route = useRoute(); +import { captcha, phoneNumberCode, exRole } from "@/api/register/index"; defineOptions({ name: "Login" @@ -35,43 +53,277 @@ const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange(); dataThemeChange(overallStyle.value); const { title } = useNav(); - -const ruleForm = reactive({ - username: "admin", - password: "admin123" +// 鑾峰彇楠岃瘉鐮� +const getCaptcha = async () => { + // if (!state.captchaEnabled) return; + state.ruleForm.code = ""; + const res = await captcha(); + state.captchaImage = "data:text/html;base64," + res.result?.img; + state.expirySeconds = res.result?.expirySeconds; + state.ruleForm.codeId = res.result?.id; +}; +import { + addDialog, + closeDialog, + updateDialog, + closeAllDialog +} from "@/components/ReDialog"; +const state = reactive({ + isShowPassword: false, + ruleForm: { + account: "", + nickName: "", + phone: "", + phoneVCode: "", + code: "", + codeId: "", + email: "", + exRoleCode: "" + }, + rules: { + code: [ + { + required: true, + message: "璇疯緭鍏ユ墜鏈洪獙璇佺爜", + trigger: "blur" + } + ], + phone: [ + { + required: true, + message: "璇疯緭鍏ユ偍鐨勬墜鏈哄彿鐮�", + trigger: "blur" + } + ], + exRoleCode: [ + { + required: true, + message: "璇烽�夋嫨瑙掕壊", + trigger: "blur" + } + ] + // code: [{ required: true, message: t('message.account.placeholder4'), trigger: 'blur' }], + }, + loading: { + signIn: false + }, + captchaImage: "", + rotateVerifyVisible: false, + // rotateVerifyImg: verifyImg, + // rotateVerifyImg: themeConfig.value.logoUrl, + secondVerEnabled: false, + // captchaEnabled: false, + isPassRotate: false, + capsLockVisible: false, + hideTenantForLogin: false, + expirySeconds: 60, // 楠岃瘉鐮佽繃鏈熸椂闂� + phoneSeconds: 0, // 鎵嬫満楠岃瘉鐮佸�掕鏃� + roleList: [], + myEnterpriseList: [], + nowRole: {} }); +let secondCode = ""; +let secondId: Ref<string | number | boolean> = ref(""); +// 楠岃瘉鐮佽繃鏈熻鏃跺櫒 +let timer: any = null; +let phonetimer: any = null; -const onLogin = async (formEl: FormInstance | undefined) => { - if (!formEl) return; - await formEl.validate(valid => { - if (valid) { - loading.value = true; - useUserStoreHook() - .loginByUsername({ - username: ruleForm.username, - password: ruleForm.password - }) - .then(res => { - if (res.success) { - // 鑾峰彇鍚庣璺敱 - return initRouter().then(() => { - disabled.value = true; - router - .push(getTopMenu(true).path) - .then(() => { - message("鐧诲綍鎴愬姛", { type: "success" }); - }) - .finally(() => (disabled.value = false)); - }); - } else { - message("鐧诲綍澶辫触", { type: "error" }); +// 椤甸潰鍒濆鍖� +onMounted(async () => { + getCaptcha(); + // 娉ㄥ唽楠岃瘉鐮佽繃鏈熻鏃跺櫒 + timer = setInterval(() => { + if (state.expirySeconds > 0) state.expirySeconds -= 1; + }, 1000); +}); +// 椤甸潰鍗歌浇 +onUnmounted(() => { + // 閿�姣侀獙璇佺爜杩囨湡璁℃椂鍣� + clearInterval(timer); + timer = null; + clearInterval(phonetimer); + phonetimer = null; +}); +const openDialog = () => { + addDialog({ + width: "30%", + title: "閫夋嫨鐧诲綍鍏徃", + contentRenderer: () => + h( + ElForm, + { + ref: ruleFormRef, + model: state.ruleForm, + rules: loginRules, + size: "large" + }, + { + default: () => { + return [ + h( + ElFormItem, + { prop: "exRoleCode", label: "" }, + { + default: () => { + return [ + h( + ElRadioGroup, + { + modelValue: secondId.value, + "onUpdate:modelValue": val => (secondId.value = val) + }, + // ElRadioGroup 鐨勫唴瀹规槸寰幆鐢熸垚鐨勶紝鐢ㄥ嚱鏁板寘瑁� + () => + state.myEnterpriseList.map(item => + h( + ElRow, + { style: { width: "100%" } }, + { + default: () => { + return [ + h( + ElCol, + { span: 24 }, + { + default: () => { + return [ + h( + ElRadio, + { + key: item.customerUserID, + value: item.customerUserID + }, + // ElRadio 鐨勫唴瀹规槸鍔ㄦ�佺殑锛岀敤鍑芥暟鍖呰9 + () => [ + // 鏂囨湰鐩存帴杩斿洖锛堜笉瑕佸祵濂楀嚱鏁帮級 + item.enterpriseName, + item.isManger + ? h( + ElTag, + { + type: "primary", + size: "small", + style: { + marginLeft: "8px", + alignSelf: "center" + } + }, + // ElTag 鐨勫唴瀹规槸闈欐�佹枃鏈紝鐩存帴杩斿洖 + { + default: () => "绠$悊鍛�" + } + ) + : null + ] + ) + ]; + } + } + ) + ]; + } + } + ) + ) + ) + ]; + } + } + ) + ]; } - }) - .finally(() => (loading.value = false)); + } + ), + // jsx 璇硶 锛堟敞鎰忓湪.vue鏂囦欢鍚敤jsx璇硶锛岄渶瑕佸湪script寮�鍚痩ang="tsx"锛� + closeCallBack: ({ options, args }) => { + if (args?.command === "cancel") { + // 鎮ㄧ偣鍑讳簡鍙栨秷鎸夐挳 + } else if (args?.command === "sure") { + let obj = { + phone: state.ruleForm.phone, + code: secondCode, + id: secondId.value + }; + useUserStoreHook() + .loginByUsername(obj) + .then(res => { + if (res?.code == 200) { + message("鐧诲綍鎴愬姛锛�", { type: "success" }); + router.replace({ + path: "/Index" + }); + } else { + message(res?.message, { type: "warning" }); + } + }); + } else { + } } }); }; +// 鐐瑰嚮鐧诲綍 +const onLogin = async (formEl: FormInstance | undefined) => { + if (!formEl) return; + await formEl.validate(valid => { + if (valid) { + let obj = { + phone: state.ruleForm.phone, + code: state.ruleForm.phoneVCode, + exRuleCode: state.ruleForm.exRoleCode + }; + loading.value = true; + useUserStoreHook() + .loginByUsername(obj) + .then(res => { + if (res.code == 200) { + // 鑾峰彇鍚庣璺敱 + return initRouter().then(() => { + let obj = res.result; + if (obj.theLastLogo) { + router + .replace({ + path: "/Index" + }) + .then(() => { + message("鐧诲綍鎴愬姛", { type: "success" }); + }) + .finally(() => (disabled.value = false)); + } else { + state.myEnterpriseList = obj.customerExs; + secondCode = obj.code; + openDialog(); + } + }); + } else { + message(res?.message || "鐧诲綍澶辫触", { type: "error" }); + } + }) + .finally(() => { + loading.value = false; + disabled.value = false; + }); + } + }); +}; + +// 鍙戦�佹墜鏈洪獙璇佺爜 +const sendValidationCode = async () => { + if (!state.ruleForm.phone) { + return message("璇峰厛杈撳叆鎵嬫満鍙�", { type: "warning" }); + } + if (!state.ruleForm.code) { + return message("璇峰厛杈撳叆楠岃瘉鐮�", { type: "warning" }); + } + const res = await phoneNumberCode(state.ruleForm); + if (res?.code != 200) { + return message(res?.message, { type: "warning" }); + } + state.phoneSeconds = 60; + phonetimer = setInterval(() => { + if (state.phoneSeconds > 0) state.phoneSeconds -= 1; + }, 1000); +}; const immediateDebounce: any = debounce( formRef => onLogin(formRef), 1000, @@ -90,31 +342,20 @@ <template> <div class="select-none"> - <img :src="bg" class="wave" /> - <div class="flex-c absolute right-5 top-3"> - <!-- 涓婚 --> - <el-switch - v-model="dataTheme" - inline-prompt - :active-icon="dayIcon" - :inactive-icon="darkIcon" - @change="dataThemeChange" - /> + <div class="wave"> + <img width="400px" :src="logo1" class="logo1" /> </div> <div class="login-container"> - <div class="img"> - <component :is="toRaw(illustration)" /> - </div> + <div class="img" /> <div class="login-box"> <div class="login-form"> - <avatar class="avatar" /> <Motion> - <h2 class="outline-hidden">{{ title }}</h2> + <h2 class="logintitle">鐧诲綍</h2> </Motion> <el-form ref="ruleFormRef" - :model="ruleForm" + :model="state.ruleForm" :rules="loginRules" size="large" > @@ -123,33 +364,80 @@ :rules="[ { required: true, - message: '璇疯緭鍏ヨ处鍙�', + message: '璇疯緭鍏ユ墜鏈哄彿', trigger: 'blur' } ]" - prop="username" + prop="phone" > <el-input - v-model="ruleForm.username" + v-model="state.ruleForm.phone" clearable - placeholder="璐﹀彿" - :prefix-icon="useRenderIcon(User)" + placeholder="鎵嬫満鍙�" /> </el-form-item> </Motion> <Motion :delay="150"> - <el-form-item prop="password"> - <el-input - v-model="ruleForm.password" - clearable - show-password - placeholder="瀵嗙爜" - :prefix-icon="useRenderIcon(Lock)" - /> + <el-form-item class="login-animation3" prop="code"> + <el-col :span="15"> + <el-input + ref="codeRef" + v-model="state.ruleForm.code" + text + maxlength="4" + placeholder="璇疯緭鍏ラ獙璇佺爜" + clearable + autocomplete="off" + /> + </el-col> + <el-col :span="1" /> + <el-col :span="8"> + <div + :class="[ + state.expirySeconds > 0 + ? 'login-content-code' + : 'login-content-code-expired' + ]" + @click="getCaptcha" + > + <img + class="login-content-code-img" + width="130px" + height="38px" + :src="state.captchaImage" + style="cursor: pointer" + /> + </div> + </el-col> </el-form-item> </Motion> - + <Motion :delay="150"> + <el-form-item prop="phoneVCode"> + <el-input + v-model.number="state.ruleForm.phoneVCode" + class="form-input" + placeholder="璇疯緭鍏ラ獙璇佺爜" + > + <template #suffix> + <span v-if="state.phoneSeconds == 0" id="suffix-span"> + <span + id="suffix-span-2" + ref="spanRef" + @click="sendValidationCode" + > + 鑾峰彇楠岃瘉鐮� + </span> + </span> + <span v-else id="suffix-span"> + <span id="suffix-span-2" ref="spanRef"> + {{ state.phoneSeconds }}绉掑悗閲嶆柊鑾峰彇 + </span> + </span> + </template> + </el-input> + </el-form-item> + </Motion> <Motion :delay="250"> <el-button class="w-full mt-4!" @@ -177,4 +465,47 @@ :deep(.el-input-group__append, .el-input-group__prepend) { padding: 0; } +#suffix-span { + cursor: pointer; +} +.login-content-code { + display: flex; + align-items: center; + justify-content: space-around; + position: relative; + + .login-content-code-img { + width: 100%; + height: 40px; + line-height: 40px; + background-color: #ffffff; + // /* border: 1px solid rgb(220, 223, 230); */ + cursor: pointer; + transition: all ease 0.2s; + border-radius: 4px; + user-select: none; + + &:hover { + border-color: #c0c4cc; + transition: all ease 0.2s; + } + } +} + +.login-content-code-expired { + @extend .login-content-code; + &::before { + content: "楠岃瘉鐮佸凡杩囨湡"; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.5); + color: #ffffff; + text-align: center; + line-height: 40px; + } +} </style> -- Gitblit v1.9.1