Browse Source

update auth api

dev
powersir 1 year ago
parent
commit
88b812d0c4
16 changed files with 420 additions and 152 deletions
  1. +0
    -0
      .env.development
  2. +0
    -1
      build/utils.ts
  3. +1
    -1
      src/layout/header/index.tsx
  4. +6
    -0
      src/models/index.ts
  5. +4
    -29
      src/models/user.ts
  6. +3
    -3
      src/pages/custom/product/sample/components/attr-editor.tsx
  7. +165
    -0
      src/pages/custom/product/sample/components/mask-picture-editor.tsx
  8. +123
    -62
      src/pages/custom/product/sample/index.tsx
  9. +25
    -16
      src/pages/login/index.tsx
  10. +61
    -14
      src/request/index.ts
  11. +16
    -0
      src/request/service/auth.ts
  12. +0
    -15
      src/request/service/login.ts
  13. +3
    -3
      src/request/service/user.ts
  14. +11
    -5
      src/store/global/index.ts
  15. +1
    -3
      src/utils/env.ts
  16. +1
    -0
      vite.config.ts

.env.develop → .env.development View File


+ 0
- 1
build/utils.ts View File

@@ -20,7 +20,6 @@ export function isReportMode(): boolean {
// Read all environment variable configuration files to process.env // Read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv { export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {}; const ret: any = {};

for (const envName of Object.keys(envConf)) { for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n'); let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName; realName = realName === 'true' ? true : realName === 'false' ? false : realName;


+ 1
- 1
src/layout/header/index.tsx View File

@@ -12,7 +12,7 @@ import { i18n, t } from '@/utils/i18n';
import { BellOutlined, MenuOutlined, SettingOutlined } from '@ant-design/icons'; import { BellOutlined, MenuOutlined, SettingOutlined } from '@ant-design/icons';
import { useUserStore } from '@/store/global/user'; import { useUserStore } from '@/store/global/user';
import { useRequest } from '@/hooks/use-request'; import { useRequest } from '@/hooks/use-request';
import loginService from '@/request/service/login';
import loginService from '@/request/service/auth';


const Header = () => { const Header = () => {




+ 6
- 0
src/models/index.ts View File

@@ -1,5 +1,11 @@
export * from './user.ts' export * from './user.ts'


export interface ResponseDTO<T>{
code: number;
msg: string;
data: T
}

export interface PageData<T> { export interface PageData<T> {
data: T[]; data: T[];
total: number; total: number;


+ 4
- 29
src/models/user.ts View File

@@ -1,9 +1,11 @@
export interface LoginDTO { export interface LoginDTO {
userName: string;
captchaVerification: string;
username: string;
password: string; password: string;
rememberMe: boolean;
tenantName: string;
} }



export interface TokenDTO { export interface TokenDTO {
userId: number, userId: number,
accessToken: string; accessToken: string;
@@ -55,33 +57,6 @@ export interface UserDTO {
idToString: string; idToString: string;
} }


export interface PopMenu {
id: number;
parentid: number;
homeid: number;
menuName: string;
parentMenuName: string;
pageUrl: string;
sort: number;
level: number;
}

export interface LoginRespDTO {
ack: number;
data: UserDTO;
msg: string;
pop: Array<PopMenu>;
}

// "id": 0,
// "parentId": 1024,
// "name": "芋道",
// "path": "post",
// "component": "system/post/index",
// "icon": "/menu/list",
// "visible": false,
// "keepAlive": false

export interface Menu { export interface Menu {
id: string; id: string;
parentId?: string; parentId?: string;


src/pages/custom/product/sample/attr-editor.tsx → src/pages/custom/product/sample/components/attr-editor.tsx View File

@@ -43,7 +43,7 @@ const getBase64 = (file: RcFile): Promise<string> =>
}); });




const CreateSampleAttr: React.FC<CreateSampleAttrProps> = (props) => {
const SampleAttrEditor: React.FC<CreateSampleAttrProps> = (props) => {


const { visible, onCancel, curRecord, onSave, editData } = props; const { visible, onCancel, curRecord, onSave, editData } = props;
const [saveLoading, setSaveLoading] = useState(false); const [saveLoading, setSaveLoading] = useState(false);
@@ -144,7 +144,7 @@ const CreateSampleAttr: React.FC<CreateSampleAttrProps> = (props) => {
okText="确认" okText="确认"
cancelText="取消" cancelText="取消"
> >
<DeleteOutlined style={{ fontSize: '16px' }}/>
<DeleteOutlined style={{ fontSize: '16px' }} className='hover:(bg-[rgb(94,53,177)]'/>
</Popconfirm> </Popconfirm>
} }
> >
@@ -207,4 +207,4 @@ const CreateSampleAttr: React.FC<CreateSampleAttrProps> = (props) => {
) )
} }


export default CreateSampleAttr;
export default SampleAttrEditor;

+ 165
- 0
src/pages/custom/product/sample/components/mask-picture-editor.tsx View File

@@ -0,0 +1,165 @@
import React, { useEffect, useMemo, useState } from 'react'
import { CloseOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { Drawer, Form, Input, Card, Space, Button, Upload, Popconfirm, Modal, FormListOperation, FormListFieldData } from 'antd'
import type { RcFile, UploadProps } from 'antd/es/upload';
import type { UploadFile } from 'antd/es/upload/interface';

const layout = {
labelCol: { span: 4, },
wrapperCol: { span: 16 },
bordered: false,
};

// {
// "id": 412,
// "prototypeId": 89,
// "": 1991125,
// "maskImgId": 1991130,
// "imgName": "3.jpg",
// "imgType": 2,
// "imgWidth": 600.0,
// "imgHeight": 600.0,
// "imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
// "maskImgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114413A061.png"
// }

export interface MaskPicture {
id: number;
prototypeId: number;
imgId: number;
maskImgId?: number;
imgName: string;
imgType: number;
imgWidth: number;
imgHeight: number;
imgUrl: string;
maskImgUrl?: string;
}

interface MaskPictureProps {
visible: boolean;
onCancel: (flag?: boolean) => void;
onSave: () => void;
dataSource?: MaskPicture[] | null;
}

const getBase64 = (file: RcFile): Promise<string> =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});


const MaskPictureEditor: React.FC<MaskPictureProps> = (props) => {

const { visible, onCancel, onSave, dataSource } = props;
const [saveLoading, setSaveLoading] = useState(false);
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState('');
const [previewTitle, setPreviewTitle] = useState('');
const [form] = Form.useForm();

useEffect(() => {
if (visible) {
setInitValue();
} else {
form.resetFields();
}
}, [visible]);

async function setInitValue() {
}

const save = async (values: any) => {
setSaveLoading(true);

setSaveLoading(false);
}

const handleCancel = () => setPreviewOpen(false);

const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj as RcFile);
}

setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1));
};

