Explorar el Código

fix:营销管理-营销配置方案页面重修,改应用为主键

luoy hace 2 semanas
padre
commit
a0da830788

+ 24 - 36
mock.json

@@ -7,46 +7,34 @@
       "records": [
         {
             "id": 1,
-            "url": "/admin/menu/tree",
-            "config": [
-              {
-                "ip": "美国",
-                "appName": "数据营销平台",
-                "download": "https://www.baidu.com"
-              },
-              {
-                "ip": "中国",
-                "appName": "数据营销平台",
-                "download": "https://www.baidu.com"
-              },
-              {
-                "ip": "英国",
-                "appName": "数据营销平台",
-                "download": "https://www.baidu.com"
-              }
+            "appName": "数据营销平台",
+            "url": "www.baidu.com",
+            "isMarketing": 1,
+            "isHttp": 0,
+            "https": [
+              "192.168.1.1",
+              "192.168.1.2"
+            ],
+            "ips": [
+              "192.168.1.1",
+              "192.168.1.2"
             ]
         },
         {
-            "id": 2,
-            "url": "/admin/menu/t63546ee",
-            "config": [
-              {
-                "ip": "美国",
-                "appName": "数据营销平台",
-                "download": "https://www.baidu.com"
-              },
-              {
-                "ip": "中国",
-                "appName": "数据营销平台",
-                "download": "https://www.baidu.com"
-              },
-              {
-                "ip": "英国",
-                "appName": "数据营销平台",
-                "download": "https://www.baidu.com"
-              }
+            "id": 1,
+            "appName": "数据营销平台",
+            "url": "www.baidu.com",
+            "isMarketing": 1,
+            "isHttp": 0,
+            "https": [
+              "192.168.1.1",
+              "192.168.1.2"
+            ],
+            "ips": [
+              "192.168.1.1",
+              "192.168.1.2"
             ]
-        }
+        }    
     ]
     }
 }

+ 7 - 0
package-lock.json

@@ -21,6 +21,7 @@
 				"dayjs": "^1.11.13",
 				"driver.js": "1.3.1",
 				"echarts": "5.5.1",
+				"echarts-countries-js": "^1.0.5",
 				"element-plus": "2.8.7",
 				"js-cookie": "3.0.5",
 				"mitt": "3.0.1",
@@ -2775,6 +2776,12 @@
 				"zrender": "5.6.0"
 			}
 		},
