浏览代码

Merge branch 'dev-ly' into dev-cmn

cmy 2 周之前
父节点
当前提交
c18121b244

+ 10 - 3
src/api/marketing/config.ts

@@ -1,9 +1,16 @@
 import request from '/@/utils/request';
 
-export const pageList = (params?: Object) => {
+export const pageListDomain = () => {
 	return request({
-		// url: '/admin/marketing/config/page',
-		url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
+		url: '/marketing/config/getDomainGroup',
+		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
+		method: 'get',
+	});
+};
+export const pageListIp = (params?: Object) => {
+	return request({
+		url: '/admin/marketing/config/getDomainGroup',
+		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
 		method: 'get',
 		params,
 	});

+ 19 - 14
src/components/JCollapse/index.vue

@@ -1,26 +1,30 @@
 
 <template>
-	<el-collapse v-model="innerActiveNames" class="coll-left mt-2">
+	<el-collapse v-model="innerActiveNames" class="coll-left mt-2 border">
 		<el-collapse-item v-for="item in props.data" :key="item.id" :name="item.id">
 			<template #title>
 				<div class="collapse-title flex items-center justify-between coll-left-title">
-					<div>{{ item.title }}</div>
+					<div style="font-size: 14px">{{ item.title }}</div>
 					<div class="action-buttons">
-						<el-button size="text" type="primary" @click.stop="handleEdit(item)" class="edit-btn">{{props.updateText}}</el-button>
-						<el-button size="text" type="danger" @click.stop="handleDelete(item)" class="delete-btn">{{props.deleteText}}</el-button>
+						<el-button size="text" type="primary" @click.stop="handleEdit(item)" class="edit-btn">{{ props.updateText }}</el-button>
+						<el-button size="text" type="danger" @click.stop="handleDelete(item)" class="delete-btn">{{ props.deleteText }}</el-button>
 					</div>
 				</div>
 			</template>
-			<div v-if="item.ips" class="">
-				<div v-for="ip in item.ips" :key="ip" class="m-2 text-sm float-left" style="color: #646464">{{ ip }}</div>
+			<div v-if="item.list" class="">
+				<div v-if="item.list.length && typeof item.list[0] === 'object'">
+					<div v-for="ip in item.list" :key="ip.id" class="m-2 float-left" style="color: #646464; font-size: 14px">{{ ip.value }}</div>
+				</div>
+				<div v-else>
+					<div v-for="ip in item.list" :key="ip" class="m-2 float-left" style="color: #646464; font-size: 14px">{{ ip }}</div>
+				</div>
 			</div>
-            <slot v-else></slot>
+			<slot v-else></slot>
 		</el-collapse-item>
 	</el-collapse>
 </template>
 
 <script setup name="jCollapse">
-const activeNames = ref(['1'])
 const emit = defineEmits(['update', 'delete']);
 const props = defineProps({
 	data: {
@@ -34,11 +38,11 @@ const props = defineProps({
 	updateText: {
 		type: String,
 		default: '修改',
-	},  
+	},
 	activeNames: {
 		type: Array,
 		default: () => [],
-	}
+	},
 });
 
 const handleDelete = (item) => {
@@ -48,9 +52,9 @@ const handleEdit = (item) => {
 	emit('update', item);
 };
 const innerActiveNames = computed({
-  get: () => props.activeNames,
-  set: (val) => emit('update:activeNames', val)
-})
+	get: () => props.activeNames,
+	set: (val) => emit('update:activeNames', val),
+});
 </script>
 <style lang="scss">
 .collapse-title {
@@ -65,7 +69,8 @@ const innerActiveNames = computed({
 	button {
 		background-color: #f4f5fa !important;
 	}
-	.coll-left-title,span.el-collapse-item__title {
+	.coll-left-title,
+	span.el-collapse-item__title {
 		order: 1;
 		margin-left: 5px;
 	}

+ 1 - 1
src/components/Title/index.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="text-lg font-medium ">
+	<div class=" font-medium " style="font-size: 16px;">
 		{{ props.title }}
 		<slot></slot>
 	</div>

+ 3 - 0
src/i18n/pages/form/zh-cn.ts

@@ -1,3 +1,5 @@
+import { save } from "/@/api/admin/menu";
+
 // 定义通用内容
 export default {
 	common: {
@@ -14,6 +16,7 @@ export default {
 		queryDeptTip: '请输入部门名称',
 		resetBtn: '重置',
 		action: '操作',
+		saveBtn: '保 存',
 		optSuccessText: '操作成功',
 		editSuccessText: '修改成功',
 		addSuccessText: '添加成功',

+ 2 - 2
src/views/marketing/apps/components/listEdit.vue

@@ -8,11 +8,11 @@
 					<el-radio value="2">黑名单</el-radio>
 				</el-radio-group>
 			</el-form-item>
-			<el-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.ip')]"
+			<el-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.ipSegment')]"
 				size="default">
 				<template #default="scope">
 					<span>{{ scope.item }}</span>
-					<el-icon v-if="scope.item === t('marketingConfig.ip')" size="12" style="margin-left: 5px;">
+					<el-icon v-if="scope.item === t('marketingConfig.ipSegment')" size="12" style="margin-left: 5px;">
 						<el-tooltip effect="light" content="输入127.0.0.1/24格式代表网段" placement="top">
 							<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
 								<path d="M7 14C8.93298 14 10.683 13.2165 11.9497 11.9497C13.2165 10.683 14 8.93298 14 7C14 5.06702 13.2165 3.31702 11.9497 2.05025C10.683 0.783503 8.93298 0 7 0C5.06702 0 3.31702 0.783503 2.05025 2.05025C0.783503 3.31702 0 5.06702 0 7C0 8.93298 0.783503 10.683 2.05025 11.9497C3.31702 13.2165 5.06702 14 7 14Z" fill="#1B4D88" fill-opacity="0.4"/>

+ 1 - 1
src/views/marketing/config/components/domainEdit.vue

@@ -9,7 +9,7 @@
 		draggable
 	>
 		<el-form ref="menuDialogFormRef" :rules="dataRules" :model="state.ruleForm" v-loading="loading">
-			<el-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.domain')]" size="large" />
+			<el-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.domain')]" size="default" />
 			<el-form-item label="" prop="grouping" v-if="activeName === t('marketingConfig.grouping')">
 				<el-select v-model="state.ruleForm.groupingTip" :placeholder="t('marketingConfig.groupingTip')" clearable>
 					<el-option v-for="item in listSelect" :key="item.value" :label="item.label" :value="item.value" />

+ 0 - 152
src/views/marketing/config/components/ipEdit.vue

@@ -1,152 +0,0 @@
-<template>
-  <el-dialog :title="sign === 'domain' ? '修改域名集合' : '修改ip集合'" width="600" v-model="visible"
-             :close-on-click-modal="false" :destroy-on-close="true" draggable>
-    <el-form ref="menuDialogFormRef" :model="state.ruleForm" 
-      label-width="90px" v-loading="loading">
-      <el-form-item :label="sign === 'domain' ? '域名集合' : 'ip集合'" prop="configs">
-        <div v-for="(item, index) in state.ruleForm.list" :key="item.index" class="flex items-center mb-2 text-sm font-normal justify-start w-full">
-          <el-input style="width: 300px; margin-right: 20px;" v-model="item.val" clearable :placeholder="t('marketingConfig.inputIPTip')"></el-input>
-          <div class="config-actions w-[50px] flex items-center justify-between ">
-            <svg 
-              v-if="index === state.ruleForm.list.length - 1" 
-              @click="state.ruleForm.list.push({val: ''})"
-              style="cursor: pointer;" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-              <path d="M19.5 3H4.5C3.67157 3 3 3.67157 3 4.5V19.5C3 20.3284 3.67157 21 4.5 21H19.5C20.3284 21 21 20.3284 21 19.5V4.5C21 3.67157 20.3284 3 19.5 3Z" stroke="#646464" stroke-linejoin="round"/>
-              <path d="M12 8V16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round"/>
-              <path d="M8 12H16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round"/>
-            </svg>
-            <svg 
-              v-if="state.ruleForm.list.length > 1" style="cursor: pointer;"
-              @click="state.ruleForm.list.splice(index, 1)"
-              width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-              <path d="M19.5 3H4.5C3.67157 3 3 3.67157 3 4.5V19.5C3 20.3284 3.67157 21 4.5 21H19.5C20.3284 21 21 20.3284 21 19.5V4.5C21 3.67157 20.3284 3 19.5 3Z" stroke="#646464" stroke-linejoin="round"/>
-              <path d="M8 12H16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round"/>
-            </svg>
-          </div>
-        </div>
-      </el-form-item>
-    </el-form>
-    <template #footer>
-			<span class="dialog-footer">
-				<el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
-				<el-button type="primary" @click="onSubmit" :disabled="loading">{{ t('common.confirmButtonText') }}</el-button>
-			</span>
-    </template>
-  </el-dialog>
-</template>
-
-<script setup name="systemMenuDialog">
-import {useI18n} from 'vue-i18n';
-import {info, getAppList, save} from '/@/api/marketing/config';
-import {useMessage} from '/@/hooks/message';
-import {rule} from '/@/utils/validate';
-
-// 定义子组件向父组件传值/事件
-const emit = defineEmits(['refresh']);
-const { t } = useI18n();
-
-// 定义变量内容
-const visible = ref(false);
-const loading = ref(false);
-const sign = ref('domain')
-const menuDialogFormRef = ref();
-// 定义需要的数据
-const state = reactive({
-  ruleForm: {
-    list: [''], 
-    https: [''], // 域名集合
-    ips: [''], // ip集合
-      // {
-      //   id: '1',
-      //   ip: '美国',
-      //   appId: '1',
-      //   appUrl: 'www.baidu.com',
-      //   appName: 'adsa'
-      // },
-  },
-  appList: [], // 应用下拉框列表
-});
-
-// // 表单校验规则
-// const dataRules = reactive({
-//   domain: [
-//     {required: true, message: '域名不能为空', trigger: 'blur'},
-//     // { validator: rule.url, trigger: 'blur' }
-//   ],
-// });
-
-// 分页参数
-const pagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0
-})
-
-// 打开弹窗
-const openDialog = async (type, row, str = 'domain') => {
-  visible.value = true;
-  sign.value = str;
-  nextTick(() => {
-    menuDialogFormRef.value?.resetFields();
-  });
-  state.ruleForm = JSON.parse(JSON.stringify(row));
-  str === 'domain' ? state.ruleForm.list = row.https.map(e => ({val: e})) : state.ruleForm.list = row.ips.map(e => ({val: e}));
-  // if (row?.id && type === 'edit') {
-  //   await getConfigDetail(row.id);
-  // } else {
-  //   state.ruleForm = {
-  //     id: null,
-  //     domain: '',
-  //     configs: [
-  //       {
-  //         id: '',
-  //         ip: '',
-  //         appId: '',
-  //         appUrl: '',
-  //         appName: ''
-  //       }
-  //     ],
-  //   };
-  // }
-  // pagination.current = 1;
-  // // 获取应用列表
-  // getAllAppData();
-};
-
-// 获取当条配置信息
-const getConfigDetail = (id) => {
-  info(id).then((res) => {
-    Object.assign(state.ruleForm, res.data);
-  });
-};
-
-// 保存数据
-const onSubmit = async () => {
-  // const valid = await menuDialogFormRef.value.validate().catch(() => {
-  // });
-  // if (!valid) return false;
-  sign.value === 'domain' ? state.ruleForm.https = state.ruleForm.list.map(e => e.val) : state.ruleForm.ips = state.ruleForm.list.map(e => e.val);
-  delete state.ruleForm.list;
-  try {
-    loading.value = true;
-    await save(state.ruleForm);
-    useMessage().success(t(state.ruleForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
-    visible.value = false;
-    emit('refresh');
-  } catch (err) {
-    useMessage().error(err.msg);
-  } finally {
-    loading.value = false;
-  }
-};
-
-// 暴露变量 只有暴漏出来的变量 父组件才能使用
-defineExpose({
-  openDialog,
-});
-</script>
-<style>
-.apps-loadmore.el-select-dropdown .el-scrollbar__wrap {
-  height: 330px !important;
-}
-</style>

+ 85 - 0
src/views/marketing/config/components/ipGroupingEdit.vue

@@ -0,0 +1,85 @@
+<template>
+	<el-dialog
+		:title="type == 'ip'? '添加IP分组':'添加域名分组'"
+		width="600"
+		v-model="props.open"
+		:close-on-click-modal="false"
+		:destroy-on-close="true"
+		@close="onCancel"
+		draggable
+	>
+		<el-form ref="menuDialogFormRef" :rules="dataRules" :model="state.ruleForm" v-loading="loading">
+			<el-form-item :label="t('marketingConfig.groupingName')" prop="grouping" >
+				<el-input v-model="state.ruleForm.grouping" type="text" :placeholder="t('marketingConfig.groupingNameTip')"></el-input>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<span class="dialog-footer">
+				<el-button @click="onCancel">{{ t('common.cancelButtonText') }}</el-button>
+				<el-button type="primary" @click="onSubmit" :disabled="loading">{{ t('common.confirmButtonText') }}</el-button>
+			</span>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup name="systemMenuDialog">
+import { useI18n } from 'vue-i18n';
+import { info, getAppList, save } from '/@/api/marketing/config';
+import { useMessage } from '/@/hooks/message';
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['update:open', 'onsuccess']);
+const { t } = useI18n();
+
+// 定义变量内容
+const loading = ref(false);
+const menuDialogFormRef = ref();
+
+// 定义需要的数据
+const state = reactive({
+	ruleForm: {
+		listType: '1', // 1: 白名单, 2: 黑名单
+		list: [''],
+		https: [''], // 域名集合
+		ips: [''], // ip集合
+	},
+	appList: [], // 应用下拉框列表
+});
+
+const props = defineProps({
+	open: {
+		type: Boolean,
+		default: false,
+	},
+	type: {
+		type: String,
+		default: 'ip', // 'ip' or 'domain'
+	},
+});
+// // 表单校验规则
+const dataRules = reactive({
+	grouping: [{ required: true, message: '分组名称不能为空', trigger: 'blur' }],
+});
+const onCancel = () => {
+	emit('update:open', false);
+};
+// 保存数据
+const onSubmit = async () => {
+	try {
+		loading.value = true;
+		// await save(state.ruleForm);
+		useMessage().success(t(state.ruleForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
+		emit('onsuccess');
+		onCancel();
+	} catch (err) {
+		useMessage().error(err.msg);
+	} finally {
+		loading.value = false;
+	}
+};
+</script>
+<style>
+.apps-loadmore.el-select-dropdown .el-scrollbar__wrap {
+	height: 330px !important;
+}
+</style>

+ 149 - 107
src/views/marketing/config/components/ipListEdit.vue

@@ -1,152 +1,194 @@
 <template>
-  <el-dialog :title="sign === 'domain' ? '修改域名集合' : '修改ip集合'" width="600" v-model="visible"
-             :close-on-click-modal="false" :destroy-on-close="true" draggable>
-    <el-form ref="menuDialogFormRef" :model="state.ruleForm" 
-      label-width="90px" v-loading="loading">
-      <el-form-item :label="sign === 'domain' ? '域名集合' : 'ip集合'" prop="configs">
-        <div v-for="(item, index) in state.ruleForm.list" :key="item.index" class="flex items-center mb-2 text-sm font-normal justify-start w-full">
-          <el-input style="width: 300px; margin-right: 20px;" v-model="item.val" clearable :placeholder="t('marketingConfig.inputIPTip')"></el-input>
-          <div class="config-actions w-[50px] flex items-center justify-between ">
-            <svg 
-              v-if="index === state.ruleForm.list.length - 1" 
-              @click="state.ruleForm.list.push({val: ''})"
-              style="cursor: pointer;" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-              <path d="M19.5 3H4.5C3.67157 3 3 3.67157 3 4.5V19.5C3 20.3284 3.67157 21 4.5 21H19.5C20.3284 21 21 20.3284 21 19.5V4.5C21 3.67157 20.3284 3 19.5 3Z" stroke="#646464" stroke-linejoin="round"/>
-              <path d="M12 8V16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round"/>
-              <path d="M8 12H16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round"/>
-            </svg>
-            <svg 
-              v-if="state.ruleForm.list.length > 1" style="cursor: pointer;"
-              @click="state.ruleForm.list.splice(index, 1)"
-              width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-              <path d="M19.5 3H4.5C3.67157 3 3 3.67157 3 4.5V19.5C3 20.3284 3.67157 21 4.5 21H19.5C20.3284 21 21 20.3284 21 19.5V4.5C21 3.67157 20.3284 3 19.5 3Z" stroke="#646464" stroke-linejoin="round"/>
-              <path d="M8 12H16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round"/>
-            </svg>
-          </div>
-        </div>
-      </el-form-item>
-    </el-form>
-    <template #footer>
+	<el-dialog
+		:title="sign === 'domain' ? '修改域名集合' : '修改ip集合'"
+		width="600"
+		v-model="visible"
+		:close-on-click-modal="false"
+		:destroy-on-close="true"
+		draggable
+	>
+		<el-form ref="menuDialogFormRef" :model="state.ruleForm" label-width="90px" v-loading="loading">
+            <el-form-item label="名称" prop="name">
+				<el-input class="!w-[300px]" v-model="state.ruleForm.name" placeholder="请输入名称"></el-input>
+			</el-form-item>
+			<el-form-item  prop="configs">
+				<template #label>
+					<div class="flex items-center">
+                        <el-tooltip effect="light" content="输入127.0.0.1/24格式代表网段" placement="top">
+						<el-icon><Warning /> </el-icon>
+					</el-tooltip>
+                    {{sign === 'domain' ? '域名集合' : 'ip集合'  }}
+                    </div>
+				</template>
+				<div v-for="(item, index) in state.ruleForm.list" :key="item.index" class="flex items-center mb-2 text-sm font-normal justify-start w-full">
+					<el-input class="!w-[300px] mr-10" v-model="item.val" clearable :placeholder="t('marketingConfig.inputIPTip')"></el-input>
+					<div class="config-actions w-[50px] flex items-center justify-between">
+						<svg
+							v-if="index === state.ruleForm.list.length - 1"
+							@click="state.ruleForm.list.push({ val: '' })"
+							style="cursor: pointer"
+							width="24"
+							height="24"
+							viewBox="0 0 24 24"
+							fill="none"
+							xmlns="http://www.w3.org/2000/svg"
+						>
+							<path
+								d="M19.5 3H4.5C3.67157 3 3 3.67157 3 4.5V19.5C3 20.3284 3.67157 21 4.5 21H19.5C20.3284 21 21 20.3284 21 19.5V4.5C21 3.67157 20.3284 3 19.5 3Z"
+								stroke="#646464"
+								stroke-linejoin="round"
+							/>
+							<path d="M12 8V16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" />
+							<path d="M8 12H16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" />
+						</svg>
+						<svg
+							v-if="state.ruleForm.list.length > 1"
+							style="cursor: pointer"
+							@click="state.ruleForm.list.splice(index, 1)"
+							width="24"
+							height="24"
+							viewBox="0 0 24 24"
+							fill="none"
+							xmlns="http://www.w3.org/2000/svg"
+						>
+							<path
+								d="M19.5 3H4.5C3.67157 3 3 3.67157 3 4.5V19.5C3 20.3284 3.67157 21 4.5 21H19.5C20.3284 21 21 20.3284 21 19.5V4.5C21 3.67157 20.3284 3 19.5 3Z"
+								stroke="#646464"
+								stroke-linejoin="round"
+							/>
+							<path d="M8 12H16" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" />
+						</svg>
+					</div>
+				</div>
+			</el-form-item>
+		</el-form>
+		<template #footer>
 			<span class="dialog-footer">
 				<el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
 				<el-button type="primary" @click="onSubmit" :disabled="loading">{{ t('common.confirmButtonText') }}</el-button>
 			</span>
-    </template>
-  </el-dialog>
+		</template>
+	</el-dialog>
 </template>
 
 <script setup name="systemMenuDialog">
-import {useI18n} from 'vue-i18n';
-import {info, getAppList, save} from '/@/api/marketing/config';
-import {useMessage} from '/@/hooks/message';
-import {rule} from '/@/utils/validate';
+import { useI18n } from 'vue-i18n';
+import { info, getAppList, save } from '/@/api/marketing/config';
+import { useMessage } from '/@/hooks/message';
+import { rule } from '/@/utils/validate';
 
 // 定义子组件向父组件传值/事件
-const emit = defineEmits(['refresh']);
+const emit = defineEmits(['onsuccess']);
 const { t } = useI18n();
 
 // 定义变量内容
 const visible = ref(false);
 const loading = ref(false);
-const sign = ref('domain')
+const sign = ref('domain');
 const menuDialogFormRef = ref();
 // 定义需要的数据
 const state = reactive({
-  ruleForm: {
-    list: [''], 
-    https: [''], // 域名集合
-    ips: [''], // ip集合
-      // {
-      //   id: '1',
-      //   ip: '美国',
-      //   appId: '1',
-      //   appUrl: 'www.baidu.com',
-      //   appName: 'adsa'
-      // },
-  },
-  appList: [], // 应用下拉框列表
+	ruleForm: {
+		list: [
+			{
+				val: '192.168.0.32',
+			},
+		],
+		https: [''], // 域名集合
+		ips: [''], // ip集合
+		// {
+		//   id: '1',
+		//   ip: '美国',
+		//   appId: '1',
+		//   appUrl: 'www.baidu.com',
+		//   appName: 'adsa'
+		// },
+	},
+	appList: [], // 应用下拉框列表
 });
 
 // // 表单校验规则
-// const dataRules = reactive({
-//   domain: [
-//     {required: true, message: '域名不能为空', trigger: 'blur'},
-//     // { validator: rule.url, trigger: 'blur' }
-//   ],
-// });
+const dataRules = reactive({
+	domain: [
+		{ required: true, message: sign.value == 'domain' ? '域名' : 'ip' + '不能为空', trigger: 'blur' },
+		{ validator: sign.value == 'domain' ? rule.domain : rule.ip, trigger: 'blur' },
+	],
+});
 
 // 分页参数
 const pagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0
-})
+	current: 1,
+	size: 10,
+	total: 0,
+});
 
 // 打开弹窗
 const openDialog = async (type, row, str = 'domain') => {
-  visible.value = true;
-  sign.value = str;
-  nextTick(() => {
-    menuDialogFormRef.value?.resetFields();
-  });
-  state.ruleForm = JSON.parse(JSON.stringify(row));
-  str === 'domain' ? state.ruleForm.list = row.https.map(e => ({val: e})) : state.ruleForm.list = row.ips.map(e => ({val: e}));
-  // if (row?.id && type === 'edit') {
-  //   await getConfigDetail(row.id); 
-  // } else {
-  //   state.ruleForm = {
-  //     id: null,
-  //     domain: '',
-  //     configs: [
-  //       {
-  //         id: '',
-  //         ip: '',
-  //         appId: '',
-  //         appUrl: '',
-  //         appName: ''
-  //       }
-  //     ],
-  //   };
-  // }
-  // pagination.current = 1;
-  // // 获取应用列表
-  // getAllAppData();
+	visible.value = true;
+	sign.value = type;
+	nextTick(() => {
+		menuDialogFormRef.value?.resetFields();
+	});
+	// state.ruleForm = JSON.parse(JSON.stringify(row));
+	// str === 'domain' ? (state.ruleForm.list = row.https.map((e) => ({ val: e }))) : (state.ruleForm.list = row.ips.map((e) => ({ val: e })));
+	// if (row?.id && type === 'edit') {
+	//   await getConfigDetail(row.id);
+	// } else {
+	//   state.ruleForm = {
+	//     id: null,
+	//     domain: '',
+	//     configs: [
+	//       {
+	//         id: '',
+	//         ip: '',
+	//         appId: '',
+	//         appUrl: '',
+	//         appName: ''
+	//       }
+	//     ],
+	//   };
+	// }
+	// pagination.current = 1;
+	// // 获取应用列表
+	// getAllAppData();
 };
 
 // 获取当条配置信息
 const getConfigDetail = (id) => {
-  info(id).then((res) => {
-    Object.assign(state.ruleForm, res.data);
-  });
+	info(id).then((res) => {
+		Object.assign(state.ruleForm, res.data);
+	});
 };
 
 // 保存数据
 const onSubmit = async () => {
-  // const valid = await menuDialogFormRef.value.validate().catch(() => {
-  // });
-  // if (!valid) return false;
-  sign.value === 'domain' ? state.ruleForm.https = state.ruleForm.list.map(e => e.val) : state.ruleForm.ips = state.ruleForm.list.map(e => e.val);
-  delete state.ruleForm.list;
-  try {
-    loading.value = true;
-    await save(state.ruleForm);
-    useMessage().success(t(state.ruleForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
-    visible.value = false;
-    emit('refresh');
-  } catch (err) {
-    useMessage().error(err.msg);
-  } finally {
-    loading.value = false;
-  }
+	// const valid = await menuDialogFormRef.value.validate().catch(() => {
+	// });
+	// if (!valid) return false;
+	// sign.value === 'domain'
+	// 	? (state.ruleForm.https = state.ruleForm.list.map((e) => e.val))
+	// 	: (state.ruleForm.ips = state.ruleForm.list.map((e) => e.val));
+	// delete state.ruleForm.list;
+	try {
+		loading.value = true;
+		// await save(state.ruleForm);
+		useMessage().success(t(state.ruleForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
+		visible.value = false;
+		emit('onsuccess');
+	} catch (err) {
+		useMessage().error(err.msg);
+	} finally {
+		loading.value = false;
+	}
 };
 
 // 暴露变量 只有暴漏出来的变量 父组件才能使用
 defineExpose({
-  openDialog,
+	openDialog,
 });
 </script>
 <style>
 .apps-loadmore.el-select-dropdown .el-scrollbar__wrap {
-  height: 330px !important;
+	height: 330px !important;
 }
 </style>

+ 1 - 1
src/views/marketing/config/components/listEdit.vue

@@ -15,7 +15,7 @@
 					<el-radio value="2">黑名单</el-radio>
 				</el-radio-group>
 			</el-form-item>
-			<el-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.domain')]" size="large" />
+			<el-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.ip')]" size="default" />
 			<el-form-item label="" prop="grouping" v-if="activeName === t('marketingConfig.grouping')">
 				<el-select v-model="state.ruleForm.groupingTip" :placeholder="t('marketingConfig.groupingTip')" clearable>
 					<el-option v-for="item in listSelect" :key="item.value" :label="item.label" :value="item.value" />

+ 16 - 2
src/views/marketing/config/i18n/zh-cn.ts

@@ -4,7 +4,7 @@ export default {
 		name: '应用名称',
 		config: '配置方案',
 		inputNameTip: '请输入应用名称名称',
-		inputIPTip: '请输入IP名称',
+		inputIPTip: '请输入IP地址',
 		app: '应用',
 		inputAppSel: '请选择应用',
 		ipList:'IP分组',
@@ -17,13 +17,27 @@ export default {
 		deleteText: '删除',
 		cancel: '取消',
 		addDomain: '添加域名',
+		addIp: '添加IP',
 		deleteDomain: '删除域名',
 		deleteIp: '删除IP',
 		grouping: '分组',
 		domain: '单域名',
+		ip: '单IP',
 		domainTip: '请输入域名',
 		groupingTip: '请选择分组名称',
 		ipTip: '请输入IP',
-		ip: 'IP/段',
+		groupingName: '分组名称',
+		groupingNameTip: '请输入分组名称',
+		triggerType: '触发类型',
+		triggerTypeTip: '请选择触发类型',
+		jumpMode: '触发方式',
+		jumpModeTip: '请选择触发方式',
+		jumpLink: '跳转链接',
+		jumpLinkTip: '请输入跳转链接',
+		triggerFrequency: '触发频率',
+		triggerFrequencyTip: '请输入触发频率',
+		prompt: '提示信息',
+		promptTip: '请输入提示信息',
+		ipSegment: 'IP/段',
 	},
 };

+ 179 - 37
src/views/marketing/config/index.vue

@@ -2,25 +2,26 @@
 	<div class="layout-padding">
 		<el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
 			<el-tab-pane :label="t('marketingConfig.ipList')" name="first" class="layout-padding-auto layout-padding-view">
-				<Title :title="t('marketingConfig.ipList')" />
+				<Title class="ml-4" :title="t('marketingConfig.ipList')" />
 				<div class="p-4 rounded">
-					<el-button type="primary" @click="onAddClick">{{ t('marketingConfig.addIpList') }}</el-button>
+					<el-button type="primary" @click="onClickAdd('ip')">{{ t('marketingConfig.addIpList') }}</el-button>
 					<JCollapse
-						@update="(item) => console.log(item)"
+						@update="(item) => onClickEdit(item, 'ip')"
 						@delete="(item) => console.log(item)"
 						:data="data"
+						:activeNames="['1', '2', '3']"
 						:deleteText="t('marketingConfig.deleteListText')"
 						:updateText="t('marketingConfig.updateText')"
 					/>
 				</div>
 			</el-tab-pane>
 			<el-tab-pane :label="t('marketingConfig.domainList')" name="second" class="layout-padding-auto layout-padding-view">
-				<Title class="" :title="t('marketingConfig.domainList')" />
+				<Title class="ml-4" :title="t('marketingConfig.domainList')" />
 				<div class="p-4 rounded">
-					<el-button type="primary" @click="onAddClick">{{ t('marketingConfig.addDomainList') }}</el-button>
+					<el-button type="primary" @click="onClickAdd('domain')">{{ t('marketingConfig.addDomainList') }}</el-button>
 					<JCollapse
-						@update="(item) => console.log(item)"
-						:data="data"
+						@update="(item) => onClickEdit(item, 'domain')"
+						:data="domainData"
 						@delete="(item) => console.log(item)"
 						:deleteText="t('marketingConfig.deleteListText')"
 						:updateText="t('marketingConfig.updateText')"
@@ -28,14 +29,15 @@
 				</div>
 			</el-tab-pane>
 			<el-tab-pane :label="t('marketingConfig.disposition')" name="third" class="layout-padding-auto layout-padding-view">
-				<Title class="" :title="t('marketingConfig.disposition')" />
+				<Title class="ml-4" :title="t('marketingConfig.disposition')" />
 				<div class="p-4 rounded">
 					<JCollapse
-						:data="[{title: 'IP集合',id: '1',}]"
-						@update="(item) => listEditOpen = true"
-						@delete="(item) => closeIpTags = !closeIpTags"
-						:deleteText="t('marketingConfig.deleteText')"
-						:updateText="t('marketingConfig.updateText')"
+						: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">
@@ -72,87 +74,220 @@
 					</JCollapse>
 
 					<JCollapse
-					    class="mt-4"
-						:data="[
-							{
-								title: '域名集合',
-								id: '1',
-							},
-						]"
-						@update="(item) => domainEditOpen = true"
-						@delete="(item) => closeDomainTags = !closeDomainTags"
-						:deleteText="closeTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteDomain')"
+						class="mt-4"
+						:data="[{ title: '域名集合', id: '1' }]"
+						:activeNames="['1']"
+						@update="(item) => (domainEditOpen = true)"
+						@delete="(item) => (closeDomainTags = !closeDomainTags)"
+						:deleteText="closeDomainTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteDomain')"
 						:updateText="t('marketingConfig.addDomain')"
 					>
 						<template #default>
-							<div class="border-b p-2 items-center flex flex-wrap">
-								<el-tag v-for="item in whiteList" :key="item" effect="light" :closable="closeDomainTags" color="#f4f4f4" round class="ml-1 cursor-pointer">
+							<div class="p-2 items-center flex flex-wrap">
+								<el-tag
+									v-for="item in whiteList"
+									:key="item"
+									effect="light"
+									:closable="closeDomainTags"
+									color="#f4f4f4"
+									round
+									class="ml-1 cursor-pointer"
+								>
 									{{ item.label }}
 								</el-tag>
 							</div>
 						</template>
 					</JCollapse>
 				</div>
+				<div class="w-[66%] ml-[-8px] mt-5">
+					<el-form ref="menuDialogFormRef" :model="formData" :rule="dataRules" label-width="90px" class="flex flex-wrap">
+						<el-form-item :label="t('marketingConfig.jumpMode')" prop="jumpMode" class="w-1/3">
+							<el-select v-model="formData.jumpMode" :placeholder="t('marketingConfig.jumpModeTip')" clearable>
+								<el-option v-for="item in listSelect" :key="item.value" :label="item.label" :value="item.value" />
+							</el-select>
+						</el-form-item>
+						<el-form-item :label="t('marketingConfig.triggerType')" prop="triggerType" class="w-1/3">
+							<el-select v-model="formData.triggerType" :placeholder="t('marketingConfig.triggerTypeTip')" clearable>
+								<el-option v-for="item in triggerSelect" :key="item.value" :label="item.label" :value="item.value" />
+							</el-select>
+						</el-form-item>
+						<el-form-item :label="t('marketingConfig.triggerFrequency')" prop="triggerFrequency" class="w-1/3">
+							<el-input-number
+								v-model="formData.triggerFrequency"
+								class="!w-40"
+								:min="1"
+								:max="20"
+								controls-position="right"
+								:placeholder="t('marketingConfig.triggerFrequencyTip')"
+							/>
+						</el-form-item>
+
+						<div class="w-full mb-[18px]">
+							<el-form-item
+								v-if="formData.jumpMode == '2' || formData.jumpMode == '3'"
+								:label="t('marketingConfig.prompt')"
+								prop="prompt"
+								class="w-1/3"
+							>
+								<el-input v-model="formData.prompt" type="text" :placeholder="t('marketingConfig.promptTip')"></el-input>
+							</el-form-item>
+							<el-form-item
+								v-if="formData.jumpMode == '1' || formData.jumpMode == '3'"
+								:label="t('marketingConfig.jumpLink')"
+								prop="jumpLink"
+								class="w-1/3"
+							>
+								<el-input v-model="formData.jumpLink" type="text" :placeholder="t('marketingConfig.jumpLinkTip')"></el-input>
+							</el-form-item>
+						</div>
+						<div class="w-full">
+							<el-button type="primary" @click="onSubmit" class="w-[80px] ml-5">{{ t('common.saveBtn') }}</el-button>
+						</div>
+					</el-form>
+				</div>
 			</el-tab-pane>
 		</el-tabs>
 		<DomainEdit v-model:open="domainEditOpen" />
 		<ListEdit v-model:open="listEditOpen" />
+		<GroupingEdit v-model:open="groupingEditOpen" :type="openType" />
+		<IpListEdit :type="openType" ref="menuDialogRef" />
 	</div>
 </template>
 
 <script lang="ts" name="marketingConfig" setup>
-import { delObj, pageList, update } from '/@/api/marketing/config';
+import { delObj, pageListDomain, update } from '/@/api/marketing/config';
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { useI18n } from 'vue-i18n';
+import { useMessage } from '/@/hooks/message';
+import { rule } from '/@/utils/validate';
+import { identity } from '@vueuse/core';
 
 // 引入组件
 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 { t } = useI18n();
 // 定义变量内容
 const activeName = ref('first');
+//关闭或打开tabs的关闭按钮
 const closeDomainTags = ref(false);
 const closeIpTags = ref(false);
+
+// 弹窗
 const domainEditOpen = ref(false);
 const listEditOpen = ref(false);
+const groupingEditOpen = ref(false);
+// const ipListEditOpen = ref(false);
+const domainData = ref([]);
+
+const listSelect = ref([
+	{
+		value: '1',
+		label: '跳转链接',
+	},
+	{
+		value: '2',
+		label: '提示信息',
+	},
+	{
+		value: '3',
+		label: '提示并跳转',
+	},
+]);
+const triggerSelect = ref([
+	{
+		value: 'group1',
+		label: '仅一次跳转',
+	},
+	{
+		value: 'group2',
+		label: '多次跳转',
+	},
+]);
+
+const menuDialogFormRef = ref();
+const formData = ref({
+	jumpMode: '1',
+	triggerType: '',
+	jumpLink: '',
+	triggerFrequency: '',
+	prompt: '',
+});
+
+// // 表单校验规则
+const dataRules = reactive({
+	jumpLink: [{ validator: rule.url, trigger: 'blur' }],
+});
+
+const openType = ref('ip'); // 'ip' or 'domain'
 const data = ref([
 	{
 		title: '分组1',
 		id: '1',
-		ips: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
+		list: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
 	},
 	{
 		title: '分组2',
 		id: '2',
-		ips: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
+		list: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
 	},
 	{
 		title: '分组3',
 		id: '3',
-		ips: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
+		list: ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'],
 	},
 ]);
 const whiteList = ref([{ label: '站大' }, { label: '站而' }, { label: '站撒' }, { label: '站爱' }]);
 
 const menuDialogRef = ref();
-const state: BasicTableProps = reactive<BasicTableProps>({
-	pageList: pageList, // H
-	queryForm: {
-		domain: '',
-	},
+const state = reactive({
+	pageListDomain: pageListDomain(),
 });
+state.pageListDomain;
 
-const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
+const onSubmit = () => {
+	useMessage().success(t(formData.value ? 'common.editSuccessText' : 'common.addSuccessText'));
+};
+
+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 handleDelete = async (ids: string[]) => {};
+onMounted(() => {
+	// 获取域名列表
+	state.pageListDomain.then((val) => {
+		domainData.value = val.data.map((item :any) => {
+			console.log(item);
+			return {
+				title: item.groupName,
+				list: item.domains.map((items :any) => {
+					return{
+						id:items.id,
+						value:items.domain
+					}
+				}),
+			};
+		});
+	console.log(domainData.value);
+
+	});
+	
+	//获取IP列表
+});
 </script>
 <style  lang="scss">
 .is-top {
@@ -161,4 +296,11 @@ const handleDelete = async (ids: string[]) => {};
 .el-collapse-item__content {
 	padding-bottom: 0 !important;
 }
+.el-tabs--card > .el-tabs__header .el-tabs__item {
+	background-color: #fff;
+}
+.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
+	background-color: #e8f2fe;
+	border-bottom-color: #e8f2fe;
+}
 </style>