From ce5e84197b43dec8c01717b116cb77535ad3c91e Mon Sep 17 00:00:00 2001
From: zhangwei <1504152376@qq.com>
Date: 星期三, 25 六月 2025 16:21:42 +0800
Subject: [PATCH] '登录注册'

---
 src/router/modules/remaining.ts       |   20 +
 src/views/login/index.vue             |  244 +++++++++++++-
 src/views/register/registersucess.vue |   36 ++
 src/api/register/index.ts             |   41 ++
 src/api/user.ts                       |    6 
 vite.config.ts                        |    9 
 src/views/home/index.vue              |   61 ++-
 src/router/modules/home.ts            |    4 
 src/views/register/index.vue          |  433 +++++++++++++++++-------
 src/router/index.ts                   |    1 
 src/api/login/index.ts                |   17 +
 mock/login.ts                         |   82 ++--
 src/store/modules/multiTags.ts        |    4 
 src/router/utils.ts                   |   19 
 src/api/util.ts                       |    4 
 15 files changed, 744 insertions(+), 237 deletions(-)

diff --git a/mock/login.ts b/mock/login.ts
index 55897d8..dd54a17 100644
--- a/mock/login.ts
+++ b/mock/login.ts
@@ -1,42 +1,42 @@
-// 鏍规嵁瑙掕壊鍔ㄦ�佺敓鎴愯矾鐢�
-import { defineFakeRoute } from "vite-plugin-fake-server/client";
+// // 鏍规嵁瑙掕壊鍔ㄦ�佺敓鎴愯矾鐢�
+// import { defineFakeRoute } from "vite-plugin-fake-server/client";
 
-export default defineFakeRoute([
-  {
-    url: "/login",
-    method: "post",
-    response: ({ body }) => {
-      if (body.username === "admin") {
-        return {
-          success: true,
-          data: {
-            avatar: "https://avatars.githubusercontent.com/u/44761321",
-            username: "admin",
-            nickname: "灏忛摥",
-            // 涓�涓敤鎴峰彲鑳芥湁澶氫釜瑙掕壊
-            roles: ["admin"],
-            // 鎸夐挳绾у埆鏉冮檺
-            permissions: ["*:*:*"],
-            accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
-            refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
-            expires: "2030/10/30 00:00:00"
-          }
-        };
-      } else {
-        return {
-          success: true,
-          data: {
-            avatar: "https://avatars.githubusercontent.com/u/52823142",
-            username: "common",
-            nickname: "灏忔灄",
-            roles: ["common"],
-            permissions: ["permission:btn:add", "permission:btn:edit"],
-            accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
-            refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",
-            expires: "2030/10/30 00:00:00"
-          }
-        };
-      }
-    }
-  }
-]);
+// export default defineFakeRoute([
+//   {
+//     url: "/login",
+//     method: "post",
+//     response: ({ body }) => {
+//       if (body.username === "admin") {
+//         return {
+//           success: true,
+//           data: {
+//             avatar: "https://avatars.githubusercontent.com/u/44761321",
+//             username: "admin",
+//             nickname: "灏忛摥",
+//             // 涓�涓敤鎴峰彲鑳芥湁澶氫釜瑙掕壊
+//             roles: ["admin"],
+//             // 鎸夐挳绾у埆鏉冮檺
+//             permissions: ["*:*:*"],
+//             accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
+//             refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
+//             expires: "2030/10/30 00:00:00"
+//           }
+//         };
+//       } else {
+//         return {
+//           success: true,
+//           data: {
+//             avatar: "https://avatars.githubusercontent.com/u/52823142",
+//             username: "common",
+//             nickname: "灏忔灄",
+//             roles: ["common"],
+//             permissions: ["permission:btn:add", "permission:btn:edit"],
+//             accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
+//             refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",
+//             expires: "2030/10/30 00:00:00"
+//           }
+//         };
+//       }
+//     }
+//   }
+// ]);
diff --git a/src/api/login/index.ts b/src/api/login/index.ts
new file mode 100644
index 0000000..b853982
--- /dev/null
+++ b/src/api/login/index.ts
@@ -0,0 +1,17 @@
+/**
+ * 锛堜笉寤鸿鍐欐垚 request.post(xxx)锛屽洜涓鸿繖鏍� post 鏃讹紝鏃犳硶 params 涓� data 鍚屾椂浼犲弬锛�
+ *
+ * 娉ㄥ唽api鎺ュ彛闆嗗悎
+ * @method login 鐧诲綍
+ */
+
+import { http } from "@/utils/http";
+import { baseUrlApi } from "../util";
+type Result = {
+  success: boolean;
+  data: Array<any>;
+};
+
+export const login = (data?: object) => {
+  return http.request("post", baseUrlApi("/api/auth/loginPhone"), { data });
+};
diff --git a/src/api/register/index.ts b/src/api/register/index.ts
new file mode 100644
index 0000000..f1ce05c
--- /dev/null
+++ b/src/api/register/index.ts
@@ -0,0 +1,41 @@
+/**
+ * 锛堜笉寤鸿鍐欐垚 request.post(xxx)锛屽洜涓鸿繖鏍� post 鏃讹紝鏃犳硶 params 涓� data 鍚屾椂浼犲弬锛�
+ *
+ * 娉ㄥ唽api鎺ュ彛闆嗗悎
+ * @method register 娉ㄥ唽
+ * @method captcha 鑾峰彇楠岃瘉鐮�
+ */
+
+import { http } from "@/utils/http";
+import { baseUrlApi } from "../util";
+type Result = {
+  success: boolean;
+  data: Array<any>;
+};
+
+export const register = (data?: object) => {
+  return http.request(
+    "post",
+    baseUrlApi("/api/customer/customerRegistration"),
+    { data }
+  );
+};
+
+export const captcha = () => {
+  return http.request<Result>("get", baseUrlApi("/api/zCSMS/captcha"));
+};
+
+//鑾峰彇瑙掕壊
+export const exRole = () => {
+  return http.request<Result>("get", baseUrlApi("/api/customer/exRole"));
+};
+
+// 鑾峰彇鎵嬫満楠岃瘉鐮�
+export const phoneNumberCode = (params?: object) => {
+  return http.request(
+    "post",
+    baseUrlApi(
+      `/api/zCSMS/sendSMS/${params.phone}/${params.code}/${params.codeId}`
+    )
+  );
+};
diff --git a/src/api/user.ts b/src/api/user.ts
index 87184b5..d4c3352 100644
--- a/src/api/user.ts
+++ b/src/api/user.ts
@@ -1,4 +1,5 @@
 import { http } from "@/utils/http";
