|
@@ -1,99 +1,182 @@
|
|
<template>
|
|
<template>
|
|
- <JCollapse :data="[
|
|
|
|
- { title: 'IP集合', id: '1' },
|
|
|
|
- ]" @update="(item: any) => listEditOpen = true" @delete="(item: any) => ipDeletable = !ipDeletable"
|
|
|
|
- :deleteText="ipDeletable ? '取消' : t('marketingConfig.deleteText')" :updateText="'新增'"
|
|
|
|
- :activeNames="['1']">
|
|
|
|
|
|
+ <JCollapse :data="[{ title: 'IP集合', id: '1' }]" @update="(item: any) => listEditOpen = true"
|
|
|
|
+ @delete="(item: any) => ipDeletable = !ipDeletable"
|
|
|
|
+ :deleteText="ipDeletable ? '取消' : t('marketingConfig.deleteText')" :updateText="'新增'" :activeNames="['1']">
|
|
<template #default>
|
|
<template #default>
|
|
- <div class="border-b p-2 items-center flex flex-wrap collapse-group min-h-[40px]" v-for="(list, index) in [ips.whitelist, ips.blacklist]" :key="index">
|
|
|
|
- <div class="collapse-group-name">{{ index == 0 ? '白名单' : '黑名单'}}:</div>
|
|
|
|
- <div class="tag-content" v-if="list.length > 0">
|
|
|
|
- <template v-for="item in list" :key="item.id">
|
|
|
|
- <!-- 具体IP(段) -->
|
|
|
|
|
|
+ <div class="border-b p-2 items-center flex flex-wrap collapse-group min-h-[40px]" v-for="type in [1, 2]"
|
|
|
|
+ :key="type">
|
|
|
|
+ <div class="collapse-group-name">{{ type === 1 ? '白名单' : '黑名单' }}:</div>
|
|
|
|
+ <div class="tag-content" v-if="getTargetList(type).length > 0">
|
|
|
|
+ <template v-for="item in getTargetList(type)" :key="item.id">
|
|
<el-tag v-if="item.sourceType == 2" effect="light" :closable="ipDeletable" @close="handleDeleteIp(item)"
|
|
<el-tag v-if="item.sourceType == 2" effect="light" :closable="ipDeletable" @close="handleDeleteIp(item)"
|
|
color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
|
|
color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
|
|
- {{ item.startIp }}{{ item.ipMode == 2 ? (' / ' + item.endIp.split('.').pop()) : '' }}
|
|
|
|
|
|
+ {{ item.startIp }}{{ item.ipMode == 2 && item.endIp ? (' / ' + item.endIp.split('.').pop()) : '' }}
|
|
</el-tag>
|
|
</el-tag>
|
|
- <!-- 分组 -->
|
|
|
|
- <el-popover v-else width="200" trigger="hover" placement="top">
|
|
|
|
- <div class="flex flex-wrap break-all">
|
|
|
|
- {{ item.groupName }}
|
|
|
|
|
|
+ <el-popover v-else width="200" trigger="hover" placement="top" @show="onLoadDetail(item)">
|
|
|
|
+ <div v-if="item.list.length > 0" class="flex flex-wrap break-all">
|
|
|
|
+ <span v-for="ip in item.list" :key="String(ip)" class="mr-2">{{ ip }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
+ <div v-else>暂无数据</div>
|
|
<template #reference>
|
|
<template #reference>
|
|
- <el-tag @click="getIpList(item)" effect="light" :closable="ipDeletable" @close="handleDeleteIp(item)"
|
|
|
|
- color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
|
|
|
|
- {{ item.groupName.length > 30 ? item.groupName.substring(0, 30) + '...' : item.groupName}}
|
|
|
|
|
|
+ <el-tag effect="light" :closable="ipDeletable" @close="handleDeleteIp(item)" color="#f4f4f4" round
|
|
|
|
+ class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
|
|
|
|
+ {{ item.groupName.length > 30 ? item.groupName.substring(0, 30) + '...' : item.groupName }}
|
|
</el-tag>
|
|
</el-tag>
|
|
</template>
|
|
</template>
|
|
</el-popover>
|
|
</el-popover>
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
- <div class="text-gray-400" v-else>
|
|
|
|
- --
|
|
|
|
- </div>
|
|
|
|
|
|
+ <div class="text-gray-400" v-else>--</div>
|
|
</div>
|
|
</div>
|
|
-
|
|
|
|
</template>
|
|
</template>
|
|
</JCollapse>
|
|
</JCollapse>
|
|
- <IpEdit v-model:open="listEditOpen" />
|
|
|
|
|
|
+ <IpEdit v-model:open="listEditOpen" @onsuccess="addIp" />
|
|
</template>
|
|
</template>
|
|
-
|
|
|
|
<script setup lang="ts" name="ipCollapse">
|
|
<script setup lang="ts" name="ipCollapse">
|
|
-import { useI18n } from 'vue-i18n';
|
|
|
|
const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
|
|
const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
|
|
const IpEdit = defineAsyncComponent(() => import('./ipEdit.vue'));
|
|
const IpEdit = defineAsyncComponent(() => import('./ipEdit.vue'));
|
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
|
+import { getGroupDetail } from '/@/api/marketing/config';
|
|
|
|
+import { ipSplicing } from '/@/utils/ipUpdate';
|
|
|
|
+import { useMessage } from '/@/hooks/message';
|
|
|
|
|
|
interface IpItem {
|
|
interface IpItem {
|
|
- id: String;
|
|
|
|
- ipMode: Number;
|
|
|
|
- ipType: Number;
|
|
|
|
- sourceType: Number; // 1:分组 2:具体Ip(段)
|
|
|
|
- startIp: String;
|
|
|
|
- endIp: String;
|
|
|
|
- groupId: String;
|
|
|
|
- groupName: String;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-interface Ips {
|
|
|
|
- blacklist: IpItem[];
|
|
|
|
- whitelist: IpItem[];
|
|
|
|
|
|
+ id: string;
|
|
|
|
+ ipMode: number;
|
|
|
|
+ ipType: number; // 1: 白名单, 2: 黑名单
|
|
|
|
+ sourceType: number;
|
|
|
|
+ startIp: string;
|
|
|
|
+ endIp: string;
|
|
|
|
+ groupId: string;
|
|
|
|
+ groupName: string;
|
|
|
|
+ list: string[];
|
|
|
|
+ modify?: boolean;
|
|
}
|
|
}
|
|
|
|
|
|
const props = defineProps(['data']);
|
|
const props = defineProps(['data']);
|
|
-
|
|
|
|
-// 定义子组件向父组件传值/事件
|
|
|
|
-const emit = defineEmits(['refresh']);
|
|
|
|
|
|
+const emit = defineEmits(['refresh', 'ips', 'delIps']);
|
|
const { t } = useI18n();
|
|
const { t } = useI18n();
|
|
|
|
|
|
-// 定义变量内容
|
|
|
|
-const ips = ref<Ips>({
|
|
|
|
- blacklist: [],
|
|
|
|
- whitelist: []
|
|
|
|
-});
|
|
|
|
-const ipDeletable = ref(false);// 控制域名列表项是否可删除
|
|
|
|
|
|
+const ips = ref<IpItem[]>([]);
|
|
|
|
+const ipDeletable = ref(false);
|
|
const listEditOpen = ref(false);
|
|
const listEditOpen = ref(false);
|
|
|
|
+const delIps = ref<String[]>([]);
|
|
|
|
+
|
|
|
|
+watch(ips, (newVal) => emit('ips', newVal), { deep: true, immediate: true });
|
|
|
|
+watch(delIps, (newVal) => emit('delIps', newVal), { deep: true, immediate: true });
|
|
|
|
+
|
|
|
|
+const getTargetList = (ipType: number) => ips.value.filter(i => i.ipType === ipType);
|
|
|
|
+
|
|
|
|
+const setTargetList = (ipType: number, newList: IpItem[]) => {
|
|
|
|
+ ips.value = [
|
|
|
|
+ ...ips.value.filter(i => i.ipType !== ipType),
|
|
|
|
+ ...newList
|
|
|
|
+ ];
|
|
|
|
+};
|
|
|
|
|
|
watchEffect(() => {
|
|
watchEffect(() => {
|
|
if (props.data) {
|
|
if (props.data) {
|
|
- ips.value.blacklist = props.data.filter((item: any) => item.ipType == '1');
|
|
|
|
- ips.value.whitelist = props.data.filter((item: any) => item.ipType == '2');
|
|
|
|
|
|
+ ips.value = props.data.map((item: any) => ({ ...item, list: [] }));
|
|
}
|
|
}
|
|
- console.log('ips', ips.value);
|
|
|
|
-})
|
|
|
|
|
|
+});
|
|
|
|
|
|
-const getIpList = (detail: IpItem) => {
|
|
|
|
- if (detail.sourceType == 1) {
|
|
|
|
- // 获取ip组
|
|
|
|
- console.log(detail.groupId);
|
|
|
|
|
|
+const handleDeleteIp = (deleteItem: IpItem) => {
|
|
|
|
+ if (!deleteItem.modify && !delIps.value.includes(deleteItem.id)) {
|
|
|
|
+ delIps.value.push(deleteItem.id);
|
|
}
|
|
}
|
|
|
|
+ ips.value = ips.value.filter(i => {
|
|
|
|
+ if (i.id !== deleteItem.id) return true;
|
|
|
|
+ if (deleteItem.sourceType === 1) return i.groupId !== deleteItem.groupId;
|
|
|
|
+ return i.startIp !== deleteItem.startIp || i.endIp !== deleteItem.endIp;
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const onLoadDetail = async (item: any) => {
|
|
|
|
+ if (item.list.length || item.modify) return;
|
|
|
|
+ const val = await getGroupDetail({ id: item.groupId });
|
|
|
|
+ item.list = val.data.ips.map((item: any) => ipSplicing(item.startIp, item.endIp));
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+function addGroupsUnique(newGroups: IpItem[], ipType: number) {
|
|
|
|
+ const targetList = getTargetList(ipType);
|
|
|
|
+ const conflictType = ipType === 1 ? 2 : 1;
|
|
|
|
+ const conflictList = getTargetList(conflictType);
|
|
|
|
+ const existIds = new Set(targetList.filter(i => i.sourceType === 1).map(i => i.groupId));
|
|
|
|
+ const conflictIds = new Set(conflictList.filter(i => i.sourceType === 1).map(i => i.groupId));
|
|
|
|
+
|
|
|
|
+ const conflict = newGroups.find(i => conflictIds.has(i.id));
|
|
|
|
+ if (conflict) {
|
|
|
|
+ useMessage().warning(`分组 ${ conflict.groupName } 已存在于 ${ ipType === 1 ? '黑名单' : '白名单'} 中`);
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
-// 删除ip
|
|
|
|
-const handleDeleteIp = (deleteItem: IpItem) => {
|
|
|
|
- ips.value.whitelist = ips.value.whitelist.filter((item: any) => item.id != deleteItem.id);
|
|
|
|
- ips.value.blacklist = ips.value.blacklist.filter((item: any) => item.id != deleteItem.id);
|
|
|
|
|
|
+const filtered = newGroups.filter(i => !existIds.has(i.id));
|
|
|
|
+
|
|
|
|
+if (filtered.length) {
|
|
|
|
+ const processedGroups = filtered.map(item => ({
|
|
|
|
+ id: item.id,
|
|
|
|
+ ipType,
|
|
|
|
+ sourceType: 1,
|
|
|
|
+ groupId: item.id,
|
|
|
|
+ groupName: item.groupName,
|
|
|
|
+ isMode: 1,
|
|
|
|
+ startIp: '',
|
|
|
|
+ endIp: '',
|
|
|
|
+ modify: true,
|
|
|
|
+ list: item.list || []
|
|
|
|
+ }));
|
|
|
|
+ setTargetList(ipType, [...targetList, ...processedGroups]);
|
|
|
|
+ useMessage().success(t('common.addSuccessText'));
|
|
|
|
+} else {
|
|
|
|
+ useMessage().warning('分组重复');
|
|
|
|
+}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function addSinglesUnique(newSingles: IpItem[], ipType: number) {
|
|
|
|
+ const targetList = getTargetList(ipType);
|
|
|
|
+ const conflictType = ipType === 1 ? 2 : 1;
|
|
|
|
+ const conflictList = getTargetList(conflictType);
|
|
|
|
+
|
|
|
|
+ const existIps = new Set(targetList.filter(i => i.sourceType === 2).map(i => i.startIp));
|
|
|
|
+ const conflictIps = new Set(conflictList.filter(i => i.sourceType === 2).map(i => i.startIp));
|
|
|
|
+
|
|
|
|
+ const conflict = newSingles.find(i => conflictIps.has(i.startIp));
|
|
|
|
+ if (conflict) {
|
|
|
|
+ console.log(conflict);
|
|
|
|
+ useMessage().warning(`IP ${ conflict.startIp } 已存在于 ${ ipType === 1 ? '黑名单' : '白名单'} 中`);
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+const filtered = newSingles.filter(i => !existIps.has(i.startIp));
|
|
|
|
+
|
|
|
|
+if (filtered.length) {
|
|
|
|
+ const processedSingles = filtered.map(i => ({
|
|
|
|
+ // id: i.id,
|
|
|
|
+ endIp: i.endIp,
|
|
|
|
+ groupId: '0',
|
|
|
|
+ groupName: '',
|
|
|
|
+ ipMode: i.endIp ? 2 : 1,
|
|
|
|
+ startIp: i.startIp,
|
|
|
|
+ sourceType: 2,
|
|
|
|
+ modify: true,
|
|
|
|
+ ipType,
|
|
|
|
+ list: []
|
|
|
|
+ }));
|
|
|
|
+ setTargetList(ipType, [...targetList, ...processedSingles]);
|
|
|
|
+ useMessage().success(t('common.addSuccessText'));
|
|
|
|
+} else {
|
|
|
|
+ useMessage().warning('IP重复');
|
|
|
|
+}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const addIp = (data: IpItem[], sourceType: 1 | 2, ipType: '1' | '2') => {
|
|
|
|
+ const ipTypeNum = Number(ipType);
|
|
|
|
+ if (sourceType === 2) {
|
|
|
|
+ data.length && addSinglesUnique(data, ipTypeNum);
|
|
|
|
+ } else {
|
|
|
|
+ data.length && addGroupsUnique(data, ipTypeNum);
|
|
|
|
+ }
|
|
|
|
+};
|
|
</script>
|
|
</script>
|
|
<style lang="scss">
|
|
<style lang="scss">
|
|
.el-collapse-item__content {
|
|
.el-collapse-item__content {
|