@pubinfo/module-crypto 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/api/modules/crypto/index.d.ts +1 -0
- package/dist/api/modules/crypto/pubSysConfigController.d.ts +22 -0
- package/dist/api/request.d.ts +1 -0
- package/dist/apiContext.d.ts +2 -0
- package/dist/context.d.ts +10 -0
- package/dist/cryptoAlg/aes.d.ts +4 -0
- package/dist/cryptoAlg/rsa.d.ts +1 -0
- package/dist/cryptoAlg/sm4-cbc.d.ts +4 -0
- package/dist/cryptoAlg/sm4-ecb.d.ts +4 -0
- package/dist/cryptoSetting.d.ts +6 -0
- package/dist/drawerCryptoUrl-pN2BPC_q.js +4 -0
- package/dist/drawerCryptoUrl.vue_vue_type_script_setup_true_lang-CztdWly1.js +136 -0
- package/dist/helper.d.ts +3 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9003 -0
- package/dist/interface.d.ts +18 -0
- package/dist/pages/enum.d.ts +7 -0
- package/dist/setting-2FfcqVsW.js +267 -0
- package/dist/setting.css +1 -0
- package/package.json +47 -0
- package/src/api/modules/crypto/index.ts +1 -0
- package/src/api/modules/crypto/pubSysConfigController.ts +52 -0
- package/src/api/modules/crypto/typings.d.ts +49 -0
- package/src/api/request.ts +4 -0
- package/src/apiContext.ts +4 -0
- package/src/assets/images/crypto-setting-bg.png +0 -0
- package/src/context.ts +12 -0
- package/src/cryptoAlg/aes.ts +52 -0
- package/src/cryptoAlg/rsa.ts +13 -0
- package/src/cryptoAlg/sm4-cbc.ts +49 -0
- package/src/cryptoAlg/sm4-ecb.ts +46 -0
- package/src/cryptoSetting.ts +67 -0
- package/src/helper.ts +21 -0
- package/src/index.ts +65 -0
- package/src/interface.ts +21 -0
- package/src/pages/components/drawerCryptoUrl.vue +116 -0
- package/src/pages/enum.ts +7 -0
- package/src/pages/setting.vue +273 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { RequestMethod } from 'pubinfo';
|
|
2
|
+
import type { cryptoInternal } from './interface';
|
|
3
|
+
import { ctx } from './context';
|
|
4
|
+
import { rsaEncrypt } from './cryptoAlg/rsa';
|
|
5
|
+
|
|
6
|
+
export function isMethodNeedCrypto(url: string, isCrypto: boolean, cryptoMode: boolean, whiteListUrls?: API.UrlEntry[]): boolean {
|
|
7
|
+
if (url === '/crypto/getPublicKeyAndConfig') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return !isUrlInWhiteList(url, cryptoMode, whiteListUrls) && isCrypto;
|
|
11
|
+
}
|
|
12
|
+
// URL匹配方法
|
|
13
|
+
export function isUrlInWhiteList(url: string, cryptoMode: boolean, whiteListUrls?: API.UrlEntry[]): boolean {
|
|
14
|
+
if (cryptoMode && whiteListUrls?.length === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
else if (!cryptoMode && whiteListUrls?.length === 0) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const urls = whiteListUrls?.map(item => item.url) || [];
|
|
22
|
+
const bo = urls.some((pattern) => {
|
|
23
|
+
pattern = pattern?.trim();
|
|
24
|
+
url = url.trim();
|
|
25
|
+
// 转义正则特殊字符,但保留 * 和 /
|
|
26
|
+
let regexPattern = pattern?.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
27
|
+
// 将 ** 替换为匹配任意字符(包括斜杠)的模式
|
|
28
|
+
regexPattern = regexPattern?.replace(/\*\*/g, '(.+)');
|
|
29
|
+
// 将 * 替换为匹配除斜杠外任意字符的模式
|
|
30
|
+
regexPattern = regexPattern?.replace(/(^|[^\\])\*/g, '$1[^/]*');
|
|
31
|
+
// 添加起始和结束锚点
|
|
32
|
+
regexPattern = `^${regexPattern}$`;
|
|
33
|
+
const regex = new RegExp(regexPattern);
|
|
34
|
+
return regex.test(url);
|
|
35
|
+
});
|
|
36
|
+
return cryptoMode ? bo : !bo;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function encryptMethod(method: RequestMethod, fun: (arg0: string) => Promise<string>) {
|
|
41
|
+
if (method.data && !(method.data instanceof FormData)) {
|
|
42
|
+
method.data = await fun(JSON.stringify(method.data));
|
|
43
|
+
}
|
|
44
|
+
if (method.config.params && JSON.stringify(method.config.params) !== '{}') {
|
|
45
|
+
// 格式化params,去除undefined值
|
|
46
|
+
const newParams = JSON.parse(JSON.stringify(method.config.params));
|
|
47
|
+
method.config.params = { secureData: decodeURIComponent(await fun(decodeURIComponent(new URLSearchParams(newParams).toString()))) };
|
|
48
|
+
}
|
|
49
|
+
if (method.config.meta && method.config.meta.pathParams) {
|
|
50
|
+
for (const item of method.config.meta.pathParams) {
|
|
51
|
+
method.url = method.url.replace(item, await fun(item));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 加密入口
|
|
57
|
+
export async function encrypt(method: RequestMethod, cryptoSetting: cryptoInternal) {
|
|
58
|
+
const KEY = ctx.use().key;
|
|
59
|
+
const iv = ctx.use().iv;
|
|
60
|
+
method.config.headers['X-Crypyto-Key'] = await rsaEncrypt(JSON.stringify({ 'Algorithm-Name': cryptoSetting?.name, 'Algorithm-Key': KEY, 'Algorithm-Iv': iv, ...cryptoSetting?.header }));
|
|
61
|
+
await encryptMethod(method, cryptoSetting.encrypt);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 解密入口
|
|
65
|
+
export async function decrypt(response: any, cryptoSetting: cryptoInternal) {
|
|
66
|
+
response.data = await cryptoSetting.decrypt(response.data);
|
|
67
|
+
}
|
package/src/helper.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RequestMethod } from 'pubinfo';
|
|
2
|
+
|
|
3
|
+
export function setMethodRole(method: RequestMethod) {
|
|
4
|
+
method.meta = {
|
|
5
|
+
authRole: null,
|
|
6
|
+
};
|
|
7
|
+
return method;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function generateKey(num: number) {
|
|
11
|
+
const library = 'ABCDEFabcdef0123456789';
|
|
12
|
+
let key = '';
|
|
13
|
+
if (num < 0) {
|
|
14
|
+
return key;
|
|
15
|
+
}
|
|
16
|
+
for (let i = 0; i < num; i++) {
|
|
17
|
+
const randomPoz = Math.floor(Math.random() * library.length);
|
|
18
|
+
key += library.substring(randomPoz, randomPoz + 1);
|
|
19
|
+
}
|
|
20
|
+
return key;
|
|
21
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ModuleOptions, RequestMethod } from 'pubinfo';
|
|
2
|
+
import type { CryptoOptions } from './interface';
|
|
3
|
+
import { defineIconModule, defineRouteModule } from 'pubinfo';
|
|
4
|
+
import modules from 'virtual:pubinfo-resolver';
|
|
5
|
+
import { getCryptoGetPublicKeyAndConfig } from './api/modules/crypto';
|
|
6
|
+
import { apictx } from './apiContext';
|
|
7
|
+
import { ctx } from './context';
|
|
8
|
+
import { setupAes } from './cryptoAlg/aes.ts';
|
|
9
|
+
import { decrypt, encrypt, isMethodNeedCrypto } from './cryptoSetting';
|
|
10
|
+
import { generateKey, setMethodRole } from './helper';
|
|
11
|
+
import 'uno.css';
|
|
12
|
+
|
|
13
|
+
export function crypto(options: CryptoOptions): ModuleOptions {
|
|
14
|
+
const { request, cryptoSetting } = options;
|
|
15
|
+
apictx.set({ request });
|
|
16
|
+
ctx.set({ isCrypto: false, cryptoMode: false, cryptoSetting: setupAes() });
|
|
17
|
+
defineRouteModule('crypto', modules.pages);
|
|
18
|
+
defineIconModule('crypto', modules.icons);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
name: 'pubinfo:crypto',
|
|
22
|
+
enforce: 'pre',
|
|
23
|
+
async setup() {
|
|
24
|
+
// 获取页面配置
|
|
25
|
+
const method = setMethodRole(getCryptoGetPublicKeyAndConfig());
|
|
26
|
+
const { data } = await method;
|
|
27
|
+
const key = generateKey(32);
|
|
28
|
+
const iv = generateKey(32);
|
|
29
|
+
ctx.callAsync({
|
|
30
|
+
publicKey: data.publicKey,
|
|
31
|
+
isCrypto: data.cryptoEnabled || false,
|
|
32
|
+
cryptoSetting,
|
|
33
|
+
cryptoMode: data.cryptoMode,
|
|
34
|
+
whiteListUrls: data.cryptoWhitePath,
|
|
35
|
+
// cbc加密模式的偏移量
|
|
36
|
+
iv,
|
|
37
|
+
key,
|
|
38
|
+
}, () => {});
|
|
39
|
+
},
|
|
40
|
+
hooks: {
|
|
41
|
+
'http:request': async (method: RequestMethod) => {
|
|
42
|
+
/**
|
|
43
|
+
* 如果X-Crypyto-Key存在 代表这是token过期后返回的接口,不需要再次加密
|
|
44
|
+
*/
|
|
45
|
+
if (!method.config?.headers?.['X-Crypyto-Key'] && isMethodNeedCrypto(method.url, ctx.use().isCrypto, ctx.use().cryptoMode, ctx.use().whiteListUrls)) {
|
|
46
|
+
await encrypt(method, ctx.use().cryptoSetting);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
'http:response': {
|
|
50
|
+
onSuccess: async (response: any) => {
|
|
51
|
+
// blob类型不解密
|
|
52
|
+
if (response.config?.responseType !== 'blob' && isMethodNeedCrypto(response.config.url, ctx.use().isCrypto, ctx.use().cryptoMode, ctx.use().whiteListUrls)) {
|
|
53
|
+
await decrypt(response, ctx.use().cryptoSetting);
|
|
54
|
+
}
|
|
55
|
+
return response;
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { setupAes } from './cryptoAlg/aes.ts';
|
|
63
|
+
export { setupSm4Cbc } from './cryptoAlg/sm4-cbc.ts';
|
|
64
|
+
export { setupSm4Ecb } from './cryptoAlg/sm4-ecb.ts';
|
|
65
|
+
export * from './interface';
|
package/src/interface.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RequestInstance } from 'pubinfo';
|
|
2
|
+
|
|
3
|
+
export interface CryptoOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 接口请求实例
|
|
6
|
+
*/
|
|
7
|
+
request: InternalContext['request']
|
|
8
|
+
cryptoSetting: cryptoInternal
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface cryptoInternal {
|
|
12
|
+
name: string
|
|
13
|
+
encrypt: (text: string) => Promise<string>
|
|
14
|
+
decrypt: (text: string) => Promise<string>
|
|
15
|
+
header?: Record<string, string>
|
|
16
|
+
};
|
|
17
|
+
export interface InternalContext {
|
|
18
|
+
request: RequestInstance
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { RequestInstance };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Rule } from 'ant-design-vue/es/form';
|
|
3
|
+
import type { FormExpose } from 'ant-design-vue/es/form/Form';
|
|
4
|
+
import { useToggle } from '@vueuse/core';
|
|
5
|
+
|
|
6
|
+
defineOptions({
|
|
7
|
+
name: 'DrawerCryptoUrl',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const emit = defineEmits(['submit']);
|
|
11
|
+
|
|
12
|
+
const rules: Record<string, Rule[]> = {
|
|
13
|
+
url: [{ required: true, validator: checkUrl, trigger: ['blur', 'change'] }],
|
|
14
|
+
};
|
|
15
|
+
const [open, setOpen] = useToggle(false);
|
|
16
|
+
const formRef = ref<FormExpose>();
|
|
17
|
+
const form = ref<Partial<API.UrlEntry>>({});
|
|
18
|
+
const isEdit = ref(false);
|
|
19
|
+
const tableList = ref<API.UrlEntry[]>([]);
|
|
20
|
+
const tableIndex = ref<number>(0); ;
|
|
21
|
+
|
|
22
|
+
function checkUrl(rule: any, value: any, callback: any) {
|
|
23
|
+
const urlReg = /^\/(?!.*\*{3})(?:[\w-]+|:\w+|\*{1,2})(?:\/(?:[\w-]+|:\w+|\*{1,2}))*$/;
|
|
24
|
+
if (!value) {
|
|
25
|
+
callback(new Error('请输入url'));
|
|
26
|
+
}
|
|
27
|
+
else if (!urlReg.test(value)) {
|
|
28
|
+
callback(new Error('url格式不正确'));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
callback();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function onOpen(edit: boolean, list: API.UrlEntry[], index?: number) {
|
|
36
|
+
setOpen(true);
|
|
37
|
+
tableList.value = list || [];
|
|
38
|
+
tableIndex.value = index || 0;
|
|
39
|
+
if (edit) {
|
|
40
|
+
isEdit.value = true;
|
|
41
|
+
form.value = { ...list?.[index || 0] };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
isEdit.value = false;
|
|
45
|
+
form.value = {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const [loading, setLoading] = useToggle(false);
|
|
50
|
+
function onSubmit() {
|
|
51
|
+
formRef.value?.validate().then(() => {
|
|
52
|
+
setLoading(true);
|
|
53
|
+
const params: any = {
|
|
54
|
+
...form.value,
|
|
55
|
+
};
|
|
56
|
+
if (isEdit.value) {
|
|
57
|
+
tableList.value[tableIndex.value] = params;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
tableList.value.push(params);
|
|
61
|
+
}
|
|
62
|
+
setLoading(false);
|
|
63
|
+
close();
|
|
64
|
+
emit('submit', tableList.value);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function close() {
|
|
69
|
+
setOpen(false);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
defineExpose({
|
|
73
|
+
open: onOpen,
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<a-drawer
|
|
79
|
+
v-model:open="open"
|
|
80
|
+
title="url信息"
|
|
81
|
+
placement="right"
|
|
82
|
+
:width="600"
|
|
83
|
+
destroy-on-close
|
|
84
|
+
:footer-style="{ textAlign: 'right' }"
|
|
85
|
+
@close="close"
|
|
86
|
+
>
|
|
87
|
+
<a-form
|
|
88
|
+
ref="formRef"
|
|
89
|
+
:model="form"
|
|
90
|
+
:rules="rules"
|
|
91
|
+
:label-col="{ style: { width: '90px' } }"
|
|
92
|
+
>
|
|
93
|
+
<a-form-item label="url地址" name="url" tooltip="支持url通配符,示例:/api/v1/user/*,/api/login/**">
|
|
94
|
+
<a-input v-model:value="form.url" :maxlength="200" />
|
|
95
|
+
</a-form-item>
|
|
96
|
+
<a-form-item label="描述" name="description">
|
|
97
|
+
<a-textarea
|
|
98
|
+
v-model:value="form.description"
|
|
99
|
+
:rows="3"
|
|
100
|
+
show-count
|
|
101
|
+
:maxlength="200"
|
|
102
|
+
/>
|
|
103
|
+
</a-form-item>
|
|
104
|
+
</a-form>
|
|
105
|
+
<template #footer>
|
|
106
|
+
<a-space>
|
|
107
|
+
<a-button @click="close">
|
|
108
|
+
取消
|
|
109
|
+
</a-button>
|
|
110
|
+
<a-button type="primary" :loading="loading" @click="onSubmit()">
|
|
111
|
+
提交
|
|
112
|
+
</a-button>
|
|
113
|
+
</a-space>
|
|
114
|
+
</template>
|
|
115
|
+
</a-drawer>
|
|
116
|
+
</template>
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ProColumns } from '@pubinfo/pro-components';
|
|
3
|
+
import type { UnwrapRef } from 'vue';
|
|
4
|
+
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
|
5
|
+
import { ProTable } from '@pubinfo/pro-components';
|
|
6
|
+
import { useToggle } from '@vueuse/core';
|
|
7
|
+
import { message, Modal } from 'ant-design-vue';
|
|
8
|
+
import { reactive, ref } from 'vue';
|
|
9
|
+
import { getCryptoGetCryptoConfig, postCryptoEditCryptoConfig } from '@/api/modules/crypto';
|
|
10
|
+
import { ctx } from '@/context';
|
|
11
|
+
import DrawerCryptoUrl from './components/drawerCryptoUrl.vue';
|
|
12
|
+
import { ACTION } from './enum';
|
|
13
|
+
|
|
14
|
+
interface FormState {
|
|
15
|
+
isCrypto: boolean
|
|
16
|
+
whiteListUrls?: API.UrlEntry[]
|
|
17
|
+
mode: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tableRef = ref();
|
|
21
|
+
const formRef = ref();
|
|
22
|
+
const drawerRef = ref();
|
|
23
|
+
const labelCol = { style: { width: 'auto' } };
|
|
24
|
+
const wrapperCol = { span: 24 };
|
|
25
|
+
const { auth } = useAuth();
|
|
26
|
+
|
|
27
|
+
const formState: UnwrapRef<FormState> = reactive({
|
|
28
|
+
isCrypto: true,
|
|
29
|
+
mode: true,
|
|
30
|
+
whiteListUrls: [],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const [loading, setLoading] = useToggle(false);
|
|
34
|
+
onMounted(getCryptoConfig);
|
|
35
|
+
|
|
36
|
+
async function getCryptoConfig() {
|
|
37
|
+
await getCryptoGetCryptoConfig().then((res) => {
|
|
38
|
+
setLoading(false);
|
|
39
|
+
if (res && res.data && res.success) {
|
|
40
|
+
formState.isCrypto = res.data.cryptoEnabled || false;
|
|
41
|
+
formState.whiteListUrls = res.data.cryptoWhitePath;
|
|
42
|
+
formState.mode = res.data.cryptoMode === undefined ? true : res.data.cryptoMode;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function handleChange(tag: boolean, checked: boolean) {
|
|
48
|
+
if (checked) {
|
|
49
|
+
formState.mode = tag;
|
|
50
|
+
onSubmit();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function onSubmit() {
|
|
54
|
+
if (loading.value) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
setLoading(true);
|
|
58
|
+
try {
|
|
59
|
+
await formRef.value?.validate();
|
|
60
|
+
const {
|
|
61
|
+
isCrypto,
|
|
62
|
+
mode,
|
|
63
|
+
whiteListUrls,
|
|
64
|
+
} = formState;
|
|
65
|
+
|
|
66
|
+
const params = {
|
|
67
|
+
cryptoEnabled: isCrypto,
|
|
68
|
+
cryptoMode: mode,
|
|
69
|
+
cryptoWhitePath: whiteListUrls,
|
|
70
|
+
};
|
|
71
|
+
const { success } = await postCryptoEditCryptoConfig({ json: JSON.stringify(params) });
|
|
72
|
+
if (success) {
|
|
73
|
+
message.success('配置更改成功');
|
|
74
|
+
ctx.callAsync({ ...ctx.use(), isCrypto: formState.isCrypto, cryptoMode: formState.mode, whiteListUrls: formState.whiteListUrls }, () => {
|
|
75
|
+
getCryptoConfig();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const columns: ProColumns<API.UrlEntry> = [
|
|
85
|
+
{
|
|
86
|
+
minWidth: 60,
|
|
87
|
+
hideInTable: false,
|
|
88
|
+
title: 'url地址',
|
|
89
|
+
dataIndex: 'url',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
hideInSearch: true,
|
|
93
|
+
minWidth: 200,
|
|
94
|
+
title: '描述',
|
|
95
|
+
dataIndex: 'description',
|
|
96
|
+
ellipsis: true,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
hideInSearch: true,
|
|
100
|
+
title: '操作',
|
|
101
|
+
dataIndex: 'action',
|
|
102
|
+
width: 200,
|
|
103
|
+
fixed: 'right',
|
|
104
|
+
hideInTable() {
|
|
105
|
+
return !auth([
|
|
106
|
+
'crypto_url_add',
|
|
107
|
+
'crypto_url_edit',
|
|
108
|
+
'crypto_url_delete',
|
|
109
|
+
]);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
function onAction(key: string, index?: number): void {
|
|
115
|
+
if (loading.value) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
switch (key) {
|
|
120
|
+
case ACTION.ADD:
|
|
121
|
+
drawerRef.value.open(false, formState.whiteListUrls);
|
|
122
|
+
break;
|
|
123
|
+
case ACTION.EDIT:
|
|
124
|
+
drawerRef.value.open(true, formState.whiteListUrls, index);
|
|
125
|
+
break;
|
|
126
|
+
case ACTION.REMOVE:
|
|
127
|
+
actionRemove(formState.whiteListUrls || [], index || 0);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function actionRemove(list: API.UrlEntry[], index: number) {
|
|
133
|
+
Modal.confirm({
|
|
134
|
+
title: '删除',
|
|
135
|
+
content: `确定要删除该条数据吗?`,
|
|
136
|
+
async onOk() {
|
|
137
|
+
list.splice(index, 1);
|
|
138
|
+
getListData(list);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getListData(list: API.UrlEntry[]) {
|
|
144
|
+
formState.whiteListUrls = list;
|
|
145
|
+
onSubmit();
|
|
146
|
+
}
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<template>
|
|
150
|
+
<div
|
|
151
|
+
w-full
|
|
152
|
+
h-full
|
|
153
|
+
pt-10px
|
|
154
|
+
px-10px
|
|
155
|
+
overflow-auto
|
|
156
|
+
>
|
|
157
|
+
<div h-full min-w-1130px>
|
|
158
|
+
<div
|
|
159
|
+
class="pt-20px pl-20px h-161px flex flex-col bg-[#ffffff] box-border bg-[url('@/assets/images/crypto-setting-bg.png')] bg-cover text-[#4e5969] dark:text-[#C9CDD4]"
|
|
160
|
+
>
|
|
161
|
+
<a-form
|
|
162
|
+
ref="formRef"
|
|
163
|
+
:model="formState"
|
|
164
|
+
:label-col="labelCol"
|
|
165
|
+
:wrapper-col="wrapperCol"
|
|
166
|
+
class="safe-rule-form"
|
|
167
|
+
>
|
|
168
|
+
<a-form-item label="开启加密" name="isCrypto">
|
|
169
|
+
<a-switch
|
|
170
|
+
v-model:checked="formState.isCrypto" :disabled="loading"
|
|
171
|
+
@change="onSubmit"
|
|
172
|
+
/>
|
|
173
|
+
</a-form-item>
|
|
174
|
+
<a-form-item label="模式" name="isCrypto">
|
|
175
|
+
<div class="flex items-center">
|
|
176
|
+
<div class="p-5px bg-[#f2f3f5] rounded-5px flex items-center">
|
|
177
|
+
<a-checkable-tag
|
|
178
|
+
:checked="formState.mode"
|
|
179
|
+
:disabled="loading"
|
|
180
|
+
:class="loading ? 'cursor-not-allowed!' : ''"
|
|
181
|
+
@change="(checked: boolean) => !loading && handleChange(true, checked)"
|
|
182
|
+
>
|
|
183
|
+
白名单
|
|
184
|
+
</a-checkable-tag>
|
|
185
|
+
<a-checkable-tag
|
|
186
|
+
:checked="!formState.mode"
|
|
187
|
+
class="mr-0px!"
|
|
188
|
+
:class="loading ? 'cursor-not-allowed!' : ''"
|
|
189
|
+
@change="(checked: boolean) => !loading && handleChange(false, checked)"
|
|
190
|
+
>
|
|
191
|
+
黑名单
|
|
192
|
+
</a-checkable-tag>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="flex items-center ml-15px text-[#a3a4a8]">
|
|
195
|
+
<QuestionCircleOutlined class="mr-5px" />白名单模式下,仅不加密过滤列表中的接口;黑名单模式下,仅加密过滤列表中的接口。
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</a-form-item>
|
|
199
|
+
</a-form>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="h-[calc(100%-165px)]">
|
|
202
|
+
<ProTable
|
|
203
|
+
ref="tableRef"
|
|
204
|
+
row-key="id"
|
|
205
|
+
:data-source="formState.whiteListUrls"
|
|
206
|
+
:columns="columns"
|
|
207
|
+
auto-height
|
|
208
|
+
:search="false"
|
|
209
|
+
:pagination="false"
|
|
210
|
+
:scroll="{ x: 1000 }"
|
|
211
|
+
>
|
|
212
|
+
<template #toolbar>
|
|
213
|
+
<a-button
|
|
214
|
+
v-auth="'crypto_url_add'"
|
|
215
|
+
type="primary"
|
|
216
|
+
:disabled="loading"
|
|
217
|
+
@click="onAction(ACTION.ADD)"
|
|
218
|
+
>
|
|
219
|
+
<PlusCircleOutlined />
|
|
220
|
+
{{ ACTION.ADD }}
|
|
221
|
+
</a-button>
|
|
222
|
+
</template>
|
|
223
|
+
<template #bodyCell="{ column, index }">
|
|
224
|
+
<template v-if="column.dataIndex === 'action'">
|
|
225
|
+
<a-space>
|
|
226
|
+
<a
|
|
227
|
+
v-auth="'crypto_url_edit'"
|
|
228
|
+
class="flex items-center hover:opacity-80"
|
|
229
|
+
@click="onAction(ACTION.EDIT, index)"
|
|
230
|
+
>
|
|
231
|
+
<PubinfoIcon name="edit" class="text-base" />
|
|
232
|
+
<span class="ml-4px">{{ ACTION.EDIT }}</span>
|
|
233
|
+
</a>
|
|
234
|
+
<span
|
|
235
|
+
v-auth="'crypto_url_delete'"
|
|
236
|
+
flex
|
|
237
|
+
items-center
|
|
238
|
+
text-red-500
|
|
239
|
+
hover:cursor-pointer
|
|
240
|
+
hover:text-red-500
|
|
241
|
+
hover:opacity-80
|
|
242
|
+
@click="onAction(ACTION.REMOVE, index)"
|
|
243
|
+
>
|
|
244
|
+
<PubinfoIcon name="remove" class="text-base" />
|
|
245
|
+
<span class="ml-4px">{{ ACTION.REMOVE }}</span>
|
|
246
|
+
</span>
|
|
247
|
+
</a-space>
|
|
248
|
+
</template>
|
|
249
|
+
</template>
|
|
250
|
+
</ProTable>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
<DrawerCryptoUrl
|
|
254
|
+
ref="drawerRef"
|
|
255
|
+
@submit="getListData"
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
258
|
+
</template>
|
|
259
|
+
|
|
260
|
+
<style scoped>
|
|
261
|
+
.safe-rule-form .ant-input,
|
|
262
|
+
.safe-rule-form .ant-select {
|
|
263
|
+
--at-apply: w-9/10;
|
|
264
|
+
}
|
|
265
|
+
.safe-rule-form :deep(.ant-tag-checkable-checked) {
|
|
266
|
+
background: #fff;
|
|
267
|
+
color: #1677ff;
|
|
268
|
+
font-size: 13px;
|
|
269
|
+
}
|
|
270
|
+
.safe-rule-form :deep(.ant-tag-checkable) {
|
|
271
|
+
font-size: 13px;
|
|
272
|
+
}
|
|
273
|
+
</style>
|