const uploadButton = (text: string) => (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>{text}</div>
</div>
);

const handleChange: UploadProps['onChange'] = (info) => {
debugger
}

const MaskPictureItem = (item: MaskPicture) => {
const mainPicture: UploadFile[] = [{
uid: `${item.imgId}`,
name: item.imgUrl.split("/")[-1],
status: 'done',
url: item.imgUrl,
}]
const maskPicture: UploadFile[] = item.maskImgId ? [{
uid: `${item.maskImgId}`,
name: item.maskImgUrl!!.split("/")[-1],
status: 'done',
url: item.maskImgUrl,
}] : []
return (
<div className='flex justify-start'>
<Upload
listType="picture-card"
fileList={mainPicture}
maxCount={1}
onPreview={handlePreview}
onChange={handleChange}>
{mainPicture.length === 0 && uploadButton("上传主图")}
</Upload>
<Upload
listType="picture-card"
fileList={maskPicture}
onPreview={handlePreview}
maxCount={1}>
{maskPicture.length === 0 && uploadButton("上传蒙版图")}
</Upload>
</div>
)
}

return (
<>
<Drawer
open={visible}
title="蒙版图"

onClose={() => { onCancel() }}
extra={
<Space>
<Button type="primary" size='middle' onClick={() => { save }}>
保存
</Button>
</Space>
}
destroyOnClose
>
{dataSource?.map((item) => {
return MaskPictureItem(item)
})}
</Drawer>
<Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel} zIndex={2000}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</>
)
}

export default MaskPictureEditor;

+ 123
- 62
src/pages/custom/product/sample/index.tsx View File