+import { baseUrlApi } from "./util";
 
 export type UserResult = {
   success: boolean;
@@ -36,10 +37,11 @@
 
 /** 鐧诲綍 */
 export const getLogin = (data?: object) => {
-  return http.request<UserResult>("post", "/login", { data });
+  return http.request("post", baseUrlApi("/api/auth/loginPhone"), { data });
+  // return http.request<UserResult>("post", "/login", { data });
 };
 
 /** 鍒锋柊`token` */
 export const refreshTokenApi = (data?: object) => {
-  return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
+  return http.request<RefreshTokenResult>("post", "/refresh-token1", { data });
 };
diff --git a/src/api/util.ts b/src/api/util.ts
new file mode 100644
index 0000000..5763e01
--- /dev/null
+++ b/src/api/util.ts
@@ -0,0 +1,4 @@
+export const baseUrlApi = (url: string) =>
+  process.env.NODE_ENV === "development"
+    ? `/api${url}`
+    : `http://192.168.0.36:5005${url}`;
diff --git a/src/router/index.ts b/src/router/index.ts
index 40689c3..91e06ca 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -106,6 +106,7 @@
 }
 
 /** 璺敱鐧藉悕鍗� */
+// const whiteList = ["/login"];
 const whiteList = [];
 
 const { VITE_HIDE_HOME } = import.meta.env;
diff --git a/src/router/modules/home.ts b/src/router/modules/home.ts
index 382b61c..ee5224f 100644
--- a/src/router/modules/home.ts
+++ b/src/router/modules/home.ts
@@ -8,7 +8,7 @@
   // redirect: "/welcome",
   meta: {
     icon: "ep/home-filled",
-    title: "棣栭〉",
+    title: "涓婚〉",
     rank: 0
   },
   children: [
@@ -17,7 +17,7 @@
       name: "Welcome",
       component: () => import("@/views/welcome/index.vue"),
       meta: {
-        title: "棣栭〉",
+        title: "涓婚〉",
         showLink: VITE_HIDE_HOME === "true" ? false : true
       }
     }
diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts
index 765e5fc..621931b 100644
--- a/src/router/modules/remaining.ts
+++ b/src/router/modules/remaining.ts
@@ -5,7 +5,11 @@
     path: "/",
     name: "Main",
     redirect: "/home",
