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