Browse Source

perf: 同步完整版分支代码

i18n
xiaoxian521 3 years ago
parent
commit
f6057458de
  1. 14
      .env
  2. 20
      .env.development
  3. 7
      .env.production
  4. 6
      .eslintrc.js
  5. 4
      README.en-US.md
  6. 4
      README.md
  7. 25
      build/index.ts
  8. 19
      build/proxy.ts
  9. 4
      package.json
  10. 89
      pnpm-lock.yaml
  11. 4
      src/api/routes.ts
  12. 26
      src/api/user.ts
  13. BIN
      src/assets/avatars.jpg
  14. 8
      src/config/index.ts
  15. 2
      src/layout/components/appMain.vue
  16. 17
      src/layout/components/navbar.vue
  17. 2
      src/layout/components/sidebar/breadCrumb.vue
  18. 13
      src/layout/components/sidebar/horizontal.vue
  19. 2
      src/layout/components/sidebar/sidebarItem.vue
  20. 3
      src/layout/components/tag/index.vue
  21. 24
      src/plugins/i18n/index.ts
  22. 32
      src/router/index.ts
  23. 16
      src/store/modules/app.ts
  24. 15
      src/store/modules/multiTags.ts
  25. 9
      src/store/modules/settings.ts
  26. 29
      src/store/modules/types.ts
  27. 84
      src/store/modules/user.ts
  28. 43
      src/utils/auth.ts
  29. 25
      src/utils/http/README.md
  30. 32
      src/utils/http/config.ts
  31. 236
      src/utils/http/core.ts
  32. 168
      src/utils/http/index.ts
  33. 45
      src/utils/http/types.d.ts
  34. 29
      src/utils/http/utils.ts
  35. 19
      src/utils/i18n.ts
  36. 53
      src/utils/storage/cookie.ts
  37. 93
      src/utils/storage/db.ts
  38. 16
      types/global.d.ts
  39. 55
      vite.config.ts

14
.env

@ -1,14 +1,2 @@
# port
# 项目本地运行端口号
VITE_PORT = 8848 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" ] ]

20
.env.development

@ -1,14 +1,12 @@
# port
# 项目本地运行端口号
VITE_PORT = 8848 VITE_PORT = 8848
# title
VITE_TITLE = vue-pure-admin
# version
VITE_VERSION = 2.6.0
# open
VITE_OPEN = false
# public path
# 开发环境读取配置文件路径
VITE_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"

7
.env.production

@ -1,2 +1,5 @@
# public path
VITE_PUBLIC_PATH = /manages/
# 线上环境项目打包路径
VITE_PUBLIC_PATH = /
# 线上环境后端地址
VITE_PROXY_DOMAIN_REAL = ""

6
.eslintrc.js

@ -70,6 +70,12 @@ module.exports = {
argsIgnorePattern: "^_", argsIgnorePattern: "^_",
varsIgnorePattern: "^_" varsIgnorePattern: "^_"
} }
],
"prettier/prettier": [
"error",
{
endOfLine: "auto"
}
] ]
} }
}; };

4
README.en-US.md

@ -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 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 ## Usage
### Installation dependencies ### Installation dependencies

4
README.md

@ -8,6 +8,10 @@
精简版是基于 https://github.com/xiaoxian521/vue-pure-admin 提炼出的架子,包含主体功能,更适合实际项目开发 精简版是基于 https://github.com/xiaoxian521/vue-pure-admin 提炼出的架子,包含主体功能,更适合实际项目开发
## 文档
<https://pure-admin-doc.vercel.app/>
## 用法 ## 用法
### 安装依赖 ### 安装依赖

25
build/utils.ts → build/index.ts

@ -1,5 +1,12 @@
// 处理环境变量
const warpperEnv = (envConf: Recordable): ViteEnv => { 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)) { for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, "\n"); let realName = envConf[envName].replace(/\\n/g, "\n");
@ -9,13 +16,6 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
if (envName === "VITE_PORT") { if (envName === "VITE_PORT") {
realName = Number(realName); realName = Number(realName);
} }
if (envName === "VITE_PROXY" && realName) {
try {
realName = JSON.parse(realName.replace(/'/g, '"'));
} catch (error) {
realName = "";
}
}
ret[envName] = realName; ret[envName] = realName;
if (typeof realName === "string") { if (typeof realName === "string") {
process.env[envName] = realName; process.env[envName] = realName;
@ -25,8 +25,15 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
} }
return ret; return ret;
}; };
// 跨域代理重写
const regExps = (value: string, reg: string): string => {
return value.replace(new RegExp(reg, "g"), "");
};
// 环境变量
const loadEnv = (): ViteEnv => { const loadEnv = (): ViteEnv => {
return import.meta.env; return import.meta.env;
}; };
export { loadEnv, warpperEnv };
export { warpperEnv, regExps, loadEnv };