+		"node_modules/echarts-countries-js": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/echarts-countries-js/-/echarts-countries-js-1.0.5.tgz",
+			"integrity": "sha512-D7XlEpQYO2W7UAHVPUYftB+xudyuBAtTBUgXyJ8T+8kAI33EdneMbNnm5g38y13HtREkDCeDF4ZMIFxEAGHwaw==",
+			"license": "ISC"
+		},
 		"node_modules/electron-to-chromium": {
 			"version": "1.5.80",
 			"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz",

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

@@ -2,11 +2,20 @@ import request from '/@/utils/request';
 
 export const pageList = (params?: Object) => {
 	return request({
-		url: '/admin/marketing/config/page',
+		// url: '/admin/marketing/config/page',
+		url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
 		method: 'get',
 		params,
 	});
 };
+export const update = (data: Object) => {
+	return request({
+		// url: `/admin/marketing/config/${id}`,
+		url: 'https://m1.apifoxmock.com/m2/6687089-6396408-default/323895237',
+		method: 'post',
+		data: data,
+	});
+};
 export const info = (id: String) => {
 	return request({
 		url: `/admin/marketing/config/${id}`,

+ 51 - 143
src/views/marketing/config/form.vue

@@ -1,50 +1,23 @@
 <template>
-  <el-dialog :title="state.ruleForm.id ? $t('common.editBtn') : $t('common.addBtn')" width="600" v-model="visible"
+  <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" :rules="dataRules" label-width="90px" v-loading="loading">
-      <el-form-item :label="$t('marketingConfig.name')" prop="domain">
-        <el-input v-model="state.ruleForm.domain" clearable :placeholder="$t('marketingConfig.inputNameTip')"></el-input>
-      </el-form-item>
-      <el-form-item :label="$t('marketingConfig.config')" prop="configs">
-        <div v-for="(item, index) in state.ruleForm.configs" :key="item.index" class="config-container">
-          <span>IP</span> <el-input style="width: 140px;" v-model="item.ip" clearable :placeholder="$t('marketingConfig.inputIPTip')"></el-input>
-          <span>{{ $t('marketingConfig.app') }}</span>
-
-          <el-select
-            style="width: 140px;"
-            v-model="item.appId"
-            clearable
-            :placeholder="$t('marketingConfig.inputAppSel')"
-            popper-class="apps-loadmore"
-            @visible-change="handleVisibleChange"
-          >
-            <!-- <el-option
-              v-for="item in state.appList"
-              :key="item.appId"
-              :label="item.appName"
-              :value="item.appId"
-            ></el-option> -->
-            
-            <el-option
-              v-for="(item, index) in state.appList"
-              :key="item.appId"
-              :label="item.appName"
-              :value="item.appId"
-            />
-          </el-select>
-
+    <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="config-container">
+          <el-input style="width: 300px; margin-right: 20px;" v-model="item.val" clearable :placeholder="$t('marketingConfig.inputIPTip')"></el-input>
           <div class="config-actions">
             <svg 
-              v-if="index === state.ruleForm.configs.length - 1" 
-              @click="state.ruleForm.configs.push({ ip: '', appId: '', appName: '' })"
+              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.configs.length > 1" style="cursor: pointer;"
-              @click="state.ruleForm.configs.splice(index, 1)"
+              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"/>
@@ -71,26 +44,18 @@ import {rule} from '/@/utils/validate';
 // 定义子组件向父组件传值/事件
 const emit = defineEmits(['refresh']);
 const {t} = useI18n();
-// 引入组件
-const IconSelector = defineAsyncComponent(() => import('/@/components/IconSelector/index.vue'));
 
 // 定义变量内容
 const visible = ref(false);
 const loading = ref(false);
+const sign = ref('domain')
 const menuDialogFormRef = ref();
 // 定义需要的数据
 const state = reactive({
   ruleForm: {
-    id: null,
-    domain: '',
-    configs: [
-      {
-        id: '',
-        ip: '',
-        appId: '',
-        appUrl: '',
-        appName: ''
-      }
+    list: [''], 
+    https: [''], // 域名集合
+    ips: [''], // ip集合
       // {
       //   id: '1',
       //   ip: '美国',
@@ -98,18 +63,17 @@ const state = reactive({
       //   appUrl: 'www.baidu.com',
       //   appName: 'adsa'
       // },
-    ],
   },
   appList: [] as any[], // 应用下拉框列表
 });
 
-// 表单校验规则
-const dataRules = reactive({
-  domain: [
-    {required: true, message: '域名不能为空', trigger: 'blur'},
-    // { validator: rule.url, trigger: 'blur' }
-  ],
-});
+// // 表单校验规则
+// const dataRules = reactive({
+//   domain: [
+//     {required: true, message: '域名不能为空', trigger: 'blur'},
+//     // { validator: rule.url, trigger: 'blur' }
+//   ],
+// });
 
 // 分页参数
 const pagination = reactive({
@@ -119,33 +83,34 @@ const pagination = reactive({
 })
 
 // 打开弹窗
-const openDialog = async (type: string, row?: any) => {
+const openDialog = async (type: string, row: any, str: string = 'domain') => {
   visible.value = true;
-
+  sign.value = str;
   nextTick(() => {
     menuDialogFormRef.value?.resetFields();
   });
-
-  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();
+  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();
 };
 
 // 获取当条配置信息
@@ -155,72 +120,15 @@ const getConfigDetail = (id: string) => {
   });
 };
 
-const handleVisibleChange = (visible: boolean) => {
-  if (visible) {
-      // 添加滚动事件监听
-      const dropdown = document.querySelector('.apps-loadmore .el-select-dropdown__wrap');
-      dropdown?.addEventListener('scroll', handleScroll);
-  } else {
-      // 移除滚动事件监听
-      const dropdown = document.querySelector('.el-select-dropdown');
-      dropdown?.removeEventListener('scroll', handleScroll);
-  }
-};
-
-//防抖函数,由于滚动一次滑轮会触发多次scroll事件,需要控制一下触发的次数。
-function debounce(func: Function, delay: Number = 100) {
-    let timeout;
-    return function(...args) {
-        const context = this;
-        clearTimeout(timeout);
-        timeout = setTimeout(() => func.apply(context, args), delay);
-    };
-}
-
-const handleScroll = debounce (event => {
-  //判断是否到达可视区域底部,
-  const bottom = event.target.scrollHeight === event.target.scrollTop + event.target.clientHeight;
-  if (bottom && state.appList.length < pagination.total) {
-      pagination.current += 1;
-      getAllAppData();
-    }
-}, 100);
-
-// 从后端获取菜单信息(含层级)
-const getAllAppData = async () => {
-  try {
-    const res = await getAppList({
-      current: pagination.current,
-      size: pagination.size
-    })
-
-    if (pagination.current === 1) {
-      state.appList = res.data.records
-    } else {
-      state.appList = [...state.appList, ...res.data.records]
-    }
-    pagination.total = res.data.total
-  } catch (error) {
-    console.error(error)
-  }
-}
-
 // 保存数据
 const onSubmit = async () => {
-  const valid = await menuDialogFormRef.value.validate().catch(() => {
-  });
-  if (!valid) return 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;
-    if (!state.ruleForm.id) {
-      delete state.ruleForm.id;
-    }
-    state.ruleForm.configs.forEach((item: any) => {
-      item.appName = state.appList.find(app => app.appId === item.appId)?.appName || '';
-      item.appUrl = state.appList.find(app => app.appId === item.appId)?.appUrl || '';
-      !item.id && delete item.id; // 删除未定义的id
-    });
     await save(state.ruleForm);
     useMessage().success(t(state.ruleForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
     visible.value = false;
@@ -245,7 +153,7 @@ defineExpose({
   font-size: 14px;
   font-weight: 400;
   width: 100%;
-  justify-content: space-between;
+  justify-content: start;
 }
 .config-actions {
   width: 50px;

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

@@ -1,9 +1,9 @@
 export default {
 	marketingConfig: {
 		index: '#',
-		name: '域名',
+		name: '应用名称',
 		config: '配置方案',
-		inputNameTip: '请输入域名名称',
+		inputNameTip: '请输入应用名称名称',
 		inputIPTip: '请输入IP名称',
 		app: '应用',
 		inputAppSel: '请选择应用',

+ 50 - 47
src/views/marketing/config/index.vue

@@ -16,12 +16,6 @@
 			</el-row>
 			<el-row>
 				<div class="mb8" style="width: 100%">
-					<el-button @click="onOpenAddMenu" class="ml10" icon="folder-add" type="primary" v-auth="'sys_menu_add'">
-						{{ $t('common.addBtn') }}
-					</el-button>
-					<el-button plain :disabled="multiple" icon="Delete" type="primary" class="ml10" v-auth="'sys_user_del'" @click="handleDelete(selectObjs)">
-						{{ $t('common.delBtn') }}
-					</el-button>
 					<right-toolbar
 						v-model:showSearch="showSearch"
 						class="ml10"
@@ -39,39 +33,48 @@
 				border
 				:cell-style="tableStyle.cellStyle"
 				:header-cell-style="tableStyle?.headerCellStyle"
-				@selection-change="handleSelectionChange"
 			>
-				<el-table-column type="selection" :selectable="handleSelectable" width="50" align="center" />
-				<el-table-column :label="$t('marketingConfig.name')" fixed prop="domain" width="240" show-overflow-tooltip></el-table-column>
-				<el-table-column :label="$t('marketingConfig.config')" prop="configs">
-					<template #default="scope">
-						<div style="display: flex; flex-wrap: wrap; margin-left: 14%;">
-							<div 
-								style="display: flex; margin-right: 30px; text-align: left; float: left; justify-content: start; line-height: 25px;" 
-								v-for="item in scope.row.configs" :key="item.ip">
-								<!-- <el-tag  class="mr10">{{ item.ip }}</el-tag>: -->
-								<span :title="item.ip" style="display: inline-block;">{{ item.ip }}</span>:
-								<el-link :title="item.appName" style="color: var(--el-color-primary); text-align: left;" :href="item.download">{{ item.appName }}</el-link>
-							</div>
+				<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>
+						</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>
 					</template>
 				</el-table-column>
-				<el-table-column :label="$t('common.action')" show-overflow-tooltip width="250">
+				<el-table-column :label="$t('common.action')" width="250">
 					<template #default="scope">
-						<el-button icon="edit-pen" @click="onOpenEditMenu('edit', scope.row)" text type="primary" v-auth="'sys_menu_edit'"
-							>{{ $t('common.editBtn') }}
+						<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>
-						<span style="margin-left: 12px">
-							<el-button
-								icon="delete"
-								@click="handleDelete([scope.row.id])"
-								text
-								type="primary"
-								v-auth="'sys_menu_del'"
-							>
-								{{ $t('common.delBtn') }}
-							</el-button>
-						</span>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -82,7 +85,7 @@
 </template>
 
 <script lang="ts" name="marketingConfig" setup>
-import { delObj, pageList } from '/@/api/marketing/config';
+import { delObj, pageList, update } from '/@/api/marketing/config';
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { useMessage, useMessageBox } from '/@/hooks/message';
 import { useI18n } from 'vue-i18n';
@@ -107,31 +110,31 @@ const state: BasicTableProps = reactive<BasicTableProps>({
 
 const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
 
-// 打开新增菜单弹窗
-const onOpenAddMenu = (type?: string, row?: any) => {
-	menuDialogRef.value.openDialog(type, row);
-};
-// 打开编辑菜单弹窗
-const onOpenEditMenu = (type: string, row: any) => {
-	menuDialogRef.value.openDialog(type, row);
-};
-
 // 搜索事件
 const query = () => {
   state.dataList = [];
   getDataList();
 };
 
+// 修改单条配置信息
+const handleChange = (row: any) => {
+	// update(row).then(() => {
+	// 	useMessage().success(t('common.updateSuccessText'));
+	// 	getDataList();
+	// }).catch((err: any) => {
+	// 	useMessage().error(err.msg);
+	// });
+};
+
 // 清空搜索条件
 const resetQuery = () => {
   queryRef.value.resetFields();
   state.dataList = [];
   getDataList();
 };
-// 多选事件
-const handleSelectionChange = (objs: { id: string }[]) => {
-	selectObjs.value = objs.map(({ id }) => id);
-	multiple.value = !objs.length;
+// 打开编辑菜单弹窗
+const onOpenEditMenu = (type: string, row: any) => {
+	menuDialogRef.value.openDialog(type, row);
 };
 
 // 删除操作