Commit 234c1d1fae6a7f2c78e456f992f91622ca599060
1 parent
3a132f3f
feat: the cache can be configured to be encrypted
Showing
18 changed files
with
225 additions
and
110 deletions
CHANGELOG.zh_CN.md
package.json
@@ -37,16 +37,16 @@ | @@ -37,16 +37,16 @@ | ||
37 | "vditor": "^3.6.3", | 37 | "vditor": "^3.6.3", |
38 | "vue": "^3.0.2", | 38 | "vue": "^3.0.2", |
39 | "vue-i18n": "^9.0.0-beta.8", | 39 | "vue-i18n": "^9.0.0-beta.8", |
40 | - "vue-router": "^4.0.0-rc.3", | 40 | + "vue-router": "^4.0.0-rc.5", |
41 | "vuex": "^4.0.0-rc.1", | 41 | "vuex": "^4.0.0-rc.1", |
42 | "vuex-module-decorators": "^1.0.1", | 42 | "vuex-module-decorators": "^1.0.1", |
43 | - "xlsx": "^0.16.8", | 43 | + "xlsx": "^0.16.9", |
44 | "zxcvbn": "^4.4.2" | 44 | "zxcvbn": "^4.4.2" |
45 | }, | 45 | }, |
46 | "devDependencies": { | 46 | "devDependencies": { |
47 | "@commitlint/cli": "^11.0.0", | 47 | "@commitlint/cli": "^11.0.0", |
48 | "@commitlint/config-conventional": "^11.0.0", | 48 | "@commitlint/config-conventional": "^11.0.0", |
49 | - "@iconify/json": "^1.1.261", | 49 | + "@iconify/json": "^1.1.262", |
50 | "@ls-lint/ls-lint": "^1.9.2", | 50 | "@ls-lint/ls-lint": "^1.9.2", |
51 | "@purge-icons/generated": "^0.4.1", | 51 | "@purge-icons/generated": "^0.4.1", |
52 | "@types/echarts": "^4.9.1", | 52 | "@types/echarts": "^4.9.1", |
@@ -72,7 +72,7 @@ | @@ -72,7 +72,7 @@ | ||
72 | "cross-env": "^7.0.2", | 72 | "cross-env": "^7.0.2", |
73 | "dot-prop": "^6.0.1", | 73 | "dot-prop": "^6.0.1", |
74 | "dotenv": "^8.2.0", | 74 | "dotenv": "^8.2.0", |
75 | - "eslint": "^7.13.0", | 75 | + "eslint": "^7.14.0", |
76 | "eslint-config-prettier": "^6.15.0", | 76 | "eslint-config-prettier": "^6.15.0", |
77 | "eslint-plugin-prettier": "^3.1.4", | 77 | "eslint-plugin-prettier": "^3.1.4", |
78 | "eslint-plugin-vue": "^7.1.0", | 78 | "eslint-plugin-vue": "^7.1.0", |
src/components/Application/index.ts
1 | import AppLocalPicker from './src/AppLocalPicker.vue'; | 1 | import AppLocalPicker from './src/AppLocalPicker.vue'; |
2 | +import AppFooterToolbar from './src/AppFooterToolbar.vue'; | ||
3 | +import { withInstall } from '../util'; | ||
2 | 4 | ||
3 | -export { AppLocalPicker }; | 5 | +export { AppLocalPicker, AppFooterToolbar }; |
6 | + | ||
7 | +export default withInstall(AppLocalPicker, AppFooterToolbar); |
src/components/Footer/src/index.vue renamed to src/components/Application/src/AppFooterToolbar.vue
@@ -10,11 +10,14 @@ | @@ -10,11 +10,14 @@ | ||
10 | </template> | 10 | </template> |
11 | <script lang="ts"> | 11 | <script lang="ts"> |
12 | import { defineComponent, computed, unref } from 'vue'; | 12 | import { defineComponent, computed, unref } from 'vue'; |
13 | + | ||
13 | import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; | 14 | import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; |
15 | + | ||
14 | import { appStore } from '/@/store/modules/app'; | 16 | import { appStore } from '/@/store/modules/app'; |
15 | import { menuStore } from '/@/store/modules/menu'; | 17 | import { menuStore } from '/@/store/modules/menu'; |
18 | + | ||
16 | export default defineComponent({ | 19 | export default defineComponent({ |
17 | - name: 'AppFooter', | 20 | + name: 'AppFooterToolbar', |
18 | setup() { | 21 | setup() { |
19 | const getMiniWidth = computed(() => { | 22 | const getMiniWidth = computed(() => { |
20 | const { | 23 | const { |
src/components/Authority/index.ts
1 | -import type { App } from 'vue'; | ||
2 | import Authority from './src/index.vue'; | 1 | import Authority from './src/index.vue'; |
3 | 2 | ||
4 | -export default (app: App): void => { | ||
5 | - app.component(Authority.name, Authority); | ||
6 | -}; | 3 | +import { withInstall } from '../util'; |
4 | + | ||
5 | +export default withInstall(Authority); | ||
7 | 6 | ||
8 | export { Authority }; | 7 | export { Authority }; |
src/components/Footer/index.ts deleted
100644 → 0
1 | -export { default as AppFooter } from './src/index.vue'; |
src/components/registerGlobComp.ts
1 | import Icon from './Icon/index'; | 1 | import Icon from './Icon/index'; |
2 | import Button from './Button/index.vue'; | 2 | import Button from './Button/index.vue'; |
3 | -import { AppFooter } from './Footer'; | ||
4 | import { | 3 | import { |
5 | // Need | 4 | // Need |
6 | Button as AntButton, | 5 | Button as AntButton, |
@@ -35,7 +34,7 @@ import { | @@ -35,7 +34,7 @@ import { | ||
35 | } from 'ant-design-vue'; | 34 | } from 'ant-design-vue'; |
36 | import { getApp } from '/@/setup/App'; | 35 | import { getApp } from '/@/setup/App'; |
37 | 36 | ||
38 | -const compList = [Icon, Button, AntButton.Group, AppFooter]; | 37 | +const compList = [Icon, Button, AntButton.Group]; |
39 | 38 | ||
40 | // Fix hmr multiple registered components | 39 | // Fix hmr multiple registered components |
41 | let registered = false; | 40 | let registered = false; |
src/components/util.ts
1 | import type { VNodeChild } from 'vue'; | 1 | import type { VNodeChild } from 'vue'; |
2 | +import type { App, Component } from 'vue'; | ||
3 | + | ||
4 | +export function withInstall(...components: Component[]) { | ||
5 | + return (app: App) => { | ||
6 | + components.forEach((comp) => { | ||
7 | + comp.name && app.component(comp.name, comp); | ||
8 | + }); | ||
9 | + }; | ||
10 | +} | ||
2 | 11 | ||
3 | export function convertToUnit( | 12 | export function convertToUnit( |
4 | str: string | number | null | undefined, | 13 | str: string | number | null | undefined, |
src/settings/cipherSetting.ts renamed to src/settings/encryptionSetting.ts
1 | +import { isDevMode } from '/@/utils/env'; | ||
2 | + | ||
1 | // System default cache time, in seconds | 3 | // System default cache time, in seconds |
2 | export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; | 4 | export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; |
3 | 5 | ||
4 | -/** | ||
5 | - * @description: | ||
6 | - */ | ||
7 | -export const storageCipher = { | 6 | +// aes encryption key |
7 | +export const cacheCipher = { | ||
8 | key: '_12345678901234@', | 8 | key: '_12345678901234@', |
9 | iv: '@12345678901234_', | 9 | iv: '@12345678901234_', |
10 | }; | 10 | }; |
11 | + | ||
12 | +// Whether the system cache is encrypted using aes | ||
13 | +export const enableStorageEncryption = !isDevMode(); |
src/utils/cache/cookie.ts
0 → 100644
1 | +import { DEFAULT_CACHE_TIME } from '../../settings/encryptionSetting'; | ||
2 | +import { getStorageShortName } from '/@/utils/helper/envHelper'; | ||
3 | +import { cacheCipher } from '/@/settings/encryptionSetting'; | ||
4 | +import Encryption from '/@/utils/encryption/aesEncryption'; | ||
5 | + | ||
6 | +export default class WebCookie { | ||
7 | + private encryption: Encryption; | ||
8 | + private hasEncrypt: boolean; | ||
9 | + | ||
10 | + constructor(hasEncrypt = true, key = cacheCipher.key, iv = cacheCipher.iv) { | ||
11 | + const encryption = new Encryption({ key, iv }); | ||
12 | + this.encryption = encryption; | ||
13 | + this.hasEncrypt = hasEncrypt; | ||
14 | + } | ||
15 | + | ||
16 | + private getKey(key: string) { | ||
17 | + return `${getStorageShortName()}${key}`.toUpperCase(); | ||
18 | + } | ||
19 | + | ||
20 | + /** | ||
21 | + * Add cookie | ||
22 | + * @param name cookie key | ||
23 | + * @param value cookie value | ||
24 | + * @param expire | ||
25 | + * If the expiration time is not set, the default management browser will automatically delete | ||
26 | + * e.g: | ||
27 | + * cookieData.set('name','value',) | ||
28 | + */ | ||
29 | + setCookie(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { | ||
30 | + value = this.hasEncrypt ? this.encryption.encryptByAES(JSON.stringify(value)) : value; | ||
31 | + document.cookie = this.getKey(key) + '=' + value + '; Max-Age=' + expire; | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * Get the cook value according to the key | ||
36 | + * @param key cookie key | ||
37 | + */ | ||
38 | + getCookie(key: string) { | ||
39 | + const arr = document.cookie.split('; '); | ||
40 | + for (let i = 0; i < arr.length; i++) { | ||
41 | + const arr2 = arr[i].split('='); | ||
42 | + if (arr2[0] === this.getKey(key)) { | ||
43 | + let message: any = null; | ||
44 | + const str = arr2[1]; | ||
45 | + if (this.hasEncrypt && str) { | ||
46 | + message = this.encryption.decryptByAES(str); | ||
47 | + try { | ||
48 | + return JSON.parse(message); | ||
49 | + } catch (e) { | ||
50 | + return str; | ||
51 | + } | ||
52 | + } | ||
53 | + return str; | ||
54 | + } | ||
55 | + } | ||
56 | + return ''; | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * Delete cookie based on cookie key | ||
61 | + * @param key cookie key | ||
62 | + */ | ||
63 | + removeCookie(key: string) { | ||
64 | + this.setCookie(key, 1, -1); | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * clear cookie | ||
69 | + */ | ||
70 | + clearCookie(): void { | ||
71 | + const keys = document.cookie.match(/[^ =;]+(?==)/g); | ||
72 | + if (keys) { | ||
73 | + for (let i = keys.length; i--; ) { | ||
74 | + document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString(); | ||
75 | + } | ||
76 | + } | ||
77 | + } | ||
78 | +} |
src/utils/storage/index.ts renamed to src/utils/cache/index.ts
1 | import { getStorageShortName } from '/@/utils/helper/envHelper'; | 1 | import { getStorageShortName } from '/@/utils/helper/envHelper'; |
2 | -import { createStorage as create } from './Storage'; | ||
3 | - | ||
4 | -// debug模式下不加密 | 2 | +import { createStorage as create } from './storageCache'; |
3 | +import { enableStorageEncryption } from '/@/settings/encryptionSetting'; | ||
5 | 4 | ||
6 | const createOptions = (storage = sessionStorage) => { | 5 | const createOptions = (storage = sessionStorage) => { |
7 | return { | 6 | return { |
7 | + // No encryption in debug mode | ||
8 | + hasEncrypt: enableStorageEncryption, | ||
8 | storage, | 9 | storage, |
9 | prefixKey: getStorageShortName(), | 10 | prefixKey: getStorageShortName(), |
10 | }; | 11 | }; |
11 | }; | 12 | }; |
13 | + | ||
12 | export const WebStorage = create(createOptions()); | 14 | export const WebStorage = create(createOptions()); |
13 | 15 | ||
14 | export const createStorage = (storage: Storage = sessionStorage) => { | 16 | export const createStorage = (storage: Storage = sessionStorage) => { |
15 | return create(createOptions(storage))!; | 17 | return create(createOptions(storage))!; |
16 | }; | 18 | }; |
19 | + | ||
17 | export default WebStorage; | 20 | export default WebStorage; |
src/utils/storage/Storage.ts renamed to src/utils/cache/storageCache.ts
1 | -import { DEFAULT_CACHE_TIME } from '/@/settings/cipherSetting'; | 1 | +import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; |
2 | +import { cacheCipher } from '/@/settings/encryptionSetting'; | ||
3 | +import Encryption, { EncryptionParams } from '/@/utils/encryption/aesEncryption'; | ||
2 | 4 | ||
3 | -// import { EncryptionParams } from '/@/utils/cipher/aesEncryption'; | ||
4 | -export interface CreateStorageParams { | 5 | +export interface CreateStorageParams extends EncryptionParams { |
5 | storage: Storage; | 6 | storage: Storage; |
7 | + | ||
6 | hasEncrypt: boolean; | 8 | hasEncrypt: boolean; |
7 | } | 9 | } |
8 | -export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) => { | 10 | +export const createStorage = ({ |
11 | + prefixKey = '', | ||
12 | + storage = sessionStorage, | ||
13 | + key = cacheCipher.key, | ||
14 | + iv = cacheCipher.iv, | ||
15 | + hasEncrypt = true, | ||
16 | +} = {}) => { | ||
17 | + if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) { | ||
18 | + throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!'); | ||
19 | + } | ||
20 | + | ||
21 | + const encryption = new Encryption({ key, iv }); | ||
22 | + | ||
9 | /** | 23 | /** |
10 | - *缓存类 | ||
11 | - *构造参数可以传入 sessionStorage,localStorage, | 24 | + *Cache class |
25 | + *Construction parameters can be passed into sessionStorage, localStorage, | ||
12 | * @class Cache | 26 | * @class Cache |
13 | * @example | 27 | * @example |
14 | */ | 28 | */ |
15 | const WebStorage = class WebStorage { | 29 | const WebStorage = class WebStorage { |
16 | private storage: Storage; | 30 | private storage: Storage; |
17 | private prefixKey?: string; | 31 | private prefixKey?: string; |
18 | - | 32 | + private encryption: Encryption; |
33 | + private hasEncrypt: boolean; | ||
19 | /** | 34 | /** |
20 | * | 35 | * |
21 | * @param {*} storage | 36 | * @param {*} storage |
@@ -23,6 +38,8 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | @@ -23,6 +38,8 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | ||
23 | constructor() { | 38 | constructor() { |
24 | this.storage = storage; | 39 | this.storage = storage; |
25 | this.prefixKey = prefixKey; | 40 | this.prefixKey = prefixKey; |
41 | + this.encryption = encryption; | ||
42 | + this.hasEncrypt = hasEncrypt; | ||
26 | } | 43 | } |
27 | 44 | ||
28 | private getKey(key: string) { | 45 | private getKey(key: string) { |
@@ -31,10 +48,10 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | @@ -31,10 +48,10 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | ||
31 | 48 | ||
32 | /** | 49 | /** |
33 | * | 50 | * |
34 | - * 设置缓存 | ||
35 | - * @param {string} key 缓存键 | ||
36 | - * @param {*} value 缓存值 | ||
37 | - * @expire 过期时间 单位秒 | 51 | + * Set cache |
52 | + * @param {string} key | ||
53 | + * @param {*} value | ||
54 | + * @expire Expiration time in seconds | ||
38 | * @memberof Cache | 55 | * @memberof Cache |
39 | */ | 56 | */ |
40 | set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { | 57 | set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { |
@@ -42,21 +59,23 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | @@ -42,21 +59,23 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | ||
42 | value, | 59 | value, |
43 | expire: expire !== null ? new Date().getTime() + expire * 1000 : null, | 60 | expire: expire !== null ? new Date().getTime() + expire * 1000 : null, |
44 | }); | 61 | }); |
45 | - this.storage.setItem(this.getKey(key), stringData); | 62 | + const stringifyValue = this.hasEncrypt |
63 | + ? this.encryption.encryptByAES(stringData) | ||
64 | + : stringData; | ||
65 | + this.storage.setItem(this.getKey(key), stringifyValue); | ||
46 | } | 66 | } |
47 | 67 | ||
48 | /** | 68 | /** |
49 | - * | ||
50 | - *读取缓存 | ||
51 | - * @param {string} key 缓存键 | ||
52 | - * @returns 缓存值 | 69 | + *Read cache |
70 | + * @param {string} key | ||
53 | * @memberof Cache | 71 | * @memberof Cache |
54 | */ | 72 | */ |
55 | get(key: string, def: any = null): any { | 73 | get(key: string, def: any = null): any { |
56 | const item = this.storage.getItem(this.getKey(key)); | 74 | const item = this.storage.getItem(this.getKey(key)); |
57 | if (item) { | 75 | if (item) { |
58 | try { | 76 | try { |
59 | - const data = JSON.parse(item); | 77 | + const decItem = this.hasEncrypt ? this.encryption.decryptByAES(item) : item; |
78 | + const data = JSON.parse(decItem); | ||
60 | const { value, expire } = data; | 79 | const { value, expire } = data; |
61 | if (expire === null || expire >= new Date().getTime()) { | 80 | if (expire === null || expire >= new Date().getTime()) { |
62 | return value; | 81 | return value; |
@@ -70,9 +89,8 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | @@ -70,9 +89,8 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | ||
70 | } | 89 | } |
71 | 90 | ||
72 | /** | 91 | /** |
73 | - * | ||
74 | - *删除缓存 | ||
75 | - * @param {string} key 缓存键 | 92 | + * Delete cache based on key |
93 | + * @param {string} key | ||
76 | * @memberof Cache | 94 | * @memberof Cache |
77 | */ | 95 | */ |
78 | remove(key: string) { | 96 | remove(key: string) { |
@@ -80,59 +98,11 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | @@ -80,59 +98,11 @@ export const createStorage = ({ prefixKey = '', storage = sessionStorage } = {}) | ||
80 | } | 98 | } |
81 | 99 | ||
82 | /** | 100 | /** |
83 | - * | ||
84 | - *删除该实例所有缓存 | ||
85 | - * @memberof Cache | 101 | + * Delete all caches of this instance |
86 | */ | 102 | */ |
87 | clear(): void { | 103 | clear(): void { |
88 | this.storage.clear(); | 104 | this.storage.clear(); |
89 | } | 105 | } |
90 | - | ||
91 | - /** | ||
92 | - * 添加cookie | ||
93 | - * @param name cookie名字 | ||
94 | - * @param value cookie内容 | ||
95 | - * @param expire | ||
96 | - * 如果过期时间未设置,默认管理浏览器自动删除 | ||
97 | - * 例子: | ||
98 | - * cookieData.set('name','value',) | ||
99 | - */ | ||
100 | - setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) { | ||
101 | - document.cookie = this.getKey(name) + '=' + value + '; Max-Age=' + expire; | ||
102 | - } | ||
103 | - | ||
104 | - /** | ||
105 | - * 根据名字获取cooki值 | ||
106 | - * @param name cookie名 | ||
107 | - * @returns {*} cookie值 | ||
108 | - */ | ||
109 | - getCookie(name: string) { | ||
110 | - const arr = document.cookie.split('; '); | ||
111 | - for (let i = 0; i < arr.length; i++) { | ||
112 | - const arr2 = arr[i].split('='); | ||
113 | - if (arr2[0] === this.getKey(name)) { | ||
114 | - return arr2[1]; | ||
115 | - } | ||
116 | - } | ||
117 | - return ''; | ||
118 | - } | ||
119 | - | ||
120 | - /** | ||
121 | - * 根据cookie名字删除cookie | ||
122 | - * @param name cookie名字 | ||
123 | - */ | ||
124 | - removeCookie(key: string) { | ||
125 | - this.setCookie(key, 1, -1); | ||
126 | - } | ||
127 | - | ||
128 | - clearCookie(): void { | ||
129 | - const keys = document.cookie.match(/[^ =;]+(?==)/g); | ||
130 | - if (keys) { | ||
131 | - for (let i = keys.length; i--; ) { | ||
132 | - document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString(); | ||
133 | - } | ||
134 | - } | ||
135 | - } | ||
136 | }; | 106 | }; |
137 | return new WebStorage(); | 107 | return new WebStorage(); |
138 | }; | 108 | }; |
src/utils/encryption/aesEncryption.ts
0 → 100644
1 | +import CryptoES from 'crypto-es'; | ||
2 | +export interface EncryptionParams { | ||
3 | + key: string; | ||
4 | + iv: string; | ||
5 | +} | ||
6 | +export class Encryption { | ||
7 | + private key; | ||
8 | + | ||
9 | + private iv; | ||
10 | + | ||
11 | + constructor(opt: EncryptionParams) { | ||
12 | + const { key, iv } = opt; | ||
13 | + this.key = CryptoES.enc.Utf8.parse(key); | ||
14 | + this.iv = CryptoES.enc.Utf8.parse(iv); | ||
15 | + } | ||
16 | + | ||
17 | + get getOpt(): CryptoES.lib.CipherCfg { | ||
18 | + return { | ||
19 | + mode: CryptoES.mode.CBC as any, | ||
20 | + padding: CryptoES.pad.Pkcs7, | ||
21 | + iv: this.iv, | ||
22 | + }; | ||
23 | + } | ||
24 | + | ||
25 | + encryptByAES(str: string) { | ||
26 | + const encrypted = CryptoES.AES.encrypt(str, this.key, this.getOpt); | ||
27 | + return encrypted.toString(); | ||
28 | + } | ||
29 | + | ||
30 | + decryptByAES(str: string) { | ||
31 | + const decrypted = CryptoES.AES.decrypt(str, this.key, this.getOpt); | ||
32 | + return decrypted.toString(CryptoES.enc.Utf8); | ||
33 | + } | ||
34 | +} | ||
35 | +export default Encryption; |
src/utils/file/base64.ts renamed to src/utils/file/base64Conver.ts
src/utils/file/download.ts
src/utils/helper/persistent.ts
1 | -import { createStorage } from '/@/utils/storage'; | 1 | +import { createStorage } from '/@/utils/cache'; |
2 | import { isIeFn } from '/@/utils/browser'; | 2 | import { isIeFn } from '/@/utils/browser'; |
3 | 3 | ||
4 | import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum'; | 4 | import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum'; |
src/views/demo/page/form/high/index.vue
@@ -16,21 +16,23 @@ | @@ -16,21 +16,23 @@ | ||
16 | </a-card> | 16 | </a-card> |
17 | </div> | 17 | </div> |
18 | 18 | ||
19 | - <app-footer> | 19 | + <AppFooterToolbar> |
20 | <template #right> | 20 | <template #right> |
21 | <a-button type="primary" @click="submitAll">提交</a-button> | 21 | <a-button type="primary" @click="submitAll">提交</a-button> |
22 | </template> | 22 | </template> |
23 | - </app-footer> | 23 | + </AppFooterToolbar> |
24 | </div> | 24 | </div> |
25 | </template> | 25 | </template> |
26 | <script lang="ts"> | 26 | <script lang="ts"> |
27 | import { BasicForm, useForm } from '/@/components/Form'; | 27 | import { BasicForm, useForm } from '/@/components/Form'; |
28 | import { defineComponent, ref } from 'vue'; | 28 | import { defineComponent, ref } from 'vue'; |
29 | import PersonTable from './PersonTable.vue'; | 29 | import PersonTable from './PersonTable.vue'; |
30 | + import { AppFooterToolbar } from '/@/components/Application'; | ||
31 | + | ||
30 | import { schemas, taskSchemas } from './data'; | 32 | import { schemas, taskSchemas } from './data'; |
31 | 33 | ||
32 | export default defineComponent({ | 34 | export default defineComponent({ |
33 | - components: { BasicForm, PersonTable }, | 35 | + components: { BasicForm, PersonTable, AppFooterToolbar }, |
34 | setup() { | 36 | setup() { |
35 | const tableRef = ref<{ getDataSource: () => any } | null>(null); | 37 | const tableRef = ref<{ getDataSource: () => any } | null>(null); |
36 | 38 |
yarn.lock
@@ -1050,10 +1050,10 @@ | @@ -1050,10 +1050,10 @@ | ||
1050 | resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" | 1050 | resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" |
1051 | integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== | 1051 | integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== |
1052 | 1052 | ||
1053 | -"@iconify/json@^1.1.261": | ||
1054 | - version "1.1.261" | ||
1055 | - resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.261.tgz#9a6986b6b36d77ca147c4be149db9a43280a8fb2" | ||
1056 | - integrity sha512-lnRk1OBqNxZ593oZyOXEMp/O+cr+lF54xaW6+F3krWdWhzxQgi0W1ffzvdiLySdbTEorQ2NvVU4e0+Af27rXPA== | 1053 | +"@iconify/json@^1.1.262": |
1054 | + version "1.1.262" | ||
1055 | + resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.262.tgz#a67067bad418d59c729ec514e3aa629d0ab3710b" | ||
1056 | + integrity sha512-PfKUS/Ue9Rn2oO0ez/Yj4Cdodvv6vDHgPjIZNSElu2+149CYaPmWahHI87ZY+r8l3bijPIu6+blyAixdJtPPMg== | ||
1057 | 1057 | ||
1058 | "@koa/cors@^3.1.0": | 1058 | "@koa/cors@^3.1.0": |
1059 | version "3.1.0" | 1059 | version "3.1.0" |
@@ -3418,10 +3418,10 @@ eslint-visitor-keys@^2.0.0: | @@ -3418,10 +3418,10 @@ eslint-visitor-keys@^2.0.0: | ||
3418 | resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" | 3418 | resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" |
3419 | integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== | 3419 | integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== |
3420 | 3420 | ||
3421 | -eslint@^7.13.0: | ||
3422 | - version "7.13.0" | ||
3423 | - resolved "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da" | ||
3424 | - integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ== | 3421 | +eslint@^7.14.0: |
3422 | + version "7.14.0" | ||
3423 | + resolved "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz#2d2cac1d28174c510a97b377f122a5507958e344" | ||
3424 | + integrity sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA== | ||
3425 | dependencies: | 3425 | dependencies: |
3426 | "@babel/code-frame" "^7.0.0" | 3426 | "@babel/code-frame" "^7.0.0" |
3427 | "@eslint/eslintrc" "^0.2.1" | 3427 | "@eslint/eslintrc" "^0.2.1" |
@@ -3686,6 +3686,11 @@ fastq@^1.6.0: | @@ -3686,6 +3686,11 @@ fastq@^1.6.0: | ||
3686 | dependencies: | 3686 | dependencies: |
3687 | reusify "^1.0.4" | 3687 | reusify "^1.0.4" |
3688 | 3688 | ||
3689 | +fflate@^0.3.8: | ||
3690 | + version "0.3.10" | ||
3691 | + resolved "https://registry.npmjs.org/fflate/-/fflate-0.3.10.tgz#0e581839a53203d2eeac7e61ce3652d855e24dcd" | ||
3692 | + integrity sha512-s5j69APkUPPbzdI20Ix4pPtQP+1Qi58YcFRpE7aO/P1kEywUYjbl2RjZRVEMdnySO9pr4MB0BHPbxkiahrtD/Q== | ||
3693 | + | ||
3689 | figures@^2.0.0: | 3694 | figures@^2.0.0: |
3690 | version "2.0.0" | 3695 | version "2.0.0" |
3691 | resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" | 3696 | resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" |
@@ -8196,10 +8201,10 @@ vue-i18n@^9.0.0-beta.8: | @@ -8196,10 +8201,10 @@ vue-i18n@^9.0.0-beta.8: | ||
8196 | dependencies: | 8201 | dependencies: |
8197 | source-map "0.6.1" | 8202 | source-map "0.6.1" |
8198 | 8203 | ||
8199 | -vue-router@^4.0.0-rc.3: | ||
8200 | - version "4.0.0-rc.3" | ||
8201 | - resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.3.tgz#70d18e90030bc6a25e81a30401d673223998ec6b" | ||
8202 | - integrity sha512-NnPqWIfanEhJC4wu8BEFBmnEDIrx9ST0/HtmBiE+oV2MQlhyRk1TmdttWwVqx6Sh7kONsrI10GQV9l3YEkcWXg== | 8204 | +vue-router@^4.0.0-rc.5: |
8205 | + version "4.0.0-rc.5" | ||
8206 | + resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.5.tgz#191d32e3d5276641ff21e881d34e33a71dc6e8f0" | ||
8207 | + integrity sha512-Q8Tt6VGwGMN5qASeIdjSydU3uRADK9AUkqnbnzmTz+zZKS0W6GZOAuP235lf3y5/MqEFSKRJGaTWPEY0t+Rjmg== | ||
8203 | 8208 | ||
8204 | vue-types@^3.0.0: | 8209 | vue-types@^3.0.0: |
8205 | version "3.0.1" | 8210 | version "3.0.1" |
@@ -8480,10 +8485,10 @@ ws@^7.3.1: | @@ -8480,10 +8485,10 @@ ws@^7.3.1: | ||
8480 | resolved "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" | 8485 | resolved "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" |
8481 | integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== | 8486 | integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== |
8482 | 8487 | ||
8483 | -xlsx@^0.16.8: | ||
8484 | - version "0.16.8" | ||
8485 | - resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.8.tgz#5546de9b0ba15169b36770d4e43b24790d3ff1b8" | ||
8486 | - integrity sha512-qWub4YCn0xLEGHI7WWhk6IJ73MDu7sPSJQImxN6/LiI8wsHi0hUhICEDbyqBT+jgFgORZxrii0HvhNSwBNAPoQ== | 8488 | +xlsx@^0.16.9: |
8489 | + version "0.16.9" | ||
8490 | + resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.16.9.tgz#dacd5bb46bda6dd3743940c9c3dc1e2171826256" | ||
8491 | + integrity sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw== | ||
8487 | dependencies: | 8492 | dependencies: |
8488 | adler-32 "~1.2.0" | 8493 | adler-32 "~1.2.0" |
8489 | cfb "^1.1.4" | 8494 | cfb "^1.1.4" |
@@ -8491,6 +8496,7 @@ xlsx@^0.16.8: | @@ -8491,6 +8496,7 @@ xlsx@^0.16.8: | ||
8491 | commander "~2.17.1" | 8496 | commander "~2.17.1" |
8492 | crc-32 "~1.2.0" | 8497 | crc-32 "~1.2.0" |
8493 | exit-on-epipe "~1.0.1" | 8498 | exit-on-epipe "~1.0.1" |
8499 | + fflate "^0.3.8" | ||
8494 | ssf "~0.11.2" | 8500 | ssf "~0.11.2" |
8495 | wmf "~1.0.1" | 8501 | wmf "~1.0.1" |
8496 | word "~0.3.0" | 8502 | word "~0.3.0" |