19
build/proxy.ts

@ -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;
}

4
package.json

@ -41,6 +41,7 @@
"element-plus": "1.2.0-beta.3", "element-plus": "1.2.0-beta.3",
"element-resize-detector": "^1.2.3", "element-resize-detector": "^1.2.3",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"js-cookie": "^3.0.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"lowdb": "^3.0.0", "lowdb": "^3.0.0",
"mitt": "^3.0.0", "mitt": "^3.0.0",
@ -49,6 +50,7 @@
"path": "^0.12.7", "path": "^0.12.7",
"path-to-regexp": "^6.2.0", "path-to-regexp": "^6.2.0",
"pinia": "^2.0.0-rc.14", "pinia": "^2.0.0-rc.14",
"qs": "^6.10.2",
"remixicon": "^2.5.0", "remixicon": "^2.5.0",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.11", "responsive-storage": "^1.0.11",
@ -62,9 +64,11 @@
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "13.1.0",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1",
"@types/mockjs": "1.0.3", "@types/mockjs": "1.0.3",
"@types/node": "14.14.14", "@types/node": "14.14.14",
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "4.31.0", "@typescript-eslint/eslint-plugin": "4.31.0",
"@typescript-eslint/parser": "4.31.0", "@typescript-eslint/parser": "4.31.0",
"@vitejs/plugin-vue": "^1.9.4", "@vitejs/plugin-vue": "^1.9.4",

89
pnpm-lock.yaml

