|
@@ -1,166 +1,565 @@
|
|
|
<template>
|
|
|
<div class="layout-padding">
|
|
|
- <div class="layout-padding-auto layout-padding-view">
|
|
|
- <el-row shadow="hover" v-show="showSearch" class="ml10">
|
|
|
- <el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
|
|
|
- <el-form-item :label="$t('marketingConfig.name')" prop="domain">
|
|
|
- <el-input :placeholder="$t('marketingConfig.inputNameTip')" clearable style="max-width: 180px" v-model="state.queryForm.domain" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button @click="query" class="ml10" icon="search" type="primary">
|
|
|
- {{ $t('common.queryBtn') }}
|
|
|
- </el-button>
|
|
|
- <el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </el-row>
|
|
|
- <el-row>
|
|
|
- <div class="mb8" style="width: 100%">
|
|
|
- <right-toolbar
|
|
|
- v-model:showSearch="showSearch"
|
|
|
- class="ml10"
|
|
|
- style="float: right; margin-right: 20px"
|
|
|
- @queryTable="getDataList"
|
|
|
- ></right-toolbar>
|
|
|
+ <el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
|
|
|
+ <el-tab-pane :label="t('marketingConfig.ipList')" name="IP分组" class="layout-padding-auto layout-padding-view">
|
|
|
+ <Title class="ml-4" :title="t('marketingConfig.ipList')" />
|
|
|
+ <div class="p-4 rounded">
|
|
|
+ <el-button style="margin-bottom: 10px;" type="primary" @click="onClickAdd('ip')">{{ t('marketingConfig.addIpList') }}</el-button><br>
|
|
|
+ 筛选分组:<el-input placeholder="请输入分组名称" clearable
|
|
|
+ style="display: inline-block; width: 200px; margin-left: 5px;"
|
|
|
+ v-model="queryIPName" />
|
|
|
+ <div v-if="ipData.length > 0" class="overflow-y-auto mt-2" style="height: calc(100vh - 250px)">
|
|
|
+ <JCollapse
|
|
|
+ @update="(item) => onClickEdit(item, 'ip')"
|
|
|
+ @delete="(item) => onOpenDelete(item, 'ip')"
|
|
|
+ :data="showIPData"
|
|
|
+ :activeNames="ipActiveId"
|
|
|
+ :deleteText="t('marketingConfig.deleteListText')"
|
|
|
+ :updateText="t('marketingConfig.updateText')"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </el-row>
|
|
|
- <el-table
|
|
|
- ref="tableRef"
|
|
|
- :data="state.dataList"
|
|
|
- row-key="path"
|
|
|
- style="width: 100%"
|
|
|
- v-loading="state.loading"
|
|
|
- border
|
|
|
- :cell-style="tableStyle.cellStyle"
|
|
|
- :header-cell-style="tableStyle?.headerCellStyle"
|
|
|
- >
|
|
|
- <el-table-column :label="$t('marketingConfig.name')" fixed prop="appName" width="280" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- <el-link :href="row.url">{{ row.appName }}</el-link>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="营销投放" fixed prop="isMarketing" width="120">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-switch v-model="row.isMarketing" inline-prompt active-text="开启" inactive-text="关闭" :active-value="1" :inactive-value="0" @change="handleChange(row)" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="域名限制" fixed prop="isHttp" width="120">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-switch v-model="row.isHttp" inline-prompt active-text="开启" inactive-text="关闭" :active-value="1" :inactive-value="0" @change="handleChange(row)" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="域名集合" fixed prop="https" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- <div style="display: flex;flex-wrap: wrap;margin-left: 10%;">
|
|
|
- <template v-for="(item, index) in row.https" :key="index">
|
|
|
- <div style="margin-right: 20px;">{{ item }};</div>
|
|
|
- </template>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane :label="t('marketingConfig.domainList')" name="域名分组" class="layout-padding-auto layout-padding-view">
|
|
|
+ <Title class="ml-4" :title="t('marketingConfig.domainList')" />
|
|
|
+ <div class="p-4 rounded">
|
|
|
+ <el-button style="margin-bottom: 10px;" type="primary" @click="onClickAdd('domain')">{{ t('marketingConfig.addDomainList') }}</el-button><br>
|
|
|
+ 筛选分组:<el-input placeholder="请输入分组名称" clearable
|
|
|
+ style="display: inline-block; width: 200px; margin-left: 5px;"
|
|
|
+ v-model="queryDomainName" />
|
|
|
+ <div v-if="domainData.length > 0" class="overflow-y-auto mt-2" style="height: calc(100vh - 250px)">
|
|
|
+ <JCollapse
|
|
|
+ @update="(item) => onClickEdit(item, 'domain')"
|
|
|
+ :data="showDomainData"
|
|
|
+ @delete="(item) => onOpenDelete(item, 'domain')"
|
|
|
+ :activeNames="domainActiveId"
|
|
|
+ :deleteText="t('marketingConfig.deleteListText')"
|
|
|
+ :updateText="t('marketingConfig.updateText')"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div v-else class="ml-2">暂无数据</div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane :label="t('marketingConfig.disposition')" name="全局配置" class="layout-padding-auto layout-padding-view">
|
|
|
+ <Title class="ml-4" :title="t('marketingConfig.disposition')" />
|
|
|
+ <div class="p-4 rounded overflow-y-auto" style="max-height: calc(100vh - 350px)">
|
|
|
+ <JCollapse
|
|
|
+ :data="[{ title: 'IP集合', id: '1' }]"
|
|
|
+ :activeNames="['1']"
|
|
|
+ @update="(item) => (listEditOpen = true)"
|
|
|
+ @delete="(item) => (closeIpTags = !closeIpTags)"
|
|
|
+ :deleteText="closeIpTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteIp')"
|
|
|
+ :updateText="t('marketingConfig.addIp')"
|
|
|
+ >
|
|
|
+ <template #default>
|
|
|
+ <div class="border-b p-2 items-center flex flex-wrap">
|
|
|
+ <span class="mr-2">白名单</span>
|
|
|
+ <span v-for="item in ipWhiteList" :key="item.id">
|
|
|
+ <el-popover v-if="item?.sourceType === 1" width="280" @show="onLoadDetail(item)" trigger="hover" placement="top">
|
|
|
+ <div v-if="item.list.length > 0" class="flex flex-wrap">
|
|
|
+ <span v-for="ip in item.list" :key="ip" class="ml-2">
|
|
|
+ {{ ip }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div v-else>暂无数据</div>
|
|
|
+ <template #reference>
|
|
|
+ <el-tag
|
|
|
+ effect="light"
|
|
|
+ color="#f4f4f4"
|
|
|
+ :closable="closeIpTags"
|
|
|
+ @close="handleDelete(item, 'ip')"
|
|
|
+ round
|
|
|
+ class="ml-1 cursor-pointer"
|
|
|
+ >
|
|
|
+ {{ item.groupName }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ <el-tag
|
|
|
+ v-else
|
|
|
+ effect="light"
|
|
|
+ color="#f4f4f4"
|
|
|
+ :closable="closeIpTags"
|
|
|
+ @close="handleDelete(item, 'ip')"
|
|
|
+ round
|
|
|
+ class="ml-1 cursor-pointer"
|
|
|
+ >
|
|
|
+ {{ ipSplicing(item.startIp, item.endIp) }}
|
|
|
+ </el-tag>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="p-2 items-center flex flex-wrap">
|
|
|
+ <span class="mr-2">黑名单</span>
|
|
|
+
|
|
|
+ <span v-for="item in ipBlackList" :key="item.id">
|
|
|
+ <el-popover v-if="item?.sourceType === 1" width="280" @show="onLoadDetail(item, ipList)" trigger="hover" placement="top">
|
|
|
+ <div v-if="item.list.length > 0" class="flex flex-wrap">
|
|
|
+ <span v-for="ip in item.list" :key="ip" class="ml-2">
|
|
|
+ {{ ip }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div v-else>暂无数据</div>
|
|
|
+ <template #reference>
|
|
|
+ <el-tag
|
|
|
+ effect="light"
|
|
|
+ color="#f4f4f4"
|
|
|
+ :closable="closeIpTags"
|
|
|
+ @close="handleDelete(item, 'ip')"
|
|
|
+ round
|
|
|
+ class="ml-1 cursor-pointer"
|
|
|
+ >
|
|
|
+ {{ item.groupName }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ <el-tag
|
|
|
+ v-else
|
|
|
+ effect="light"
|
|
|
+ color="#f4f4f4"
|
|
|
+ :closable="closeIpTags"
|
|
|
+ @close="handleDelete(item, 'ip')"
|
|
|
+ round
|
|
|
+ class="ml-1 cursor-pointer"
|
|
|
+ >
|
|
|
+ {{ ipSplicing(item.startIp, item.endIp) }}
|
|
|
+ </el-tag>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </JCollapse>
|
|
|
+ <JCollapse
|
|
|
+ class="mt-4"
|
|
|
+ :data="[{ title: '域名集合', id: '1' }]"
|
|
|
+ :activeNames="['1']"
|
|
|
+ @update="() => (domainEditOpen = true)"
|
|
|
+ @delete="() => (closeDomainTags = !closeDomainTags)"
|
|
|
+ :deleteText="closeDomainTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteDomain')"
|
|
|
+ :updateText="t('marketingConfig.addDomain')"
|
|
|
+ >
|
|
|
+ <template #default>
|
|
|
+ <div class="p-2 items-center flex flex-wrap">
|
|
|
+ <span v-if="domainList.length > 0">
|
|
|
+ <span v-for="item in domainList" :key="item.id">
|
|
|
+ <el-popover v-if="item?.sourceType === 1" width="280" @show="onLoadDetail(item)" trigger="hover" placement="top">
|
|
|
+ <div v-if="item.list.length > 0" class="flex flex-wrap">
|
|
|
+ <span v-for="domain in item.list" :key="domain.id" class="ml-2">
|
|
|
+ {{ domain.domain }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div v-else>暂无数据</div>
|
|
|
+ <template #reference>
|
|
|
+ <el-tag
|
|
|
+ effect="light"
|
|
|
+ color="#f4f4f4"
|
|
|
+ :closable="closeDomainTags"
|
|
|
+ @close="handleDelete(item, 'domain')"
|
|
|
+ round
|
|
|
+ class="ml-1 cursor-pointer"
|
|
|
+ >
|
|
|
+ {{ item.groupName }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ <el-tag
|
|
|
+ v-else
|
|
|
+ effect="light"
|
|
|
+ color="#f4f4f4"
|
|
|
+ :closable="closeDomainTags"
|
|
|
+ @close="handleDelete(item, 'domain')"
|
|
|
+ round
|
|
|
+ class="ml-1 cursor-pointer"
|
|
|
+ >
|
|
|
+ {{ item.domain }}
|
|
|
+ </el-tag>
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
+ <div class="text-gray-400 ml-2" v-else>--</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </JCollapse>
|
|
|
+ </div>
|
|
|
+ <div class="w-[66%] ml-[-8px] mt-5">
|
|
|
+ <el-form ref="ruleFormRef" :model="formData" :rules="dataRules" label-width="90px" class="flex flex-wrap">
|
|
|
+ <el-form-item :label="t('marketingConfig.jumpMode')" prop="triggerMode" class="w-1/3">
|
|
|
+ <JDictSelect
|
|
|
+ v-model:value="formData.triggerMode"
|
|
|
+ :placeholder="t('marketingConfig.jumpModeTip')"
|
|
|
+ :dictType="'triggerMode'"
|
|
|
+ :selectFirst="true"
|
|
|
+ :styleClass="'w-full'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('marketingConfig.triggerType')" prop="triggerRule" class="w-1/3">
|
|
|
+ <JDictSelect
|
|
|
+ v-model:value="formData.triggerRule"
|
|
|
+ :placeholder="t('marketingConfig.triggerTypeTip')"
|
|
|
+ :dictType="'triggerRule'"
|
|
|
+ :selectFirst="true"
|
|
|
+ :styleClass="'w-full'"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item :label="t('marketingConfig.triggerFrequency')" prop="triggerNum" class="w-1/3">
|
|
|
+ <el-input v-model="formData.triggerNum" type="text" :placeholder="t('marketingConfig.triggerFrequencyTip')" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <div class="w-full mb-[18px]">
|
|
|
+ <el-form-item
|
|
|
+ v-if="formData.triggerMode == '2' || formData.triggerMode == '3'"
|
|
|
+ :label="t('marketingConfig.prompt')"
|
|
|
+ prop="promptMsg"
|
|
|
+ class="w-1/3"
|
|
|
+ >
|
|
|
+ <el-input v-model="formData.promptMsg" type="text" :placeholder="t('marketingConfig.promptTip')"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ v-if="formData.triggerMode == '1' || formData.triggerMode == '3'"
|
|
|
+ :label="t('marketingConfig.jumpLink')"
|
|
|
+ prop="url"
|
|
|
+ class="w-1/3"
|
|
|
+ >
|
|
|
+ <el-input v-model="formData.url" type="text" :placeholder="t('marketingConfig.jumpLinkTip')"></el-input>
|
|
|
+ </el-form-item>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="ip集合" fixed prop="ips" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- <div style="display: flex;flex-wrap: wrap; margin-left: 10%;">
|
|
|
- <template v-for="(item, index) in row.ips" :key="index">
|
|
|
- <div style="margin-right: 20px;">{{ item }};</div>
|
|
|
- </template>
|
|
|
+ <div class="w-full">
|
|
|
+ <el-button type="primary" @click="onSubmit(ruleFormRef)" class="w-[80px] ml-5">{{ t('common.saveBtn') }}</el-button>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column :label="$t('common.action')" width="250">
|
|
|
- <template #default="scope">
|
|
|
- <el-button icon="edit-pen" @click="onOpenEditMenu('edit', scope.row, 'domain')" text type="primary" v-auth="'sys_menu_edit'"
|
|
|
- >域名
|
|
|
- </el-button>
|
|
|
- <el-button icon="edit-pen" @click="onOpenEditMenu('edit', scope.row, 'ip')" text type="primary" v-auth="'sys_menu_edit'"
|
|
|
- >IP
|
|
|
- </el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- <pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
|
|
- </div>
|
|
|
- <MenuDialog @refresh="getDataList()" ref="menuDialogRef" />
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ <DomainEdit :select-data="domainData" v-model:open="domainEditOpen" @onsuccess="getConfig" />
|
|
|
+ <ListEdit :select-data="ipData" v-model:open="listEditOpen" @onsuccess="getConfig" />
|
|
|
+ <GroupingEdit v-model:open="groupingEditOpen" :type="openType" @onsuccess="getData" />
|
|
|
+ <IpListEdit :type="openType" ref="menuDialogRef" @onsuccess="getData" />
|
|
|
+ <el-dialog v-model="delOpen" title="提示" width="500" @close="delOpen = false">
|
|
|
+ <span>确认删除{{ delObj?.title }}吗?</span>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="delOpen = false">取消</el-button>
|
|
|
+ <el-button type="primary" :disabled="loading" @click="onDel(delObj)"> 确定 </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" name="marketingConfig" setup>
|
|
|
-import { delObj, pageList, update } from '/@/api/marketing/config';
|
|
|
-import { BasicTableProps, useTable } from '/@/hooks/table';
|
|
|
-import { useMessage, useMessageBox } from '/@/hooks/message';
|
|
|
+import {
|
|
|
+ delGroup,
|
|
|
+ pageListDomain,
|
|
|
+ pageListIp,
|
|
|
+ getConfigIpList,
|
|
|
+ getConfigDomainList,
|
|
|
+ delIpList,
|
|
|
+ delDomainList,
|
|
|
+ getGroupDetail,
|
|
|
+ getConfigDetail,
|
|
|
+ saveConfigDetail,
|
|
|
+} from '/@/api/marketing/config';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
+import { useMessage } from '/@/hooks/message';
|
|
|
+import { rule } from '/@/utils/validate';
|
|
|
+import { ipSplicing } from '/@/utils/ipUpdate';
|
|
|
+
|
|
|
// 引入组件
|
|
|
-const MenuDialog = defineAsyncComponent(() => import('./form.vue'));
|
|
|
+const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
|
|
|
+const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
|
|
|
+const DomainEdit = defineAsyncComponent(() => import('./components/domainEdit.vue'));
|
|
|
+const ListEdit = defineAsyncComponent(() => import('./components/listEdit.vue'));
|
|
|
+const GroupingEdit = defineAsyncComponent(() => import('./components/ipGroupingEdit.vue'));
|
|
|
+const IpListEdit = defineAsyncComponent(() => import('./components/ipListEdit.vue'));
|
|
|
+const JDictSelect = defineAsyncComponent(() => import('/@/components/JDictSelect/index.vue'));
|
|
|
const { t } = useI18n();
|
|
|
// 定义变量内容
|
|
|
-const tableRef = ref();
|
|
|
-const menuDialogRef = ref();
|
|
|
-const queryRef = ref();
|
|
|
-const showSearch = ref(true);
|
|
|
-// 多选rows
|
|
|
-const selectObjs = ref([]) as any;
|
|
|
-// 是否可以多选
|
|
|
-const multiple = ref(true);
|
|
|
-const state: BasicTableProps = reactive<BasicTableProps>({
|
|
|
- pageList: pageList, // H
|
|
|
- queryForm: {
|
|
|
- domain: '',
|
|
|
- }
|
|
|
+const activeName = ref('IP分组');
|
|
|
+const queryIPName = ref('');
|
|
|
+const queryDomainName = ref('');
|
|
|
+const showIPData = computed(() => {
|
|
|
+ return ipData.value.filter((item) => item.groupName.includes(queryIPName.value));
|
|
|
});
|
|
|
+const showDomainData = computed(() => {
|
|
|
+ return domainData.value.filter((item) => item.groupName.includes(queryDomainName.value));
|
|
|
+});
|
|
|
+//关闭或打开tabs的关闭按钮
|
|
|
+const closeDomainTags = ref(false);
|
|
|
+const closeIpTags = ref(false);
|
|
|
|
|
|
-const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
|
|
+// 弹窗
|
|
|
+const domainEditOpen = ref(false);
|
|
|
+const listEditOpen = ref(false);
|
|
|
+const groupingEditOpen = ref(false);
|
|
|
+const delOpen = ref(false);
|
|
|
+const loading = ref(false);
|
|
|
+const menuDialogRef = ref();
|
|
|
|
|
|
-// 搜索事件
|
|
|
-const query = () => {
|
|
|
- state.dataList = [];
|
|
|
- getDataList();
|
|
|
-};
|
|
|
+const domainActiveId = ref([]);
|
|
|
+const ipActiveId = ref([]);
|
|
|
|
|
|
-// 修改单条配置信息
|
|
|
-const handleChange = (row: any) => {
|
|
|
- // update(row).then(() => {
|
|
|
- // useMessage().success(t('common.updateSuccessText'));
|
|
|
- // getDataList();
|
|
|
- // }).catch((err: any) => {
|
|
|
- // useMessage().error(err.msg);
|
|
|
- // });
|
|
|
-};
|
|
|
+// const ipListEditOpen = ref(false);
|
|
|
+const domainData = ref([]);
|
|
|
+const ipData = ref([]);
|
|
|
+const delObj = ref({});
|
|
|
+const openType = ref('ip'); // 'ip' or 'domain'
|
|
|
+const ipWhiteList = ref([]); // ip白名单
|
|
|
+const ipBlackList = ref([]); // ip黑名单
|
|
|
+const domainList = ref([]);
|
|
|
+const ruleFormRef = ref();
|
|
|
+const formData = ref({
|
|
|
+ promptMsg: '',
|
|
|
+ triggerMode: '',
|
|
|
+ triggerRule: '',
|
|
|
+ url: '',
|
|
|
+ triggerNum: '',
|
|
|
+});
|
|
|
+const onOpenDelete = (item: any, type: any) => {
|
|
|
+ console.log(item);
|
|
|
|
|
|
-// 清空搜索条件
|
|
|
-const resetQuery = () => {
|
|
|
- queryRef.value.resetFields();
|
|
|
- state.dataList = [];
|
|
|
- getDataList();
|
|
|
+ delObj.value = { ...item, type };
|
|
|
+ console.log(delObj.value);
|
|
|
+
|
|
|
+ delOpen.value = true;
|
|
|
};
|
|
|
-// 打开编辑菜单弹窗
|
|
|
-const onOpenEditMenu = (type: string, row: any) => {
|
|
|
- menuDialogRef.value.openDialog(type, row);
|
|
|
+const getData = () => {
|
|
|
+ openType.value === 'ip' ? getIpData() : getDomainData();
|
|
|
};
|
|
|
+const onLoadDetail = async (item: any) => {
|
|
|
+ if (item.list.length !== 0) return;
|
|
|
+ await getGroupDetail({ id: item.groupId }).then((val) => {
|
|
|
+ item.list = val.data.ips.map((item) => {
|
|
|
+ return ipSplicing(item.startIp, item.endIp);
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// // 表单校验规则
|
|
|
+const dataRules = reactive({
|
|
|
+ url: [
|
|
|
+ { required: true, message: '跳转连接不能为空', trigger: 'blur' },
|
|
|
+ { validator: rule.domain, trigger: 'blur' },
|
|
|
+ ],
|
|
|
+ promptMsg: [
|
|
|
+ { required: true, message: '提示信息不能为空', trigger: 'blur' },
|
|
|
+ ],
|
|
|
+ triggerNum: [
|
|
|
+ { required: true, message: '触发频率不能为空', trigger: 'blur' },
|
|
|
+ {
|
|
|
+ pattern: /^(10000|[1-9]\d{0,3}|0)$|^(100%|[1-9]?\d%|0%)$/,
|
|
|
+ message: '请输入 0-10000 的正整数或 0%-100% 的百分比',
|
|
|
+ trigger: 'blur',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
|
|
|
-// 删除操作
|
|
|
-const handleDelete = async (ids: string[]) => {
|
|
|
+const onSubmit = async () => {
|
|
|
try {
|
|
|
- await useMessageBox().confirm(t('common.delConfirmText'));
|
|
|
- } catch {
|
|
|
+ await ruleFormRef.value.validateField('triggerNum');
|
|
|
+ } catch (error) {
|
|
|
+ // 验证失败,阻止后续逻辑执行
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (formData.value.triggerMode === '1' || formData.value.triggerMode === '3') {
|
|
|
+ try {
|
|
|
+ await ruleFormRef.value.validateField('url');
|
|
|
+ } catch (error) {
|
|
|
+ // 验证失败,阻止后续逻辑执行
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ loading.value = true;
|
|
|
+
|
|
|
+ // 处理 triggerNum 参数
|
|
|
+ let triggerNum = formData.value.triggerNum;
|
|
|
+ if (typeof triggerNum === 'string' && triggerNum.includes('%')) {
|
|
|
+ // 如果包含百分比符号,去除百分比符号并除以100
|
|
|
+ const num = parseFloat(triggerNum.replace('%', ''));
|
|
|
+ if (!isNaN(num)) {
|
|
|
+ triggerNum = num / 100;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ await saveConfigDetail({
|
|
|
+ ...formData.value,
|
|
|
+ triggerNum: triggerNum.toString(),
|
|
|
+ });
|
|
|
+
|
|
|
+ useMessage().success(t('common.editSuccessText'));
|
|
|
+ } catch (err) {
|
|
|
+ useMessage().error(err.msg);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ getConfig();
|
|
|
+ }
|
|
|
+};
|
|
|
+const handleClick = (data: any) => {
|
|
|
+ if (data.props.label === 'IP分组') {
|
|
|
+ getIpData();
|
|
|
+ } else if (data.props.label === '域名分组') {
|
|
|
+ getDomainData();
|
|
|
+ } else {
|
|
|
+ getConfig();
|
|
|
+ }
|
|
|
+};
|
|
|
+const handleDelete = async (item: any, type: string) => {
|
|
|
+ console.log(item, type);
|
|
|
+ delObj.value = { ...item, delListType: type };
|
|
|
+ delOpen.value = true;
|
|
|
+};
|
|
|
+const onDel = async (data: any) => {
|
|
|
+ if (data?.delListType) {
|
|
|
+ try {
|
|
|
+ if (data?.delListType === 'ip') {
|
|
|
+ await delIpList(data.id);
|
|
|
+ useMessage().success(t('common.delSuccessText'));
|
|
|
+ } else if (data?.delListType === 'domain') {
|
|
|
+ await delDomainList(data.id);
|
|
|
+ useMessage().success(t('common.delSuccessText'));
|
|
|
+ }
|
|
|
+ } catch (err: any) {
|
|
|
+ useMessage().error(err.msg);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ delOpen.value = false;
|
|
|
+ getConfig();
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
try {
|
|
|
- await delObj(ids);
|
|
|
- getDataList();
|
|
|
+ loading.value = true;
|
|
|
+ await delGroup({
|
|
|
+ groupType: data.type === 'ip' ? 1 : 2,
|
|
|
+ ids: [data.id],
|
|
|
+ });
|
|
|
useMessage().success(t('common.delSuccessText'));
|
|
|
} catch (err: any) {
|
|
|
useMessage().error(err.msg);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ delOpen.value = false;
|
|
|
+ data.type === 'ip' ? getIpData() : getDomainData();
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
+const onClickAdd = (type: string) => {
|
|
|
+ openType.value = type;
|
|
|
+ groupingEditOpen.value = true;
|
|
|
+};
|
|
|
+const onClickEdit = (item: any, type: string) => {
|
|
|
+ openType.value = type;
|
|
|
+ // ipListEditOpen.value = true;
|
|
|
+ onOpenEditMenu(type, item);
|
|
|
+};
|
|
|
+
|
|
|
+// 打开编辑菜单弹窗
|
|
|
+const onOpenEditMenu = (type: string, row: any) => {
|
|
|
+ menuDialogRef.value.openDialog(type, row);
|
|
|
+};
|
|
|
+const getConfig = () => {
|
|
|
+ configIp();
|
|
|
+ configDomain();
|
|
|
+};
|
|
|
+const configDomain = async () => {
|
|
|
+ await getConfigDomainList().then((val) => {
|
|
|
+ domainList.value = val.data.map((item) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ list: [],
|
|
|
+ };
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+const configIp = async () => {
|
|
|
+ await getConfigIpList().then((val) => {
|
|
|
+ ipWhiteList.value = val.data
|
|
|
+ .filter((item) => item.ipType === 1)
|
|
|
+ .map((item) => {
|
|
|
+ return { ...item, list: [] };
|
|
|
+ });
|
|
|
+ ipBlackList.value = val.data
|
|
|
+ .filter((item) => item.ipType === 2)
|
|
|
+ .map((item) => {
|
|
|
+ return { ...item, list: [] };
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+const getDomainData = async () => {
|
|
|
+ await pageListDomain().then((val) => {
|
|
|
+ domainActiveId.value = [];
|
|
|
+ domainData.value = val.data.map((item: any) => {
|
|
|
+ domainActiveId.value.push(item.id);
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ title: item.groupName,
|
|
|
+ id: item.id,
|
|
|
+ list: item.domains.map((items: any) => {
|
|
|
+ return {
|
|
|
+ ...items,
|
|
|
+ id: items.id,
|
|
|
+ value: items.domain,
|
|
|
+ };
|
|
|
+ }),
|
|
|
+ };
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+const getIpData = async () => {
|
|
|
+ await pageListIp().then((val) => {
|
|
|
+ ipActiveId.value = [];
|
|
|
+ ipData.value = val.data.map((item: any) => {
|
|
|
+ ipActiveId.value.push(item.id);
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ title: item.groupName,
|
|
|
+ id: item.id,
|
|
|
+ list: item.ips.map((items: any) => {
|
|
|
+ return {
|
|
|
+ ...items,
|
|
|
+ id: items.id,
|
|
|
+ value: ipSplicing(items.startIp, items.endIp),
|
|
|
+ };
|
|
|
+ }),
|
|
|
+ };
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ await getConfigDetail().then((val) => {
|
|
|
+ formData.value = {
|
|
|
+ promptMsg: '',
|
|
|
+ triggerMode: '',
|
|
|
+ triggerRule: '',
|
|
|
+ url: '',
|
|
|
+ triggerNum: '',
|
|
|
+ };
|
|
|
+ formData.value = {
|
|
|
+ ...val.data,
|
|
|
+ triggerMode: val.data?.triggerMode.toString(),
|
|
|
+ triggerRule: val.data?.triggerRule.toString(),
|
|
|
+ triggerNum: parseFloat(val.data?.triggerNum) < 1 ? parseFloat(val.data?.triggerNum) * 100 + '%' : val.data?.triggerNum,
|
|
|
+ };
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ //获取IP列表
|
|
|
+ getIpData();
|
|
|
+ getConfig();
|
|
|
+});
|
|
|
</script>
|
|
|
-<style scoped>
|
|
|
-:deep(.el-link__inner) {
|
|
|
- display: inline-block;
|
|
|
- width: 100%;
|
|
|
- justify-content: start;
|
|
|
+<style lang="scss">
|
|
|
+.is-top {
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+}
|
|
|
+.el-collapse-item__content {
|
|
|
+ padding-bottom: 0 !important;
|
|
|
+}
|
|
|
+.el-tabs--card > .el-tabs__header .el-tabs__item {
|
|
|
+ background-color: #fff;
|
|
|
}
|
|
|
-:deep(.el-link.is-underline:hover:after) {
|
|
|
- display: none;
|
|
|
+.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
|
|
|
+ background-color: #e8f2fe;
|
|
|
+ border-bottom-color: #e8f2fe;
|
|
|
}
|
|
|
</style>
|