@@ -4,8 +4,10 @@ import { t } from '@/utils/i18n';
import { IconBuguang } from '@/assets/icons/buguang'; import { IconBuguang } from '@/assets/icons/buguang';
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { TableRowSelection } from 'antd/es/table/interface'; import type { TableRowSelection } from 'antd/es/table/interface';
import CreateSampleAttr from './attr-editor'
import type { SampleAttribute } from './attr-editor'
import SampleAttrEditor from './components/attr-editor'
import MaskPictureEditor from './components/mask-picture-editor';
import type { SampleAttribute } from './components/attr-editor'
import type { MaskPicture } from './components/mask-picture-editor';


interface DataType { interface DataType {
id: number; id: number;
@@ -50,7 +52,7 @@ const TablePage: React.FC = () => {
key: 'prototypeName', key: 'prototypeName',
}, },
{ {
title:'类目',
title: '类目',
key: 'categoryName', key: 'categoryName',
dataIndex: 'categoryName' dataIndex: 'categoryName'
}, },
@@ -60,7 +62,7 @@ const TablePage: React.FC = () => {
key: 'createName', key: 'createName',
}, },
{ {
title:'创建时间',
title: '创建时间',
key: 'createTime', key: 'createTime',
dataIndex: 'createTime' dataIndex: 'createTime'
}, },
@@ -69,11 +71,13 @@ const TablePage: React.FC = () => {
key: 'action', key: 'action',
render: (_, record) => ( render: (_, record) => (
<Space size="middle"> <Space size="middle">
<a>蒙版图 </a>
<a onClick={() => {
setMaskEditorVisible(true)
}}>蒙版图 </a>
<a <a
onClick={() => { onClick={() => {
// setEditData(record); // setEditData(record);
setCreateVisible(true);
setAttrEditorVisible(true);
}}>属性设置</a> }}>属性设置</a>
<a>编辑</a> <a>编辑</a>
<a>删除</a> <a>删除</a>
@@ -96,72 +100,123 @@ const TablePage: React.FC = () => {
dictDetails: [] dictDetails: []
}, },
{ {
id: 76,
createTime: "2023-07-13 17:24:25",
spuCode: "2-47GEE7",
categoryId: 1264,
prototypeName: "kfc-test",
createId: 2,
categoryName: "男士T恤",
createName: "陈相荣✨",
oneImgUrl: "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
dictDetails: []
id: 76,
createTime: "2023-07-13 17:24:25",
spuCode: "2-47GEE7",
categoryId: 1264,
prototypeName: "kfc-test",
createId: 2,
categoryName: "男士T恤",
createName: "陈相荣✨",
oneImgUrl: "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
dictDetails: []
} }
]; ];


const attrData: SampleAttribute[] = [ const attrData: SampleAttribute[] = [
{ {
"id": 105,
"prototypeId": 88,
"attrName": "Color",
"isContainImg": 1,
"attrVals": [
{
"id": 273,
"attrId": 105,
"valName": "Pink",
"imgId": 1990260,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821104322A047.jpg"
},
{
"id": 274,
"attrId": 105,
"valName": "Black",
"imgId": 1990263,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821104322A050.jpg"
}
]
"id": 105,
"prototypeId": 88,
"attrName": "Color",
"isContainImg": 1,
"attrVals": [
{
"id": 273,
"attrId": 105,
"valName": "Pink",
"imgId": 1990260,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821104322A047.jpg"
},
{
"id": 274,
"attrId": 105,
"valName": "Black",
"imgId": 1990263,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821104322A050.jpg"
}
]
}, },
{ {
"id": 106,
"prototypeId": 88,
"attrName": "Size",
"isContainImg": 2,
"attrVals": [
{
"id": 275,
"attrId": 106,
"valName": "XL"
},
{
"id": 276,
"attrId": 106,
"valName": "XXL"
}
]
"id": 106,
"prototypeId": 88,
"attrName": "Size",
"isContainImg": 2,
"attrVals": [
{
"id": 275,
"attrId": 106,
"valName": "XL"
},
{
"id": 276,
"attrId": 106,
"valName": "XXL"
}
]
} }
]
]