@ -8,9 +8,11 @@ specifiers:
"@fortawesome/free-solid-svg-icons": ^5.15.4 "@fortawesome/free-solid-svg-icons": ^5.15.4
"@fortawesome/vue-fontawesome": ^3.0.0-5 "@fortawesome/vue-fontawesome": ^3.0.0-5
"@types/element-resize-detector": 1.1.3 "@types/element-resize-detector": 1.1.3
"@types/js-cookie": ^3.0.1
"@types/mockjs": 1.0.3 "@types/mockjs": 1.0.3
"@types/node": 14.14.14 "@types/node": 14.14.14
"@types/nprogress": 0.2.0 "@types/nprogress": 0.2.0
"@types/qs": ^6.9.7
"@typescript-eslint/eslint-plugin": 4.31.0 "@typescript-eslint/eslint-plugin": 4.31.0
"@typescript-eslint/parser": 4.31.0 "@typescript-eslint/parser": 4.31.0
"@vitejs/plugin-vue": ^1.9.4 "@vitejs/plugin-vue": ^1.9.4
@ -36,6 +38,7 @@ specifiers:
eslint-plugin-vue: 7.17.0 eslint-plugin-vue: 7.17.0
font-awesome: ^4.7.0 font-awesome: ^4.7.0
husky: 7.0.2 husky: 7.0.2
js-cookie: ^3.0.1
lint-staged: 11.1.2 lint-staged: 11.1.2
lodash-es: ^4.17.21 lodash-es: ^4.17.21
lowdb: ^3.0.0 lowdb: ^3.0.0
@ -49,6 +52,7 @@ specifiers:
postcss-import: 14.0.0 postcss-import: 14.0.0
prettier: 2.3.2 prettier: 2.3.2
pretty-quick: 3.1.1 pretty-quick: 3.1.1
qs: ^6.10.2
remixicon: ^2.5.0 remixicon: ^2.5.0
resize-observer-polyfill: ^1.5.1 resize-observer-polyfill: ^1.5.1
responsive-storage: ^1.0.11 responsive-storage: ^1.0.11
@ -86,6 +90,7 @@ dependencies:
element-plus: 1.2.0-beta.3_vue@3.2.21 element-plus: 1.2.0-beta.3_vue@3.2.21
element-resize-detector: 1.2.3 element-resize-detector: 1.2.3
font-awesome: 4.7.0 font-awesome: 4.7.0
js-cookie: 3.0.1
lodash-es: 4.17.21 lodash-es: 4.17.21
lowdb: 3.0.0 lowdb: 3.0.0
mitt: 3.0.0 mitt: 3.0.0
@ -94,6 +99,7 @@ dependencies:
path: 0.12.7 path: 0.12.7
path-to-regexp: 6.2.0 path-to-regexp: 6.2.0
pinia: 2.0.2_typescript@4.4.2+vue@3.2.21 pinia: 2.0.2_typescript@4.4.2+vue@3.2.21
qs: 6.10.2
remixicon: 2.5.0 remixicon: 2.5.0
resize-observer-polyfill: 1.5.1 resize-observer-polyfill: 1.5.1
responsive-storage: 1.0.11_vue@3.2.21 responsive-storage: 1.0.11_vue@3.2.21
@ -107,9 +113,11 @@ devDependencies:
"@commitlint/cli": 13.1.0 "@commitlint/cli": 13.1.0
"@commitlint/config-conventional": 13.1.0 "@commitlint/config-conventional": 13.1.0
"@types/element-resize-detector": 1.1.3 "@types/element-resize-detector": 1.1.3
"@types/js-cookie": 3.0.1
"@types/mockjs": 1.0.3 "@types/mockjs": 1.0.3
"@types/node": 14.14.14 "@types/node": 14.14.14
"@types/nprogress": 0.2.0 "@types/nprogress": 0.2.0
"@types/qs": 6.9.7
"@typescript-eslint/eslint-plugin": 4.31.0_f4e6dc0776b3600ef484e3c64a523136 "@typescript-eslint/eslint-plugin": 4.31.0_f4e6dc0776b3600ef484e3c64a523136
"@typescript-eslint/parser": 4.31.0_eslint@7.30.0+typescript@4.4.2 "@typescript-eslint/parser": 4.31.0_eslint@7.30.0+typescript@4.4.2
"@vitejs/plugin-vue": 1.9.4_vite@2.6.14 "@vitejs/plugin-vue": 1.9.4_vite@2.6.14
@ -1017,6 +1025,13 @@ packages:
} }
dev: true dev: true
/@types/js-cookie/3.0.1:
resolution:
{
integrity: sha512-7wg/8gfHltklehP+oyJnZrz9XBuX5ZPP4zB6UsI84utdlkRYLnOm2HfpLXazTwZA+fpGn0ir8tGNgVnMEleBGQ==
}
dev: true
/@types/json-schema/7.0.9: /@types/json-schema/7.0.9:
resolution: resolution:
{ {
@ -1089,6 +1104,13 @@ packages:
} }
dev: true dev: true
/@types/qs/6.9.7:
resolution:
{
integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
}
dev: true
/@types/resolve/1.17.1: /@types/resolve/1.17.1:
resolution: resolution:
{ {
@ -1870,6 +1892,16 @@ packages:
engines: { node: ">=6" } engines: { node: ">=6" }
dev: true 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: /callsites/3.1.0:
resolution: resolution:
{ {
@ -3379,7 +3411,6 @@ packages:
{ {
integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
} }
dev: true
/functional-red-black-tree/1.0.1: /functional-red-black-tree/1.0.1:
resolution: { integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= } resolution: { integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= }
@ -3401,6 +3432,17 @@ packages:
engines: { node: 6.* || 8.* || >= 10.* } engines: { node: 6.* || 8.* || >= 10.* }
dev: true 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: /get-own-enumerable-property-symbols/3.0.2:
resolution: resolution:
{ {
@ -3586,6 +3628,14 @@ packages:
engines: { node: ">=8" } engines: { node: ">=8" }
dev: true dev: true
/has-symbols/1.0.2:
resolution:
{
integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
}
engines: { node: ">= 0.4" }
dev: false
/has/1.0.3: /has/1.0.3:
resolution: resolution:
{ {
@ -3594,7 +3644,6 @@ packages:
engines: { node: ">= 0.4.0" } engines: { node: ">= 0.4.0" }
dependencies: dependencies:
function-bind: 1.1.1 function-bind: 1.1.1
dev: true
/hash-sum/2.0.0: /hash-sum/2.0.0:
resolution: resolution:
@ -3932,6 +3981,14 @@ packages:
resolution: { integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= } resolution: { integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= }
dev: true dev: true
/js-cookie/3.0.1:
resolution:
{
integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
}
engines: { node: ">=12" }
dev: false
/js-tokens/4.0.0: /js-tokens/4.0.0:
resolution: resolution:
{ {
@ -4576,6 +4633,13 @@ packages:
resolution: { integrity: sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= } resolution: { integrity: sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= }
dev: true dev: true
/object-inspect/1.11.1:
resolution:
{
integrity: sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==
}
dev: false
/on-finished/2.3.0: /on-finished/2.3.0:
resolution: { integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= } resolution: { integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= }
engines: { node: ">= 0.8" } engines: { node: ">= 0.8" }
@ -5103,6 +5167,16 @@ packages:
engines: { node: ">=0.6.0", teleport: ">=0.2.0" } engines: { node: ">=0.6.0", teleport: ">=0.2.0" }
dev: true 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: /queue-microtask/1.2.3:
resolution: resolution:
{ {
@ -5469,6 +5543,17 @@ packages:
engines: { node: ">=8" } engines: { node: ">=8" }
dev: true 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: /signal-exit/3.0.5:
resolution: resolution:
{ {

4
src/api/routes.ts

@ -1,5 +1,5 @@
import { http } from "../utils/http"; 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

@ -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

After

Width: 400  |  Height: 400  |  Size: 23 KiB

8
src/config/index.ts

@ -1,6 +1,9 @@
import { App } from "vue"; import { App } from "vue";
import axios from "axios"; import axios from "axios";
import { loadEnv } from "@build/index";
let config: object = {}; let config: object = {};
const { VITE_PUBLIC_PATH } = loadEnv();
const setConfig = (cfg?: unknown) => { const setConfig = (cfg?: unknown) => {
config = Object.assign(config, cfg); config = Object.assign(config, cfg);
@ -30,10 +33,7 @@ export const getServerConfig = async (app: App): Promise<undefined> => {
return axios({ return axios({
baseURL: "", baseURL: "",
method: "get", method: "get",
url:
process.env.NODE_ENV === "production"
? "/manages/serverConfig.json"
: "/serverConfig.json"
url: `${VITE_PUBLIC_PATH}serverConfig.json`
}) })
.then(({ data: config }) => { .then(({ data: config }) => {
let $config = app.config.globalProperties.$config; let $config = app.config.globalProperties.$config;

2
src/layout/components/appMain.vue

@ -75,7 +75,7 @@ const transitionMain = defineComponent({
hideTabs && layout ? 'padding-top: 48px;' : '', hideTabs && layout ? 'padding-top: 48px;' : '',
!hideTabs && layout ? 'padding-top: 85px;' : '', !hideTabs && layout ? 'padding-top: 85px;' : '',
hideTabs && !layout ? 'padding-top: 48px' : '', hideTabs && !layout ? 'padding-top: 48px' : '',
!hideTabs && !layout ? 'padding-top: 98px;' : ''
!hideTabs && !layout ? 'padding-top: 85px;' : ''
]" ]"
> >
<router-view> <router-view>

17
src/layout/components/navbar.vue

@ -1,17 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { emitter } from "/@/utils/mitt"; 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 Hamburger from "./sidebar/hamBurger.vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { storageSession } from "/@/utils/storage"; import { storageSession } from "/@/utils/storage";
import Breadcrumb from "./sidebar/breadCrumb.vue"; import Breadcrumb from "./sidebar/breadCrumb.vue";
import Notice from "./notice/index.vue";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import { unref, watch, getCurrentInstance } from "vue"; import { unref, watch, getCurrentInstance } from "vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import screenfull from "../components/screenfull/index.vue"; import screenfull from "../components/screenfull/index.vue";
import globalization from "/@/assets/svg/globalization.svg"; import globalization from "/@/assets/svg/globalization.svg";
import { transformI18n } from "/@/utils/i18n";
const instance = const instance =
getCurrentInstance().appContext.config.globalProperties.$storage; getCurrentInstance().appContext.config.globalProperties.$storage;
@ -106,16 +107,15 @@ function translationEn() {
<!-- 退出登陆 --> <!-- 退出登陆 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link"> <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> <p>{{ usename }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <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> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@ -254,6 +254,7 @@ function translationEn() {
.logout { .logout {
.el-dropdown-menu__item { .el-dropdown-menu__item {
display: inline-flex;
padding: 0 18px !important; padding: 0 18px !important;
} }

2
src/layout/components/sidebar/breadCrumb.vue

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { useRoute, useRouter, RouteLocationMatched } from "vue-router"; import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
import { transformI18n } from "/@/utils/i18n";
import { transformI18n } from "/@/plugins/i18n";
const levelList = ref([]); const levelList = ref([]);
const route = useRoute(); const route = useRoute();

13
src/layout/components/sidebar/horizontal.vue

@ -12,6 +12,7 @@ import { emitter } from "/@/utils/mitt";
import Notice from "../notice/index.vue"; import Notice from "../notice/index.vue";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import SidebarItem from "./sidebarItem.vue"; import SidebarItem from "./sidebarItem.vue";
import avatars from "/@/assets/avatars.jpg";
import { algorithm } from "/@/utils/algorithm"; import { algorithm } from "/@/utils/algorithm";
import screenfull from "../screenfull/index.vue"; import screenfull from "../screenfull/index.vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
@ -174,16 +175,15 @@ onMounted(() => {
<!-- 退出登陆 --> <!-- 退出登陆 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link"> <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> <p>{{ usename }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <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> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@ -225,6 +225,7 @@ onMounted(() => {
.logout { .logout {
.el-dropdown-menu__item { .el-dropdown-menu__item {
display: inline-flex;
padding: 0 18px !important; padding: 0 18px !important;
} }

2
src/layout/components/sidebar/sidebarItem.vue

@ -4,7 +4,7 @@ import { PropType, ref, nextTick, getCurrentInstance } from "vue";
import { childrenType } from "../../types"; import { childrenType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import Icon from "/@/components/ReIcon/src/Icon.vue"; import Icon from "/@/components/ReIcon/src/Icon.vue";
import { transformI18n } from "/@/utils/i18n";
import { transformI18n } from "/@/plugins/i18n";
import { findIconReg } from "/@/components/ReIcon"; import { findIconReg } from "/@/components/ReIcon";
const instance = getCurrentInstance().appContext.app.config.globalProperties; const instance = getCurrentInstance().appContext.app.config.globalProperties;
const menuMode = instance.$storage.layout?.layout === "vertical"; const menuMode = instance.$storage.layout?.layout === "vertical";

3
src/layout/components/tag/index.vue

@ -18,7 +18,7 @@ import closeOther from "/@/assets/svg/close_other.svg";
import closeRight from "/@/assets/svg/close_right.svg"; import closeRight from "/@/assets/svg/close_right.svg";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { transformI18n } from "/@/utils/i18n";
import { transformI18n } from "/@/plugins/i18n";
import { storageLocal } from "/@/utils/storage"; import { storageLocal } from "/@/utils/storage";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { RouteConfigs, tagsViewsType } from "../../types"; import { RouteConfigs, tagsViewsType } from "../../types";
@ -251,6 +251,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
obj obj
]); ]);
} else { } else {
// @ts-ignore
delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", { delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", {
startIndex, startIndex,
length length

24
src/plugins/i18n/index.ts

@ -13,3 +13,27 @@ export const i18n = createI18n({
export function usI18n(app: App) { export function usI18n(app: App) {
app.use(i18n); 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;
}
}

32
src/router/index.ts

@ -1,16 +1,18 @@
import { import {
Router, Router,
RouteMeta,
createRouter, createRouter,
RouteComponent, RouteComponent,
RouteRecordName,
createWebHashHistory, createWebHashHistory,
RouteRecordNormalized RouteRecordNormalized
} from "vue-router"; } 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 { openLink } from "/@/utils/link";
import NProgress from "/@/utils/progress"; import NProgress from "/@/utils/progress";
import { split, uniqBy } from "lodash-es";
import { useTimeoutFn } from "@vueuse/core"; import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "/@/layout/types";
import { transformI18n } from "/@/plugins/i18n";
import { storageSession, storageLocal } from "/@/utils/storage"; import { storageSession, storageLocal } from "/@/utils/storage";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
@ -224,7 +226,29 @@ router.beforeEach((to, _from, next) => {
if (usePermissionStoreHook().wholeRoutes.length === 0) if (usePermissionStoreHook().wholeRoutes.length === 0)
initRouter(name.username).then((router: Router) => { initRouter(name.username).then((router: Router) => {
if (!useMultiTagsStoreHook().getMultiTagsCache) { 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); router.push(to.path);
// 刷新页面更新标签栏与页面路由匹配 // 刷新页面更新标签栏与页面路由匹配

16
src/store/modules/app.ts

@ -1,23 +1,13 @@
import { storageLocal } from "/@/utils/storage"; import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import { defineStore } from "pinia";
import { store } from "/@/store"; import { store } from "/@/store";
import { appType } from "./types";
import { defineStore } from "pinia";
import { getConfig } from "/@/config"; import { getConfig } from "/@/config";
interface AppState {
sidebar: {
opened: boolean;
withoutAnimation: boolean;
// 判断是否手动点击Hamburger
isClickHamburger: boolean;
};
layout: string;
device: string;
}
export const useAppStore = defineStore({ export const useAppStore = defineStore({
id: "pure-app", id: "pure-app",
state: (): AppState => ({
state: (): appType => ({
sidebar: { sidebar: {
opened: storageLocal.getItem("sidebarStatus") opened: storageLocal.getItem("sidebarStatus")
? !!+storageLocal.getItem("sidebarStatus") ? !!+storageLocal.getItem("sidebarStatus")

15
src/store/modules/multiTags.ts

@ -1,15 +1,8 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { store } from "/@/store"; import { store } from "/@/store";
import { getConfig } from "/@/config"; import { getConfig } from "/@/config";
import { positionType } from "./types";
import { storageLocal } from "/@/utils/storage"; import { storageLocal } from "/@/utils/storage";
interface Itag {
path: string;
parentPath: string;
name: string;
meta: any;
}
import { multiType, positionType } from "./types";
export const useMultiTagsStore = defineStore({ export const useMultiTagsStore = defineStore({
id: "pure-multiTags", id: "pure-multiTags",
@ -43,16 +36,16 @@ export const useMultiTagsStore = defineStore({
}, },
handleTags<T>( handleTags<T>(
mode: string, mode: string,
value?: T | Itag,
value?: T | multiType,
position?: positionType position?: positionType
): any {
): T {
switch (mode) { switch (mode) {
case "equal": case "equal":
this.multiTags = value; this.multiTags = value;
break; break;
case "push": case "push":
{ {
const tagVal = value as Itag;
const tagVal = value as multiType;
// 判断tag是否已存在: // 判断tag是否已存在:
const tagHasExits = this.multiTags.some(tag => { const tagHasExits = this.multiTags.some(tag => {
return tag.path === tagVal?.path; return tag.path === tagVal?.path;

9
src/store/modules/settings.ts

@ -1,16 +1,11 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { store } from "/@/store"; import { store } from "/@/store";
import { setType } from "./types";
import { getConfig } from "/@/config"; import { getConfig } from "/@/config";
interface SettingState {
title: string;
fixedHeader: boolean;
hiddenSideBar: boolean;
}
export const useSettingStore = defineStore({ export const useSettingStore = defineStore({
id: "pure-setting", id: "pure-setting",
state: (): SettingState => ({
state: (): setType => ({
title: getConfig().Title, title: getConfig().Title,
fixedHeader: getConfig().FixedHeader, fixedHeader: getConfig().FixedHeader,
hiddenSideBar: getConfig().HiddenSideBar hiddenSideBar: getConfig().HiddenSideBar

29
src/store/modules/types.ts

@ -9,3 +9,32 @@ export type positionType = {
startIndex?: number; startIndex?: number;
length?: 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

@ -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

@ -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

@ -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 });
```

32
src/utils/http/config.ts

@ -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"];

236
src/utils/http/core.ts

@ -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;

168
src/utils/http/index.ts

@ -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();

45
src/utils/http/types.d.ts

@ -1,50 +1,39 @@
import Axios, { import Axios, {
AxiosRequestConfig,
Canceler,
AxiosResponse,
Method, Method,
AxiosError
AxiosError,
AxiosResponse,
AxiosRequestConfig
} from "axios"; } from "axios";
import { METHODS } from "./config";
export type cancelTokenType = { cancelKey: string; cancelExecutor: Canceler };
export type resultType = {
accessToken?: string;
};
export type RequestMethods = Extract< export type RequestMethods = Extract<
Method, Method,
"get" | "post" | "put" | "delete" | "patch" | "option" | "head" "get" | "post" | "put" | "delete" | "patch" | "option" | "head"
>; >;
export interface EnclosureHttpRequestConfig extends AxiosRequestConfig {
beforeRequestCallback?: (request: EnclosureHttpRequestConfig) => void; // 请求发送之前
beforeResponseCallback?: (response: EnclosureHttpResoponse) => void; // 相应返回之前
export interface PureHttpError extends AxiosError {
isCancelRequest?: boolean;
} }
export interface EnclosureHttpResoponse extends AxiosResponse {
config: EnclosureHttpRequestConfig;
export interface PureHttpResoponse extends AxiosResponse {
config: PureHttpRequestConfig;
} }
export interface EnclosureHttpError extends AxiosError {
isCancelRequest?: boolean;
export interface PureHttpRequestConfig extends AxiosRequestConfig {
beforeRequestCallback?: (request: PureHttpRequestConfig) => void;
beforeResponseCallback?: (response: PureHttpResoponse) => void;
} }
export default class EnclosureHttp {
cancelTokenList: Array<cancelTokenType>;
clearCancelTokenList(): void;
export default class PureHttp {
request<T>( request<T>(
method: RequestMethods, method: RequestMethods,
url: string, url: string,
param?: AxiosRequestConfig, 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>; ): Promise<T>;
post<T>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<T>;
get<T>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<T>;
} }

29
src/utils/http/utils.ts

@ -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
};
}

19
src/utils/i18n.ts

@ -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;
}
}

53
src/utils/storage/cookie.ts

@ -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();

93
src/utils/storage/db.ts

@ -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

@ -65,21 +65,9 @@ declare global {
declare interface ViteEnv { declare interface ViteEnv {
VITE_PORT: number; VITE_PORT: number;
VITE_TITLE: string;
VITE_VERSION: string;
VITE_USE_MOCK: boolean;
VITE_USE_PWA: boolean;
VITE_PUBLIC_PATH: string; 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 { declare interface ServerConfigs {

55
vite.config.ts

@ -1,40 +1,39 @@
import { resolve } from "path"; import { resolve } from "path";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import svgLoader from "vite-svg-loader";
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from "@vitejs/plugin-vue-jsx";
import { warpperEnv } from "./build/utils";
import { createProxy } from "./build/proxy";
import { warpperEnv, regExps } from "./build";
import { viteMockServe } from "vite-plugin-mock"; import { viteMockServe } from "vite-plugin-mock";
import svgLoader from "vite-svg-loader";
import ElementPlus from "unplugin-element-plus/vite"; import ElementPlus from "unplugin-element-plus/vite";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor"; import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
// 当前执行node命令时文件夹的地址(工作目录)
const root: string = process.cwd();
// 路径查找
const pathResolve = (dir: string): string => { const pathResolve = (dir: string): string => {
return resolve(__dirname, ".", dir); return resolve(__dirname, ".", dir);
}; };
// 设置别名
const alias: Record<string, string> = { const alias: Record<string, string> = {
"/@": pathResolve("src"), "/@": pathResolve("src"),
"@build": pathResolve("build"), "@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" "vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js"
}; };
const root: string = process.cwd();
export default ({ command, mode }: ConfigEnv): UserConfigExport => { 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; const prodMock = true;
return { return {
/**
*
* /manages/ /manages/
* @default '/'
*/
base:
process.env.NODE_ENV === "production" ? "/manages/" : VITE_PUBLIC_PATH,
base: VITE_PUBLIC_PATH,
root, root,
resolve: { resolve: {
alias alias
@ -43,18 +42,27 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
server: { server: {
// 是否开启 https // 是否开启 https
https: false, https: false,
/**
*
* @default 3000
*/
// 端口号
port: VITE_PORT, port: VITE_PORT,
host: "0.0.0.0", 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: [ plugins: [
vue(), vue(),
// jsx、tsx语法支持
vueJsx(), vueJsx(),
// 自定义主题
themePreprocessorPlugin({ themePreprocessorPlugin({
scss: { scss: {
multipleScopeVars: [ multipleScopeVars: [
@ -111,8 +119,10 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
customThemeCssFileName: scopeName => scopeName customThemeCssFileName: scopeName => scopeName
} }
}), }),
// svg组件化支持
svgLoader(), svgLoader(),
ElementPlus({}), ElementPlus({}),
// mock支持
viteMockServe({ viteMockServe({
mockPath: "mock", mockPath: "mock",
localEnabled: command === "serve", localEnabled: command === "serve",
@ -132,7 +142,6 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
exclude: ["@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"] exclude: ["@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"]
}, },
build: { build: {
// @ts-ignore
sourcemap: false, sourcemap: false,
brotliSize: false, brotliSize: false,
// 消除打包大小超过500kb警告 // 消除打包大小超过500kb警告

Loading…
Cancel
Save