diff --git a/.env.development b/.env.development index cc6dd92..cf33834 100644 --- a/.env.development +++ b/.env.development @@ -8,7 +8,7 @@ VITE_APP_BASE_API='/api' VITE_APP_TITLE=开发环境 # 网络请求公用地址 -VITE_API_BASE_URL='http://127.0.0.1:3000' +VITE_API_BASE_URL='http://127.0.0.1:5050' #VITE_API_BASE_URL='http://1.95.0.111:4000' VITE_TITLE_NAME='五味子云存储' diff --git a/package.json b/package.json index 8b46032..5fa5ac7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "react-rotate-captcha": "^1.0.26", "react-router-dom": "^6.23.1", "regenerator-runtime": "^0.14.1", + "timer-manager-lib": "^1.0.2", "vite-plugin-compression": "^0.5.1", "vite-plugin-html": "^3.2.2", "vite-plugin-svg-icons": "^2.0.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e06bc91..2c6b39c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,6 +68,9 @@ dependencies: regenerator-runtime: specifier: ^0.14.1 version: 0.14.1 + timer-manager-lib: + specifier: ^1.0.2 + version: 1.0.2 vite-plugin-compression: specifier: ^0.5.1 version: 0.5.1(vite@5.2.12) @@ -3058,6 +3061,13 @@ packages: - supports-color dev: true + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4399,6 +4409,15 @@ packages: engines: {node: '>= 0.6'} dev: false + /event-message-center@1.4.0: + resolution: {integrity: sha512-duhwyD0E6k2blGo6zU8BD1mQaTNVy1cZxugnKVxEkA5IXf5kk7rcbPVCwRqWSYqwFFPI1kTsYWRK3ReyEHwldg==} + dev: false + + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + /expand-brackets@2.1.4: resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} engines: {node: '>=0.10.0'} @@ -5195,6 +5214,19 @@ packages: resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} dev: false + /js-log-lib@1.1.6: + resolution: {integrity: sha512-9g3EbJe617FLE+WQFH7ij05Z4be5Kw9D18moK3l33lgRXdqeKLxBcw8BwvpOBhbbSCsKPfvFcxlzLlwskmusdw==} + dependencies: + utils-lib-js: 2.0.24 + dev: false + + /js-request-lib@1.0.10: + resolution: {integrity: sha512-NwhX/ckFwxGgjh+3jF/NBcf2l1dhZx93hhprNUqsY3pAYU+SDPKupeoofFLkrM3Mn0kpXfWT5FZxob+9+PrZcQ==} + dependencies: + abort-controller: 3.0.0 + utils-lib-js: 2.0.21 + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -7729,6 +7761,12 @@ packages: strip-ansi: 6.0.1 dev: true + /task-queue-lib@1.5.0: + resolution: {integrity: sha512-olJ7+AgYKL0rWQBIy0p+2P2P9GSylginODcyDK2OxBIszswTcCEJno9j+QCrwkUye+e/rPOESlYPs7rmEE+KSQ==} + dependencies: + utils-lib-js: 2.0.24 + dev: false + /terser@5.31.0: resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} engines: {node: '>=10'} @@ -7748,6 +7786,12 @@ packages: engines: {node: '>=12.22'} dev: false + /timer-manager-lib@1.0.2: + resolution: {integrity: sha512-btUrO0W8bA7EJXdyvhjIczHqnHO/61oPnt4ljbx3AnJbm2Oys3Wz8eM1s00ScSXdQGuu468dFaekmwG8ZgbziQ==} + dependencies: + utils-lib-js: 2.0.24 + dev: false + /tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} dev: false @@ -7976,6 +8020,26 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /utils-lib-js@2.0.21: + resolution: {integrity: sha512-v81mvUrUA0WGz05fwPyLhXfuiqJgaVAf0iNbp27jLsMgk2508LxNxxHdEDr01u4KhSsbUMl+a1HzXIOrVM4Gvw==} + dependencies: + event-message-center: 1.4.0 + js-log-lib: 1.1.6 + js-request-lib: 1.0.10 + task-queue-lib: 1.5.0 + timer-manager-lib: 1.0.2 + dev: false + + /utils-lib-js@2.0.24: + resolution: {integrity: sha512-4hSxIiQe/Vt4p+3L+cKIGBnkcIOw2s3TeZVT49R5E59vLZDQ71Jg4Zg6Fsgrb/1lnzU35Gjgnm0PXkC7qp7uSQ==} + dependencies: + event-message-center: 1.4.0 + js-log-lib: 1.1.6 + js-request-lib: 1.0.10 + task-queue-lib: 1.5.0 + timer-manager-lib: 1.0.2 + dev: false + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} diff --git a/src/api/captcha/api.ts b/src/api/captcha/api.ts index 5fda7ed..79a9d21 100644 --- a/src/api/captcha/api.ts +++ b/src/api/captcha/api.ts @@ -7,7 +7,7 @@ import web from "@/utils/axios/web.ts"; */ export const getCaptcha = () => { return web.request({ - url: "/ReactRotateCaptcha/get", + url: "/auth/ReactRotateCaptcha/get", method: "get", }); }; @@ -18,7 +18,7 @@ export const getCaptcha = () => { */ export const VerfiyCaptcha = (data: any) => { return web.request({ - url: "/ReactRotateCaptcha/verfiy", + url: "/auth/ReactRotateCaptcha/verfiy", method: "post", headers: { "Content-Type": "application/json;charset=UTF-8", diff --git a/src/api/oss/minio/index.ts b/src/api/oss/minio/index.ts index 17de588..d56291e 100644 --- a/src/api/oss/minio/index.ts +++ b/src/api/oss/minio/index.ts @@ -7,12 +7,9 @@ import web from "@/utils/axios/web.ts"; */ export const initMinio = (data: any) => { return web.request({ - url: "/oss/minio/init", - headers: { - "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", - }, + url: "/oss/oss/minio/init", method: "post", - data: { + params: { userId: data, }, }); @@ -20,7 +17,7 @@ export const initMinio = (data: any) => { export const getBaseInfo = (data: any) => { return web.request({ - url: "/oss/minio/getBaseInfo", + url: "/oss/oss/minio/getBaseInfo", method: "post", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", diff --git a/src/api/user/index.ts b/src/api/user/index.ts index e6bf573..04da960 100644 --- a/src/api/user/index.ts +++ b/src/api/user/index.ts @@ -2,22 +2,13 @@ import web from "@/utils/axios/web.ts"; -/** - * 获取所有Minio信息 - */ -export const getAllMinioInfo = () => { - return web.request({ - url: "/oss/minio/getAllMinioInfo", - method: "get", - }); -}; /** * 第三方登录 * @param type */ export const oauthLogin = (type: string) => { return web.request({ - url: "/oauth/render/" + type, + url: "/auth/oauth/render/" + type, method: "get", }); }; @@ -28,7 +19,7 @@ export const oauthLogin = (type: string) => { */ export const getSms = (data: any) => { return web.request({ - url: "/sms/sendByTemplate/", + url: "/auth/sms/sendByTemplate/", method: "post", headers: { "Content-Type": "application/json;charset=UTF-8", @@ -43,7 +34,7 @@ export const getSms = (data: any) => { */ export const register = (data: API.PhoneRegisterRequest) => { return web.request({ - url: "/auth/user/register", + url: "/auth/auth/user/register", method: "post", data: data, }); @@ -55,7 +46,7 @@ export const register = (data: API.PhoneRegisterRequest) => { */ export const login = (data: API.LoginRequest) => { return web.request({ - url: "/auth/user/login", + url: "/auth/auth/user/login", method: "post", data: data, }); @@ -67,7 +58,7 @@ export const login = (data: API.LoginRequest) => { */ export const loginByPhone = (data: API.LoginByPhoneRequest) => { return web.request({ - url: "/auth/user/loginByPhone", + url: "/auth/auth/user/loginByPhone", method: "post", data: data, }); @@ -78,8 +69,56 @@ export const loginByPhone = (data: API.LoginByPhoneRequest) => { */ export const findPassword = (data: API.findPasswordRequest) => { return web.request({ - url: "/auth/user/findPassword", + url: "/auth/auth/user/findPassword", method: "post", data: data, }); }; +/** + * 生成客户端id + */ +export const createClientId = () => { + return web.request({ + url: "/auth/auth/user/createClientId", + method: "get", + }); +}; +/** + * 获取客户端id + * @param clientId + */ +export const getClientId = (clientId: string) => { + return web.request({ + url: "/auth/auth/user/getClientId", + method: "post", + data: { + clientId: clientId, + }, + }); +}; +/** + * 获取客户端token + * @param clientId + */ +export const getClientToken = (clientId: string) => { + return web.request({ + url: "/auth/auth/user/getClientToken", + method: "post", + params: { + clientId: clientId, + }, + }); +}; +/** + * 生成微信登录二维码 + * @param clientId + */ +export const generateQRCode = (clientId: string) => { + return web.request({ + url: "/wechat/wx/generateQRCode", + method: "get", + params: { + clientId: clientId, + }, + }); +}; diff --git a/src/assets/icons/oschina.svg b/src/assets/icons/oschina.svg new file mode 100644 index 0000000..bd162ee --- /dev/null +++ b/src/assets/icons/oschina.svg @@ -0,0 +1 @@ + diff --git a/src/main.tsx b/src/main.tsx index 57c471f..ac37b61 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,5 @@ /** @format */ -import React from "react"; import ReactDOM from "react-dom/client"; import "./assets/styles/index.less"; import "virtual:svg-icons-register"; @@ -10,11 +9,9 @@ import { BrowserRouter } from "react-router-dom"; import App from "@/App.tsx"; ReactDOM.createRoot(document.getElementById("root")!).render( - - - - - - - , + + + + + , ); diff --git a/src/views/User/Forget/index.tsx b/src/views/User/Forget/index.tsx index 88bcc3c..564cc7e 100644 --- a/src/views/User/Forget/index.tsx +++ b/src/views/User/Forget/index.tsx @@ -2,19 +2,21 @@ import { LockOutlined, MobileOutlined, SafetyOutlined, WechatOutlined } from "@ant-design/icons"; import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components"; -import { Alert, Button, ConfigProvider, Form, Image, message, Space, Tabs } from "antd"; -import { useRef, useState } from "react"; +import { Alert, Button, ConfigProvider, Form, Image, message, Space, Spin, Tabs } from "antd"; +import { useEffect, useRef, useState } from "react"; import { TinyColor } from "@ctrl/tinycolor"; import logo from "@/assets/images/logo.png"; -// import background from '@/assets/images/background.png' -import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg"; import styles from "./index.module.less"; import { observer } from "mobx-react"; import FooterComponent from "@/components/Footer"; -import { findPassword, getSms } from "@/api/user"; +import { createClientId, findPassword, generateQRCode, getClientToken, getSms } from "@/api/user"; import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha"; import { get, load } from "@/api/captcha"; -// import useStore from '@/utils/store/useStore.tsx' +import { useNavigate } from "react-router-dom"; +import useStore from "@/utils/store/useStore.tsx"; +import { setStorage } from "@/utils/localStorage/config.ts"; +import { TimerManager } from "timer-manager-lib"; + type LoginType = "phone"; export default observer(() => { @@ -24,6 +26,12 @@ export default observer(() => { const findPasswordCaptcha = useRef(null); const [loginType, setLoginType] = useState("phone"); const colors = ["#fc6076", "#ff9a44", "#ef9d43", "#e75516"]; + const [QRCode, setQRCode] = useState(""); + const navigate = useNavigate(); + const store = useStore("user"); + const timerManager = new TimerManager(); + const [loading, setLoading] = useState(true); + const getHoverColors = (colors: string[]) => colors.map((color) => new TinyColor(color).lighten(5).toString()); const getActiveColors = (colors: string[]) => @@ -104,6 +112,50 @@ export default observer(() => { }, ]; + async function wechatLogin() { + createClientId().then((res: any) => { + generateQRCode(res.data).then((response: any) => { + if (response.success) { + setQRCode(response.data.qrCodeUrl); + setLoading(false); + timerManager.add(() => { + getClientToken(res.data).then((result: any) => { + if (result.success) { + timerManager.clear(); + store.setToken(result.data.tokenValue); + store.setUserId(result.data.loginId); + setStorage("token", result.data.tokenValue, 24 * 60 * 30); + setStorage("userId", result.data.loginId, 24 * 60 * 30); + message + .open({ + content: "登录成功!", + type: "success", + }) + .then(); + if (store.getToken() !== null || store.getUserId() !== null) { + setTimeout(() => { + navigate("/main"); + }, 2000); + } + } + }); + }, 3000); + } else { + message + .open({ + content: response.data, + type: "error", + }) + .then(); + } + }); + }); + } + + useEffect(() => { + wechatLogin().then(); + }, []); + return (
@@ -120,23 +172,25 @@ export default observer(() => { 微信扫码登录 - + + + 微信扫码关注公众号)} description={
微信扫码 - - 关注公众号 - + 关注公众号
登录更快更安全 diff --git a/src/views/User/Login/index.tsx b/src/views/User/Login/index.tsx index 02f237e..ec8a248 100644 --- a/src/views/User/Login/index.tsx +++ b/src/views/User/Login/index.tsx @@ -9,6 +9,7 @@ import { SafetyOutlined, UserOutlined, WechatOutlined, + WechatWorkOutlined, } from "@ant-design/icons"; import { CaptFieldRef, @@ -25,23 +26,33 @@ import { Image, message, Space, + Spin, Tabs, Tooltip, } from "antd"; -import { CSSProperties, useRef, useState } from "react"; +import { CSSProperties, useEffect, useRef, useState } from "react"; import logo from "@/assets/images/logo.png"; -import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg"; import styles from "./index.module.less"; import { observer } from "mobx-react"; import useStore from "@/utils/store/useStore.tsx"; import FooterComponent from "@/components/Footer"; import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha"; import { get, load } from "@/api/captcha/index.ts"; -import { getSms, login, loginByPhone, oauthLogin } from "@/api/user"; +import { + createClientId, + generateQRCode, + getClientToken, + getSms, + login, + loginByPhone, + oauthLogin, +} from "@/api/user"; import { TinyColor } from "@ctrl/tinycolor"; import { useNavigate } from "react-router-dom"; import { setStorage } from "@/utils/localStorage/config.ts"; +import { TimerManager } from "timer-manager-lib"; + type LoginType = "account" | "phone"; const iconStyles: CSSProperties = { @@ -57,9 +68,12 @@ export default observer(() => { const loginCaptcha = useRef(null); const loginByPhoneCaptcha = useRef(null); const captchaRef = useRef(); + const [QRCode, setQRCode] = useState(""); const navigate = useNavigate(); const store = useStore("user"); const colors = ["#40e495", "#30dd8a", "#2bb673"]; + const timerManager = new TimerManager(); + const [loading, setLoading] = useState(true); const getHoverColors = (colors: string[]) => colors.map((color) => new TinyColor(color).lighten(5).toString()); const getActiveColors = (colors: string[]) => @@ -180,7 +194,7 @@ export default observer(() => { async function oAuthLogin(type: string) { const res: any = await oauthLogin(type); - window.open(res.data); + window.open(res.data, "_self"); } const [loginType, setLoginType] = useState("account"); @@ -197,6 +211,50 @@ export default observer(() => { loginByPhoneCaptcha.current!.open(); } + async function wechatLogin() { + createClientId().then((res: any) => { + generateQRCode(res.data).then((response: any) => { + if (response.success) { + setQRCode(response.data.qrCodeUrl); + setLoading(false); + timerManager.add(() => { + getClientToken(res.data).then((result: any) => { + if (result.success) { + timerManager.clear(); + store.setToken(result.data.tokenValue); + store.setUserId(result.data.loginId); + setStorage("token", result.data.tokenValue, 24 * 60 * 30); + setStorage("userId", result.data.loginId, 24 * 60 * 30); + message + .open({ + content: "登录成功!", + type: "success", + }) + .then(); + if (store.getToken() !== null || store.getUserId() !== null) { + setTimeout(() => { + navigate("/main"); + }, 2000); + } + } + }); + }, 3000); + } else { + message + .open({ + content: response.data, + type: "error", + }) + .then(); + } + }); + }); + } + + useEffect(() => { + wechatLogin().then(); + }, []); + return (
@@ -220,23 +278,24 @@ export default observer(() => { 微信扫码登录 - + + + 微信扫码关注公众号)} description={
微信扫码 - - 关注公众号 - + 关注公众号
登录更快更安全 @@ -461,7 +520,7 @@ export default observer(() => { borderRadius: "50%", }}> - diff --git a/src/views/User/Register/index.tsx b/src/views/User/Register/index.tsx index 55e722a..ec95a48 100644 --- a/src/views/User/Register/index.tsx +++ b/src/views/User/Register/index.tsx @@ -8,19 +8,21 @@ import { WechatOutlined, } from "@ant-design/icons"; import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components"; -import { Alert, Button, ConfigProvider, Form, Image, message, Space, Tabs } from "antd"; -import { useRef, useState } from "react"; +import { Alert, Button, ConfigProvider, Form, Image, message, Space, Spin, Tabs } from "antd"; +import { useEffect, useRef, useState } from "react"; import logo from "@/assets/images/logo.png"; // import background from '@/assets/images/background.png' -import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg"; import styles from "./index.module.less"; import { observer } from "mobx-react"; import FooterComponent from "@/components/Footer"; -import { getSms, register } from "@/api/user"; +import { createClientId, generateQRCode, getClientToken, getSms, register } from "@/api/user"; import { TinyColor } from "@ctrl/tinycolor"; import { get, load } from "@/api/captcha"; import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha"; import { useNavigate } from "react-router-dom"; +import { setStorage } from "@/utils/localStorage/config.ts"; +import useStore from "@/utils/store/useStore.tsx"; +import { TimerManager } from "timer-manager-lib"; // import useStore from '@/utils/store/useStore.tsx' type LoginType = "phone"; @@ -29,8 +31,13 @@ export default observer(() => { const registerCaptcha = useRef(null); const smsCaptcha = useRef(null); const captchaRef = useRef(); + const [QRCode, setQRCode] = useState(""); const navigate = useNavigate(); + const store = useStore("user"); const colors = ["#6253E1", "#04BEFE"]; + const timerManager = new TimerManager(); + const [loading, setLoading] = useState(true); + const getHoverColors = (colors: string[]) => colors.map((color) => new TinyColor(color).lighten(5).toString()); const getActiveColors = (colors: string[]) => @@ -115,6 +122,50 @@ export default observer(() => { return res; } + async function wechatLogin() { + createClientId().then((res: any) => { + generateQRCode(res.data).then((response: any) => { + if (response.success) { + setQRCode(response.data.qrCodeUrl); + setLoading(false); + timerManager.add(() => { + getClientToken(res.data).then((result: any) => { + if (result.success) { + timerManager.clear(); + store.setToken(result.data.tokenValue); + store.setUserId(result.data.loginId); + setStorage("token", result.data.tokenValue, 24 * 60 * 30); + setStorage("userId", result.data.loginId, 24 * 60 * 30); + message + .open({ + content: "登录成功!", + type: "success", + }) + .then(); + if (store.getToken() !== null || store.getUserId() !== null) { + setTimeout(() => { + navigate("/main"); + }, 2000); + } + } + }); + }, 3000); + } else { + message + .open({ + content: response.data, + type: "error", + }) + .then(); + } + }); + }); + } + + useEffect(() => { + wechatLogin().then(); + }, []); + return (
@@ -125,14 +176,17 @@ export default observer(() => { 微信扫码登录 - + + + 微信扫码关注公众号)} description={