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