feat: update
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import type { TicketInfoType, TokenInfoType } from "react-rotate-captcha";
|
||||
import { getCaptcha, VerfiyCaptcha } from "@/api/captcha/api.ts";
|
||||
/** @format */
|
||||
|
||||
import type { TokenInfoType } from "react-rotate-captcha";
|
||||
import { getCaptcha } from "@/api/captcha/api.ts";
|
||||
|
||||
export type ActionType = {
|
||||
code: 0 | 1;
|
||||
@@ -36,11 +38,10 @@ export function sleep(time: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function verify(token: string, deg: number): Promise<TicketInfoType> {
|
||||
const data: any = {
|
||||
token: token,
|
||||
deg: deg,
|
||||
};
|
||||
const res: any = await VerfiyCaptcha(data);
|
||||
return res;
|
||||
}
|
||||
// export async function verify(token: string, deg: number): Promise<TicketInfoType> {
|
||||
// const data: any = {
|
||||
// token: token,
|
||||
// deg: deg,
|
||||
// };
|
||||
// return await VerfiyCaptcha(data);
|
||||
// }
|
||||
|
@@ -21,3 +21,26 @@ export const oauthLogin = (type: string) => {
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取短信验证码
|
||||
* @param phone
|
||||
*/
|
||||
export const getSms = (phone: string) => {
|
||||
return web.request({
|
||||
url: "/sms/sendByTemplate/" + phone,
|
||||
method: "post",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册
|
||||
* @param data
|
||||
*/
|
||||
export const register = (data: API.PhoneRegisterRequest) => {
|
||||
return web.request({
|
||||
url: "/auth/user/register",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
};
|
||||
|
1
src/assets/icons/gitee.svg
Normal file
1
src/assets/icons/gitee.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#ea580c" d="M11.984 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12a12 12 0 0 0 12-12A12 12 0 0 0 12 0zm6.09 5.333c.328 0 .593.266.592.593v1.482a.594.594 0 0 1-.593.592H9.777c-.982 0-1.778.796-1.778 1.778v5.63c0 .327.266.592.593.592h5.63c.982 0 1.778-.796 1.778-1.778v-.296a.593.593 0 0 0-.592-.593h-4.15a.59.59 0 0 1-.592-.592v-1.482a.593.593 0 0 1 .593-.592h6.815c.327 0 .593.265.593.592v3.408a4 4 0 0 1-4 4H5.926a.593.593 0 0 1-.593-.593V9.778a4.444 4.444 0 0 1 4.445-4.444h8.296Z"/></svg>
|
After Width: | Height: | Size: 579 B |
@@ -5,7 +5,7 @@ import "./index.less";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import useStore from "@/utils/store/useStore.tsx";
|
||||
import { observer } from "mobx-react";
|
||||
import localforage from "localforage";
|
||||
import { setStorage } from "@/utils/localStorage/config.ts";
|
||||
|
||||
const LoadingPage = () => {
|
||||
const [search] = useSearchParams();
|
||||
@@ -15,7 +15,7 @@ const LoadingPage = () => {
|
||||
const store = useStore("user");
|
||||
store.setToken(token);
|
||||
store.setUserId(userId);
|
||||
localforage.setItem("token", token).then();
|
||||
setStorage("token", token, 24 * 60 * 30);
|
||||
useEffect(() => {
|
||||
document.body.classList.add("loading-body");
|
||||
if (store.getToken() !== null && store.getUserId() !== null) {
|
||||
|
@@ -10,7 +10,7 @@ import {
|
||||
useNavigate,
|
||||
useRoutes,
|
||||
} from "react-router-dom";
|
||||
import localforage from "localforage";
|
||||
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
|
||||
|
||||
//递归查询对应的路由
|
||||
export function searchRouteDetail(path: string, routes: RouteObject[]): RouteObject | null {
|
||||
@@ -28,21 +28,21 @@ async function guard(location: Location, navigate: NavigateFunction, routes: Rou
|
||||
const { pathname } = location;
|
||||
|
||||
//找到对应的路由信息
|
||||
const routedetail: RouteObject | null = searchRouteDetail(pathname, routes);
|
||||
const routerDetail: RouteObject | null = searchRouteDetail(pathname, routes);
|
||||
|
||||
//没有找到路由,跳转404
|
||||
if (!routedetail) {
|
||||
if (!routerDetail) {
|
||||
navigate("/404");
|
||||
return false;
|
||||
}
|
||||
//如果需要权限验证
|
||||
if (
|
||||
routedetail.path !== "/login" &&
|
||||
routedetail.path !== "/register" &&
|
||||
routedetail.path !== "/" &&
|
||||
routedetail.path !== "/404"
|
||||
routerDetail.path !== "/login" &&
|
||||
routerDetail.path !== "/register" &&
|
||||
routerDetail.path !== "/" &&
|
||||
routerDetail.path !== "/404"
|
||||
) {
|
||||
const token: string | null = await localforage.getItem("token");
|
||||
const token: string | null = getStorageFromKey("token");
|
||||
if (!token) {
|
||||
message.warning("请先登录!").then();
|
||||
navigate("/login");
|
||||
|
40
src/types/user/user.d.ts
vendored
40
src/types/user/user.d.ts
vendored
@@ -1,7 +1,13 @@
|
||||
// @ts-ignore
|
||||
/** @format */
|
||||
/* eslint-disable */
|
||||
|
||||
declare namespace API {
|
||||
type PhoneRegisterRequest = {
|
||||
phone?: string;
|
||||
password?: string;
|
||||
confirmPassword?: string;
|
||||
activeCode?: string;
|
||||
};
|
||||
// type ApiResponse<T> = {
|
||||
// success?: boolean;
|
||||
// code?: number;
|
||||
@@ -124,23 +130,23 @@ declare namespace API {
|
||||
// type?: NoticeIconItemType;
|
||||
// };
|
||||
|
||||
type GenerateMpRegCode = {
|
||||
data?: {
|
||||
regCode?: string;
|
||||
qrCodeUrl?: string;
|
||||
expireSeconds?: number;
|
||||
ticket?: string;
|
||||
url?: string;
|
||||
};
|
||||
}
|
||||
// type GenerateMpRegCode = {
|
||||
// data?: {
|
||||
// regCode?: string;
|
||||
// qrCodeUrl?: string;
|
||||
// expireSeconds?: number;
|
||||
// ticket?: string;
|
||||
// url?: string;
|
||||
// };
|
||||
// }
|
||||
|
||||
// type GetClientId = {
|
||||
// data?: string;
|
||||
// }
|
||||
|
||||
type GenerateBase64Code = {
|
||||
data?: string;
|
||||
}
|
||||
// type GenerateBase64Code = {
|
||||
// data?: string;
|
||||
// }
|
||||
|
||||
// type GetClientToken = {
|
||||
// data?: {
|
||||
@@ -150,14 +156,6 @@ declare namespace API {
|
||||
// };
|
||||
// }
|
||||
|
||||
type PhoneRegisterRequest = {
|
||||
clientId?: string;
|
||||
phone?: string;
|
||||
password?: string;
|
||||
confirmPassword?: string;
|
||||
smsCode?: number;
|
||||
}
|
||||
|
||||
// type PhoneRegisterResponse = ResponseStructure & {
|
||||
// data?: number;
|
||||
// }
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
||||
import { message } from "antd";
|
||||
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
|
||||
|
||||
class Request {
|
||||
private instance: AxiosInstance | undefined;
|
||||
@@ -11,6 +12,11 @@ class Request {
|
||||
// 全局请求拦截
|
||||
this.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token: string | null = getStorageFromKey("token");
|
||||
if (token) {
|
||||
config.headers.Authorization = `schisandra ${token}`;
|
||||
}
|
||||
|
||||
// if (config.method == "post") {
|
||||
// config.data = EncryptData(JSON.stringify(config.data));
|
||||
// }
|
||||
|
@@ -1,17 +1,10 @@
|
||||
/** @format */
|
||||
|
||||
import Request from "./request";
|
||||
import localforage from "localforage";
|
||||
|
||||
async function getToken() {
|
||||
return await localforage.getItem("token");
|
||||
}
|
||||
const token = await getToken();
|
||||
const web: Request = new Request({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
headers: {
|
||||
token: import.meta.env.VITE_APP_TOKEN_KEY + " " + token,
|
||||
},
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
export default web;
|
||||
|
@@ -9,17 +9,23 @@ import {
|
||||
UserOutlined,
|
||||
WechatOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ProFormCaptcha, ProFormCheckbox, ProFormText } from "@ant-design/pro-components";
|
||||
import { Alert, Button, Divider, Form, Image, message, Space, Tabs } from "antd";
|
||||
import { CSSProperties, useRef, useState } from "react";
|
||||
import {
|
||||
CaptFieldRef,
|
||||
ProFormCaptcha,
|
||||
ProFormCheckbox,
|
||||
ProFormText,
|
||||
} from "@ant-design/pro-components";
|
||||
import { Alert, Button, Divider, Form, Image, message, Space, Tabs, Tooltip } from "antd";
|
||||
import { CSSProperties, useEffect, useRef, useState } from "react";
|
||||
import logo from "@/assets/icons/schisandra.svg";
|
||||
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 RotateCaptcha, { CaptchaInstance } from "react-rotate-captcha";
|
||||
import { get, load, verify } from "@/api/captcha/index.ts";
|
||||
import { oauthLogin } from "@/api/user";
|
||||
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
|
||||
import { get, load } from "@/api/captcha/index.ts";
|
||||
import { getSms, oauthLogin } from "@/api/user";
|
||||
import { VerfiyCaptcha } from "@/api/captcha/api.ts";
|
||||
// import useStore from '@/utils/store/useStore.tsx'
|
||||
type LoginType = "account" | "phone";
|
||||
|
||||
@@ -33,6 +39,17 @@ const iconStyles: CSSProperties = {
|
||||
export default observer(() => {
|
||||
const [form] = Form.useForm();
|
||||
const captcha = useRef<CaptchaInstance>(null);
|
||||
const captchaRef = useRef<CaptFieldRef | null | undefined>();
|
||||
|
||||
async function verify(token: string, deg: number): Promise<TicketInfoType> {
|
||||
const data: any = {
|
||||
token: token,
|
||||
deg: deg,
|
||||
};
|
||||
const res = await VerfiyCaptcha(data);
|
||||
return res;
|
||||
}
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: (
|
||||
@@ -56,15 +73,21 @@ export default observer(() => {
|
||||
|
||||
async function oAuthLogin(type: string) {
|
||||
const res: any = await oauthLogin(type);
|
||||
console.log(res);
|
||||
window.location.href = res.data;
|
||||
window.open(res.data, "_blank");
|
||||
}
|
||||
|
||||
const [loginType, setLoginType] = useState<LoginType>("account");
|
||||
|
||||
async function openCaptcha() {
|
||||
captcha.current!.open();
|
||||
}
|
||||
|
||||
const onSubmit = async (formData: object) => {
|
||||
console.log(formData);
|
||||
openCaptcha().then(() => {
|
||||
console.log(formData);
|
||||
});
|
||||
};
|
||||
useEffect(() => {}, []);
|
||||
return (
|
||||
<RotateCaptcha get={get} load={load} verify={verify} limit={2} ref={captcha}>
|
||||
<div className={styles.container}>
|
||||
@@ -137,7 +160,7 @@ export default observer(() => {
|
||||
size: "large",
|
||||
prefix: <UserOutlined className={"prefixIcon"} />,
|
||||
}}
|
||||
placeholder={"请输入账号/邮箱/电话号码"}
|
||||
placeholder={"请输入邮箱或电话号码"}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -165,26 +188,6 @@ export default observer(() => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/*<ProFormText*/}
|
||||
{/* addonAfter={CodeImg}*/}
|
||||
{/* name='code'*/}
|
||||
{/* fieldProps={{*/}
|
||||
{/* size: 'large',*/}
|
||||
{/* prefix: <BarcodeOutlined className={'prefixIcon'} />,*/}
|
||||
{/* autoComplete: 'off',*/}
|
||||
{/* }}*/}
|
||||
{/* placeholder='请输入图形验证码'*/}
|
||||
{/* rules={[*/}
|
||||
{/* {*/}
|
||||
{/* required: true,*/}
|
||||
{/* message: '请输入图形验证码!',*/}
|
||||
{/* },*/}
|
||||
{/* {*/}
|
||||
{/* pattern: /^[a-zA-Z0-9]{5}$/,*/}
|
||||
{/* message: '图形验证码格式不正确',*/}
|
||||
{/* },*/}
|
||||
{/* ]}*/}
|
||||
{/*/>*/}
|
||||
</>
|
||||
)}
|
||||
{loginType === "phone" && (
|
||||
@@ -208,26 +211,6 @@ export default observer(() => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/*<ProFormText*/}
|
||||
{/* addonAfter={CodeImg}*/}
|
||||
{/* name='code'*/}
|
||||
{/* fieldProps={{*/}
|
||||
{/* size: 'large',*/}
|
||||
{/* prefix: <BarcodeOutlined className={'prefixIcon'} />,*/}
|
||||
{/* autoComplete: 'off',*/}
|
||||
{/* }}*/}
|
||||
{/* placeholder='请输入图形验证码'*/}
|
||||
{/* rules={[*/}
|
||||
{/* {*/}
|
||||
{/* required: true,*/}
|
||||
{/* message: '请输入图形验证码!',*/}
|
||||
{/* },*/}
|
||||
{/* {*/}
|
||||
{/* pattern: /^[a-zA-Z0-9]{5}$/,*/}
|
||||
{/* message: '图形验证码格式不正确',*/}
|
||||
{/* },*/}
|
||||
{/* ]}*/}
|
||||
{/*/>*/}
|
||||
<ProFormCaptcha
|
||||
fieldProps={{
|
||||
size: "large",
|
||||
@@ -237,22 +220,30 @@ export default observer(() => {
|
||||
size: "large",
|
||||
}}
|
||||
placeholder={"请输入验证码"}
|
||||
captchaTextRender={(timing, count) => {
|
||||
captchaTextRender={(timing: boolean) => {
|
||||
if (timing) {
|
||||
return `${count} ${"获取验证码"}`;
|
||||
// return `${count} ${"获取验证码"}`;
|
||||
return `${"获取验证码"}`;
|
||||
}
|
||||
return "获取验证码";
|
||||
}}
|
||||
name="captcha"
|
||||
phoneName={"mobile"}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "请输入验证码!",
|
||||
},
|
||||
]}
|
||||
onGetCaptcha={async () => {
|
||||
captcha.current!.open();
|
||||
message.success("获取验证码成功!验证码为:1234");
|
||||
fieldRef={captchaRef}
|
||||
countDown={300}
|
||||
onGetCaptcha={async (mobile: string) => {
|
||||
const res: any = await getSms(mobile);
|
||||
if (res && res.success) {
|
||||
message.success(res.data, 3);
|
||||
} else {
|
||||
message.warning(res.data, 3);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -261,7 +252,7 @@ export default observer(() => {
|
||||
<ProFormCheckbox noStyle name="autoLogin">
|
||||
自动登录
|
||||
</ProFormCheckbox>
|
||||
<a style={{ float: "right" }}>忘记密码 </a>
|
||||
<a style={{ float: "right" }}>忘记密码</a>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
@@ -271,21 +262,18 @@ export default observer(() => {
|
||||
onClick={async () => {
|
||||
let validateFields;
|
||||
if (loginType === "account") {
|
||||
validateFields = ["username", "password", "code"];
|
||||
validateFields = ["username", "password"];
|
||||
} else {
|
||||
validateFields = ["mobile", "captcha", "code"];
|
||||
validateFields = ["mobile", "captcha"];
|
||||
}
|
||||
|
||||
await form
|
||||
.validateFields(validateFields)
|
||||
.then(async (values) => {
|
||||
if (loginType === "account") {
|
||||
captcha.current!.open();
|
||||
}
|
||||
await onSubmit(values as API.PhoneRegisterRequest);
|
||||
await onSubmit(values);
|
||||
})
|
||||
.catch((errorInfo) => {
|
||||
console.error(errorInfo);
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}}>
|
||||
登录
|
||||
@@ -320,9 +308,11 @@ export default observer(() => {
|
||||
border: "1px solid #D4D8DD",
|
||||
borderRadius: "50%",
|
||||
}}>
|
||||
<QqOutlined
|
||||
style={{ ...iconStyles, color: "#1677FF" }}
|
||||
/>
|
||||
<Tooltip title="QQ登录" color={"blue"}>
|
||||
<QqOutlined
|
||||
style={{ ...iconStyles, color: "#1677FF" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@@ -335,9 +325,11 @@ export default observer(() => {
|
||||
border: "1px solid #D4D8DD",
|
||||
borderRadius: "50%",
|
||||
}}>
|
||||
<WechatOutlined
|
||||
style={{ ...iconStyles, color: "#08a327" }}
|
||||
/>
|
||||
<Tooltip title="企业微信登录" color={"green"}>
|
||||
<WechatOutlined
|
||||
style={{ ...iconStyles, color: "#08a327" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@@ -350,12 +342,14 @@ export default observer(() => {
|
||||
border: "1px solid #D4D8DD",
|
||||
borderRadius: "50%",
|
||||
}}>
|
||||
<GithubOutlined
|
||||
onClick={() => {
|
||||
oAuthLogin("github").then();
|
||||
}}
|
||||
style={{ ...iconStyles, color: "#333333" }}
|
||||
/>
|
||||
<Tooltip title="github登录" color={"black"}>
|
||||
<GithubOutlined
|
||||
onClick={() => {
|
||||
oAuthLogin("github").then();
|
||||
}}
|
||||
style={{ ...iconStyles, color: "#333333" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@@ -368,12 +362,14 @@ export default observer(() => {
|
||||
border: "1px solid #D4D8DD",
|
||||
borderRadius: "50%",
|
||||
}}>
|
||||
<GitlabOutlined
|
||||
onClick={() => {
|
||||
oAuthLogin("gitee").then();
|
||||
}}
|
||||
style={{ ...iconStyles, color: "#FF6A10" }}
|
||||
/>
|
||||
<Tooltip title="gitee登录" color={"orange"}>
|
||||
<GitlabOutlined
|
||||
onClick={() => {
|
||||
oAuthLogin("gitee").then();
|
||||
}}
|
||||
style={{ ...iconStyles, color: "#FF6A10" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
import { LockOutlined, MobileOutlined, WechatOutlined } from "@ant-design/icons";
|
||||
import { ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
|
||||
import { Space, Tabs, message, Image, Alert, Form, Button } from "antd";
|
||||
import { Alert, Button, Form, Image, message, Space, Tabs } from "antd";
|
||||
import { useState } from "react";
|
||||
import logo from "@/assets/icons/schisandra.svg";
|
||||
// import background from '@/assets/images/background.png'
|
||||
@@ -10,6 +10,7 @@ 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 useStore from '@/utils/store/useStore.tsx'
|
||||
type LoginType = "phone";
|
||||
|
||||
@@ -29,7 +30,12 @@ export default observer(() => {
|
||||
const [loginType, setLoginType] = useState<LoginType>("phone");
|
||||
|
||||
const onSubmit = async (formData: object) => {
|
||||
console.log(formData);
|
||||
const res: any = await register(formData);
|
||||
if (res && res.success) {
|
||||
message.success(res.data);
|
||||
} else {
|
||||
message.error(res.data);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
@@ -168,21 +174,28 @@ export default observer(() => {
|
||||
size: "large",
|
||||
}}
|
||||
placeholder={"请输入验证码"}
|
||||
captchaTextRender={(timing, count) => {
|
||||
captchaTextRender={(timing: boolean) => {
|
||||
if (timing) {
|
||||
return `${count} ${"获取验证码"}`;
|
||||
return `${"获取验证码"}`;
|
||||
}
|
||||
return "获取验证码";
|
||||
}}
|
||||
name="captcha"
|
||||
name="activeCode"
|
||||
phoneName={"phone"}
|
||||
countDown={300}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "请输入验证码!",
|
||||
},
|
||||
]}
|
||||
onGetCaptcha={async () => {
|
||||
message.success("获取验证码成功!验证码为:1234");
|
||||
onGetCaptcha={async (phone: string) => {
|
||||
const res: any = await getSms(phone);
|
||||
if (res && res.success) {
|
||||
message.success(res.data, 3);
|
||||
} else {
|
||||
message.warning(res.data, 3);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -193,10 +206,8 @@ export default observer(() => {
|
||||
onClick={async () => {
|
||||
const validateFields = [
|
||||
"phone",
|
||||
"username",
|
||||
"password",
|
||||
"captcha",
|
||||
"code",
|
||||
"activeCode",
|
||||
"confirmPassword",
|
||||
];
|
||||
await form
|
||||
@@ -204,8 +215,8 @@ export default observer(() => {
|
||||
.then(async (values) => {
|
||||
await onSubmit(values as API.PhoneRegisterRequest);
|
||||
})
|
||||
.catch((errorInfo) => {
|
||||
console.error(errorInfo);
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}}>
|
||||
注册
|
||||
|
@@ -2,10 +2,10 @@
|
||||
|
||||
import { defineConfig, loadEnv } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import * as path from "path";
|
||||
import { resolve } from "path";
|
||||
// icons plugin
|
||||
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
|
||||
import * as path from "path";
|
||||
import imagemin from "unplugin-imagemin/vite";
|
||||
import viteCompression from "vite-plugin-compression";
|
||||
import { createHtmlPlugin } from "vite-plugin-html";
|
||||
|
Reference in New Issue
Block a user