const maskPictures: MaskPicture[] = [
{
"id": 412,
"prototypeId": 89,
"imgId": 1991125,
"maskImgId": 1991130,
"imgName": "3.jpg",
"imgType": 2,
"imgWidth": 600.0,
"imgHeight": 600.0,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
"maskImgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114413A061.png"
},
{
"id": 413,
"prototypeId": 89,
"imgId": 1991122,
"maskImgId": 1991131,
"imgName": "1.jpg",
"imgType": 2,
"imgWidth": 600.0,
"imgHeight": 600.0,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A053.jpg",
"maskImgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114413A062.png"
},
{
"id": 414,
"prototypeId": 89,
"imgId": 1991127,
"imgName": "f60911abe38e82e9dd2aaa75f3292ed2.jpg",
"imgType": 2,
"imgWidth": 1000.0,
"imgHeight": 1000.0,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A058.jpg"
},
{
"id": 415,
"prototypeId": 89,
"imgId": 1991123,
"imgName": "d247c5ec0ef78f302b803c6bf00658a2.jpg",
"imgType": 2,
"imgWidth": 1000.0,
"imgHeight": 1000.0,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A054.jpg"
}
]


const [createVisible, setCreateVisible] = useState(false);
const [attrEditorVisible, setAttrEditorVisible] = useState(false);
const [maskEditorVisible, setMaskEditorVisible] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);


const cancelHandle = () => { const cancelHandle = () => {
setCreateVisible(false);
setAttrEditorVisible(false);
}; };


const saveHandle = () => {
setCreateVisible(false);
const saveAttributeHandle = () => {
setAttrEditorVisible(false);
}

const saveMaskPictureHandle = () => {
setMaskEditorVisible(false);
} }


const onSelectChange = (newSelectedRowKeys: React.Key[]) => { const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
@@ -211,16 +266,22 @@ const TablePage: React.FC = () => {
return ( return (
<div> <div>
<div className="dark:bg-[rgb(33,41,70)] rounded-md"> <div className="dark:bg-[rgb(33,41,70)] rounded-md">
<Table rowKey="id" rowSelection={rowSelection} scroll={{ x: true }} columns={columns} dataSource={data} className='bg-transparent'
<Table rowKey="id" rowSelection={rowSelection} scroll={{ x: true }} columns={columns} dataSource={data} className='bg-transparent'
pagination={{ position: ['bottomRight'] }} pagination={{ position: ['bottomRight'] }}
/> />
</div> </div>
<CreateSampleAttr
onSave={saveHandle}
<SampleAttrEditor
onSave={saveAttributeHandle}
onCancel={cancelHandle} onCancel={cancelHandle}
visible={createVisible}
visible={attrEditorVisible}
curRecord={attrData} curRecord={attrData}
editData={attrData}></CreateSampleAttr>
editData={attrData} />

<MaskPictureEditor
onSave={saveMaskPictureHandle}
onCancel={()=>{ setMaskEditorVisible(false) }}
visible={maskEditorVisible}
dataSource={maskPictures} />
</div> </div>
); );
}; };


+ 25
- 16
src/pages/login/index.tsx View File

@@ -6,32 +6,31 @@ import { useNavigate } from 'react-router-dom';
import { useRequest } from '@/hooks/use-request'; import { useRequest } from '@/hooks/use-request';
import { useUserStore } from '@/store/global/user'; import { useUserStore } from '@/store/global/user';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import loginService from '@/request/service/login';
import loginService from '@/request/service/auth';
import userService from '@/request/service/user'; import userService from '@/request/service/user';
import homeService from '@/request/service/home';
import { LoginDTO } from '@/models'; import { LoginDTO } from '@/models';
import './index.css' import './index.css'


