浏览代码

Merge branch 'dev-cmn' into dev-ly

cmy 1 周之前
父节点
当前提交
85b88108e8

+ 72 - 4
src/api/marketing/apps.ts

@@ -20,13 +20,81 @@ export const pageList = (params?: Object) => {
 };
 
 /**
- * 根据id拉黑应用
- * @param id 应用ID
+ * 分页查询拉黑应用列表
+ * @param size	每页显示条数
+ * @param current	当前页
+ * @param appId	应用ID
+ * @param appName	应用名称
+ * @param domainType	应用类型
+ * @param remark	备注
  * @returns 
  */
-export const delAppById = (id?: String|Number) => {
+export const pageDel = (params?: Object) => {
 	return request({
-		url: '/marketing/app/del/' + id,
+		url: '/marketing/app/pageDel',
 		method: 'get',
+		params,
+	});
+};
+
+/**
+ * 拉黑/恢复应用(设置应用状态)
+ * @param id  true	
+ * @param status 状态,true-启用,false-拉黑
+ * @returns 
+ */
+export const delAppById = (data: Object) => {
+	return request({
+		url: '/marketing/app/setStatus',
+		method: 'post',
+		data: data,
+	});
+};
+
+/**
+ * 批量更新应用信息
+ * @param 
+ [
+  {
+    "id": 0,
+    "domainLimit": true,
+    "launch": true,
+    "triggerRule": 0,
+    "triggerNum": 0,
+    "remark": "",
+    "delIps": [], // 删除的ip和域名id放这里
+    "delDomains": [],
+    "ips": [ // 新增,修改 不变的都放在这里
+      {
+        "id": 0, // 新增 没有id
+        "ipType": 0,
+        "sourceType": 0,
+        "groupId": 0,
+        "groupName": "",
+        "ipMode": 0,
+        "startIp": "",
+        "endIp": "",
+        "modify": true // 修改为true
+      }
+    ],
+    "domains": [
+      {
+        "id": 0,
+        "sourceType": 0,
+        "groupId": 0,
+        "groupName": "",
+        "domain": "",
+        "modify": true
+      }
+    ]
+  }
+]
+ * @returns 
+ */
+export const appUpdate = (data: Object) => {
+	return request({
+		url: '/marketing/app/update',
+		method: 'post',
+		data: data,
 	});
 };

+ 19 - 7
src/views/marketing/apps/components/domainCell.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="cell">
-    <div class="flex">
-      <div class="item" v-for="item in list.slice(0, 4)" :key="item">{{item}}</div>
+    <div class="ellipsis" style="text-align: left;">
+      <span class="item" v-for="item in list.slice(0, 4)" :key="item">{{item}}</span>
     </div>
     <div class="action" :style="{ textAlign: list.length > 0 ? 'left' : 'center'}">
       <el-link type="info" @click="handleEdit" style="color: #167AF0; line-height: 18px;">
@@ -25,6 +25,7 @@ watch(
     list.value = [];
     let temp = '';
     (newVal || []).forEach(item => {
+      // sourceType 1:分组 2: 具体域名
       if (item.sourceType == '2') {
         temp = item.domain;
       } else {
@@ -44,11 +45,11 @@ const handleEdit = () => {
 <style scoped lang="scss">
 .cell {
   padding: 10px 0;
-  .flex {
-    display: flex;
-    flex-wrap: wrap;
-    margin-bottom: 5px;
-  }
+  // .flex {
+  //   display: flex;
+  //   flex-wrap: wrap;
+  //   margin-bottom: 5px;
+  // }
   .item {
     box-sizing: border-box;
     padding: 4px 0;
@@ -60,5 +61,16 @@ const handleEdit = () => {
   .action {
     width: 100%;
   }
+
+  .ellipsis {
+    display: -webkit-box;          /* 使用弹性盒子布局 */
+    -webkit-box-orient: vertical;  /* 垂直方向排列 */
+    -webkit-line-clamp: 2;         /* 限制显示的行数 */
+    overflow: hidden;              /* 隐藏超出部分 */
+    text-overflow: ellipsis;       /* 超出时显示省略号 */
+    word-break: break-word;        /* 允许单词断行(可选) */
+
+    
+  }
 }
 </style>

+ 84 - 16
src/views/marketing/apps/components/domainCollapse.vue

@@ -5,10 +5,10 @@
     :deleteText="domainDeletable ? '取消' : t('marketingConfig.deleteText')" :updateText="'新增'" :activeNames="['1']">
     <template #default>
       <div class="p-2 items-center flex flex-wrap">
-        <template v-for="item in domains" :key="item.id">
+        <template v-for="item in domains" >
           <!-- 具体域名 -->
-          <el-tag v-if="item.sourceType == 2" @click="getDomainList(item)" effect="light" :closable="domainDeletable"
-            @close="handleDeleteDomain(item)" color="#f4f4f4" round class="ml-1 cursor-pointer"
+            <el-tag v-if="item.sourceType == 2" @click="getDomainList(item)" effect="light" :closable="domainDeletable"
+            @close="handleDeleteDomain(item, 'domain')" color="#f4f4f4" round class="ml-1 cursor-pointer"
             style="margin-bottom: 4px;">
             {{ item.domain }}
           </el-tag>
@@ -19,7 +19,7 @@
             </div>
             <template #reference>
               <el-tag @click="getDomainList(item)" effect="light" :closable="domainDeletable"
-                @close="handleDeleteDomain(item)" color="#f4f4f4" round class="ml-1 cursor-pointer"
+                @close="handleDeleteDomain(item, 'group')" color="#f4f4f4" round class="ml-1 cursor-pointer"
                 style="margin-bottom: 4px;">
                 {{ item.groupName }}
               </el-tag>
@@ -37,6 +37,7 @@
 
 <script setup lang="ts" name="domainCollapse">
 import { useI18n } from 'vue-i18n';
+import { useMessage } from '/@/hooks/message';
 const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
 const DomainEdit = defineAsyncComponent(() => import('./domainEdit.vue'));
 
@@ -46,24 +47,36 @@ interface DomianItem {
   sourceType: Number; // 1:分组 2:具体域名
   groupId: String;
   groupName: String;
+  modify: boolean; // 是否被修改/新增
 }
 
 const props = defineProps(['data']);
 
 // 定义子组件向父组件传值/事件
-const emit = defineEmits(['refresh']);
+const emit = defineEmits(['refresh','domains', 'delDomains']);
 const { t } = useI18n();
 
 // 定义变量内容
-const domains = ref<DomianItem[]>([]);
 const domainDeletable = ref(false);// 控制域名列表项是否可删除
 const domainEditOpen = ref(false);
+const delDomains = ref<String[]>([]);
+const domains = ref<DomianItem[]>([]);
 
-watch(props.data, ()=>{
-  if (props.data) {
-    domains.value = props.data;
-  }
-})
+watch(() => props.data, (newVal = []) => {
+  domains.value = [];
+  newVal.forEach((item: DomianItem) => {
+    item.modify = false;
+    domains.value = [...domains.value, item];
+  })
+}, { immediate: true });
+
+// 监听domains变化,emit给父组件
+watch(domains, (newVal) => {
+  emit('domains', newVal);
+}, { deep: true, immediate: true });
+watch(delDomains, (newVal) => {
+  emit('delDomains', newVal);
+}, { deep: true, immediate: true });
 
 const getDomainList = (detail: DomianItem) => {
   if (detail.sourceType == 1) {
@@ -73,13 +86,68 @@ const getDomainList = (detail: DomianItem) => {
 }
 
 // 删除域名
-const handleDeleteDomain = (deleteItem: DomianItem) => {
-  domains.value = domains.value.filter((item: any) => item.id != deleteItem.id);
+const handleDeleteDomain = (deleteItem: DomianItem, type: 'group' | 'domain') => {
+  console.log(deleteItem)
+  domains.value = domains.value.filter(i=>{
+    if(!i.modify && i.id == deleteItem.id && !delDomains.value.includes(i.id)) {
+      delDomains.value = [...delDomains.value, i.id];
+    }
+    return i.id != deleteItem.id
+  });
 }
 
-const addDomain = (data: DomianItem[]) => {
-  console.log(data);
-  domains.value = [...domains.value, ...data];
+// 分组去重
+function addGroupsUnique(newGroups: DomianItem[]) {
+  const existIds = new Set([
+    ...domains.value.map(item => item.id),
+  ]);
+  const filtered = newGroups.filter(item => !existIds.has(item.id));
+  if(filtered.length) {
+    filtered.forEach(item => {
+      Object.assign(item, {
+        "sourceType": 1,
+        "modify": true,
+        // "domain": "",
+      })
+    })
+    domains.value = [...domains.value, ...filtered];
+    console.log(filtered);
+		useMessage().success(t('common.addSuccessText'));
+  }else{
+    useMessage().warning('分组重复');
+  }
+}
+
+// 域名去重
+function addSinglesUnique(newSingles: DomianItem[]) {
+  const existDomains = new Set([
+    ...domains.value.map(item => item.domain),
+  ]);
+  const filtered = newSingles.filter(item => !existDomains.has(item.domain));
+  if(filtered.length) {
+    filtered.forEach(item => {
+      Object.assign(item, {
+        "sourceType": 2,
+        "modify": true,
+        // "groupId": 0,
+        // "groupName": "",
+        // "domain": "",
+      })
+    })
+    domains.value = [...domains.value, ...filtered];
+    console.log(filtered);
+		useMessage().success(t('common.addSuccessText'));
+  }else{
+    useMessage().warning('域名重复');
+  }
+}
+
+const addDomain = (data: DomianItem[], type: 'group' | 'domain') => {
+  if (type === 'domain') {
+    data.length && addSinglesUnique(data);
+  } else {
+    data.length && addGroupsUnique(data);
+  }
 }
 
 </script>

+ 18 - 14
src/views/marketing/apps/components/domainEdit.vue

@@ -5,13 +5,13 @@
 			v-loading="loading">
 			<div class="custom-style">
 				<el-segmented v-model="activeName"
-					:options="[t('marketingConfig.grouping'), t('marketingConfig.domain')]" size="default">
+					:options="['group', 'domain']" size="default">
 					<template #default="scope">
-						<div style="min-width: 50px; line-height: 32px;">{{ scope.item }}</div>
+						<div style="min-width: 50px; line-height: 32px;">{{ scope.item == 'group' ? t('marketingConfig.grouping') : t('marketingConfig.domain') }}</div>
 					</template>
 				</el-segmented>
 			</div>
-			<el-form-item label="" prop="grouping" v-if="activeName === t('marketingConfig.grouping')">
+			<el-form-item label="" prop="grouping" v-if="activeName === 'group'">
 				<el-select multiple collapse-tags collapse-tags-tooltip :max-collapse-tags="5"
 					v-model="state.ruleForm.selectedIds" :placeholder="t('marketingConfig.groupingTip')" clearable>
 					<el-option v-for="item in listSelect" :key="item.id" :label="item.groupName" :value="item.id" />
@@ -50,7 +50,7 @@ interface DomainItem {
 // 定义变量内容
 const loading = ref(false);
 const type = ref('add'); // 'add' or 'edit'
-const activeName = ref(t('marketingConfig.grouping'));
+const activeName = ref('group');
 const dialogFormRef = ref();
 const listSelect = ref<DomainItem[]>([]);
 const selectedObjects = ref<DomainItem[]>([]);
@@ -70,6 +70,12 @@ const props = defineProps({
 	},
 });
 
+watch(() => props.open, async (val) => {
+	if (val) {
+	    getDomainList();
+	}
+})
+
 // 表单校验规则
 const dataRules = reactive({
 	domain: [
@@ -81,16 +87,18 @@ const onCancel = () => {
 };
 // 保存数据
 const onSubmit = async () => {
-	selectedObjects.value = [];
 	try {
 		loading.value = true;
-		console.log(state.ruleForm.selectedIds);
-		selectedObjects.value = listSelect.value.filter(item=>state.ruleForm.selectedIds.includes(item.id));
-		console.log(selectedObjects.value);
-		useMessage().success(t('common.addSuccessText'));
-		emit('onsuccess', selectedObjects.value);
+		if (activeName.value == 'group') {
+			selectedObjects.value = [];
+			selectedObjects.value = listSelect.value.filter(item => state.ruleForm.selectedIds.includes(item.id));
+			emit('onsuccess', selectedObjects.value, activeName.value);
+		}else if(state.ruleForm.domain.length > 0) {
+			emit('onsuccess', [{domain: state.ruleForm.domain}], activeName.value);
+		}
 		onCancel();
 	} catch (err) {
+		console.log(err);
 		useMessage().error(err.msg);
 	} finally {
 		loading.value = false;
@@ -102,10 +110,6 @@ const getDomainList = async ()=>{
 	listSelect.value = data.data;
 }
 
-onMounted(()=>{
-	getDomainList();
-})
-
 </script>
 <style scoped>
 .apps-loadmore.el-select-dropdown .el-scrollbar__wrap {

+ 15 - 1
src/views/marketing/apps/components/domainForm.vue

@@ -3,7 +3,8 @@
     <el-dialog
       append-to-body :title="'修改域名'" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
       draggable>
-      <DomainCollapse :data=domains></DomainCollapse>
+      <DomainCollapse :data=domains @domains="updateDomains"
+        @delDomains="updateDelDomains"></DomainCollapse>
       <template #footer>
         <span class="dialog-footer">
           <el-button @click="visible = false">{{ t('common.cancelButtonText') }}</el-button>
@@ -36,17 +37,30 @@ const { t } = useI18n();
 const visible = ref(false);
 const loading = ref(false);
 const domains = ref<DomianItem[]>([]);
+const childDomains = ref();
+const delDomains = ref<String[]>([]);
 
 // 打开弹窗
 const openDialog = async (data: any) => {
   visible.value = true;
   domains.value = data;
+  childDomains.value = [];
+  delDomains.value = [];
 };
 
+const updateDomains = (data: DomianItem[])=>{
+  childDomains.value = data;
+}
+const updateDelDomains = (data: String[]) => {
+  delDomains.value = data;
+}
+
 // 保存数据
 const onSubmit = async () => {
   ElMessage.success('提交成功!');
   visible.value = false;
+  console.log(childDomains.value);
+  console.log(delDomains.value);
 };
 
 // 暴露变量 只有暴漏出来的变量 父组件才能使用

+ 23 - 3
src/views/marketing/apps/components/form.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="apps-form">
-    <el-dialog :title="'修改营销配置'" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
+    <el-dialog :title="(!state.ruleForm.id ? '批量' : '')+'修改营销配置'" width="880" v-model="visible" :close-on-click-modal="false" :destroy-on-close="true"
       draggable>
       <el-form ref="appDialogFormRef" :rules="dataRules" :model="state.ruleForm" class="demo-form-inline" v-loading="loading">
         <el-form-item v-if="state.ruleForm.appName" :label="'应用名称'" prop="appName">
@@ -38,7 +38,7 @@
           <IpCollapse :data=state.ruleForm.ips></IpCollapse>
         </div>
         <div class="mt-7">
-          <DomainCollapse :data=state.ruleForm.domains></DomainCollapse>
+          <DomainCollapse :data=state.ruleForm.domains @domains="updateDomains" @delDomains="updateDelDomains"></DomainCollapse>
         </div>
         <div class="title">备注</div>
         <el-form-item>
@@ -106,6 +106,8 @@ const { t } = useI18n();
 const visible = ref(false);
 const loading = ref(false);
 const appDialogFormRef = ref();
+const domains = ref();
+const delDomains = ref<String[]>([]);
 
 const triggerRules = [
   {
@@ -123,10 +125,23 @@ const state = reactive({
   ruleForm: {} as Form,
 });
 
+watch(() => state.ruleForm, async (val) => {
+  console.log('val', val);
+})
+
+const updateDomains = (data: DomianItem[])=>{
+  domains.value = data;
+}
+const updateDelDomains = (data: String[]) => {
+  delDomains.value = data;
+}
+
 // 打开弹窗
 const openDialog = async (type: string, row: any, str: string = 'domain') => {
   visible.value = true;
   state.ruleForm = row;
+  domains.value = [];
+  delDomains.value = [];
   console.log(row);
 };
 
@@ -144,8 +159,13 @@ const dataRules = reactive({
 
 // 保存数据
 const onSubmit = async () => {
-  console.log(state.ruleForm)
+  console.log({
+    ...state.ruleForm,
+    domains: domains.value,
+    delDomains: delDomains.value
+  });
   ElMessage.success('提交成功!');
+
   visible.value = false;
   // try {
   //   loading.value = true;

+ 5 - 5
src/views/marketing/apps/components/ipCell.vue

@@ -49,11 +49,11 @@ const handleEdit = () => {
 <style scoped lang="scss">
 .cell {
   padding: 10px 0;
-  .flex {
-    display: flex;
-    flex-wrap: wrap;
-    margin-bottom: 5px;
-  }
+  // .flex {
+  //   display: flex;
+  //   flex-wrap: wrap;
+  //   margin-bottom: 5px;
+  // }
   .title {
     text-align: left;
     line-height: 14px;

+ 0 - 1
src/views/marketing/apps/components/ipEdit.vue

@@ -52,7 +52,6 @@
 
 <script setup name="systemMenuDialog">
 import { useI18n } from 'vue-i18n';
-import { info, getAppList, save } from '/@/api/marketing/config';
 import { useMessage } from '/@/hooks/message';
 
 // 定义子组件向父组件传值/事件

+ 7 - 8
src/views/marketing/apps/index.vue

@@ -158,7 +158,7 @@
 </template>
 
 <script lang="ts" name="marketingApps" setup>
-import { pageList, delAppById } from '/@/api/marketing/apps';
+import { pageList, pageDel, delAppById } from '/@/api/marketing/apps';
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { useMessage, useMessageBox } from '/@/hooks/message';
 import { useI18n } from 'vue-i18n';
@@ -189,7 +189,7 @@ const selectObjs = ref([]) as any;
 // 是否可以多选
 const multiple = ref(true);
 const state: BasicTableProps = reactive<BasicTableProps>({
-  pageList: pageList,
+  pageList: activeName.value == 'tab1' ? pageList : pageDel,
   createdIsNeed: false,
   queryForm: {
     appId: '',
@@ -282,13 +282,12 @@ const onOpenStatistical = (row: any) => {
 // 删除操作
 const handleDelete = async (row: any) => {
 	try {
-    if(activeName.value == 'tab1'){
       await useMessageBox().confirm('是否确认拉黑该应用');
-      await delAppById(row.id);
-      ElMessage.success('拉黑成功!');
-    }else{
-      ElMessage.success('还原成功!');
-    }
+      await delAppById({
+        id: row.id,
+        status: activeName.value == 'tab2'
+      });
+      ElMessage.success(activeName.value == 'tab2' ? '还原成功!' : '拉黑成功!');    
 	} catch {
 		return;
 	}