-    meta: {}
+    meta: {
+      title: "棣栭〉",
+      showLink: false,
+      rank: 101
+    }
   },
   {
     path: "/home",
@@ -20,7 +24,7 @@
   {
     path: "/login",
     name: "Login",
-    component: () => import("@/views/home/index.vue"),
+    component: () => import("@/views/login/index.vue"),
     meta: {
       title: "鐧诲綍",
       showLink: false,
@@ -34,7 +38,17 @@
     meta: {
       title: "娉ㄥ唽",
       showLink: false,
-      rank: 101
+      rank: 104
+    }
+  },
+  {
+    path: "/registersucess",
+    name: "RegisterSucess",
+    component: () => import("@/views/register/registersucess.vue"),
+    meta: {
+      title: "娉ㄥ唽鎴愬姛",
+      showLink: false,
+      rank: 103
     }
   },
   {
diff --git a/src/router/utils.ts b/src/router/utils.ts
index da5f0cd..bb895a1 100644
--- a/src/router/utils.ts
+++ b/src/router/utils.ts
@@ -33,7 +33,7 @@
   const { name, path, parentId, meta } = routeInfo;
   return isAllEmpty(parentId)
     ? isAllEmpty(meta?.rank) ||
-      (meta?.rank === 0 && name !== "Home" && path !== "/")
+      (meta?.rank === 0 && name !== "Welcome" && path !== "/welcome")
       ? true
       : false
     : false;
@@ -151,7 +151,7 @@
 
 /** 澶勭悊鍔ㄦ�佽矾鐢憋紙鍚庣杩斿洖鐨勮矾鐢憋級 */
 function handleAsyncRoutes(routeList) {
-  if (routeList.length === 0) {
+  if (routeList?.length === 0) {
     usePermissionStoreHook().handleWholeMenus(routeList);
   } else {
     formatFlatteningRoutes(addAsyncRoutes(routeList)).map(
@@ -171,7 +171,7 @@
           if (!router.hasRoute(v?.name)) router.addRoute(v);
           const flattenRouters: any = router
             .getRoutes()
-            .find(n => n.path === "/");
+            .find(n => n.path === "/welcome");
           // 淇濇寔router.options.routes[0].children涓巔ath涓�"/"鐨刢hildren涓�鑷达紝闃叉鏁版嵁涓嶄竴鑷村鑷村紓甯�
           flattenRouters.children = router.options.routes[0].children;
           router.addRoute(flattenRouters);
@@ -205,8 +205,8 @@
     } else {
       return new Promise(resolve => {
         getAsyncRoutes().then(({ data }) => {
-          handleAsyncRoutes(cloneDeep(data));
-          storageLocal().setItem(key, data);
+          // handleAsyncRoutes(cloneDeep(data));
+          // storageLocal().setItem(key, data);
           resolve(router);
         });
       });
@@ -214,7 +214,7 @@
   } else {
     return new Promise(resolve => {
       getAsyncRoutes().then(({ data }) => {
-        handleAsyncRoutes(cloneDeep(data));
+        // handleAsyncRoutes(cloneDeep(data));
         resolve(router);
       });
     });
@@ -227,7 +227,7 @@
  * @returns 杩斿洖澶勭悊鍚庣殑涓�缁磋矾鐢�
  */
 function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
-  if (routesList.length === 0) return routesList;
+  if (routesList?.length === 0) return routesList;
   let hierarchyList = buildHierarchyTree(routesList);
   for (let i = 0; i < hierarchyList.length; i++) {
     if (hierarchyList[i].children) {
@@ -246,10 +246,10 @@
  * @returns 杩斿洖灏嗕竴缁存暟缁勯噸鏂板鐞嗘垚瑙勫畾璺敱鐨勬牸寮�
  */
 function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
-  if (routesList.length === 0) return routesList;
+  if (routesList?.length === 0) return routesList;
   const newRoutesList: RouteRecordRaw[] = [];
   routesList.forEach((v: RouteRecordRaw) => {
-    if (v.path === "/") {
+    if (v.path === "/welcome") {
       newRoutesList.push({
         component: v.component,
         name: v.name,
@@ -387,6 +387,7 @@
     usePermissionStoreHook().wholeMenus[0]?.children[0]
   );
   tag && useMultiTagsStoreHook().handleTags("push", topMenu);
+  console.log(topMenu, "topMenu");
   return topMenu;
 }
 
diff --git a/src/store/modules/multiTags.ts b/src/store/modules/multiTags.ts
index 5d1b669..839b8f7 100644
--- a/src/store/modules/multiTags.ts
+++ b/src/store/modules/multiTags.ts
@@ -79,10 +79,10 @@
             // showLink:false 涓嶆坊鍔犲埌鏍囩椤�
             if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
               return;
-            const tagPath = tagVal.path;
+            const tagPath = tagVal?.path;
             // 鍒ゆ柇tag鏄惁宸插瓨鍦�
             const tagHasExits = this.multiTags.some(tag => {
-              return tag.path === tagPath;
+              return tag?.path === tagPath;
             });
 
             // 鍒ゆ柇tag涓殑query閿�兼槸鍚︾浉绛�
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
index f565703..041ec08 100644
--- a/src/views/home/index.vue
+++ b/src/views/home/index.vue
@@ -42,8 +42,8 @@
     </div>
     <div class="right">
       <el-tabs v-model="activeName" class="demo-tabs">
-        <el-tab-pane label="宸ョ▼鎷涙爣" name="first">
-          宸ョ▼鎷涙爣
+        <el-tab-pane label="鎰忓悜鍏紑" name="first">
+          鎰忓悜鍏紑
           <!-- <div class="item">
             <span
               ><span style="color: #145ccd; font-weight: 600">路</span
@@ -94,8 +94,9 @@
             <span>2024-04-15 18:10</span>
           </div> -->
         </el-tab-pane>
-        <el-tab-pane label="璐х墿鎷涙爣" name="second">璐х墿鎷涙爣</el-tab-pane>
-        <el-tab-pane label="鏈嶅姟鎷涙爣" name="third">鏈嶅姟鎷涙爣</el-tab-pane>
+        <el-tab-pane label="宸ョ▼鎷涙爣" name="second">宸ョ▼鎷涙爣</el-tab-pane>
+        <el-tab-pane label="璐х墿鎷涙爣" name="third">璐х墿鎷涙爣</el-tab-pane>
+        <el-tab-pane label="鏈嶅姟鎷涙爣" name="fourth">鏈嶅姟鎷涙爣</el-tab-pane>
         <el-tab-pane label="缃戜笂绔炰环" name="fourth">缃戜笂绔炰环</el-tab-pane>
       </el-tabs>
     </div>
@@ -147,9 +148,13 @@
               src="@/assets/home/car1.png"
               alt=""
             />
-            閲囪喘浜�/鎷涙爣浜�
+            閲囪喘浜�
           </div>
-          <div>娉ㄥ唽<span class="m-2">|</span>鐧诲綍</div>
+          <div>
+            <span class="hover:cursor-pointer" @click="toRegister">娉ㄥ唽</span
+            ><span class="m-2">|</span
+            ><span class="hover:cursor-pointer" @click="toLogin">鐧诲綍</span>
+          </div>
         </div>
         <div class="item">
           <div class="box">
@@ -159,20 +164,13 @@
               src="@/assets/home/car.png"
               alt=""
             />
-            閲囪喘浜�
+            浠g悊鏈烘瀯
           </div>
-          <div>娉ㄥ唽<span class="m-2">|</span>鐧诲綍</div>
-        </div>
-        <div class="item">
-          <div class="box">
-            <img
-              width="18px"
-              height="18px"
-              src="@/assets/home/car.png"
-              alt=""
-            />鎷涙爣浠g悊鏈烘瀯
+          <div>
+            <span class="hover:cursor-pointer" @click="toRegister">娉ㄥ唽</span
+            ><span class="m-2">|</span
+            ><span class="hover:cursor-pointer" @click="toLogin">鐧诲綍</span>
           </div>
-          <div>娉ㄥ唽<span class="m-2">|</span>鐧诲綍</div>
         </div>
         <div class="item">
           <div class="box">
@@ -183,7 +181,11 @@
               alt=""
             />渚涘簲鍟�
           </div>
-          <div>娉ㄥ唽<span class="m-2">|</span>鐧诲綍</div>
+          <div>
+            <span class="hover:cursor-pointer" @click="toRegister">娉ㄥ唽</span
+            ><span class="m-2">|</span
+            ><span class="hover:cursor-pointer" @click="toLogin">鐧诲綍</span>
+          </div>
         </div>
         <div class="item">
           <div class="box">
@@ -194,7 +196,11 @@
               alt=""
             />璇勫涓撳
           </div>
-          <div>娉ㄥ唽<span class="m-2">|</span>鐧诲綍</div>
+          <div>
+            <span class="hover:cursor-pointer" @click="toRegister">娉ㄥ唽</span
+            ><span class="m-2">|</span
+            ><span class="hover:cursor-pointer" @click="toLogin">鐧诲綍</span>
+          </div>
         </div>
       </div>
       <div class="right" />
@@ -396,6 +402,17 @@
 import { ref } from "vue";
 import myFooter from "./component/myFooter.vue";
 let activeName = ref("first");
+import { useRoute, useRouter } from "vue-router";
+defineOptions({
+  name: "Home"
+});
+const router = useRouter();
+const toRegister = () => {
+  router.push({ name: "Register" });
+};
+const toLogin = () => {
+  router.push({ name: "Login" });
+};
 </script>
 
 <style lang="scss" scoped>
@@ -518,7 +535,7 @@
   padding: 40px 0;
   .all {
     width: 72%;
-    height: 482px;
+    height: 385px;
     background: #fff;
     margin: 0 auto;
     display: flex;
@@ -535,7 +552,7 @@
         justify-content: space-between;
         align-items: center;
         padding: 0 30px;
-        height: 20%;
+        height: 25%;
         text-align: left;
         color: #5f5f5f;
         .box {
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
index 1172b01..15a2618 100644
--- a/src/views/login/index.vue
+++ b/src/views/login/index.vue
@@ -3,7 +3,15 @@
 import { useRouter } from "vue-router";
 import { message } from "@/utils/message";
 import { loginRules } from "./utils/rule";
-import { ref, reactive, toRaw } from "vue";
+import {
+  reactive,
+  computed,
+  ref,
+  onMounted,
+  defineAsyncComponent,
+  onUnmounted,
+  watch
+} from "vue";
 import { debounce } from "@pureadmin/utils";
 import { useNav } from "@/layout/hooks/useNav";
 import { useEventListener } from "@vueuse/core";
@@ -12,13 +20,17 @@
 import { useUserStoreHook } from "@/store/modules/user";
 import { initRouter, getTopMenu } from "@/router/utils";
 import { bg, avatar, logo1 } from "./utils/static";
-import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+// 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";
+const route = useRoute();
+
+// 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";
+import { captcha, phoneNumberCode, exRole } from "@/api/register/index.ts";
 
 defineOptions({
   name: "Login"
@@ -35,12 +47,114 @@
 const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
 dataThemeChange(overallStyle.value);
 const { title } = useNav();
+// 鑾峰彇楠岃瘉鐮�
+const getCaptcha = async () => {
+  // if (!state.captchaEnabled) return;
 
-const ruleForm = reactive({
-  username: "admin",
-  password: "admin123"
+  state.ruleForm.code = "";
+  const res = await captcha();
+  console.log(res);
+
+  state.captchaImage = "data:text/html;base64," + res.result?.img;
+  state.expirySeconds = res.result?.expirySeconds;
+  state.ruleForm.codeId = res.result?.id;
+};
+const state = reactive({
+  isShowPassword: false,
+  ruleForm: {
+    account: "",
+    nickName: "",
+    phone: "",
+    phoneVCode: "",
+    // tenantId: props.tenantInfo.id,
+    code: "",
+    codeId: 0,
+    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: []
 });
+// 楠岃瘉鐮佽繃鏈熻鏃跺櫒
+let timer: any = null;
+let phonetimer: any = null;
 
+// 椤甸潰鍒濆鍖�
+onMounted(async () => {
+  // 鑻RL甯︽湁Token鍙傛暟锛堢涓夋柟鐧诲綍锛�
+  const accessToken = route.query.token;
+  // if (accessToken) await saveTokenAndInitRoutes(accessToken);
+  // watch(
+  //   () => themeConfig.value.isLoaded,
+  //   isLoaded => {
+  //     if (isLoaded) {
+  // 鑾峰彇鐧诲綍閰嶇疆
+  // state.hideTenantForLogin = themeConfig.value.hideTenantForLogin ?? true;
+  // state.secondVerEnabled = themeConfig.value.secondVer ?? true;
+  // state.captchaEnabled = themeConfig.value.captcha ?? true;
+
+  // 鑾峰彇楠岃瘉鐮�
+  getCaptcha();
+  exRole().then(res => {
+    state.roleList = res.result;
+  });
+  // 娉ㄥ唽楠岃瘉鐮佽繃鏈熻鏃跺櫒
+  // if (state.captchaEnabled) {
+  timer = setInterval(() => {
+    if (state.expirySeconds > 0) state.expirySeconds -= 1;
+  }, 1000);
+
+  // }
+  // }
+  // },
+  // { immediate: true }
+  // );
+});
+// 椤甸潰鍗歌浇
+onUnmounted(() => {
+  // 閿�姣侀獙璇佺爜杩囨湡璁℃椂鍣�
+  clearInterval(timer);
+  timer = null;
+  clearInterval(phonetimer);
+  phonetimer = null;
+});
 const onLogin = async (formEl: FormInstance | undefined) => {
   if (!formEl) return;
   await formEl.validate(valid => {
@@ -48,30 +162,46 @@
       loading.value = true;
       useUserStoreHook()
         .loginByUsername({
-          username: ruleForm.username,
-          password: ruleForm.password
+          phone: state.ruleForm.phone,
+          code: state.ruleForm.phoneVCode,
+          exRuleCode: state.ruleForm.exRoleCode
         })
         .then(res => {
-          if (res.success) {
+          if (res.code == 200) {
             // 鑾峰彇鍚庣璺敱
             return initRouter().then(() => {
               disabled.value = true;
               router
-                .push(getTopMenu(true).path)
+                .push(getTopMenu(true)?.path)
                 .then(() => {
                   message("鐧诲綍鎴愬姛", { type: "success" });
                 })
                 .finally(() => (disabled.value = false));
             });
           } else {
-            message("鐧诲綍澶辫触", { type: "error" });
+            message(res?.message || "鐧诲綍澶辫触", { type: "error" });
           }
         })
         .finally(() => (loading.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,
@@ -117,32 +247,76 @@
 
           <el-form
             ref="ruleFormRef"
-            :model="ruleForm"
+            :model="state.ruleForm"
             :rules="loginRules"
             size="large"
           >
+            <Motion :delay="150">
+              <el-form-item prop="exRoleCode">
+                <el-radio-group v-model="state.ruleForm.exRoleCode">
+                  <el-radio
+                    v-for="item in state.roleList"
+                    :key="item.id"
+                    :value="item.code"
+                    >{{ item.name }}</el-radio
+                  >
+                </el-radio-group>
+              </el-form-item>
+            </Motion>
             <Motion :delay="100">
               <el-form-item
                 :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-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>
+
+              <!-- <el-form-item prop="password">
                 <el-input
                   v-model="ruleForm.password"
                   clearable
@@ -150,9 +324,34 @@
                   placeholder="瀵嗙爜"
                   :prefix-icon="useRenderIcon(Lock)"
                 />
+              </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(state.ruleForm.phone)"
+                      >
+                        鑾峰彇楠岃瘉鐮�
+                      </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!"
@@ -180,4 +379,7 @@
 :deep(.el-input-group__append, .el-input-group__prepend) {
   padding: 0;
 }
+#suffix-span {
+  cursor: pointer;
+}
 </style>
diff --git a/src/views/register/index.vue b/src/views/register/index.vue
index d92a8dc..ba4182c 100644
--- a/src/views/register/index.vue
+++ b/src/views/register/index.vue
@@ -18,18 +18,22 @@
           <el-form
             ref="ruleFormRef"
             style="max-width: 600px"
-            :model="ruleForm"
-            :rules="rules"
+            :model="state.ruleForm"
+            :rules="state.rules"
             label-width="auto"
+            size="large"
           >
-            <el-form-item label="娉ㄥ唽韬唤" prop="resource">
-              <el-radio-group v-model="ruleForm.resource">
-                <el-radio value="Sponsorship">渚涘簲鍟�</el-radio>
-                <el-radio value="Venue">浠g悊鏈烘瀯</el-radio>
-                <el-radio value="cgr">閲囪喘浜�</el-radio>
+            <el-form-item label="娉ㄥ唽韬唤" prop="exRoleCode">
+              <el-radio-group v-model="state.ruleForm.exRoleCode">
+                <el-radio
+                  v-for="item in state.roleList"
+                  :key="item.id"
+                  :value="item.code"
+                  >{{ item.name }}</el-radio
+                >
               </el-radio-group>
             </el-form-item>
-            <el-form-item label="浼佷笟鍚嶇О" prop="name">
+            <!-- <el-form-item label="浼佷笟鍚嶇О" prop="name">
               <el-input
                 v-model="ruleForm.name"
                 placeholder="璇疯緭鍏ヨ惀涓氭墽鐓т笂鐨勪紒涓氬悕绉�"
@@ -37,8 +41,20 @@
             </el-form-item>
             <el-form-item label="鐢ㄦ埛鍚�" prop="region">
               <el-input v-model="ruleForm.region" placeholder="璇疯緭鍏ョ敤鎴峰悕" />
+            </el-form-item> -->
+            <el-form-item label="濮撳悕" prop="nickName">
+              <el-input
+                v-model="state.ruleForm.nickName"
+                placeholder="璇疯緭鍏ュ鍚�"
+              />
             </el-form-item>
-            <el-form-item label="鐧诲綍瀵嗙爜" prop="password">
+            <el-form-item label="鎵嬫満鍙风爜" prop="phone">
+              <el-input
+                v-model="state.ruleForm.phone"
+                placeholder="璇疯緭鍏ユ偍鐨勬墜鏈哄彿鐮�"
+              />
+            </el-form-item>
+            <!-- <el-form-item label="鐧诲綍瀵嗙爜" prop="password">
               <el-input
                 v-model="ruleForm.password"
                 placeholder="8-20浣嶆暟瀛�+澶у皬鍐欏瓧姣�+鐗规畩瀛楃鐨勭粍鍚�"
@@ -55,48 +71,74 @@
                 v-model="ruleForm.repassword"
                 placeholder="璇疯緭鍏ヨ仈绯讳汉濮撳悕"
               />
-            </el-form-item>
-            <el-form-item label="閭" prop="repassword">
+            </el-form-item> -->
+            <el-form-item label="閭" prop="email">
               <el-input
-                v-model="ruleForm.repassword"
+                v-model="state.ruleForm.email"
                 placeholder="璇疯緭鍏ヨ仈绯婚偖绠�"
               />
             </el-form-item>
-            <el-form-item label="鎵嬫満鍙风爜" prop="repassword">
-              <el-input
-                v-model="ruleForm.repassword"
-                placeholder="璇疯緭鍏ユ偍鐨勬墜鏈哄彿鐮�"
-              />
+            <el-form-item label="楠岃瘉鐮�" 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>
-            <el-form-item label="鎵嬫満鍙风爜" prop="repassword">
+            <el-form-item prop="phoneVCode" label="鎵嬫満楠岃瘉鐮�">
               <el-input
-                v-model="ruleForm.repassword"
-                placeholder="璇疯緭鍏ユ偍鐨勬墜鏈哄彿鐮�"
-              />
-            </el-form-item>
-            <el-form-item prop="validationCode" label="鎵嬫満楠岃瘉鐮�">
-              <el-input
-                v-model.number="ruleForm.validationCode"
+                v-model.number="state.ruleForm.phoneVCode"
                 class="form-input"
                 placeholder="璇疯緭鍏ラ獙璇佺爜"
               >
                 <template #suffix>
-                  <span id="suffix-span">
+                  <span v-if="state.phoneSeconds == 0" id="suffix-span">
                     <span
                       id="suffix-span-2"
                       ref="spanRef"
-                      @click="sendValidationCode(ruleForm.repassword)"
+                      @click="sendValidationCode(state.ruleForm.phone)"
                     >
-                      {{ isSendValidationCode }}
+                      鑾峰彇楠岃瘉鐮�
+                    </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>
-            <el-form-item prop="repassword" label=" ">
-              <el-checkbox value="Online activities" name="type">
-                鎴戝凡闃呰骞跺悓鎰� 銆婇潪鏀块噰鎷涙爣閲囪喘浜ゆ槗骞冲彴鐢ㄦ埛鍗忚銆�
-              </el-checkbox>
+            <el-form-item label=" ">
+              <el-checkbox v-model="checked1" name="type" />鎴戝凡闃呰骞跺悓鎰�
+              <el-link type="primary" :underline="false"
+                >銆婇潪鏀块噰鎷涙爣閲囪喘浜ゆ槗骞冲彴鐢ㄦ埛鍗忚銆�</el-link
+              >
             </el-form-item>
             <el-form-item label=" ">
               <el-button type="primary" @click="submitForm(ruleFormRef)">
@@ -104,7 +146,8 @@
               </el-button>
             </el-form-item>
             <el-form-item label=" ">
-              <span>宸叉湁璐﹀彿锛熺珛鍗崇櫥褰�</span>
+              <span>宸叉湁璐﹀彿锛�</span>
+              <el-link type="primary" :underline="false">绔嬪嵆鐧诲綍</el-link>
             </el-form-item>
           </el-form>
         </div>
@@ -114,120 +157,197 @@
 </template>
 
 <script lang="ts" setup>
-import { reactive, ref } from "vue";
+import {
+  reactive,
+  computed,
+  ref,
+  onMounted,
+  defineAsyncComponent,
+  onUnmounted,
+  watch
+} from "vue";
 import type { FormInstance, FormRules } from "element-plus";
-
-interface RuleForm {
-  name: string;
-  region: string;
-  count: string;
-  password: string;
-  repassword: string;
-  validationCode: string;
-  date1: string;
-  date2: string;
-  delivery: boolean;
-  location: string;
-  type: string[];
-  resource: string;
-  desc: string;
-}
-
-const ruleFormRef = ref<FormInstance>();
-const ruleForm = reactive<RuleForm>({
-  name: "",
-  region: "",
-  password: "",
-  validationCode: "",
-  repassword: "",
-  count: "",
-  date1: "",
-  date2: "",
-  delivery: false,
-  location: "",
-  type: [],
-  resource: "",
-  desc: ""
+import {
+  captcha,
+  phoneNumberCode,
+  register,
+  exRole
+} from "@/api/register/index.ts";
+import { useRoute, useRouter } from "vue-router";
+import { message } from "@/utils/message";
+defineOptions({
+  name: "Register"
 });
+
+const route = useRoute();
+const router = useRouter();
+const checked1 = ref(false);
+const state = reactive({
+  isShowPassword: false,
+  ruleForm: {
+    account: "",
+    nickName: "",
+    phone: "",
+    phoneVCode: "",
+    // tenantId: props.tenantInfo.id,
+    code: "",
+    codeId: 0,
+    email: "",
+    exRoleCode: ""
+  },
+  rules: {
+    phoneVCode: [
+      {
+        required: true,
+        message: "璇疯緭鍏ユ墜鏈洪獙璇佺爜",
+        trigger: "blur"
+      }
+    ],
+    nickName: [
+      {
+        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: []
+});
+// 楠岃瘉鐮佽繃鏈熻鏃跺櫒
+let timer: any = null;
+let phonetimer: any = null;
+
+// 椤甸潰鍒濆鍖�
+onMounted(async () => {
+  // 鑻RL甯︽湁Token鍙傛暟锛堢涓夋柟鐧诲綍锛�
+  const accessToken = route.query.token;
+  // if (accessToken) await saveTokenAndInitRoutes(accessToken);
+  // watch(
+  //   () => themeConfig.value.isLoaded,
+  //   isLoaded => {
+  //     if (isLoaded) {
+  // 鑾峰彇鐧诲綍閰嶇疆
+  // state.hideTenantForLogin = themeConfig.value.hideTenantForLogin ?? true;
+  // state.secondVerEnabled = themeConfig.value.secondVer ?? true;
+  // state.captchaEnabled = themeConfig.value.captcha ?? true;
+
+  // 鑾峰彇楠岃瘉鐮�
+  getCaptcha();
+  exRole().then(res => {
+    state.roleList = res.result;
+  });
+  // 娉ㄥ唽楠岃瘉鐮佽繃鏈熻鏃跺櫒
+  // if (state.captchaEnabled) {
+  timer = setInterval(() => {
+    if (state.expirySeconds > 0) state.expirySeconds -= 1;
+  }, 1000);
+
+  // }
+  // }
+  // },
+  // { immediate: true }
+  // );
+
+  // 妫�娴嬪ぇ灏忓啓鎸夐敭/CapsLK
+  document.addEventListener("keyup", handleKeyPress);
+});
+
+// 椤甸潰鍗歌浇
+onUnmounted(() => {
+  // 閿�姣侀獙璇佺爜杩囨湡璁℃椂鍣�
+  clearInterval(timer);
+  timer = null;
+  clearInterval(phonetimer);
+  phonetimer = null;
+
+  document.removeEventListener("keyup", handleKeyPress);
+});
+
+// 妫�娴嬪ぇ灏忓啓鎸夐敭
+const handleKeyPress = (e: KeyboardEvent) => {
+  if (e.getModifierState != undefined)
+    state.capsLockVisible = e.getModifierState("CapsLock");
+};
+// 鑾峰彇楠岃瘉鐮�
+const getCaptcha = async () => {
+  // if (!state.captchaEnabled) return;
+
+  state.ruleForm.code = "";
+  const res = await captcha();
+  console.log(res);
+
+  state.captchaImage = "data:text/html;base64," + res.result?.img;
+  state.expirySeconds = res.result?.expirySeconds;
+  state.ruleForm.codeId = res.result?.id;
+};
+const ruleFormRef = ref<FormInstance>();
 
 const locationOptions = ["Home", "Company", "School"];
-
-const rules = reactive<FormRules<RuleForm>>({
-  name: [
-    { required: true, message: "Please input Activity name", trigger: "blur" },
-    { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" }
-  ],
-  region: [
-    {
-      required: true,
-      message: "Please select Activity zone",
-      trigger: "change"
-    }
-  ],
-  count: [
-    {
-      required: true,
-      message: "Please select Activity count",
-      trigger: "change"
-    }
-  ],
-  date1: [
-    {
-      type: "date",
-      required: true,
-      message: "Please pick a date",
-      trigger: "change"
-    }
-  ],
-  date2: [
-    {
-      type: "date",
-      required: true,
-      message: "Please pick a time",
-      trigger: "change"
-    }
-  ],
-  location: [
-    {
-      required: true,
-      message: "Please select a location",
-      trigger: "change"
-    }
-  ],
-  type: [
-    {
-      type: "array",
-      required: true,
-      message: "Please select at least one activity type",
-      trigger: "change"
-    }
-  ],
-  resource: [
-    {
-      required: true,
-      message: "Please select activity resource",
-      trigger: "change"
-    }
-  ],
-  desc: [
-    { required: true, message: "Please input activity form", trigger: "blur" }
-  ]
-});
 // 楠岃瘉鐮佸尯鍩熸枃瀛楄鏄�
 const spanRef = ref();
-const isSendValidationCode = ref<string>("鑾峰彇楠岃瘉鐮�");
 const submitForm = async (formEl: FormInstance | undefined) => {
   if (!formEl) return;
   await formEl.validate((valid, fields) => {
     if (valid) {
-      console.log("submit!");
-    } else {
-      console.log("error submit!", fields);
-    }
+      state.ruleForm.account = state.ruleForm.phone;
+      register(state.ruleForm).then(res => {
+        if (res?.code == 200) {
+          router.replace("/RegisterSucess");
+          return message("娉ㄥ唽鎴愬姛锛�", { type: "success" });
+        } else {
+          return message(res?.message, { type: "warning" });
+        }
+      });
+    } else if (!checked1.value)
+      return message("璇峰嬀閫夌敤鎴峰崗璁�", { type: "warning" });
   });
 };
 
-const sendValidationCode = () => {};
+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 resetForm = (formEl: FormInstance | undefined) => {
   if (!formEl) return;
@@ -296,4 +416,49 @@
     }
   }
 }
+.el-form-item {
+  align-items: center;
+}
+#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;
+  }
+}
 </style>
diff --git a/src/views/register/registersucess.vue b/src/views/register/registersucess.vue
new file mode 100644
index 0000000..71e4d7b
--- /dev/null
+++ b/src/views/register/registersucess.vue
@@ -0,0 +1,36 @@
+<template>
+  <div class="content">
+    <div class="header">
+      <div class="headimg">
+        <img width="167px" height="44px" src="@/assets/home/logo.png" alt="" />
+      </div>
+    </div>
+    <div class="center w-[70%] h-[584px] bg-white mx-auto mt-25">
+      <el-button type="primary">椹笂鐧诲綍</el-button>
+    </div>
+  </div>
+</template>
+<script setup>
+defineOptions({
+  name: "RegisterSucess"
+});
+</script>
+<style lang="scss" scoped>
+.content {
+  background-color: #f8f8f8;
+  width: 100%;
+  height: 100%;
+  .header {
+    display: flex;
+    align-items: center;
+    height: 80px;
+    width: 100%;
+    margin: 0 auto;
+    background-color: #fff;
+    .headimg {
+      width: 1200px;
+      margin: 0 auto;
+    }
+  }
+}
+</style>
diff --git a/vite.config.ts b/vite.config.ts
index 1d1b01a..0fc8d03 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -24,7 +24,14 @@
       port: VITE_PORT,
       host: "0.0.0.0",
       // 鏈湴璺ㄥ煙浠g悊 https://cn.vitejs.dev/config/server-options.html#server-proxy
-      proxy: {},
+      proxy: {
+        "/api": {
+          // 杩欓噷濉啓鍚庣鍦板潃
+          target: "http://192.168.0.36:5005",
+          changeOrigin: true,
+          rewrite: path => path.replace(/^\/api/, "")
+        }
+      },
       // 棰勭儹鏂囦欢浠ユ彁鍓嶈浆鎹㈠拰缂撳瓨缁撴灉锛岄檷浣庡惎鍔ㄦ湡闂寸殑鍒濆椤甸潰鍔犺浇鏃堕暱骞堕槻姝㈣浆鎹㈢�戝竷
       warmup: {
         clientFiles: ["./index.html", "./src/{views,components}/*"]

--
Gitblit v1.9.1