index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <template>
  2. <div class="layout-padding">
  3. <el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
  4. <el-tab-pane :label="t('marketingConfig.ipList')" name="IP分组" class="layout-padding-auto layout-padding-view">
  5. <Title class="ml-4" :title="t('marketingConfig.ipList')" />
  6. <div class="p-4 rounded">
  7. <el-button type="primary" @click="onClickAdd('ip')">{{ t('marketingConfig.addIpList') }}</el-button>
  8. <JCollapse
  9. @update="(item) => onClickEdit(item, 'ip')"
  10. @delete="(item) => onOpenDelete(item, 'ip')"
  11. :data="ipData"
  12. :activeNames="ipActiveId"
  13. :deleteText="t('marketingConfig.deleteListText')"
  14. :updateText="t('marketingConfig.updateText')"
  15. />
  16. </div>
  17. </el-tab-pane>
  18. <el-tab-pane :label="t('marketingConfig.domainList')" name="域名分组" class="layout-padding-auto layout-padding-view">
  19. <Title class="ml-4" :title="t('marketingConfig.domainList')" />
  20. <div class="p-4 rounded">
  21. <el-button type="primary" @click="onClickAdd('domain')">{{ t('marketingConfig.addDomainList') }}</el-button>
  22. <JCollapse
  23. @update="(item) => onClickEdit(item, 'domain')"
  24. :data="domainData"
  25. @delete="(item) => onOpenDelete(item, 'domain')"
  26. :activeNames="domainActiveId"
  27. :deleteText="t('marketingConfig.deleteListText')"
  28. :updateText="t('marketingConfig.updateText')"
  29. />
  30. </div>
  31. </el-tab-pane>
  32. <el-tab-pane :label="t('marketingConfig.disposition')" name="全局配置" class="layout-padding-auto layout-padding-view">
  33. <Title class="ml-4" :title="t('marketingConfig.disposition')" />
  34. <div class="p-4 rounded">
  35. <JCollapse
  36. :data="[{ title: 'IP集合', id: '1' }]"
  37. :activeNames="['1']"
  38. @update="(item) => (listEditOpen = true)"
  39. @delete="(item) => (closeIpTags = !closeIpTags)"
  40. :deleteText="closeIpTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteIp')"
  41. :updateText="t('marketingConfig.addIp')"
  42. >
  43. <template #default>
  44. <div class="border-b p-2 items-center flex flex-wrap">
  45. <span class="mr-2">白名单</span>
  46. <el-popover v-for="item in data" :key="item.id" width="200" trigger="hover" placement="top">
  47. <div class="flex flex-wrap">
  48. <span v-for="ip in item.list" :key="ip" class="ml-2">
  49. {{ ip }}
  50. </span>
  51. </div>
  52. <template #reference>
  53. <el-tag effect="light" color="#f4f4f4" :closable="closeIpTags" round class="ml-1 cursor-pointer">
  54. {{ item.title }}
  55. </el-tag>
  56. </template>
  57. </el-popover>
  58. </div>
  59. <div class="p-2 items-center flex flex-wrap">
  60. <span class="mr-2">黑名单</span>
  61. <el-popover v-for="item in data" :key="item.id" width="200" trigger="hover" placement="top">
  62. <div class="flex flex-wrap">
  63. <span v-for="ip in item.list" :key="ip" class="ml-2">
  64. {{ ip }}
  65. </span>
  66. </div>
  67. <template #reference>
  68. <el-tag effect="light" color="#f4f4f4" :closable="closeIpTags" round class="ml-1 cursor-pointer">
  69. {{ item.title }}
  70. </el-tag>
  71. </template>
  72. </el-popover>
  73. </div>
  74. </template>
  75. </JCollapse>
  76. <JCollapse
  77. class="mt-4"
  78. :data="[{ title: '域名集合', id: '1' }]"
  79. :activeNames="['1']"
  80. @update="(item) => (domainEditOpen = true)"
  81. @delete="(item) => (closeDomainTags = !closeDomainTags)"
  82. :deleteText="closeDomainTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteDomain')"
  83. :updateText="t('marketingConfig.addDomain')"
  84. >
  85. <template #default>
  86. <div class="p-2 items-center flex flex-wrap">
  87. <el-tag
  88. v-for="item in whiteList"
  89. :key="item"
  90. effect="light"
  91. :closable="closeDomainTags"
  92. color="#f4f4f4"
  93. round
  94. class="ml-1 cursor-pointer"
  95. >
  96. {{ item.label }}
  97. </el-tag>
  98. </div>
  99. </template>
  100. </JCollapse>
  101. </div>
  102. <div class="w-[66%] ml-[-8px] mt-5">
  103. <el-form ref="menuDialogFormRef" :model="formData" :rule="dataRules" label-width="90px" class="flex flex-wrap">
  104. <el-form-item :label="t('marketingConfig.jumpMode')" prop="jumpMode" class="w-1/3">
  105. <el-select v-model="formData.jumpMode" :placeholder="t('marketingConfig.jumpModeTip')" clearable>
  106. <el-option v-for="item in listSelect" :key="item.value" :label="item.label" :value="item.value" />
  107. </el-select>
  108. </el-form-item>
  109. <el-form-item :label="t('marketingConfig.triggerType')" prop="triggerType" class="w-1/3">
  110. <el-select v-model="formData.triggerType" :placeholder="t('marketingConfig.triggerTypeTip')" clearable>
  111. <el-option v-for="item in triggerSelect" :key="item.value" :label="item.label" :value="item.value" />
  112. </el-select>
  113. </el-form-item>
  114. <el-form-item :label="t('marketingConfig.triggerFrequency')" prop="triggerFrequency" class="w-1/3">
  115. <el-input-number
  116. v-model="formData.triggerFrequency"
  117. class="!w-40"
  118. :min="1"
  119. :max="20"
  120. controls-position="right"
  121. :placeholder="t('marketingConfig.triggerFrequencyTip')"
  122. />
  123. </el-form-item>
  124. <div class="w-full mb-[18px]">
  125. <el-form-item
  126. v-if="formData.jumpMode == '2' || formData.jumpMode == '3'"
  127. :label="t('marketingConfig.prompt')"
  128. prop="prompt"
  129. class="w-1/3"
  130. >
  131. <el-input v-model="formData.prompt" type="text" :placeholder="t('marketingConfig.promptTip')"></el-input>
  132. </el-form-item>
  133. <el-form-item
  134. v-if="formData.jumpMode == '1' || formData.jumpMode == '3'"
  135. :label="t('marketingConfig.jumpLink')"
  136. prop="jumpLink"
  137. class="w-1/3"
  138. >
  139. <el-input v-model="formData.jumpLink" type="text" :placeholder="t('marketingConfig.jumpLinkTip')"></el-input>
  140. </el-form-item>
  141. </div>
  142. <div class="w-full">
  143. <el-button type="primary" @click="onSubmit" class="w-[80px] ml-5">{{ t('common.saveBtn') }}</el-button>
  144. </div>
  145. </el-form>
  146. </div>
  147. </el-tab-pane>
  148. </el-tabs>
  149. <DomainEdit v-model:open="domainEditOpen" />
  150. <ListEdit v-model:open="listEditOpen" />
  151. <GroupingEdit v-model:open="groupingEditOpen" :type="openType" @onsuccess="getData" />
  152. <IpListEdit :type="openType" ref="menuDialogRef" @onsuccess="getData" />
  153. <el-dialog v-model="delOpen" title="提示" width="500" @close="delOpen = false">
  154. <span>确认删除{{ delObj?.title }}分组吗?</span>
  155. <template #footer>
  156. <div class="dialog-footer">
  157. <el-button @click="delOpen = false">取消</el-button>
  158. <el-button type="primary" :disabled="loading" @click="onDel(delObj)"> 确定 </el-button>
  159. </div>
  160. </template>
  161. </el-dialog>
  162. </div>
  163. </template>
  164. <script lang="ts" name="marketingConfig" setup>
  165. import { delGroup, pageListDomain, pageListIp ,getConfigIpList } from '/@/api/marketing/config';
  166. import { useI18n } from 'vue-i18n';
  167. import { useMessage } from '/@/hooks/message';
  168. import { rule } from '/@/utils/validate';
  169. import { ipSplicing } from '/@/utils/ipUpdate';
  170. // 引入组件
  171. const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
  172. const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
  173. const DomainEdit = defineAsyncComponent(() => import('./components/domainEdit.vue'));
  174. const ListEdit = defineAsyncComponent(() => import('./components/listEdit.vue'));
  175. const GroupingEdit = defineAsyncComponent(() => import('./components/ipGroupingEdit.vue'));
  176. const IpListEdit = defineAsyncComponent(() => import('./components/ipListEdit.vue'));
  177. const { t } = useI18n();
  178. // 定义变量内容
  179. const activeName = ref('IP分组');
  180. //关闭或打开tabs的关闭按钮
  181. const closeDomainTags = ref(false);
  182. const closeIpTags = ref(false);
  183. // 弹窗
  184. const domainEditOpen = ref(false);
  185. const listEditOpen = ref(false);
  186. const groupingEditOpen = ref(false);
  187. const delOpen = ref(false);
  188. const loading = ref(false);
  189. const menuDialogRef = ref();
  190. const domainActiveId = ref([]);
  191. const ipActiveId = ref([]);
  192. // const ipListEditOpen = ref(false);
  193. const domainData = ref([]);
  194. const ipData = ref([]);
  195. const delObj = ref({});
  196. const openType = ref('ip'); // 'ip' or 'domain'
  197. const listSelect = ref([
  198. {
  199. value: '1',
  200. label: '跳转链接',
  201. },
  202. {
  203. value: '2',
  204. label: '提示信息',
  205. },
  206. {
  207. value: '3',
  208. label: '提示并跳转',
  209. },
  210. ]);
  211. const triggerSelect = ref([
  212. {
  213. value: '1',
  214. label: '仅一次跳转',
  215. },
  216. {
  217. value: '2',
  218. label: '多次跳转',
  219. },
  220. ]);
  221. const menuDialogFormRef = ref();
  222. const formData = ref({
  223. jumpMode: '1',
  224. triggerType: '',
  225. jumpLink: '',
  226. triggerFrequency: '',
  227. prompt: '',
  228. });
  229. const onOpenDelete = (item: any, type: any) => {
  230. console.log(item);
  231. delObj.value = { ...item, type };
  232. console.log(delObj.value);
  233. delOpen.value = true;
  234. };
  235. const getData = () => {
  236. openType.value === 'ip' ? getIpData() : getDomainData();
  237. };
  238. // // 表单校验规则
  239. const dataRules = reactive({
  240. jumpLink: [{ validator: rule.url, trigger: 'blur' }],
  241. });
  242. const data = ref([
  243. {
  244. title: '分组1',
  245. id: '1',
  246. list: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
  247. },
  248. {
  249. title: '分组2',
  250. id: '2',
  251. list: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
  252. },
  253. {
  254. title: '分组3',
  255. id: '3',
  256. list: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
  257. },
  258. ]);
  259. const whiteList = ref([{ label: '站大' }, { label: '站而' }, { label: '站撒' }, { label: '站爱' }]);
  260. const onSubmit = () => {
  261. useMessage().success(t(formData.value ? 'common.editSuccessText' : 'common.addSuccessText'));
  262. };
  263. const handleClick = (data: any) => {
  264. if (data.props.label === 'IP分组') {
  265. getIpData();
  266. } else if (data.props.label === '域名分组') {
  267. getDomainData();
  268. } else {
  269. getConfig()
  270. }
  271. };
  272. const onDel = async (data: any) => {
  273. try {
  274. loading.value = true;
  275. await delGroup({
  276. ids: [data.id],
  277. groupType: data.type === 'ip' ? 1 : 2,
  278. });
  279. useMessage().success(t('common.delSuccessText'));
  280. } catch (err: any) {
  281. useMessage().error(err.msg);
  282. } finally {
  283. loading.value = false;
  284. delOpen.value = false;
  285. data.type === 'ip' ? getIpData() : getDomainData();
  286. }
  287. };
  288. const onClickAdd = (type: string) => {
  289. openType.value = type;
  290. groupingEditOpen.value = true;
  291. };
  292. const onClickEdit = (item: any, type: string) => {
  293. openType.value = type;
  294. // ipListEditOpen.value = true;
  295. onOpenEditMenu(type, item);
  296. };
  297. // 打开编辑菜单弹窗
  298. const onOpenEditMenu = (type: string, row: any) => {
  299. menuDialogRef.value.openDialog(type, row);
  300. };
  301. const getConfig = () => {
  302. configIp()
  303. };
  304. const configIp = async()=>{
  305. await getConfigIpList().then((val) => {
  306. console.log(val.data);
  307. });
  308. }
  309. const getDomainData = async () => {
  310. await pageListDomain().then((val) => {
  311. domainActiveId.value = [];
  312. domainData.value = val.data.map((item: any) => {
  313. domainActiveId.value.push(item.id);
  314. return {
  315. ...item,
  316. title: item.groupName,
  317. id: item.id,
  318. list: item.domains.map((items: any) => {
  319. return {
  320. ...items,
  321. id: items.id,
  322. value: items.domain,
  323. };
  324. }),
  325. };
  326. });
  327. });
  328. };
  329. const getIpData = async () => {
  330. await pageListIp().then((val) => {
  331. ipActiveId.value = [];
  332. ipData.value = val.data.map((item: any) => {
  333. ipActiveId.value.push(item.id);
  334. return {
  335. ...item,
  336. title: item.groupName,
  337. id: item.id,
  338. list: item.ips.map((items: any) => {
  339. return {
  340. ...items,
  341. id: items.id,
  342. value: ipSplicing(items.startIp, items.endIp),
  343. };
  344. }),
  345. };
  346. });
  347. });
  348. };
  349. onMounted(() => {
  350. //获取IP列表
  351. getIpData();
  352. });
  353. </script>
  354. <style lang="scss">
  355. .is-top {
  356. margin-bottom: 0 !important;
  357. }
  358. .el-collapse-item__content {
  359. padding-bottom: 0 !important;
  360. }
  361. .el-tabs--card > .el-tabs__header .el-tabs__item {
  362. background-color: #fff;
  363. }
  364. .el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
  365. background-color: #e8f2fe;
  366. border-bottom-color: #e8f2fe;
  367. }
  368. </style>