const Login = () => { const Login = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { runAsync: getUserInfo } = useRequest(userService.getUserInfo, { manual: true });
const { runAsync: getAnnouncement } = useRequest(homeService.getAnnouncement, { manual: true });
const { runAsync: listMenus } = useRequest(userService.listMenus, { manual: true });
const { runAsync: login, loading } = useRequest(loginService.login, { manual: true }); const { runAsync: login, loading } = useRequest(loginService.login, { manual: true });
const { runAsync: rerefshToken } = useRequest(loginService.rerefshToken, { manual: true });
const { setCurrentUser } = useUserStore(); const { setCurrentUser } = useUserStore();
const { setCookie } = useGlobalStore();
const { setToken, setRefreshToken } = useGlobalStore();
const onFinish = async (values: LoginDTO) => { const onFinish = async (values: LoginDTO) => {
const [loginError, data] = await login(values);
values.captchaVerification = "jKXK3cg440w2wbLQWBHOjDURNqT5sfMsQmkwMAEvzapLhQZh7YiMlEah/WhVXqygFmNO4SXEC4MzkjoRqgYK7A=="
values.rememberMe = true
const [loginError, {data}] = await login(values);
if (loginError) { if (loginError) {
return; return;
} }
data.data.avatarUrl = 'https://test.vogocm.com:9010/eshop/eshop_img/2023/5/24/43853633d16749bfb291f81bebb73451_20230524150631A001.jpg';
setCookie(data.msg);
setCurrentUser(data.data);
const [e, userInfo] = await getUserInfo();
const [ _, announcementData ] = await getAnnouncement();
console.log(announcementData)
console.log(userInfo)
// data.data.avatarUrl = 'https://test.vogocm.com:9010/eshop/eshop_img/2023/5/24/43853633d16749bfb291f81bebb73451_20230524150631A001.jpg';
setRefreshToken(data.refreshToken);
setToken(data.accessToken);
const [ _, { data: menus } ] = await listMenus()
// const [ error, {data: tokenData}] = await rerefshToken(data.refreshToken)
debugger
navigate('/'); navigate('/');
}; };


@@ -51,12 +50,22 @@ const Login = () => {
<Form <Form
name="super-admin" name="super-admin"
className="login-form" className="login-form"
initialValues={{ userName: 'admin', password: '1' }}
initialValues={{ username: 'admin', password: 'admin123', tenantName: '芋道源码' }}
onFinish={onFinish} onFinish={onFinish}
size="large" size="large"
> >
<Form.Item <Form.Item
name="userName"
name="tenantName"
rules={[{ required: true, message: '请输入用户名' }]}
>
<Input
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder={t("RNISycbR" /* 账号 */)}
size="large"
/>
</Form.Item>
<Form.Item
name="username"
rules={[{ required: true, message: t("wVzXBuYs" /* 请输入账号 */) }]} rules={[{ required: true, message: t("wVzXBuYs" /* 请输入账号 */) }]}
> >
<Input <Input


+ 61
- 14
src/request/index.ts View File

@@ -5,13 +5,16 @@ import axios, {
CreateAxiosDefaults, CreateAxiosDefaults,
InternalAxiosRequestConfig, InternalAxiosRequestConfig,
} from 'axios'; } from 'axios';
import {antdUtils} from '@/utils/antd';

import {useGlobalStore} from '@/store/global';
import { useGlobSetting } from '@/hooks/use-global-settings'; import { useGlobSetting } from '@/hooks/use-global-settings';
import loginService from '@/request/service/auth';
import {antdUtils} from '@/utils/antd';
import { ResponseDTO } from '@/models';


const { apiUrl = '' } = useGlobSetting(); const { apiUrl = '' } = useGlobSetting();


// axios.defaults.withCredentials = true;

const refreshTokenUrl = '/api/auth/refresh/token';


export type Response<T> = Promise<[boolean, T, AxiosResponse<T>]>; export type Response<T> = Promise<[boolean, T, AxiosResponse<T>]>;


@@ -32,6 +35,7 @@ class Request {


private axiosInstance: AxiosInstance; private axiosInstance: AxiosInstance;


private refreshTokenFlag = false;
private requestQueue: { private requestQueue: {
resolve: any; resolve: any;
config: any; config: any;
@@ -48,7 +52,11 @@ class Request {
private async requestInterceptor( private async requestInterceptor(
axiosConfig: InternalAxiosRequestConfig axiosConfig: InternalAxiosRequestConfig
): Promise<any> { ): Promise<any> {
if (this.requestingCount >= this.limit) {
if ([refreshTokenUrl].includes(axiosConfig.url || '')) {
return Promise.resolve(axiosConfig);
}

if (this.refreshTokenFlag || this.requestingCount >= this.limit) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.requestQueue.push({ this.requestQueue.push({
resolve, resolve,
@@ -59,6 +67,12 @@ class Request {
} }


this.requestingCount += 1; this.requestingCount += 1;

const {token} = useGlobalStore.getState();

if (token) {
axiosConfig.headers.Authorization = `Bearer ${token}`;
}
return Promise.resolve(axiosConfig); return Promise.resolve(axiosConfig);
} }


@@ -83,20 +97,47 @@ class Request {
resolve(await this.request(config)); resolve(await this.request(config));
} else if (type === 'reuqest') { } else if (type === 'reuqest') {
this.requestingCount += 1; this.requestingCount += 1;
//TODO: cookie
const {token} = useGlobalStore.getState();
config.headers.Authorization = `Bearer ${token}`;
resolve(config); resolve(config);
} }
} }
); );
} }


private async refreshToken() {
const {refreshToken} = useGlobalStore.getState();

if (!refreshToken) {
this.toLoginPage();
}

const [error, {data}] = await loginService.rerefshToken(refreshToken);

if (error) {
this.toLoginPage();
}

useGlobalStore.setState({
refreshToken: data.refreshToken,
token: data.accessToken,
});

this.refreshTokenFlag = false;

this.requestByQueue();
}

private async responseSuccessInterceptor( private async responseSuccessInterceptor(
response: AxiosResponse<any, any> response: AxiosResponse<any, any>
): Promise<any> { ): Promise<any> {
this.requestingCount -= 1;
if (this.requestQueue.length) {
this.requestByQueue();
if (response.config.url !== refreshTokenUrl) {
this.requestingCount -= 1;
if (this.requestQueue.length) {
this.requestByQueue();
}
} }

return Promise.resolve([false, response.data, response]); return Promise.resolve([false, response.data, response]);
} }


@@ -107,6 +148,10 @@ class Request {
if (status === 401) { if (status === 401) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.requestQueue.unshift({resolve, config, type: 'response'}); this.requestQueue.unshift({resolve, config, type: 'response'});
if (this.refreshTokenFlag) return;

this.refreshTokenFlag = true;
this.refreshToken();
}); });
} else { } else {
antdUtils.notification?.error({ antdUtils.notification?.error({
@@ -119,18 +164,20 @@ class Request {


private reset() { private reset() {
this.requestQueue = []; this.requestQueue = [];
this.refreshTokenFlag = false;
this.requestingCount = 0; this.requestingCount = 0;
} }


private toLoginPage() { private toLoginPage() {
this.reset(); this.reset();
// router.navigate('/user/login');
} }


request<T, D = any>(config: AxiosRequestConfig<D>): Response<T> {
request<T, D = any>(config: AxiosRequestConfig<D>): Response<ResponseDTO<T>> {
return this.axiosInstance(config); return this.axiosInstance(config);
} }


get<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Response<T> {
get<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Response<ResponseDTO<T>> {
return this.axiosInstance.get(url, config); return this.axiosInstance.get(url, config);
} }


@@ -138,7 +185,7 @@ class Request {
url: string, url: string,
data?: D, data?: D,
config?: AxiosRequestConfig<D> config?: AxiosRequestConfig<D>
): Response<T> {
): Response<ResponseDTO<T>> {
return this.axiosInstance.post(url, data, config); return this.axiosInstance.post(url, data, config);
} }


@@ -146,15 +193,15 @@ class Request {
url: string, url: string,
data?: D, data?: D,
config?: AxiosRequestConfig<D> config?: AxiosRequestConfig<D>
): Response<T> {
): Response<ResponseDTO<T>> {
return this.axiosInstance.put(url, data, config); return this.axiosInstance.put(url, data, config);
} }


delete<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Response<T> {
delete<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Response<ResponseDTO<T>> {
return this.axiosInstance.delete(url, config); return this.axiosInstance.delete(url, config);
} }
} }


const request = new Request({timeout: 60 * 1000 * 5, baseURL: apiUrl});
const request = new Request({timeout: 60 * 1000 * 5, baseURL: apiUrl});


export default request; export default request;

+ 16
- 0
src/request/service/auth.ts View File

@@ -0,0 +1,16 @@
import request from '@/request';
import { LoginDTO, TokenDTO } from '@/models'

export default {
// 登录
login: (loginDTO: LoginDTO) => {
return request.post<TokenDTO>('/admin-api/system/auth/login', loginDTO);
},

logout: () => {
return request.post<any>('/app-api/member/auth/logout');
},
rerefshToken: (refreshToken: string) => {
return request.post<TokenDTO>('/app-api/member/auth/refresh-token', { refreshToken });
}
};

+ 0
- 15
src/request/service/login.ts View File

@@ -1,15 +0,0 @@
import request from '@/request';
import { LoginDTO, LoginRespDTO } from '@/models'

const loginService = {
// 登录
login: (loginDTO: LoginDTO) => {
return request.post<LoginRespDTO>('/api/login', loginDTO, { withCredentials: false });
},

logout: () => {
return request.get<any>('/api/logout');
}
};

export default loginService;

+ 3
- 3
src/request/service/user.ts View File

@@ -1,8 +1,8 @@
import request from '@/request'; import request from '@/request';
import { LoginRespDTO } from '@/models';
import { Menu } from '@/models';


export default { export default {
getUserInfo: () => {
return request.get<LoginRespDTO>('/api/userinfo/getUserInfo');
listMenus: () => {
return request.get<Menu>('/admin-api/system/auth/list-menus');
} }
}; };

+ 11
- 5
src/store/global/index.ts View File

@@ -5,14 +5,16 @@ interface State {
darkMode: boolean; darkMode: boolean;
collapsed: boolean; collapsed: boolean;
lang: string; lang: string;
cookie: string;
token: string;
refreshToken: string;
} }


interface Action { interface Action {
setDarkMode: (darkMode: State['darkMode']) => void; setDarkMode: (darkMode: State['darkMode']) => void;
setCollapsed: (collapsed: State['collapsed']) => void; setCollapsed: (collapsed: State['collapsed']) => void;
setLang: (lang: State['lang']) => void; setLang: (lang: State['lang']) => void;
setCookie: (cookie: State['cookie']) => void;
setToken: (token: State['token']) => void;
setRefreshToken: (refreshToken: State['refreshToken']) => void;
} }


export const useGlobalStore = create<State & Action>()( export const useGlobalStore = create<State & Action>()(
@@ -22,7 +24,8 @@ export const useGlobalStore = create<State & Action>()(
darkMode: false, darkMode: false,
collapsed: false, collapsed: false,
lang: 'zh', lang: 'zh',
cookie: '',
token: '',
refreshToken: '',
setDarkMode: (darkMode: State['darkMode']) => set({ setDarkMode: (darkMode: State['darkMode']) => set({
darkMode, darkMode,
}), }),
@@ -32,8 +35,11 @@ export const useGlobalStore = create<State & Action>()(
setLang: (lang: State['lang']) => set({ setLang: (lang: State['lang']) => set({
lang, lang,
}), }),
setCookie: (cookie: State['cookie']) => set({
cookie,
setToken: (token: State['token']) => set({
token,
}),
setRefreshToken: (refreshToken: State['refreshToken']) => set({
refreshToken,
}), }),
}; };
}, },


+ 1
- 3
src/utils/env.ts View File

@@ -16,12 +16,11 @@ export function getStorageShortName() {


export function getAppEnvConfig() { export function getAppEnvConfig() {
const ENV_NAME = getConfigFileName(import.meta.env); const ENV_NAME = getConfigFileName(import.meta.env);
console.log(import.meta.env)
const ENV = (import.meta.env.DEV const ENV = (import.meta.env.DEV
? // Get the global configuration (the configuration will be extracted independently when packaging) ? // Get the global configuration (the configuration will be extracted independently when packaging)
(import.meta.env as unknown as GlobEnvConfig) (import.meta.env as unknown as GlobEnvConfig)
: window[ENV_NAME as any]) as unknown as GlobEnvConfig; : window[ENV_NAME as any]) as unknown as GlobEnvConfig;

const { const {
VITE_GLOB_APP_TITLE, VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL, VITE_GLOB_API_URL,
@@ -29,7 +28,6 @@ export function getAppEnvConfig() {
VITE_GLOB_API_URL_PREFIX, VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL, VITE_GLOB_UPLOAD_URL,
} = ENV; } = ENV;

if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) { if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn( warn(
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`, `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`,


+ 1
- 0
vite.config.ts View File

@@ -9,6 +9,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {


const root = process.cwd(); const root = process.cwd();
const env = loadEnv(mode, root); const env = loadEnv(mode, root);
console.log(env)
const viteEnv = wrapperEnv(env); const viteEnv = wrapperEnv(env);
const { VITE_PORT } = viteEnv; const { VITE_PORT } = viteEnv;




Loading…
Cancel
Save