feat: add localforage
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
VITE_NODE_ENV='development'
|
VITE_NODE_ENV='development'
|
||||||
|
|
||||||
# 开发环境
|
# 开发环境
|
||||||
VITE_APP_BASE_API='/dev-api'
|
VITE_APP_BASE_API='/api'
|
||||||
|
|
||||||
# 页面 title 前缀
|
# 页面 title 前缀
|
||||||
VITE_APP_TITLE=开发环境
|
VITE_APP_TITLE=开发环境
|
||||||
@@ -11,3 +11,9 @@ VITE_APP_TITLE=开发环境
|
|||||||
VITE_API_BASE_URL='http://127.0.0.1:3000'
|
VITE_API_BASE_URL='http://127.0.0.1:3000'
|
||||||
|
|
||||||
VITE_TITLE_NAME='五味子云存储'
|
VITE_TITLE_NAME='五味子云存储'
|
||||||
|
|
||||||
|
# token key
|
||||||
|
VITE_APP_TOKEN_KEY='token'
|
||||||
|
|
||||||
|
# the upload url
|
||||||
|
VITE_UPLOAD_URL='http://127.0.0.1:3000'
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
VITE_NODE_ENV= 'production'
|
VITE_NODE_ENV='production'
|
||||||
# 生产环境
|
# 生产环境
|
||||||
VITE_APP_BASE_API = '/api'
|
VITE_APP_BASE_API='/api'
|
||||||
|
|
||||||
# 页面 title 前缀
|
# 页面 title 前缀
|
||||||
VITE_APP_TITLE=生产环境
|
VITE_APP_TITLE=生产环境
|
||||||
@@ -10,3 +10,9 @@ VITE_APP_TITLE=生产环境
|
|||||||
VITE_API_BASE_URL=''
|
VITE_API_BASE_URL=''
|
||||||
|
|
||||||
VITE_TITLE_NAME='五味子云存储'
|
VITE_TITLE_NAME='五味子云存储'
|
||||||
|
|
||||||
|
# token key
|
||||||
|
VITE_APP_TOKEN_KEY='token'
|
||||||
|
|
||||||
|
# the upload url
|
||||||
|
VITE_UPLOAD_URL='http://127.0.0.1:3000'
|
||||||
|
@@ -50,5 +50,6 @@ module.exports = {
|
|||||||
'@typescript-eslint/no-empty-interface': ['off'],
|
'@typescript-eslint/no-empty-interface': ['off'],
|
||||||
'@typescript-eslint/no-unused-vars': ['off'],
|
'@typescript-eslint/no-unused-vars': ['off'],
|
||||||
'@typescript-eslint/no-non-null-assertion': ['off'],
|
'@typescript-eslint/no-non-null-assertion': ['off'],
|
||||||
|
"no-control-regex": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
|
"jsencrypt": "^3.3.2",
|
||||||
|
"localforage": "^1.10.0",
|
||||||
"mobx": "^6.12.3",
|
"mobx": "^6.12.3",
|
||||||
"mobx-persist-store": "^1.1.4",
|
"mobx-persist-store": "^1.1.4",
|
||||||
"mobx-react": "^9.1.1",
|
"mobx-react": "^9.1.1",
|
||||||
|
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@@ -35,6 +35,12 @@ dependencies:
|
|||||||
gsap:
|
gsap:
|
||||||
specifier: ^3.12.5
|
specifier: ^3.12.5
|
||||||
version: 3.12.5
|
version: 3.12.5
|
||||||
|
jsencrypt:
|
||||||
|
specifier: ^3.3.2
|
||||||
|
version: 3.3.2
|
||||||
|
localforage:
|
||||||
|
specifier: ^1.10.0
|
||||||
|
version: 1.10.0
|
||||||
mobx:
|
mobx:
|
||||||
specifier: ^6.12.3
|
specifier: ^6.12.3
|
||||||
version: 6.12.3
|
version: 6.12.3
|
||||||
@@ -5382,6 +5388,10 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
|
||||||
|
/immediate@3.0.6:
|
||||||
|
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/import-fresh@3.3.0:
|
/import-fresh@3.3.0:
|
||||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -5739,6 +5749,10 @@ packages:
|
|||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jsencrypt@3.3.2:
|
||||||
|
resolution: {integrity: sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jsesc@0.5.0:
|
/jsesc@0.5.0:
|
||||||
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
|
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -5870,6 +5884,12 @@ packages:
|
|||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lie@3.1.1:
|
||||||
|
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
||||||
|
dependencies:
|
||||||
|
immediate: 3.0.6
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lines-and-columns@1.2.4:
|
/lines-and-columns@1.2.4:
|
||||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
|
|
||||||
@@ -5890,6 +5910,12 @@ packages:
|
|||||||
pkg-types: 1.0.3
|
pkg-types: 1.0.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/localforage@1.10.0:
|
||||||
|
resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
|
||||||
|
dependencies:
|
||||||
|
lie: 3.1.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/locate-path@6.0.0:
|
/locate-path@6.0.0:
|
||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import service from '@/utils/axios/service.ts'
|
import web from '@/utils/axios/web.ts'
|
||||||
|
|
||||||
export function getUserInfo(params: object) {
|
export const getPublicKey = () => {
|
||||||
return service.get('/user/info', params)
|
return web.get('/encrypt/getPublicKey')
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
// import React from 'react'
|
||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from 'react-dom/client'
|
||||||
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
|
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
|
||||||
import './assets/styles/index.less'
|
import './assets/styles/index.less'
|
||||||
@@ -8,9 +8,9 @@ import { Provider as MobxProvider } from 'mobx-react'
|
|||||||
import { RootStore } from '@/store'
|
import { RootStore } from '@/store'
|
||||||
const router = createBrowserRouter(routeConfig)
|
const router = createBrowserRouter(routeConfig)
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
// <React.StrictMode>
|
||||||
<MobxProvider {...RootStore}>
|
<MobxProvider {...RootStore}>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
</MobxProvider>
|
</MobxProvider>,
|
||||||
</React.StrictMode>,
|
// </React.StrictMode>,
|
||||||
)
|
)
|
||||||
|
@@ -1,22 +1,23 @@
|
|||||||
import { action, makeAutoObservable } from 'mobx'
|
import { action, makeAutoObservable } from 'mobx'
|
||||||
import { makePersistable, isHydrated } from 'mobx-persist-store'
|
import { makePersistable, isHydrated } from 'mobx-persist-store'
|
||||||
|
import { handleLocalforage } from '@/utils/localforage'
|
||||||
export class useUserStore {
|
export class useUserStore {
|
||||||
user: any = {}
|
token: any = ''
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(
|
makeAutoObservable(
|
||||||
this,
|
this,
|
||||||
{
|
{
|
||||||
setUserInfo: action,
|
setToken: action,
|
||||||
},
|
},
|
||||||
{ autoBind: true },
|
{ autoBind: true },
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
makePersistable(this, {
|
makePersistable(this, {
|
||||||
// 在构造函数内使用 makePersistable
|
// 在构造函数内使用 makePersistable
|
||||||
name: 'userStore', // 保存的name,用于在storage中的名称标识,只要不和storage中其他名称重复就可以
|
name: 'token', // 保存的name,用于在storage中的名称标识,只要不和storage中其他名称重复就可以
|
||||||
properties: ['user'], // 要保存的字段,这些字段会被保存在name对应的storage中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get字段例外。get数据会在数据返回后再自动计算
|
properties: ['token'], // 要保存的字段,这些字段会被保存在name对应的storage中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get字段例外。get数据会在数据返回后再自动计算
|
||||||
storage: window.localStorage, // 保存的位置:看自己的业务情况选择,可以是localStorage,sessionstorage
|
storage: handleLocalforage, // 保存的位置:看自己的业务情况选择,可以是localStorage,sessionstorage
|
||||||
// 。。还有一些其他配置参数,例如数据过期时间等等,可以康康文档,像storage这种字段可以配置在全局配置里,详见文档
|
// 。。还有一些其他配置参数,例如数据过期时间等等,可以康康文档,像storage这种字段可以配置在全局配置里,详见文档
|
||||||
}).then(
|
}).then(
|
||||||
action(() => {
|
action(() => {
|
||||||
@@ -25,16 +26,13 @@ export class useUserStore {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
get getUserInfo() {
|
get getToken() {
|
||||||
return this.user
|
return this.token ? this.token : null
|
||||||
}
|
|
||||||
get token() {
|
|
||||||
return this.user ? this.user.token : ''
|
|
||||||
}
|
}
|
||||||
get isHydrated() {
|
get isHydrated() {
|
||||||
return isHydrated(this)
|
return isHydrated(this)
|
||||||
}
|
}
|
||||||
setUserInfo(user: object) {
|
setToken(token: string) {
|
||||||
this.user = user
|
this.token = token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
184
src/utils/axios/request.ts
Normal file
184
src/utils/axios/request.ts
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
|
||||||
|
import { message } from 'antd'
|
||||||
|
import {
|
||||||
|
aesDecrypt,
|
||||||
|
aesEncrypt,
|
||||||
|
get16RandomNum,
|
||||||
|
getRsaKeys,
|
||||||
|
rsaDecrypt,
|
||||||
|
rsaEncrypt,
|
||||||
|
} from '@/utils/encrypt/encrypt.ts'
|
||||||
|
import { handleLocalforage } from '@/utils/localforage'
|
||||||
|
|
||||||
|
let frontPrivateKey: any
|
||||||
|
let afterPublicKey: any
|
||||||
|
async function getAfterPublicKey() {
|
||||||
|
afterPublicKey = await handleLocalforage.getItem('afterPublicKey')
|
||||||
|
}
|
||||||
|
class Request {
|
||||||
|
private instance: AxiosInstance | undefined
|
||||||
|
|
||||||
|
constructor(config: AxiosRequestConfig) {
|
||||||
|
this.instance = axios.create(config)
|
||||||
|
// 全局请求拦截
|
||||||
|
this.instance.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
if (config.headers['isEncrypt']) {
|
||||||
|
if (config.method === 'post' || config.method === 'put') {
|
||||||
|
let privateKey: any
|
||||||
|
let publicKey: any
|
||||||
|
getRsaKeys().then((res: any) => {
|
||||||
|
privateKey = res.privateKey
|
||||||
|
publicKey = res.publicKey
|
||||||
|
})
|
||||||
|
getAfterPublicKey()
|
||||||
|
frontPrivateKey = privateKey
|
||||||
|
|
||||||
|
//每次请求生成aeskey
|
||||||
|
const aesKey = get16RandomNum()
|
||||||
|
if (afterPublicKey) {
|
||||||
|
//用登陆后后端生成并返回给前端的的RSA密钥对的公钥将AES16位密钥进行加密
|
||||||
|
const aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)
|
||||||
|
|
||||||
|
//使用AES16位的密钥将请求报文加密(使用的是加密前的aes密钥)
|
||||||
|
if (config.data) {
|
||||||
|
const data = aesEncrypt(aesKey, JSON.stringify(config.data))
|
||||||
|
config.data = {
|
||||||
|
data: data,
|
||||||
|
aeskey: aesKeyByRsa,
|
||||||
|
frontPublicKey: publicKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.params) {
|
||||||
|
const data = aesEncrypt(aesKey, JSON.stringify(config.params))
|
||||||
|
config.params = {
|
||||||
|
params: data,
|
||||||
|
aeskey: aesKeyByRsa,
|
||||||
|
frontPublicKey: publicKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全局响应拦截
|
||||||
|
this.instance.interceptors.response.use(
|
||||||
|
(res) => {
|
||||||
|
if (res.data.code && res.data.code !== 200) {
|
||||||
|
message.error(res.data.message)
|
||||||
|
return Promise.reject(res.data)
|
||||||
|
}
|
||||||
|
//后端返回的通过rsa加密后的aes密钥
|
||||||
|
const aesKeyByRsa: any = res.data.aesKeyByRsa
|
||||||
|
if (aesKeyByRsa) {
|
||||||
|
localStorage.setItem('afterPublicKey', aesKeyByRsa)
|
||||||
|
//通过rsa的私钥对后端返回的加密的aeskey进行解密
|
||||||
|
const aesKey: any = rsaDecrypt(aesKeyByRsa, frontPrivateKey)
|
||||||
|
//使用解密后的aeskey对加密的返回报文进行解密
|
||||||
|
res.data = JSON.parse(JSON.parse(aesDecrypt(aesKey, res.data)))
|
||||||
|
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
return res.data
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
const { response } = error
|
||||||
|
if (response) {
|
||||||
|
this.handleCode(response.status)
|
||||||
|
}
|
||||||
|
if (!window.navigator.onLine) {
|
||||||
|
message.error('网络连接失败')
|
||||||
|
// return router.push({
|
||||||
|
// path: '/404',
|
||||||
|
// })
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
handleCode(code: number): void {
|
||||||
|
switch (code) {
|
||||||
|
case 400:
|
||||||
|
message.error('请求错误(400)')
|
||||||
|
break
|
||||||
|
case 401:
|
||||||
|
message.error('未授权,请重新登录(401)')
|
||||||
|
break
|
||||||
|
case 403:
|
||||||
|
message.error('拒绝访问(403)')
|
||||||
|
break
|
||||||
|
case 404:
|
||||||
|
message.error('请求出错(404)')
|
||||||
|
break
|
||||||
|
case 408:
|
||||||
|
message.error('请求超时(408)')
|
||||||
|
break
|
||||||
|
case 500:
|
||||||
|
message.error('服务器错误(500)')
|
||||||
|
break
|
||||||
|
case 501:
|
||||||
|
message.error('服务未实现(501)')
|
||||||
|
break
|
||||||
|
case 502:
|
||||||
|
message.error('网络错误(502)')
|
||||||
|
break
|
||||||
|
case 503:
|
||||||
|
message.error('服务不可用(503)')
|
||||||
|
break
|
||||||
|
case 504:
|
||||||
|
message.error('网络超时(504)')
|
||||||
|
break
|
||||||
|
case 505:
|
||||||
|
message.error('HTTP版本不受支持(505)')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
message.error(`连接出错(${code})!`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request<T>(config: AxiosRequestConfig<T>): Promise<T> {
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
this.instance
|
||||||
|
?.request<any, T>(config)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get(url: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.instance
|
||||||
|
?.get(url)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
post(url: string, data = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.instance
|
||||||
|
?.post(url, data)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Request
|
@@ -1,16 +1,30 @@
|
|||||||
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
|
import axios, {
|
||||||
|
AxiosError,
|
||||||
|
AxiosInstance,
|
||||||
|
AxiosRequestConfig,
|
||||||
|
AxiosResponse,
|
||||||
|
InternalAxiosRequestConfig,
|
||||||
|
} from 'axios'
|
||||||
import { message } from 'antd'
|
import { message } from 'antd'
|
||||||
|
import router from '@/router'
|
||||||
|
// import { aesEncrypt, get16RandomNum, getRsaKeys, rsaEncrypt } from '@/utils/encrypt/encrypt.ts'
|
||||||
// 数据返回的接口
|
// 数据返回的接口
|
||||||
// 定义请求响应参数,不含data
|
// 定义请求响应参数,不含data
|
||||||
interface Result {
|
// interface Result {
|
||||||
code: number
|
// code: number
|
||||||
msg: string
|
// msg: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 请求响应参数,包含data
|
// 请求响应参数,包含data
|
||||||
interface ResultData<T = never> extends Result {
|
// interface ResultData<T = never> extends Result {
|
||||||
data?: T
|
// data?: T
|
||||||
|
// }
|
||||||
|
interface UploadFileItemModel {
|
||||||
|
name: string
|
||||||
|
value: string | Blob
|
||||||
}
|
}
|
||||||
|
type UploadRequestConfig = Omit<AxiosRequestConfig, 'url' | 'data'>
|
||||||
|
|
||||||
const URL = import.meta.env.VITE_API_BASE_URL
|
const URL = import.meta.env.VITE_API_BASE_URL
|
||||||
enum RequestEnums {
|
enum RequestEnums {
|
||||||
TIMEOUT = 20000,
|
TIMEOUT = 20000,
|
||||||
@@ -26,6 +40,7 @@ const config = {
|
|||||||
// 跨域时候允许携带凭证
|
// 跨域时候允许携带凭证
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
}
|
}
|
||||||
|
// let frontPrivateKey :string = ''
|
||||||
class RequestHttp {
|
class RequestHttp {
|
||||||
// 定义成员变量并指定类型
|
// 定义成员变量并指定类型
|
||||||
service: AxiosInstance
|
service: AxiosInstance
|
||||||
@@ -37,7 +52,8 @@ class RequestHttp {
|
|||||||
* 客户端发送请求 -> [请求拦截器] -> 服务器
|
* 客户端发送请求 -> [请求拦截器] -> 服务器
|
||||||
*/
|
*/
|
||||||
this.service.interceptors.request.use(
|
this.service.interceptors.request.use(
|
||||||
(config: AxiosRequestConfig | any) => {
|
(config: InternalAxiosRequestConfig | any) => {
|
||||||
|
if (localStorage.getItem('token')) {
|
||||||
const token = localStorage.getItem('token') || ''
|
const token = localStorage.getItem('token') || ''
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
@@ -45,10 +61,40 @@ class RequestHttp {
|
|||||||
'x-access-token': token, // 请求头中携带token信息
|
'x-access-token': token, // 请求头中携带token信息
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// if (config.headers['isEncrypt']) {
|
||||||
|
// config.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
|
// if (config.method === 'post' || config.method === 'put') {
|
||||||
|
// const { privateKey, publicKey } = await getRsaKeys()
|
||||||
|
// const afterPublicKey = sessionStorage.getItem('afterPublicKey')
|
||||||
|
// frontPrivateKey = privateKey
|
||||||
|
// //每次请求生成aeskey
|
||||||
|
// const aesKey = get16RandomNum()
|
||||||
|
// //用登陆后后端生成并返回给前端的的RSA密钥对的公钥将AES16位密钥进行加密
|
||||||
|
// const aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)
|
||||||
|
// //使用AES16位的密钥将请求报文加密(使用的是加密前的aes密钥)
|
||||||
|
// if (config.data) {
|
||||||
|
// const data = aesEncrypt(aesKey, JSON.stringify(config.data))
|
||||||
|
// config.data = {
|
||||||
|
// data: data,
|
||||||
|
// aesKey: aesKeyByRsa,
|
||||||
|
// frontPublicKey: publicKey,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (config.params) {
|
||||||
|
// const data = aesEncrypt(aesKey, JSON.stringify(config.params))
|
||||||
|
// config.params = {
|
||||||
|
// params: data,
|
||||||
|
// aesKey: aesKeyByRsa,
|
||||||
|
// frontPublicKey: publicKey,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
(error: AxiosError) => {
|
(error: AxiosError) => {
|
||||||
// 请求报错
|
// 请求报错
|
||||||
Promise.reject(error)
|
return Promise.reject(error)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,9 +127,9 @@ class RequestHttp {
|
|||||||
if (!window.navigator.onLine) {
|
if (!window.navigator.onLine) {
|
||||||
message.error('网络连接失败')
|
message.error('网络连接失败')
|
||||||
// 可以跳转到错误页面,也可以不做操作
|
// 可以跳转到错误页面,也可以不做操作
|
||||||
// return router.replace({
|
return router.push({
|
||||||
// path: '/404'
|
path: '/404',
|
||||||
// });
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -128,18 +174,59 @@ class RequestHttp {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request<T = any>(config: AxiosRequestConfig): Promise<T> {
|
||||||
|
/**
|
||||||
|
* TODO: execute other methods according to config
|
||||||
|
*/
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this.service
|
||||||
|
.request<T>(config)
|
||||||
|
.then((res: any) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
return Promise.reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
// 常用方法封装
|
// 常用方法封装
|
||||||
get<T>(url: string, params?: object): Promise<ResultData<T>> {
|
get<T = any>(config: AxiosRequestConfig): Promise<T> {
|
||||||
return this.service.get(url, { params })
|
return this.request({ method: 'GET', ...config })
|
||||||
}
|
}
|
||||||
post<T>(url: string, params?: object): Promise<ResultData<T>> {
|
post<T = any>(config: AxiosRequestConfig): Promise<T> {
|
||||||
return this.service.post(url, params)
|
return this.request({ method: 'POST', ...config })
|
||||||
}
|
}
|
||||||
put<T>(url: string, params?: object): Promise<ResultData<T>> {
|
put<T = any>(config: AxiosRequestConfig): Promise<T> {
|
||||||
return this.service.put(url, params)
|
return this.request({ method: 'PUT', ...config })
|
||||||
}
|
}
|
||||||
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
|
delete<T = any>(config: AxiosRequestConfig): Promise<T> {
|
||||||
return this.service.delete(url, { params })
|
return this.request({ method: 'DELETE', ...config })
|
||||||
|
}
|
||||||
|
upload<T = string>(
|
||||||
|
fileItem: UploadFileItemModel,
|
||||||
|
config?: UploadRequestConfig,
|
||||||
|
): Promise<T> | null {
|
||||||
|
if (!import.meta.env.VITE_UPLOAD_URL) return null
|
||||||
|
|
||||||
|
const fd = new FormData()
|
||||||
|
fd.append(fileItem.name, fileItem.value)
|
||||||
|
let configCopy: UploadRequestConfig
|
||||||
|
if (!config) {
|
||||||
|
configCopy = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config.headers!['Content-Type'] = 'multipart/form-data'
|
||||||
|
configCopy = config
|
||||||
|
}
|
||||||
|
return this.request({ url: import.meta.env.VITE_UPLOAD_URL, data: fd, ...configCopy })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 导出一个实例对象
|
// 导出一个实例对象
|
||||||
|
14
src/utils/axios/web.ts
Normal file
14
src/utils/axios/web.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Request from './request'
|
||||||
|
import { handleLocalforage } from '@/utils/localforage'
|
||||||
|
|
||||||
|
const token = String(handleLocalforage.getItem('token'))
|
||||||
|
const web: Request = new Request({
|
||||||
|
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default web
|
169
src/utils/encrypt/encrypt.ts
Normal file
169
src/utils/encrypt/encrypt.ts
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import JSEncrypt from 'jsencrypt'
|
||||||
|
import CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
export function rsaEncrypt(Str: string, afterPublicKey: string) {
|
||||||
|
const encryptor = new JSEncrypt()
|
||||||
|
encryptor.setPublicKey(afterPublicKey) // 设置公钥
|
||||||
|
return encryptor.encrypt(Str) // 对数据进行加密
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
export function rsaDecrypt(Str: string, frontPrivateKey: string) {
|
||||||
|
const encryptor = new JSEncrypt()
|
||||||
|
encryptor.setPrivateKey(frontPrivateKey) // 设置私钥
|
||||||
|
return encryptor.decrypt(Str) // 对数据进行解密
|
||||||
|
}
|
||||||
|
|
||||||
|
export function aesEncrypt(aeskey: string, Str: string) {
|
||||||
|
// 设置一个默认值,如果第二个参数为空采用默认值,不为空则采用新设置的密钥
|
||||||
|
const key = CryptoJS.enc.Utf8.parse(aeskey)
|
||||||
|
const srcs = CryptoJS.enc.Utf8.parse(Str)
|
||||||
|
const encrypted = CryptoJS.AES.encrypt(srcs, key, {
|
||||||
|
// 切记 需要和后端算法模式一致
|
||||||
|
mode: CryptoJS.mode.ECB,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
})
|
||||||
|
|
||||||
|
return encrypted.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function aesDecrypt(aeskey: string, Str: string) {
|
||||||
|
const key = CryptoJS.enc.Utf8.parse(aeskey)
|
||||||
|
const decrypt = CryptoJS.AES.decrypt(Str, key, {
|
||||||
|
// 切记 需要和后端算法模式一致
|
||||||
|
mode: CryptoJS.mode.ECB,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
})
|
||||||
|
return CryptoJS.enc.Utf8.stringify(decrypt).toString()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取16位随机码AES
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function get16RandomNum() {
|
||||||
|
const chars = [
|
||||||
|
'0',
|
||||||
|
'1',
|
||||||
|
'2',
|
||||||
|
'3',
|
||||||
|
'4',
|
||||||
|
'5',
|
||||||
|
'6',
|
||||||
|
'7',
|
||||||
|
'8',
|
||||||
|
'9',
|
||||||
|
'A',
|
||||||
|
'B',
|
||||||
|
'C',
|
||||||
|
'D',
|
||||||
|
'E',
|
||||||
|
'F',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'I',
|
||||||
|
'J',
|
||||||
|
'K',
|
||||||
|
'L',
|
||||||
|
'M',
|
||||||
|
'N',
|
||||||
|
'O',
|
||||||
|
'P',
|
||||||
|
'Q',
|
||||||
|
'R',
|
||||||
|
'S',
|
||||||
|
'T',
|
||||||
|
'U',
|
||||||
|
'V',
|
||||||
|
'W',
|
||||||
|
'X',
|
||||||
|
'Y',
|
||||||
|
'Z',
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'd',
|
||||||
|
'e',
|
||||||
|
'f',
|
||||||
|
'g',
|
||||||
|
'h',
|
||||||
|
'i',
|
||||||
|
'j',
|
||||||
|
'k',
|
||||||
|
'l',
|
||||||
|
'm',
|
||||||
|
'n',
|
||||||
|
'o',
|
||||||
|
'p',
|
||||||
|
'q',
|
||||||
|
'r',
|
||||||
|
's',
|
||||||
|
't',
|
||||||
|
'u',
|
||||||
|
'v',
|
||||||
|
'w',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
|
'z',
|
||||||
|
]
|
||||||
|
let nums = ''
|
||||||
|
//这个地方切记要选择16位,因为美国对密钥长度有限制,选择32位的话加解密会报错,需要根据jdk版本去修改相关jar包,有点恼火,选择16位就不用处理。
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
const id = parseInt(String(Math.random() * 61))
|
||||||
|
nums += chars[id]
|
||||||
|
}
|
||||||
|
return nums
|
||||||
|
}
|
||||||
|
//获取rsa密钥对
|
||||||
|
export function getRsaKeys() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
window.crypto.subtle
|
||||||
|
.generateKey(
|
||||||
|
{
|
||||||
|
name: 'RSA-OAEP',
|
||||||
|
modulusLength: 2048, //can be 1024, 2048, or 4096
|
||||||
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
|
hash: { name: 'SHA-512' }, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
||||||
|
},
|
||||||
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
|
['encrypt', 'decrypt'], //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
|
||||||
|
)
|
||||||
|
.then(function (key) {
|
||||||
|
window.crypto.subtle
|
||||||
|
.exportKey('pkcs8', key.privateKey)
|
||||||
|
.then(function (keydata1) {
|
||||||
|
window.crypto.subtle
|
||||||
|
.exportKey('spki', key.publicKey)
|
||||||
|
.then(function (keydata2) {
|
||||||
|
const privateKey = RSA2text(keydata1, 1)
|
||||||
|
|
||||||
|
const publicKey = RSA2text(keydata2)
|
||||||
|
|
||||||
|
resolve({ privateKey, publicKey })
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function RSA2text(buffer: any, _isPrivate: number = 0) {
|
||||||
|
let binary = ''
|
||||||
|
const bytes = new Uint8Array(buffer)
|
||||||
|
const len = bytes.byteLength
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i])
|
||||||
|
}
|
||||||
|
const base64 = window.btoa(binary)
|
||||||
|
const text = base64.replace(/[^\x00-\xff]/g, '$&\x01').replace(/.{64}\x01?/g, '$&\n')
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
@@ -1,96 +1,96 @@
|
|||||||
import { encrypt, decrypt } from './encry';
|
import { encrypt, decrypt } from './encry'
|
||||||
import { globalConfig } from './interface';
|
import { globalConfig } from './interface'
|
||||||
|
|
||||||
const config: globalConfig = {
|
const config: globalConfig = {
|
||||||
type: 'localStorage', //存储类型,localStorage | sessionStorage
|
type: 'localStorage', //存储类型,localStorage | sessionStorage
|
||||||
prefix: 'react-view-ui_0.0.1', //版本号
|
prefix: 'schisandra_', //版本号
|
||||||
expire: 24 * 60, //过期时间,默认为一天,单位为分钟
|
expire: 24 * 60, //过期时间,默认为一天,单位为分钟
|
||||||
isEncrypt: true, //支持加密、解密数据处理
|
isEncrypt: true, //支持加密、解密数据处理
|
||||||
};
|
}
|
||||||
|
|
||||||
const setStorage = (key: string, value: any, expire: number = 24 * 60): boolean => {
|
const setStorage = (key: string, value: any, expire: number = 24 * 60): boolean => {
|
||||||
//设定值
|
//设定值
|
||||||
if (value === '' || value === null || value === undefined) {
|
if (value === '' || value === null || value === undefined) {
|
||||||
//空值重置
|
//空值重置
|
||||||
value = null;
|
value = null
|
||||||
}
|
}
|
||||||
if (isNaN(expire) || expire < 0) {
|
if (isNaN(expire) || expire < 0) {
|
||||||
//过期时间值合理性判断
|
//过期时间值合理性判断
|
||||||
throw new Error('Expire must be a number');
|
throw new Error('Expire must be a number')
|
||||||
}
|
}
|
||||||
const data = {
|
const data = {
|
||||||
value, //存储值
|
value, //存储值
|
||||||
time: Date.now(), //存储日期
|
time: Date.now(), //存储日期
|
||||||
expire: Date.now() + 1000 * 60 * expire, //过期时间
|
expire: Date.now() + 1000 * 60 * expire, //过期时间
|
||||||
};
|
}
|
||||||
//是否需要加密,判断装载加密数据或原数据
|
//是否需要加密,判断装载加密数据或原数据
|
||||||
window[config.type].setItem(
|
window[config.type].setItem(
|
||||||
autoAddPreFix(key),
|
autoAddPreFix(key),
|
||||||
config.isEncrypt ? encrypt(JSON.stringify(data)) : JSON.stringify(data),
|
config.isEncrypt ? encrypt(JSON.stringify(data)) : JSON.stringify(data),
|
||||||
);
|
)
|
||||||
return true;
|
return true
|
||||||
};
|
}
|
||||||
|
|
||||||
const getStorageFromKey = (key: string) => {
|
const getStorageFromKey = (key: string) => {
|
||||||
//获取指定值
|
//获取指定值
|
||||||
if (config.prefix) {
|
if (config.prefix) {
|
||||||
key = autoAddPreFix(key);
|
key = autoAddPreFix(key)
|
||||||
}
|
}
|
||||||
if (!window[config.type].getItem(key)) {
|
if (!window[config.type].getItem(key)) {
|
||||||
//不存在判断
|
//不存在判断
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const storageVal = config.isEncrypt
|
const storageVal = config.isEncrypt
|
||||||
? JSON.parse(decrypt(window[config.type].getItem(key) as string))
|
? JSON.parse(decrypt(window[config.type].getItem(key) as string))
|
||||||
: JSON.parse(window[config.type].getItem(key) as string);
|
: JSON.parse(window[config.type].getItem(key) as string)
|
||||||
const now = Date.now();
|
const now = Date.now()
|
||||||
if (now >= storageVal.expire) {
|
if (now >= storageVal.expire) {
|
||||||
//过期销毁
|
//过期销毁
|
||||||
removeStorageFromKey(key);
|
removeStorageFromKey(key)
|
||||||
return null;
|
return null
|
||||||
//不过期回值
|
//不过期回值
|
||||||
} else {
|
} else {
|
||||||
return storageVal.value;
|
return storageVal.value
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
const getAllStorage = () => {
|
const getAllStorage = () => {
|
||||||
//获取所有值
|
//获取所有值
|
||||||
const storageList: any = {};
|
const storageList: any = {}
|
||||||
const keys = Object.keys(window[config.type]);
|
const keys = Object.keys(window[config.type])
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
const value = getStorageFromKey(autoRemovePreFix(key));
|
const value = getStorageFromKey(autoRemovePreFix(key))
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
//如果值没有过期,加入到列表中
|
//如果值没有过期,加入到列表中
|
||||||
storageList[autoRemovePreFix(key)] = value;
|
storageList[autoRemovePreFix(key)] = value
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return storageList;
|
return storageList
|
||||||
};
|
}
|
||||||
const getStorageLength = () => {
|
const getStorageLength = () => {
|
||||||
//获取值列表长度
|
//获取值列表长度
|
||||||
return window[config.type].length;
|
return window[config.type].length
|
||||||
};
|
}
|
||||||
const removeStorageFromKey = (key: string) => {
|
const removeStorageFromKey = (key: string) => {
|
||||||
//删除值
|
//删除值
|
||||||
if (config.prefix) {
|
if (config.prefix) {
|
||||||
key = autoAddPreFix(key);
|
key = autoAddPreFix(key)
|
||||||
}
|
}
|
||||||
window[config.type].removeItem(key);
|
window[config.type].removeItem(key)
|
||||||
};
|
}
|
||||||
const clearStorage = () => {
|
const clearStorage = () => {
|
||||||
window[config.type].clear();
|
window[config.type].clear()
|
||||||
};
|
}
|
||||||
const autoAddPreFix = (key: string) => {
|
const autoAddPreFix = (key: string) => {
|
||||||
//添加前缀,保持唯一性
|
//添加前缀,保持唯一性
|
||||||
const prefix = config.prefix || '';
|
const prefix = config.prefix || ''
|
||||||
return `${prefix}_${key}`;
|
return `${prefix}_${key}`
|
||||||
};
|
}
|
||||||
const autoRemovePreFix = (key: string) => {
|
const autoRemovePreFix = (key: string) => {
|
||||||
//删除前缀,进行增删改查
|
//删除前缀,进行增删改查
|
||||||
const lineIndex = config.prefix.length + 1;
|
const lineIndex = config.prefix.length + 1
|
||||||
return key.substr(lineIndex);
|
return key.substr(lineIndex)
|
||||||
};
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
setStorage,
|
setStorage,
|
||||||
@@ -99,4 +99,4 @@ export {
|
|||||||
getStorageLength,
|
getStorageLength,
|
||||||
removeStorageFromKey,
|
removeStorageFromKey,
|
||||||
clearStorage,
|
clearStorage,
|
||||||
};
|
}
|
||||||
|
74
src/utils/localforage/index.ts
Normal file
74
src/utils/localforage/index.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import localforage from 'localforage'
|
||||||
|
import CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
|
const SECRET_KEY = CryptoJS.enc.Utf8.parse('3333e6e143439161') //十六位十六进制数作为密钥
|
||||||
|
const SECRET_IV = CryptoJS.enc.Utf8.parse('e3bbe7e3ba84431a') //十六位十六进制数作为密钥偏移量
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
* @param data
|
||||||
|
* @param output
|
||||||
|
*/
|
||||||
|
export const encrypt = (data: string, output?: any) => {
|
||||||
|
const dataHex = CryptoJS.enc.Utf8.parse(data)
|
||||||
|
const encrypted = CryptoJS.AES.encrypt(dataHex, SECRET_KEY, {
|
||||||
|
iv: SECRET_IV,
|
||||||
|
mode: CryptoJS.mode.CBC,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
})
|
||||||
|
return encrypted.ciphertext.toString(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const decrypt = (data: string | null) => {
|
||||||
|
if (data === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const encryptedHex = CryptoJS.enc.Hex.parse(data)
|
||||||
|
const encryptedHexStr = CryptoJS.enc.Base64.stringify(encryptedHex)
|
||||||
|
const decrypted = CryptoJS.AES.decrypt(encryptedHexStr, SECRET_KEY, {
|
||||||
|
iv: SECRET_IV,
|
||||||
|
mode: CryptoJS.mode.CBC,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
})
|
||||||
|
const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8)
|
||||||
|
return decryptedStr.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleLocalforage = {
|
||||||
|
config: async (options?: LocalForageOptions) => localforage.config(options || {}),
|
||||||
|
setItem: async (key: string, value: string): Promise<void> => {
|
||||||
|
await localforage.setItem(key, encrypt(value))
|
||||||
|
},
|
||||||
|
getItem: async function getItem<T>(key: string): Promise<T | string | null> {
|
||||||
|
try {
|
||||||
|
const value: any = decrypt(await localforage.getItem(key)) as any
|
||||||
|
// 如果值是 undefined,返回 null
|
||||||
|
if (value === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// 如果值是 T 类型,直接返回
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
return value as T
|
||||||
|
}
|
||||||
|
// 如果值是 string 类型,直接返回
|
||||||
|
return value as string
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error retrieving data from localforage:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItem: async (key: string): Promise<void> => {
|
||||||
|
await localforage.removeItem(key)
|
||||||
|
},
|
||||||
|
clear: async () => {
|
||||||
|
return await localforage.clear()
|
||||||
|
},
|
||||||
|
createInstance: async (name: string) => {
|
||||||
|
return localforage.createInstance({
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
@@ -1,17 +1,16 @@
|
|||||||
// import FileSharing from '@/components/FileSharing'
|
|
||||||
// import DefaultLayOut from '@/layout/default'
|
|
||||||
|
|
||||||
import HomeIndex from '@/components/HomeIndex'
|
import HomeIndex from '@/components/HomeIndex'
|
||||||
|
import { useEffect } from 'react'
|
||||||
// import Loading from '@/components/Loading'
|
import { handleLocalforage } from '@/utils/localforage'
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
useEffect(() => {
|
||||||
|
handleLocalforage.getItem('token').then((res: any) => {
|
||||||
|
console.log(JSON.parse(res))
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/*<DefaultLayOut />*/}
|
|
||||||
{/*<BlurCard />*/}
|
|
||||||
<HomeIndex />
|
<HomeIndex />
|
||||||
{/*<Loading />*/}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
2
src/vite-env.d.ts
vendored
2
src/vite-env.d.ts
vendored
@@ -6,6 +6,8 @@ declare interface ImportMetaEnv {
|
|||||||
readonly VITE_API_BASE_URL: string
|
readonly VITE_API_BASE_URL: string
|
||||||
readonly VITE_NODE_ENV: string
|
readonly VITE_NODE_ENV: string
|
||||||
readonly VITE_TITLE_NAME: string
|
readonly VITE_TITLE_NAME: string
|
||||||
|
readonly VITE_APP_TOKEN_KEY?: string
|
||||||
|
readonly VITE_UPLOAD_URL?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
@@ -159,5 +159,16 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
[env.VITE_APP_BASE_API]: {
|
||||||
|
//后端接口的baseurl
|
||||||
|
target: env.VITE_API_BASE_URL,
|
||||||
|
//是否允许跨域
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(RegExp(`^${env.VITE_APP_BASE_API}`), ''),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user