perf: 同步完整版分支代码
This commit is contained in:
parent
e9dc8274a0
commit
f6057458de
14
.env
14
.env
@ -1,14 +1,2 @@
|
||||
# port
|
||||
# 项目本地运行端口号
|
||||
VITE_PORT = 8848
|
||||
# title
|
||||
VITE_TITLE = vue-pure-admin
|
||||
# version
|
||||
VITE_VERSION = 2.6.0
|
||||
# open
|
||||
VITE_OPEN = false
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# Cross-domain proxy, you can configure multiple
|
||||
VITE_PROXY = [ ["/api", "http://127.0.0.1:3000" ] ]
|
||||
|
@ -1,14 +1,12 @@
|
||||
# port
|
||||
# 项目本地运行端口号
|
||||
VITE_PORT = 8848
|
||||
# title
|
||||
VITE_TITLE = vue-pure-admin
|
||||
# version
|
||||
VITE_VERSION = 2.6.0
|
||||
# open
|
||||
VITE_OPEN = false
|
||||
|
||||
# public path
|
||||
# 开发环境读取配置文件路径
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# Cross-domain proxy, you can configure multiple
|
||||
VITE_PROXY = [ ["/api", "http://127.0.0.1:3000" ] ]
|
||||
# 开发环境代理
|
||||
VITE_PROXY_DOMAIN = /api
|
||||
|
||||
# 开发环境后端地址
|
||||
VITE_PROXY_DOMAIN_REAL = "http://127.0.0.1:3000"
|
||||
|
||||
|
@ -1,2 +1,5 @@
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /manages/
|
||||
# 线上环境项目打包路径
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 线上环境后端地址
|
||||
VITE_PROXY_DOMAIN_REAL = ""
|
||||
|
@ -70,6 +70,12 @@ module.exports = {
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_"
|
||||
}
|
||||
],
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
endOfLine: "auto"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -8,6 +8,10 @@
|
||||
|
||||
The lite version is based on the shelf extracted from https://github.com/xiaoxian521/vue-pure-admin, which contains the main functions and is more suitable for actual project development
|
||||
|
||||
## Docs
|
||||
|
||||
<https://pure-admin-doc.vercel.app/>
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation dependencies
|
||||
|
@ -8,6 +8,10 @@
|
||||
|
||||
精简版是基于 https://github.com/xiaoxian521/vue-pure-admin 提炼出的架子,包含主体功能,更适合实际项目开发
|
||||
|
||||
## 文档
|
||||
|
||||
<https://pure-admin-doc.vercel.app/>
|
||||
|
||||
## 用法
|
||||
|
||||
### 安装依赖
|
||||
|
@ -1,5 +1,12 @@
|
||||
// 处理环境变量
|
||||
const warpperEnv = (envConf: Recordable): ViteEnv => {
|
||||
const ret: any = {};
|
||||
// 此处为默认值,无需修改
|
||||
const ret: ViteEnv = {
|
||||
VITE_PORT: 8848,
|
||||
VITE_PUBLIC_PATH: "",
|
||||
VITE_PROXY_DOMAIN: "",
|
||||
VITE_PROXY_DOMAIN_REAL: ""
|
||||
};
|
||||
|
||||
for (const envName of Object.keys(envConf)) {
|
||||
let realName = envConf[envName].replace(/\\n/g, "\n");
|
||||
@ -9,13 +16,6 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
|
||||
if (envName === "VITE_PORT") {
|
||||
realName = Number(realName);
|
||||
}
|
||||
if (envName === "VITE_PROXY" && realName) {
|
||||
try {
|
||||
realName = JSON.parse(realName.replace(/'/g, '"'));
|
||||
} catch (error) {
|
||||
realName = "";
|
||||
}
|
||||
}
|
||||
ret[envName] = realName;
|
||||
if (typeof realName === "string") {
|
||||
process.env[envName] = realName;
|
||||
@ -25,8 +25,15 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
// 跨域代理重写
|
||||
const regExps = (value: string, reg: string): string => {
|
||||
return value.replace(new RegExp(reg, "g"), "");
|
||||
};
|
||||
|
||||
// 环境变量
|
||||
const loadEnv = (): ViteEnv => {
|
||||
return import.meta.env;
|
||||
};
|
||||
|
||||
export { loadEnv, warpperEnv };
|
||||
export { warpperEnv, regExps, loadEnv };
|
@ -1,19 +0,0 @@
|
||||
type ProxyItem = [string, string];
|
||||
|
||||
type ProxyList = ProxyItem[];
|
||||
|
||||
const regExps = (value: string, reg: string): string => {
|
||||
return value.replace(new RegExp(reg, "g"), "");
|
||||
};
|
||||
|
||||
export function createProxy(list: ProxyList = []) {
|
||||
const ret: any = {};
|
||||
for (const [prefix, target] of list) {
|
||||
ret[prefix] = {
|
||||
target: target,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => regExps(path, prefix)
|
||||
};
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -41,6 +41,7 @@
|
||||
"element-plus": "1.2.0-beta.3",
|
||||
"element-resize-detector": "^1.2.3",
|
||||
"font-awesome": "^4.7.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lowdb": "^3.0.0",
|
||||
"mitt": "^3.0.0",
|
||||
@ -49,6 +50,7 @@
|
||||
"path": "^0.12.7",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "^2.0.0-rc.14",
|
||||
"qs": "^6.10.2",
|
||||
"remixicon": "^2.5.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"responsive-storage": "^1.0.11",
|
||||
@ -62,9 +64,11 @@
|
||||
"@commitlint/cli": "13.1.0",
|
||||
"@commitlint/config-conventional": "13.1.0",
|
||||
"@types/element-resize-detector": "1.1.3",
|
||||
"@types/js-cookie": "^3.0.1",
|
||||
"@types/mockjs": "1.0.3",
|
||||
"@types/node": "14.14.14",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@typescript-eslint/eslint-plugin": "4.31.0",
|
||||
"@typescript-eslint/parser": "4.31.0",
|
||||
"@vitejs/plugin-vue": "^1.9.4",
|
||||
|
89
pnpm-lock.yaml
generated
89
pnpm-lock.yaml
generated
@ -8,9 +8,11 @@ specifiers:
|
||||
"@fortawesome/free-solid-svg-icons": ^5.15.4
|
||||
"@fortawesome/vue-fontawesome": ^3.0.0-5
|
||||
"@types/element-resize-detector": 1.1.3
|
||||
"@types/js-cookie": ^3.0.1
|
||||
"@types/mockjs": 1.0.3
|
||||
"@types/node": 14.14.14
|
||||
"@types/nprogress": 0.2.0
|
||||
"@types/qs": ^6.9.7
|
||||
"@typescript-eslint/eslint-plugin": 4.31.0
|
||||
"@typescript-eslint/parser": 4.31.0
|
||||
"@vitejs/plugin-vue": ^1.9.4
|
||||
@ -36,6 +38,7 @@ specifiers:
|
||||
eslint-plugin-vue: 7.17.0
|
||||
font-awesome: ^4.7.0
|
||||
husky: 7.0.2
|
||||
js-cookie: ^3.0.1
|
||||
lint-staged: 11.1.2
|
||||
lodash-es: ^4.17.21
|
||||
lowdb: ^3.0.0
|
||||
@ -49,6 +52,7 @@ specifiers:
|
||||
postcss-import: 14.0.0
|
||||
prettier: 2.3.2
|
||||
pretty-quick: 3.1.1
|
||||
qs: ^6.10.2
|
||||
remixicon: ^2.5.0
|
||||
resize-observer-polyfill: ^1.5.1
|
||||
responsive-storage: ^1.0.11
|
||||
@ -86,6 +90,7 @@ dependencies:
|
||||
element-plus: 1.2.0-beta.3_vue@3.2.21
|
||||
element-resize-detector: 1.2.3
|
||||
font-awesome: 4.7.0
|
||||
js-cookie: 3.0.1
|
||||
lodash-es: 4.17.21
|
||||
lowdb: 3.0.0
|
||||
mitt: 3.0.0
|
||||
@ -94,6 +99,7 @@ dependencies:
|
||||
path: 0.12.7
|
||||
path-to-regexp: 6.2.0
|
||||
pinia: 2.0.2_typescript@4.4.2+vue@3.2.21
|
||||
qs: 6.10.2
|
||||
remixicon: 2.5.0
|
||||
resize-observer-polyfill: 1.5.1
|
||||
responsive-storage: 1.0.11_vue@3.2.21
|
||||
@ -107,9 +113,11 @@ devDependencies:
|
||||
"@commitlint/cli": 13.1.0
|
||||
"@commitlint/config-conventional": 13.1.0
|
||||
"@types/element-resize-detector": 1.1.3
|
||||
"@types/js-cookie": 3.0.1
|
||||
"@types/mockjs": 1.0.3
|
||||
"@types/node": 14.14.14
|
||||
"@types/nprogress": 0.2.0
|
||||
"@types/qs": 6.9.7
|
||||
"@typescript-eslint/eslint-plugin": 4.31.0_f4e6dc0776b3600ef484e3c64a523136
|
||||
"@typescript-eslint/parser": 4.31.0_eslint@7.30.0+typescript@4.4.2
|
||||
"@vitejs/plugin-vue": 1.9.4_vite@2.6.14
|
||||
@ -1017,6 +1025,13 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/js-cookie/3.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7wg/8gfHltklehP+oyJnZrz9XBuX5ZPP4zB6UsI84utdlkRYLnOm2HfpLXazTwZA+fpGn0ir8tGNgVnMEleBGQ==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema/7.0.9:
|
||||
resolution:
|
||||
{
|
||||
@ -1089,6 +1104,13 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/qs/6.9.7:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/resolve/1.17.1:
|
||||
resolution:
|
||||
{
|
||||
@ -1870,6 +1892,16 @@ packages:
|
||||
engines: { node: ">=6" }
|
||||
dev: true
|
||||
|
||||
/call-bind/1.0.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
|
||||
}
|
||||
dependencies:
|
||||
function-bind: 1.1.1
|
||||
get-intrinsic: 1.1.1
|
||||
dev: false
|
||||
|
||||
/callsites/3.1.0:
|
||||
resolution:
|
||||
{
|
||||
@ -3379,7 +3411,6 @@ packages:
|
||||
{
|
||||
integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/functional-red-black-tree/1.0.1:
|
||||
resolution: { integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= }
|
||||
@ -3401,6 +3432,17 @@ packages:
|
||||
engines: { node: 6.* || 8.* || >= 10.* }
|
||||
dev: true
|
||||
|
||||
/get-intrinsic/1.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
|
||||
}
|
||||
dependencies:
|
||||
function-bind: 1.1.1
|
||||
has: 1.0.3
|
||||
has-symbols: 1.0.2
|
||||
dev: false
|
||||
|
||||
/get-own-enumerable-property-symbols/3.0.2:
|
||||
resolution:
|
||||
{
|
||||
@ -3586,6 +3628,14 @@ packages:
|
||||
engines: { node: ">=8" }
|
||||
dev: true
|
||||
|
||||
/has-symbols/1.0.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
|
||||
}
|
||||
engines: { node: ">= 0.4" }
|
||||
dev: false
|
||||
|
||||
/has/1.0.3:
|
||||
resolution:
|
||||
{
|
||||
@ -3594,7 +3644,6 @@ packages:
|
||||
engines: { node: ">= 0.4.0" }
|
||||
dependencies:
|
||||
function-bind: 1.1.1
|
||||
dev: true
|
||||
|
||||
/hash-sum/2.0.0:
|
||||
resolution:
|
||||
@ -3932,6 +3981,14 @@ packages:
|
||||
resolution: { integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= }
|
||||
dev: true
|
||||
|
||||
/js-cookie/3.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
|
||||
}
|
||||
engines: { node: ">=12" }
|
||||
dev: false
|
||||
|
||||
/js-tokens/4.0.0:
|
||||
resolution:
|
||||
{
|
||||
@ -4576,6 +4633,13 @@ packages:
|
||||
resolution: { integrity: sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= }
|
||||
dev: true
|
||||
|
||||
/object-inspect/1.11.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==
|
||||
}
|
||||
dev: false
|
||||
|
||||
/on-finished/2.3.0:
|
||||
resolution: { integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= }
|
||||
engines: { node: ">= 0.8" }
|
||||
@ -5103,6 +5167,16 @@ packages:
|
||||
engines: { node: ">=0.6.0", teleport: ">=0.2.0" }
|
||||
dev: true
|
||||
|
||||
/qs/6.10.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==
|
||||
}
|
||||
engines: { node: ">=0.6" }
|
||||
dependencies:
|
||||
side-channel: 1.0.4
|
||||
dev: false
|
||||
|
||||
/queue-microtask/1.2.3:
|
||||
resolution:
|
||||
{
|
||||
@ -5469,6 +5543,17 @@ packages:
|
||||
engines: { node: ">=8" }
|
||||
dev: true
|
||||
|
||||
/side-channel/1.0.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
|
||||
}
|
||||
dependencies:
|
||||
call-bind: 1.0.2
|
||||
get-intrinsic: 1.1.1
|
||||
object-inspect: 1.11.1
|
||||
dev: false
|
||||
|
||||
/signal-exit/3.0.5:
|
||||
resolution:
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { http } from "../utils/http";
|
||||
|
||||
export const getAsyncRoutes = (data?: object) => {
|
||||
return http.request("get", "/getAsyncRoutes", data);
|
||||
export const getAsyncRoutes = (params?: object) => {
|
||||
return http.request("get", "/getAsyncRoutes", { params });
|
||||
};
|
||||
|
26
src/api/user.ts
Normal file
26
src/api/user.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { http } from "../utils/http";
|
||||
|
||||
interface userType extends Promise<any> {
|
||||
svg?: string;
|
||||
code?: number;
|
||||
info?: object;
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export const getVerify = (): userType => {
|
||||
return http.request("get", "/captcha");
|
||||
};
|
||||
|
||||
// 登录
|
||||
export const getLogin = (data: object) => {
|
||||
return http.request("post", "/login", { data });
|
||||
};
|
||||
|
||||
// 刷新token
|
||||
export const refreshToken = (data: object) => {
|
||||
return http.request("post", "/refreshToken", { data });
|
||||
};
|
||||
|
||||
// export const searchVague = (data: object) => {
|
||||
// return http.request("post", "/searchVague", { data });
|
||||
// };
|
BIN
src/assets/avatars.jpg
Normal file
BIN
src/assets/avatars.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
@ -1,6 +1,9 @@
|
||||
import { App } from "vue";
|
||||
import axios from "axios";
|
||||
import { loadEnv } from "@build/index";
|
||||
|
||||
let config: object = {};
|
||||
const { VITE_PUBLIC_PATH } = loadEnv();
|
||||
|
||||
const setConfig = (cfg?: unknown) => {
|
||||
config = Object.assign(config, cfg);
|
||||
@ -30,10 +33,7 @@ export const getServerConfig = async (app: App): Promise<undefined> => {
|
||||
return axios({
|
||||
baseURL: "",
|
||||
method: "get",
|
||||
url:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "/manages/serverConfig.json"
|
||||
: "/serverConfig.json"
|
||||
url: `${VITE_PUBLIC_PATH}serverConfig.json`
|
||||
})
|
||||
.then(({ data: config }) => {
|
||||
let $config = app.config.globalProperties.$config;
|
||||
|
@ -75,7 +75,7 @@ const transitionMain = defineComponent({
|
||||
hideTabs && layout ? 'padding-top: 48px;' : '',
|
||||
!hideTabs && layout ? 'padding-top: 85px;' : '',
|
||||
hideTabs && !layout ? 'padding-top: 48px' : '',
|
||||
!hideTabs && !layout ? 'padding-top: 98px;' : ''
|
||||
!hideTabs && !layout ? 'padding-top: 85px;' : ''
|
||||
]"
|
||||
>
|
||||
<router-view>
|
||||
|
@ -1,17 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import Notice from "./notice/index.vue";
|
||||
import avatars from "/@/assets/avatars.jpg";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import Hamburger from "./sidebar/hamBurger.vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { storageSession } from "/@/utils/storage";
|
||||
import Breadcrumb from "./sidebar/breadCrumb.vue";
|
||||
import Notice from "./notice/index.vue";
|
||||
import { useAppStoreHook } from "/@/store/modules/app";
|
||||
import { unref, watch, getCurrentInstance } from "vue";
|
||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||
import screenfull from "../components/screenfull/index.vue";
|
||||
import globalization from "/@/assets/svg/globalization.svg";
|
||||
import { transformI18n } from "/@/utils/i18n";
|
||||
|
||||
const instance =
|
||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||
@ -106,16 +107,15 @@ function translationEn() {
|
||||
<!-- 退出登陆 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/44761321?s=400&u=30907819abd29bb3779bc247910873e7c7f7c12f&v=4"
|
||||
/>
|
||||
<img :src="avatars" />
|
||||
<p>{{ usename }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item icon="el-icon-switch-button" @click="logout">{{
|
||||
$t("message.hsLoginOut")
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">
|
||||
<i class="ri-logout-circle-r-line"></i
|
||||
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@ -254,6 +254,7 @@ function translationEn() {
|
||||
|
||||
.logout {
|
||||
.el-dropdown-menu__item {
|
||||
display: inline-flex;
|
||||
padding: 0 18px !important;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
|
||||
import { transformI18n } from "/@/utils/i18n";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
|
||||
const levelList = ref([]);
|
||||
const route = useRoute();
|
||||
|
@ -12,6 +12,7 @@ import { emitter } from "/@/utils/mitt";
|
||||
import Notice from "../notice/index.vue";
|
||||
import { templateRef } from "@vueuse/core";
|
||||
import SidebarItem from "./sidebarItem.vue";
|
||||
import avatars from "/@/assets/avatars.jpg";
|
||||
import { algorithm } from "/@/utils/algorithm";
|
||||
import screenfull from "../screenfull/index.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
@ -174,16 +175,15 @@ onMounted(() => {
|
||||
<!-- 退出登陆 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/44761321?s=400&u=30907819abd29bb3779bc247910873e7c7f7c12f&v=4"
|
||||
/>
|
||||
<img :src="avatars" />
|
||||
<p>{{ usename }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item icon="el-icon-switch-button" @click="logout">{{
|
||||
$t("message.hsLoginOut")
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">
|
||||
<i class="ri-logout-circle-r-line"></i
|
||||
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@ -225,6 +225,7 @@ onMounted(() => {
|
||||
|
||||
.logout {
|
||||
.el-dropdown-menu__item {
|
||||
display: inline-flex;
|
||||
padding: 0 18px !important;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { PropType, ref, nextTick, getCurrentInstance } from "vue";
|
||||
import { childrenType } from "../../types";
|
||||
import { useAppStoreHook } from "/@/store/modules/app";
|
||||
import Icon from "/@/components/ReIcon/src/Icon.vue";
|
||||
import { transformI18n } from "/@/utils/i18n";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { findIconReg } from "/@/components/ReIcon";
|
||||
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
||||
const menuMode = instance.$storage.layout?.layout === "vertical";
|
||||
|
@ -18,7 +18,7 @@ import closeOther from "/@/assets/svg/close_other.svg";
|
||||
import closeRight from "/@/assets/svg/close_right.svg";
|
||||
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import { transformI18n } from "/@/utils/i18n";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { storageLocal } from "/@/utils/storage";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { RouteConfigs, tagsViewsType } from "../../types";
|
||||
@ -251,6 +251,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
obj
|
||||
]);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", {
|
||||
startIndex,
|
||||
length
|
||||
|
@ -13,3 +13,27 @@ export const i18n = createI18n({
|
||||
export function usI18n(app: App) {
|
||||
app.use(i18n);
|
||||
}
|
||||
|
||||
/**
|
||||
* 国际化转换工具函数
|
||||
* @param message message
|
||||
* @param isI18n 如果true,获取对应的消息,否则返回本身
|
||||
* @returns message
|
||||
*/
|
||||
export function transformI18n(message: string | object = "", isI18n = false) {
|
||||
if (!message) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 处理存储动态路由的title,格式 {zh:"",en:""}
|
||||
if (typeof message === "object") {
|
||||
return message[i18n.global?.locale];
|
||||
}
|
||||
|
||||
if (isI18n) {
|
||||
//@ts-ignore
|
||||
return i18n.global.tc.call(i18n.global, message);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
import {
|
||||
Router,
|
||||
RouteMeta,
|
||||
createRouter,
|
||||
RouteComponent,
|
||||
RouteRecordName,
|
||||
createWebHashHistory,
|
||||
RouteRecordNormalized
|
||||
} from "vue-router";
|
||||
import { RouteConfigs } from "/@/layout/types";
|
||||
import { split, uniqBy } from "lodash-es";
|
||||
import { transformI18n } from "../utils/i18n";
|
||||
import { openLink } from "/@/utils/link";
|
||||
import NProgress from "/@/utils/progress";
|
||||
import { split, uniqBy } from "lodash-es";
|
||||
import { useTimeoutFn } from "@vueuse/core";
|
||||
import { RouteConfigs } from "/@/layout/types";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { storageSession, storageLocal } from "/@/utils/storage";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
@ -224,7 +226,29 @@ router.beforeEach((to, _from, next) => {
|
||||
if (usePermissionStoreHook().wholeRoutes.length === 0)
|
||||
initRouter(name.username).then((router: Router) => {
|
||||
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
||||
return router.push("/");
|
||||
const handTag = (
|
||||
path: string,
|
||||
parentPath: string,
|
||||
name: RouteRecordName,
|
||||
meta: RouteMeta
|
||||
): void => {
|
||||
useMultiTagsStoreHook().handleTags("push", {
|
||||
path,
|
||||
parentPath,
|
||||
name,
|
||||
meta
|
||||
});
|
||||
};
|
||||
const parentPath = to.matched[0]?.path;
|
||||
if (to.meta?.realPath) {
|
||||
const { path, name, meta } = to.matched[0]?.children[0];
|
||||
handTag(path, parentPath, name, meta);
|
||||
return router.push(path);
|
||||
} else {
|
||||
const { path, name, meta } = to;
|
||||
handTag(path, parentPath, name, meta);
|
||||
return router.push(to.path);
|
||||
}
|
||||
}
|
||||
router.push(to.path);
|
||||
// 刷新页面更新标签栏与页面路由匹配
|
||||
|
@ -1,23 +1,13 @@
|
||||
import { storageLocal } from "/@/utils/storage";
|
||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "/@/store";
|
||||
import { appType } from "./types";
|
||||
import { defineStore } from "pinia";
|
||||
import { getConfig } from "/@/config";
|
||||
|
||||
interface AppState {
|
||||
sidebar: {
|
||||
opened: boolean;
|
||||
withoutAnimation: boolean;
|
||||
// 判断是否手动点击Hamburger
|
||||
isClickHamburger: boolean;
|
||||
};
|
||||
layout: string;
|
||||
device: string;
|
||||
}
|
||||
|
||||
export const useAppStore = defineStore({
|
||||
id: "pure-app",
|
||||
state: (): AppState => ({
|
||||
state: (): appType => ({
|
||||
sidebar: {
|
||||
opened: storageLocal.getItem("sidebarStatus")
|
||||
? !!+storageLocal.getItem("sidebarStatus")
|
||||
|
@ -1,15 +1,8 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "/@/store";
|
||||
import { getConfig } from "/@/config";
|
||||
import { positionType } from "./types";
|
||||
import { storageLocal } from "/@/utils/storage";
|
||||
|
||||
interface Itag {
|
||||
path: string;
|
||||
parentPath: string;
|
||||
name: string;
|
||||
meta: any;
|
||||
}
|
||||
import { multiType, positionType } from "./types";
|
||||
|
||||
export const useMultiTagsStore = defineStore({
|
||||
id: "pure-multiTags",
|
||||
@ -43,16 +36,16 @@ export const useMultiTagsStore = defineStore({
|
||||
},
|
||||
handleTags<T>(
|
||||
mode: string,
|
||||
value?: T | Itag,
|
||||
value?: T | multiType,
|
||||
position?: positionType
|
||||
): any {
|
||||
): T {
|
||||
switch (mode) {
|
||||
case "equal":
|
||||
this.multiTags = value;
|
||||
break;
|
||||
case "push":
|
||||
{
|
||||
const tagVal = value as Itag;
|
||||
const tagVal = value as multiType;
|
||||
// 判断tag是否已存在:
|
||||
const tagHasExits = this.multiTags.some(tag => {
|
||||
return tag.path === tagVal?.path;
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "/@/store";
|
||||
import { setType } from "./types";
|
||||
import { getConfig } from "/@/config";
|
||||
|
||||
interface SettingState {
|
||||
title: string;
|
||||
fixedHeader: boolean;
|
||||
hiddenSideBar: boolean;
|
||||
}
|
||||
|
||||
export const useSettingStore = defineStore({
|
||||
id: "pure-setting",
|
||||
state: (): SettingState => ({
|
||||
state: (): setType => ({
|
||||
title: getConfig().Title,
|
||||
fixedHeader: getConfig().FixedHeader,
|
||||
hiddenSideBar: getConfig().HiddenSideBar
|
||||
|
@ -9,3 +9,32 @@ export type positionType = {
|
||||
startIndex?: number;
|
||||
length?: number;
|
||||
};
|
||||
|
||||
export type appType = {
|
||||
sidebar: {
|
||||
opened: boolean;
|
||||
withoutAnimation: boolean;
|
||||
// 判断是否手动点击Hamburger
|
||||
isClickHamburger: boolean;
|
||||
};
|
||||
layout: string;
|
||||
device: string;
|
||||
};
|
||||
|
||||
export type multiType = {
|
||||
path: string;
|
||||
parentPath: string;
|
||||
name: string;
|
||||
meta: any;
|
||||
};
|
||||
|
||||
export type setType = {
|
||||
title: string;
|
||||
fixedHeader: boolean;
|
||||
hiddenSideBar: boolean;
|
||||
};
|
||||
|
||||
export type userType = {
|
||||
token: string;
|
||||
name?: string;
|
||||
};
|
||||
|
84
src/store/modules/user.ts
Normal file
84
src/store/modules/user.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "/@/store";
|
||||
import { userType } from "./types";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getLogin, refreshToken } from "/@/api/user";
|
||||
import { storageLocal, storageSession } from "/@/utils/storage";
|
||||
import { getToken, setToken, removeToken } from "/@/utils/auth";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
|
||||
const data = getToken();
|
||||
let token = "";
|
||||
let name = "";
|
||||
if (data) {
|
||||
const dataJson = JSON.parse(data);
|
||||
if (dataJson) {
|
||||
token = dataJson?.accessToken;
|
||||
name = dataJson?.name ?? "admin";
|
||||
}
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore({
|
||||
id: "pure-user",
|
||||
state: (): userType => ({
|
||||
token,
|
||||
name
|
||||
}),
|
||||
actions: {
|
||||
SET_TOKEN(token) {
|
||||
this.token = token;
|
||||
},
|
||||
SET_NAME(name) {
|
||||
this.name = name;
|
||||
},
|
||||
// 登入
|
||||
async loginByUsername(data) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
getLogin(data)
|
||||
.then(data => {
|
||||
if (data) {
|
||||
setToken(data);
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
// 登出 清空缓存
|
||||
logOut() {
|
||||
this.token = "";
|
||||
this.name = "";
|
||||
removeToken();
|
||||
storageLocal.clear();
|
||||
storageSession.clear();
|
||||
useMultiTagsStoreHook().handleTags("equal", [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "message.hshome",
|
||||
icon: "el-icon-s-home",
|
||||
i18n: true,
|
||||
showLink: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
useRouter().push("/login");
|
||||
},
|
||||
// 刷新token
|
||||
async refreshToken(data) {
|
||||
return refreshToken(data).then(data => {
|
||||
if (data) {
|
||||
setToken(data);
|
||||
return data;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function useUserStoreHook() {
|
||||
return useUserStore(store);
|
||||
}
|
43
src/utils/auth.ts
Normal file
43
src/utils/auth.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import Cookies from "js-cookie";
|
||||
import { useUserStoreHook } from "/@/store/modules/user";
|
||||
|
||||
const TokenKey = "authorized-token";
|
||||
|
||||
type paramsMapType = {
|
||||
name: string;
|
||||
expires: number;
|
||||
accessToken: string;
|
||||
};
|
||||
|
||||
// 获取token
|
||||
export function getToken() {
|
||||
// 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错
|
||||
return Cookies.get("authorized-token");
|
||||
}
|
||||
|
||||
// 设置token以及过期时间(cookies、sessionStorage各一份)
|
||||
// 后端需要将用户信息和token以及过期时间都返回给前端,过期时间主要用于刷新token
|
||||
export function setToken(data) {
|
||||
const { accessToken, expires, name } = data;
|
||||
// 提取关键信息进行存储
|
||||
const paramsMap: paramsMapType = {
|
||||
name,
|
||||
expires: Date.now() + parseInt(expires),
|
||||
accessToken
|
||||
};
|
||||
const dataString = JSON.stringify(paramsMap);
|
||||
useUserStoreHook().SET_TOKEN(accessToken);
|
||||
useUserStoreHook().SET_NAME(name);
|
||||
expires > 0
|
||||
? Cookies.set(TokenKey, dataString, {
|
||||
expires: expires / 86400000
|
||||
})
|
||||
: Cookies.set(TokenKey, dataString);
|
||||
sessionStorage.setItem(TokenKey, dataString);
|
||||
}
|
||||
|
||||
// 删除token
|
||||
export function removeToken() {
|
||||
Cookies.remove(TokenKey);
|
||||
sessionStorage.removeItem(TokenKey);
|
||||
}
|
25
src/utils/http/README.md
Normal file
25
src/utils/http/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
## 用法
|
||||
|
||||
### Get 请求
|
||||
|
||||
```
|
||||
import { http } from "/@/utils/http";
|
||||
|
||||
// params传参
|
||||
http.request('get', '/xxx', { params: param });
|
||||
|
||||
// url拼接传参
|
||||
http.request('get', '/xxx?message=' + msg);
|
||||
```
|
||||
|
||||
### Post 请求
|
||||
|
||||
```
|
||||
import { http } from "/@/utils/http";
|
||||
|
||||
// params传参
|
||||
http.request('get', '/xxx', { params: param });
|
||||
|
||||
// data传参
|
||||
http.request('get', '/xxx', { data: param });
|
||||
```
|
@ -1,32 +0,0 @@
|
||||
import { AxiosRequestConfig } from "axios";
|
||||
import { excludeProps } from "./utils";
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
export const defaultConfig: AxiosRequestConfig = {
|
||||
baseURL: "",
|
||||
//10秒超时
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
"Content-Type": "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
}
|
||||
};
|
||||
|
||||
export function genConfig(config?: AxiosRequestConfig): AxiosRequestConfig {
|
||||
if (!config) {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
const { headers } = config;
|
||||
if (headers && typeof headers === "object") {
|
||||
defaultConfig.headers = {
|
||||
...defaultConfig.headers,
|
||||
...headers
|
||||
};
|
||||
}
|
||||
return { ...excludeProps(config!, "headers"), ...defaultConfig };
|
||||
}
|
||||
|
||||
export const METHODS = ["post", "get", "put", "delete", "option", "patch"];
|
@ -1,236 +0,0 @@
|
||||
import Axios, {
|
||||
AxiosRequestConfig,
|
||||
CancelTokenStatic,
|
||||
AxiosInstance
|
||||
} from "axios";
|
||||
|
||||
import NProgress from "../progress";
|
||||
|
||||
import { genConfig } from "./config";
|
||||
|
||||
import { transformConfigByMethod } from "./utils";
|
||||
|
||||
import {
|
||||
cancelTokenType,
|
||||
RequestMethods,
|
||||
EnclosureHttpRequestConfig,
|
||||
EnclosureHttpResoponse,
|
||||
EnclosureHttpError
|
||||
} from "./types.d";
|
||||
|
||||
class EnclosureHttp {
|
||||
constructor() {
|
||||
this.httpInterceptorsRequest();
|
||||
this.httpInterceptorsResponse();
|
||||
}
|
||||
// 初始化配置对象
|
||||
private static initConfig: EnclosureHttpRequestConfig = {};
|
||||
|
||||
// 保存当前Axios实例对象
|
||||
private static axiosInstance: AxiosInstance = Axios.create(genConfig());
|
||||
|
||||
// 保存 EnclosureHttp实例
|
||||
private static EnclosureHttpInstance: EnclosureHttp;
|
||||
|
||||
// axios取消对象
|
||||
private CancelToken: CancelTokenStatic = Axios.CancelToken;
|
||||
|
||||
// 取消的凭证数组
|
||||
private sourceTokenList: Array<cancelTokenType> = [];
|
||||
|
||||
// 记录当前这一次cancelToken的key
|
||||
private currentCancelTokenKey = "";
|
||||
|
||||
public get cancelTokenList(): Array<cancelTokenType> {
|
||||
return this.sourceTokenList;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
public set cancelTokenList(value) {
|
||||
throw new Error("cancelTokenList不允许赋值");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 私有构造不允许实例化
|
||||
* @returns void 0
|
||||
*/
|
||||
// constructor() {}
|
||||
|
||||
/**
|
||||
* @description 生成唯一取消key
|
||||
* @param config axios配置
|
||||
* @returns string
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
private static genUniqueKey(config: EnclosureHttpRequestConfig): string {
|
||||
return `${config.url}--${JSON.stringify(config.data)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 取消重复请求
|
||||
* @returns void 0
|
||||
*/
|
||||
private cancelRepeatRequest(): void {
|
||||
const temp: { [key: string]: boolean } = {};
|
||||
|
||||
this.sourceTokenList = this.sourceTokenList.reduce<Array<cancelTokenType>>(
|
||||
(res: Array<cancelTokenType>, cancelToken: cancelTokenType) => {
|
||||
const { cancelKey, cancelExecutor } = cancelToken;
|
||||
if (!temp[cancelKey]) {
|
||||
temp[cancelKey] = true;
|
||||
res.push(cancelToken);
|
||||
} else {
|
||||
cancelExecutor();
|
||||
}
|
||||
return res;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 删除指定的CancelToken
|
||||
* @returns void 0
|
||||
*/
|
||||
private deleteCancelTokenByCancelKey(cancelKey: string): void {
|
||||
this.sourceTokenList =
|
||||
this.sourceTokenList.length < 1
|
||||
? this.sourceTokenList.filter(
|
||||
cancelToken => cancelToken.cancelKey !== cancelKey
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 拦截请求
|
||||
* @returns void 0
|
||||
*/
|
||||
|
||||
private httpInterceptorsRequest(): void {
|
||||
EnclosureHttp.axiosInstance.interceptors.request.use(
|
||||
(config: EnclosureHttpRequestConfig) => {
|
||||
const $config = config;
|
||||
NProgress.start(); // 每次切换页面时,调用进度条
|
||||
const cancelKey = EnclosureHttp.genUniqueKey($config);
|
||||
$config.cancelToken = new this.CancelToken(
|
||||
(cancelExecutor: (cancel: any) => void) => {
|
||||
this.sourceTokenList.push({ cancelKey, cancelExecutor });
|
||||
}
|
||||
);
|
||||
this.cancelRepeatRequest();
|
||||
this.currentCancelTokenKey = cancelKey;
|
||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||
if (typeof config.beforeRequestCallback === "function") {
|
||||
config.beforeRequestCallback($config);
|
||||
return $config;
|
||||
}
|
||||
if (EnclosureHttp.initConfig.beforeRequestCallback) {
|
||||
EnclosureHttp.initConfig.beforeRequestCallback($config);
|
||||
return $config;
|
||||
}
|
||||
return $config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 清空当前cancelTokenList
|
||||
* @returns void 0
|
||||
*/
|
||||
public clearCancelTokenList(): void {
|
||||
this.sourceTokenList.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 拦截响应
|
||||
* @returns void 0
|
||||
*/
|
||||
private httpInterceptorsResponse(): void {
|
||||
const instance = EnclosureHttp.axiosInstance;
|
||||
instance.interceptors.response.use(
|
||||
(response: EnclosureHttpResoponse) => {
|
||||
const $config = response.config;
|
||||
// 请求每次成功一次就删除当前canceltoken标记
|
||||
const cancelKey = EnclosureHttp.genUniqueKey($config);
|
||||
this.deleteCancelTokenByCancelKey(cancelKey);
|
||||
|
||||
NProgress.done();
|
||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||
if (typeof $config.beforeResponseCallback === "function") {
|
||||
$config.beforeResponseCallback(response);
|
||||
return response.data;
|
||||
}
|
||||
if (EnclosureHttp.initConfig.beforeResponseCallback) {
|
||||
EnclosureHttp.initConfig.beforeResponseCallback(response);
|
||||
return response.data;
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
(error: EnclosureHttpError) => {
|
||||
const $error = error;
|
||||
// 判断当前的请求中是否在 取消token数组理存在,如果存在则移除(单次请求流程)
|
||||
if (this.currentCancelTokenKey) {
|
||||
const haskey = this.sourceTokenList.filter(
|
||||
cancelToken => cancelToken.cancelKey === this.currentCancelTokenKey
|
||||
).length;
|
||||
if (haskey) {
|
||||
this.sourceTokenList = this.sourceTokenList.filter(
|
||||
cancelToken =>
|
||||
cancelToken.cancelKey !== this.currentCancelTokenKey
|
||||
);
|
||||
this.currentCancelTokenKey = "";
|
||||
}
|
||||
}
|
||||
$error.isCancelRequest = Axios.isCancel($error);
|
||||
NProgress.done();
|
||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||
return Promise.reject($error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public request<T>(
|
||||
method: RequestMethods,
|
||||
url: string,
|
||||
param?: AxiosRequestConfig,
|
||||
axiosConfig?: EnclosureHttpRequestConfig
|
||||
): Promise<T> {
|
||||
const config = transformConfigByMethod(param, {
|
||||
method,
|
||||
url,
|
||||
...axiosConfig
|
||||
} as EnclosureHttpRequestConfig);
|
||||
// 单独处理自定义请求/响应回掉
|
||||
return new Promise((resolve, reject) => {
|
||||
EnclosureHttp.axiosInstance
|
||||
.request(config)
|
||||
.then((response: undefined) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public post<T>(
|
||||
url: string,
|
||||
params?: T,
|
||||
config?: EnclosureHttpRequestConfig
|
||||
): Promise<T> {
|
||||
return this.request<T>("post", url, params, config);
|
||||
}
|
||||
|
||||
public get<T>(
|
||||
url: string,
|
||||
params?: T,
|
||||
config?: EnclosureHttpRequestConfig
|
||||
): Promise<T> {
|
||||
return this.request<T>("get", url, params, config);
|
||||
}
|
||||
}
|
||||
|
||||
export default EnclosureHttp;
|
@ -1,2 +1,166 @@
|
||||
import EnclosureHttp from "./core";
|
||||
export const http = new EnclosureHttp();
|
||||
import Axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
||||
import {
|
||||
resultType,
|
||||
PureHttpError,
|
||||
RequestMethods,
|
||||
PureHttpResoponse,
|
||||
PureHttpRequestConfig
|
||||
} from "./types.d";
|
||||
import qs from "qs";
|
||||
import NProgress from "../progress";
|
||||
// import { loadEnv } from "@build/index";
|
||||
import { getToken } from "/@/utils/auth";
|
||||
import { useUserStoreHook } from "/@/store/modules/user";
|
||||
|
||||
// 加载环境变量 VITE_PROXY_DOMAIN(开发环境) VITE_PROXY_DOMAIN_REAL(打包后的线上环境)
|
||||
// const { VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL } = loadEnv();
|
||||
|
||||
// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
|
||||
const defaultConfig: AxiosRequestConfig = {
|
||||
// baseURL:
|
||||
// process.env.NODE_ENV === "production"
|
||||
// ? VITE_PROXY_DOMAIN_REAL
|
||||
// : VITE_PROXY_DOMAIN,
|
||||
// 当前使用mock模拟请求,将baseURL制空,如果你的环境用到了http请求,请删除下面的baseURL启用上面的baseURL,并将11行、16行代码注释取消
|
||||
baseURL: "",
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
"Content-Type": "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
// 数组格式参数序列化
|
||||
paramsSerializer: params => qs.stringify(params, { indices: false })
|
||||
};
|
||||
|
||||
class PureHttp {
|
||||
constructor() {
|
||||
this.httpInterceptorsRequest();
|
||||
this.httpInterceptorsResponse();
|
||||
}
|
||||
// 初始化配置对象
|
||||
private static initConfig: PureHttpRequestConfig = {};
|
||||
|
||||
// 保存当前Axios实例对象
|
||||
private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
|
||||
|
||||
// 请求拦截
|
||||
private httpInterceptorsRequest(): void {
|
||||
PureHttp.axiosInstance.interceptors.request.use(
|
||||
(config: PureHttpRequestConfig) => {
|
||||
const $config = config;
|
||||
// 开启进度条动画
|
||||
NProgress.start();
|
||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||
if (typeof config.beforeRequestCallback === "function") {
|
||||
config.beforeRequestCallback($config);
|
||||
return $config;
|
||||
}
|
||||
if (PureHttp.initConfig.beforeRequestCallback) {
|
||||
PureHttp.initConfig.beforeRequestCallback($config);
|
||||
return $config;
|
||||
}
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
const data = JSON.parse(token);
|
||||
const now = new Date().getTime();
|
||||
const expired = parseInt(data.expires) - now <= 0;
|
||||
if (expired) {
|
||||
// token过期刷新
|
||||
useUserStoreHook()
|
||||
.refreshToken(data)
|
||||
.then((res: resultType) => {
|
||||
config.headers["Authorization"] = "Bearer " + res.accessToken;
|
||||
return $config;
|
||||
});
|
||||
} else {
|
||||
config.headers["Authorization"] = "Bearer " + data.accessToken;
|
||||
return $config;
|
||||
}
|
||||
} else {
|
||||
return $config;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 响应拦截
|
||||
private httpInterceptorsResponse(): void {
|
||||
const instance = PureHttp.axiosInstance;
|
||||
instance.interceptors.response.use(
|
||||
(response: PureHttpResoponse) => {
|
||||
const $config = response.config;
|
||||
// 关闭进度条动画
|
||||
NProgress.done();
|
||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||
if (typeof $config.beforeResponseCallback === "function") {
|
||||
$config.beforeResponseCallback(response);
|
||||
return response.data;
|
||||
}
|
||||
if (PureHttp.initConfig.beforeResponseCallback) {
|
||||
PureHttp.initConfig.beforeResponseCallback(response);
|
||||
return response.data;
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
(error: PureHttpError) => {
|
||||
const $error = error;
|
||||
$error.isCancelRequest = Axios.isCancel($error);
|
||||
// 关闭进度条动画
|
||||
NProgress.done();
|
||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||
return Promise.reject($error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 通用请求工具函数
|
||||
public request<T>(
|
||||
method: RequestMethods,
|
||||
url: string,
|
||||
param?: AxiosRequestConfig,
|
||||
axiosConfig?: PureHttpRequestConfig
|
||||
): Promise<T> {
|
||||
const config = {
|
||||
method,
|
||||
url,
|
||||
...param,
|
||||
...axiosConfig
|
||||
} as PureHttpRequestConfig;
|
||||
|
||||
// 单独处理自定义请求/响应回掉
|
||||
return new Promise((resolve, reject) => {
|
||||
PureHttp.axiosInstance
|
||||
.request(config)
|
||||
.then((response: undefined) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 单独抽离的post工具函数
|
||||
public post<T>(
|
||||
url: string,
|
||||
params?: T,
|
||||
config?: PureHttpRequestConfig
|
||||
): Promise<T> {
|
||||
return this.request<T>("post", url, params, config);
|
||||
}
|
||||
|
||||
// 单独抽离的get工具函数
|
||||
public get<T>(
|
||||
url: string,
|
||||
params?: T,
|
||||
config?: PureHttpRequestConfig
|
||||
): Promise<T> {
|
||||
return this.request<T>("get", url, params, config);
|
||||
}
|
||||
}
|
||||
|
||||
export const http = new PureHttp();
|
||||
|
51
src/utils/http/types.d.ts
vendored
51
src/utils/http/types.d.ts
vendored
@ -1,50 +1,39 @@
|
||||
import Axios, {
|
||||
AxiosRequestConfig,
|
||||
Canceler,
|
||||
AxiosResponse,
|
||||
Method,
|
||||
AxiosError
|
||||
AxiosError,
|
||||
AxiosResponse,
|
||||
AxiosRequestConfig
|
||||
} from "axios";
|
||||
|
||||
import { METHODS } from "./config";
|
||||
|
||||
export type cancelTokenType = { cancelKey: string; cancelExecutor: Canceler };
|
||||
export type resultType = {
|
||||
accessToken?: string;
|
||||
};
|
||||
|
||||
export type RequestMethods = Extract<
|
||||
Method,
|
||||
"get" | "post" | "put" | "delete" | "patch" | "option" | "head"
|
||||
>;
|
||||
|
||||
export interface EnclosureHttpRequestConfig extends AxiosRequestConfig {
|
||||
beforeRequestCallback?: (request: EnclosureHttpRequestConfig) => void; // 请求发送之前
|
||||
beforeResponseCallback?: (response: EnclosureHttpResoponse) => void; // 相应返回之前
|
||||
}
|
||||
|
||||
export interface EnclosureHttpResoponse extends AxiosResponse {
|
||||
config: EnclosureHttpRequestConfig;
|
||||
}
|
||||
|
||||
export interface EnclosureHttpError extends AxiosError {
|
||||
export interface PureHttpError extends AxiosError {
|
||||
isCancelRequest?: boolean;
|
||||
}
|
||||
|
||||
export default class EnclosureHttp {
|
||||
cancelTokenList: Array<cancelTokenType>;
|
||||
clearCancelTokenList(): void;
|
||||
export interface PureHttpResoponse extends AxiosResponse {
|
||||
config: PureHttpRequestConfig;
|
||||
}
|
||||
|
||||
export interface PureHttpRequestConfig extends AxiosRequestConfig {
|
||||
beforeRequestCallback?: (request: PureHttpRequestConfig) => void;
|
||||
beforeResponseCallback?: (response: PureHttpResoponse) => void;
|
||||
}
|
||||
|
||||
export default class PureHttp {
|
||||
request<T>(
|
||||
method: RequestMethods,
|
||||
url: string,
|
||||
param?: AxiosRequestConfig,
|
||||
axiosConfig?: EnclosureHttpRequestConfig
|
||||
): Promise<T>;
|
||||
post<T>(
|
||||
url: string,
|
||||
params?: T,
|
||||
config?: EnclosureHttpRequestConfig
|
||||
): Promise<T>;
|
||||
get<T>(
|
||||
url: string,
|
||||
params?: T,
|
||||
config?: EnclosureHttpRequestConfig
|
||||
axiosConfig?: PureHttpRequestConfig
|
||||
): Promise<T>;
|
||||
post<T>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<T>;
|
||||
get<T>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<T>;
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
import { EnclosureHttpRequestConfig } from "./types.d";
|
||||
|
||||
export function excludeProps<T extends { [key: string]: any }>(
|
||||
origin: T,
|
||||
prop: string
|
||||
): { [key: string]: T } {
|
||||
return Object.keys(origin)
|
||||
.filter(key => !prop.includes(key))
|
||||
.reduce((res, key) => {
|
||||
res[key] = origin[key];
|
||||
return res;
|
||||
}, {} as { [key: string]: T });
|
||||
}
|
||||
|
||||
export function transformConfigByMethod(
|
||||
params: any,
|
||||
config: EnclosureHttpRequestConfig
|
||||
): EnclosureHttpRequestConfig {
|
||||
const { method } = config;
|
||||
const props = ["delete", "get", "head", "options"].includes(
|
||||
method!.toLocaleLowerCase()
|
||||
)
|
||||
? "params"
|
||||
: "data";
|
||||
return {
|
||||
...config,
|
||||
[props]: params
|
||||
};
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { i18n } from "../plugins/i18n";
|
||||
|
||||
/**
|
||||
* 消息转换
|
||||
* @param message message
|
||||
* @param isI18n 如果true,获取对应的消息,否则返回this
|
||||
* @returns message
|
||||
*/
|
||||
export function transformI18n(message = "", isI18n = false) {
|
||||
if (!message) {
|
||||
return "";
|
||||
}
|
||||
if (isI18n) {
|
||||
//@ts-ignore
|
||||
return i18n.global.tc.call(i18n.global, message);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import { loadEnv } from "@build/utils";
|
||||
import { merge } from "lodash-es";
|
||||
import tsCookies from "typescript-cookie/dist/src/compat";
|
||||
|
||||
class Cookies {
|
||||
private static env = loadEnv();
|
||||
constructor() {}
|
||||
/**
|
||||
* 存储 cookie 值
|
||||
* @param name
|
||||
* @param value
|
||||
* @param cookieSetting
|
||||
*/
|
||||
set(name = "default", value = "", cookieSetting = {}) {
|
||||
const currentCookieSetting = {
|
||||
expires: 1
|
||||
};
|
||||
merge(currentCookieSetting, cookieSetting);
|
||||
tsCookies.set(
|
||||
`${Cookies.env.VITE_TITLE}-${Cookies.env.VITE_VERSION}-${name}`,
|
||||
value,
|
||||
currentCookieSetting
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 拿到 cookie 值
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
get(name = "default") {
|
||||
return tsCookies.get(
|
||||
`${Cookies.env.VITE_TITLE}-${Cookies.env.VITE_VERSION}-${name}`
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 拿到 cookie 全部的值
|
||||
* @returns
|
||||
*/
|
||||
getAll() {
|
||||
return tsCookies.get();
|
||||
}
|
||||
/**
|
||||
* 删除 cookie
|
||||
* @param name
|
||||
*/
|
||||
remove(name = "default") {
|
||||
tsCookies.remove(
|
||||
`${Cookies.env.VITE_TITLE}-${Cookies.env.VITE_VERSION}-${name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const cookies = new Cookies();
|
@ -1,93 +0,0 @@
|
||||
import { loadEnv } from "@build/utils";
|
||||
import { LocalStorage, LowSync } from "lowdb";
|
||||
import { chain, cloneDeep } from "lodash-es";
|
||||
import { storageLocal } from ".";
|
||||
import { cookies } from "./cookie";
|
||||
type Data = {
|
||||
database: {};
|
||||
sys: {};
|
||||
};
|
||||
/**
|
||||
* db 数据存储,采用 LocalStorage存储
|
||||
*/
|
||||
class DB {
|
||||
private db: LowSync<Data>;
|
||||
private static env = loadEnv();
|
||||
constructor() {
|
||||
this.db = new LowSync<Data>(
|
||||
new LocalStorage<Data>(`${DB.env.VITE_TITLE}-${DB.env.VITE_VERSION}`)
|
||||
);
|
||||
this.initialization();
|
||||
// @ts-ignore
|
||||
this.db.chain = chain(this.db.data);
|
||||
}
|
||||
private initialization() {
|
||||
this.db.data = storageLocal.getItem(
|
||||
`${DB.env.VITE_TITLE}-${DB.env.VITE_VERSION}`
|
||||
) || { database: {}, sys: {} };
|
||||
this.db.write();
|
||||
}
|
||||
/**
|
||||
* 检查路径是否存在 不存在的话初始化
|
||||
* @param param0
|
||||
* @returns path
|
||||
*/
|
||||
pathInit({
|
||||
dbName = "database",
|
||||
path = "",
|
||||
user = true,
|
||||
validator = () => true,
|
||||
defaultValue = ""
|
||||
}): string {
|
||||
const uuid = cookies.get("uuid") || "ghost-uuid";
|
||||
const currentPath = `${dbName}.${user ? `user.${uuid}` : "public"}${
|
||||
path ? `.${path}` : ""
|
||||
}`;
|
||||
// @ts-ignore
|
||||
const value = this.db.chain.get(currentPath).value();
|
||||
// @ts-ignore
|
||||
if (!(value !== undefined && validator(value))) {
|
||||
// @ts-ignore
|
||||
this.db.chain.set(currentPath, defaultValue).value();
|
||||
this.db.write();
|
||||
}
|
||||
return currentPath;
|
||||
}
|
||||
/**
|
||||
*将数据存储到指定位置 | 路径不存在会自动初始化
|
||||
*
|
||||
* 效果类似于取值 dbName.path = value
|
||||
* @param param0
|
||||
*/
|
||||
dbSet({ dbName = "database", path = "", value = "", user = false }): void {
|
||||
const currentPath = this.pathInit({
|
||||
dbName,
|
||||
path,
|
||||
user
|
||||
});
|
||||
// @ts-ignore
|
||||
this.db.chain.set(currentPath, value).value();
|
||||
this.db.write();
|
||||
}
|
||||
/**
|
||||
* 获取数据
|
||||
*
|
||||
* 效果类似于取值 dbName.path || defaultValue
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
dbGet({
|
||||
dbName = "database",
|
||||
path = "",
|
||||
defaultValue = "",
|
||||
user = false
|
||||
}): any {
|
||||
// @ts-ignore
|
||||
const values = this.db.chain
|
||||
.get(this.pathInit({ dbName, path, user, defaultValue }))
|
||||
.value();
|
||||
return cloneDeep(values);
|
||||
}
|
||||
}
|
||||
|
||||
export const db = new DB();
|
16
types/global.d.ts
vendored
16
types/global.d.ts
vendored
@ -65,21 +65,9 @@ declare global {
|
||||
|
||||
declare interface ViteEnv {
|
||||
VITE_PORT: number;
|
||||
VITE_TITLE: string;
|
||||
VITE_VERSION: string;
|
||||
VITE_USE_MOCK: boolean;
|
||||
VITE_USE_PWA: boolean;
|
||||
VITE_PUBLIC_PATH: string;
|
||||
VITE_PROXY: [string, string][];
|
||||
VITE_GLOB_APP_TITLE: string;
|
||||
VITE_GLOB_APP_SHORT_NAME: string;
|
||||
VITE_USE_CDN: boolean;
|
||||
VITE_DROP_CONSOLE: boolean;
|
||||
VITE_BUILD_COMPRESS: "gzip" | "brotli" | "none";
|
||||
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
|
||||
VITE_LEGACY: boolean;
|
||||
VITE_USE_IMAGEMIN: boolean;
|
||||
VITE_GENERATE_UI: string;
|
||||
VITE_PROXY_DOMAIN: string;
|
||||
VITE_PROXY_DOMAIN_REAL: string;
|
||||
}
|
||||
|
||||
declare interface ServerConfigs {
|
||||
|
@ -1,40 +1,39 @@
|
||||
import { resolve } from "path";
|
||||
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import { warpperEnv } from "./build/utils";
|
||||
import { createProxy } from "./build/proxy";
|
||||
import { viteMockServe } from "vite-plugin-mock";
|
||||
import svgLoader from "vite-svg-loader";
|
||||
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import { warpperEnv, regExps } from "./build";
|
||||
import { viteMockServe } from "vite-plugin-mock";
|
||||
import ElementPlus from "unplugin-element-plus/vite";
|
||||
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
|
||||
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
|
||||
|
||||
// 当前执行node命令时文件夹的地址(工作目录)
|
||||
const root: string = process.cwd();
|
||||
|
||||
// 路径查找
|
||||
const pathResolve = (dir: string): string => {
|
||||
return resolve(__dirname, ".", dir);
|
||||
};
|
||||
|
||||
// 设置别名
|
||||
const alias: Record<string, string> = {
|
||||
"/@": pathResolve("src"),
|
||||
"@build": pathResolve("build"),
|
||||
//解决开发环境下的警告 You are running the esm-bundler build of vue-i18n. It is recommended to configure your bundler to explicitly replace feature flag globals with boolean literals to get proper tree-shaking in the final bundle.
|
||||
//解决开发环境下的警告
|
||||
"vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js"
|
||||
};
|
||||
|
||||
const root: string = process.cwd();
|
||||
|
||||
export default ({ command, mode }: ConfigEnv): UserConfigExport => {
|
||||
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = warpperEnv(
|
||||
loadEnv(mode, root)
|
||||
);
|
||||
const {
|
||||
VITE_PORT,
|
||||
VITE_PUBLIC_PATH,
|
||||
VITE_PROXY_DOMAIN,
|
||||
VITE_PROXY_DOMAIN_REAL
|
||||
} = warpperEnv(loadEnv(mode, root));
|
||||
const prodMock = true;
|
||||
return {
|
||||
/**
|
||||
* 基本公共路径
|
||||
* /manages/ 可根据项目部署域名的后缀自行填写(全局搜/manages/替换)
|
||||
* @default '/'
|
||||
*/
|
||||
base:
|
||||
process.env.NODE_ENV === "production" ? "/manages/" : VITE_PUBLIC_PATH,
|
||||
base: VITE_PUBLIC_PATH,
|
||||
root,
|
||||
resolve: {
|
||||
alias
|
||||
@ -43,18 +42,27 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
|
||||
server: {
|
||||
// 是否开启 https
|
||||
https: false,
|
||||
/**
|
||||
* 端口号
|
||||
* @default 3000
|
||||
*/
|
||||
// 端口号
|
||||
port: VITE_PORT,
|
||||
host: "0.0.0.0",
|
||||
// 本地跨域代理
|
||||
proxy: createProxy(VITE_PROXY)
|
||||
proxy:
|
||||
VITE_PROXY_DOMAIN_REAL.length > 0
|
||||
? {
|
||||
[VITE_PROXY_DOMAIN]: {
|
||||
target: VITE_PROXY_DOMAIN_REAL,
|
||||
// ws: true,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => regExps(path, VITE_PROXY_DOMAIN)
|
||||
}
|
||||
}
|
||||
: null
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
// jsx、tsx语法支持
|
||||
vueJsx(),
|
||||
// 自定义主题
|
||||
themePreprocessorPlugin({
|
||||
scss: {
|
||||
multipleScopeVars: [
|
||||
@ -111,8 +119,10 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
|
||||
customThemeCssFileName: scopeName => scopeName
|
||||
}
|
||||
}),
|
||||
// svg组件化支持
|
||||
svgLoader(),
|
||||
ElementPlus({}),
|
||||
// mock支持
|
||||
viteMockServe({
|
||||
mockPath: "mock",
|
||||
localEnabled: command === "serve",
|
||||
@ -132,7 +142,6 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
|
||||
exclude: ["@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"]
|
||||
},
|
||||
build: {
|
||||
// @ts-ignore
|
||||
sourcemap: false,
|
||||
brotliSize: false,
|
||||
// 消除打包大小超过500kb警告
|
||||
|
Loading…
Reference in New Issue
Block a user