cmy 2 周之前
父節點
當前提交
ea21523e61
共有 3 個文件被更改,包括 276 次插入70 次删除
  1. 19 0
      src/api/marketing/apps.ts
  2. 202 41
      src/views/marketing/apps/form.vue
  3. 55 29
      src/views/marketing/apps/index.vue

+ 19 - 0
src/api/marketing/apps.ts

@@ -0,0 +1,19 @@
+import request from '/@/utils/request';
+
+export const getDetail = (params?: Object) => {
+	return request({
+		// url: '/admin/marketing/config/page',
+		url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/apps/edit',
+		method: 'get',
+		params,
+	});
+};
+
+export const pageList = (params?: Object) => {
+	return request({
+		// url: '/app/page',
+		url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/app/page',
+		method: 'get',
+		params,
+	});
+};

+ 202 - 41
src/views/marketing/apps/form.vue

@@ -8,25 +8,115 @@
           style="margin-bottom: -25px;"
           v-loading="loading">
         <el-form-item :label="'营销开关'" prop="isMarketing">
-          <el-switch v-model="state.ruleForm.isMarketing" style="--el-switch-on-color: rgb(48, 185, 113);" inline-prompt :active-value="1" :inactive-value="0" />
+          <el-switch v-model="state.ruleForm.isMarketing" style="--el-switch-on-color: rgb(48, 185, 113);" 
+            inline-prompt :active-value="1" :inactive-value="0" />
         </el-form-item>
-        <el-form-item :label="'域名限制'" prop="isHttp">
-          <el-switch v-model="state.ruleForm.isHttp" style="--el-switch-on-color: rgb(48, 185, 113);" inline-prompt :active-value="1" :inactive-value="0" />
+        <el-form-item :label="'域名限制'" prop="domainLimit">
+          <el-switch v-model="state.ruleForm.domainLimit" style="--el-switch-on-color: rgb(48, 185, 113);" 
+            inline-prompt :active-value="true" :inactive-value="false" />
         </el-form-item>
       </el-form>
-      <div class="title">IP集合</div>
-      <JCollapse />
-      <div class="title">域名集合</div>
-      <JCollapse />
+      <JCollapse
+        :data="[
+          { title: 'IP集合', id: '1'},
+        ]"
+        @update="(item: any) => console.log(item)"
+        @delete="(item: any) => ipDeletable = !ipDeletable"
+        :deleteText="t('marketingConfig.deleteText')"
+        :updateText="'新增'"
+        style="margin-top: 30px;"
+      >
+        <template #default>
+          <div class="border-b p-2 items-center flex flex-wrap collapse-group">
+            <div class="collapse-group-name">白名单:</div>
+            <div class="tag-content">
+              <template v-for="item in ips.whitelist" :key="item.id">
+                <!-- 具体IP(段) -->
+                <el-tag v-if="item.sourceType == 2" @click="getIpList(item)" effect="light" :closable="ipDeletable"
+                  @close="handleDelectIp(item)"
+                  color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
+                  {{ item.startIp }}{{ item.ipMode == 2 ? (' / ' + item.endIp.split('.').pop()) : '' }}
+                </el-tag>
+                <!-- 分组 -->
+                <el-popover v-else width="200" trigger="hover" placement="top">
+                  <div class="flex flex-wrap">
+                    <!-- <span v-for="ip in item.ips" :key="ip" class="ml-2">
+                      {{ ip }}
+                    </span> -->
+                    {{ item.groupName }}
+                  </div>
+                  <template #reference>
+                    <el-tag @click="getIpList(item)" effect="light" :closable="ipDeletable"
+                      @close="handleDelectIp(item)"
+                      color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
+                      {{ item.groupName }}
+                    </el-tag>
+                  </template>
+                </el-popover>
+              </template>
+            </div>
+          </div>
+          <div class="p-2 items-center flex flex-wrap collapse-group">
+            <div class="collapse-group-name">黑名单:</div>
+            <div class="tag-content">
+              <template v-for="item in ips.blacklist" :key="item.id">
+                <!-- 具体IP(段) -->
+                <el-tag v-if="item.sourceType == 2" @click="getIpList(item)" effect="light" :closable="ipDeletable"
+                  @close="handleDelectIp(item)"
+                  color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
+                  {{ item.startIp }}{{ item.ipMode == 2 ? (' / ' + item.endIp.split('.').pop()) : '' }}
+                </el-tag>
+                <!-- 分组 -->
+                <el-popover v-else width="200" trigger="hover" placement="top">
+                  <div class="flex flex-wrap">
+                    <!-- <span v-for="ip in item.ips" :key="ip" class="ml-2">
+                      {{ ip }}
+                    </span> -->
+                    {{ item.groupName }}
+                  </div>
+                  <template #reference>
+                    <el-tag @click="getIpList(item)" effect="light" :closable="ipDeletable"
+                      @close="handleDelectIp(item)"
+                      color="#f4f4f4" round class="ml-1 cursor-pointer" style="margin-bottom: 4px;">
+                      {{ item.groupName }}
+                    </el-tag>
+                  </template>
+                </el-popover>
+              </template>
+            </div>
+          </div>
+        </template>
+      </JCollapse>
+      <JCollapse
+        :data="[
+          { title: '域名集合', id: '1'},
+        ]"
+        @update="(item: any) => console.log(item)"
+				@delete="(item: any) => domainDeletable = !domainDeletable"
+        :deleteText="t('marketingConfig.deleteText')"
+        :updateText="'新增'"
+        style="margin-top: 30px;"
+      >
+        <template #default>
+          <div class="p-2 items-center flex flex-wrap">
+            <el-tag v-for="item in https" :key="item" effect="light" :closable="domainDeletable"
+              style="margin-bottom: 4px;" color="#f4f4f4" round class="ml-1 cursor-pointer">
+              {{ item.domain }}
+            </el-tag>
+          </div>
+        </template>
+      </JCollapse>
       <div class="title">备注</div>
       <el-input
         :rows="4"
+        v-model="state.ruleForm.remark"
         type="textarea"
+        placeholder="请输入备注"
       ></el-input>
       <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>
+          <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>
@@ -35,9 +125,20 @@
 
 <script setup lang="ts" name="systemMenuDialog">
 import {useI18n} from 'vue-i18n';
-import {save} from '/@/api/marketing/config';
-import {useMessage} from '/@/hooks/message';
 import JCollapse from '/@/components/JCollapse/index.vue';
+import { getDetail } from '/@/api/marketing/apps';
+import { ElMessage, messageConfig } from 'element-plus';
+
+interface Item {
+	id: string;
+	title: string;
+	list: string[];
+}
+
+interface Ips {
+  blacklist: String[];
+  whitelist: String[];
+}
 
 // 定义子组件向父组件传值/事件
 const emit = defineEmits(['refresh']);
@@ -46,53 +147,98 @@ const {t} = useI18n();
 // 定义变量内容
 const visible = ref(false);
 const loading = ref(false);
-const sign = ref('domain')
 const add = ref(false);
 const menuDialogFormRef = ref();
+const ips = ref<Ips>({
+  blacklist: [],
+  whitelist: []
+});
+const https = ref<string[]>([]);
+const ipDeletable = ref(false);// 控制 IP 列表项是否可删除
+const domainDeletable = ref(false);// 控制域名列表项是否可删除
 
 // 定义需要的数据
 const state = reactive({
   ruleForm: {
-    list: [''], 
-    https: [''], // 域名集合
-    ips: [''], // ip集合
+    marketingEnabled: false, 
+    domainRestriction: false,
+    ipSets: [''], // ip集合
+    domainSets: [''], // ip集合
+    remark: '',
   },
-  appList: [] as any[], // 应用下拉框列表
+  domainSets: [] as any[], // 应用下拉框列表
 });
 
-// 分页参数
-const pagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0
-})
+const handleDelect = ()=>{
+  console.log('delect')
+}
+
+const getIpList = (detail) => {
+  if(detail.sourceType == 1) {
+    // 获取ip组
+    console.log(detail.groupId);
+
+  }
+}
+
+// 删除ip
+const handleDelectIp = (delectItem)=>{
+  ips.value.whitelist = ips.value.whitelist.filter((item: any) => item.id != delectItem.id);
+  ips.value.blacklist = ips.value.blacklist.filter((item: any) => item.id != delectItem.id);
+}
+
+// 删除域名
+const handleDelectDomain = (item)=>{
+  console.log('delectDomain')
+}
 
 // 打开弹窗
 const openDialog = async (type: string, row: any, str: string = 'domain') => {
+  state.ruleForm = {
+    marketingEnabled: false, 
+    domainRestriction: false,
+    ipSets: [''],
+    domainSets: [''],
+    remark: '',
+  };
   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}));
+  // loading.value = true;
+  // try {
+  //   const res = await getDetail(row.id);
+  //   if (res) {
+  //     state.ruleForm = res;
+  //     console.log(res);
+  //     ips.value = res.ipSets;
+  //     https.value = res.domainSets;
+  //   }
+  // } catch(e) {
+  //   console.error(e);
+  // } finally {
+  //   loading.value = false;
+  // }
+  state.ruleForm = row;
+  console.log(row);
+  ips.value.whitelist = row.ips.filter((item: any) => item.ipType == '1');
+  ips.value.blacklist = row.ips.filter((item: any) => item.ipType == '2');
+  console.log(ips.value);
+  
+  https.value = row.domains;
 };
 
 // 保存数据
 const onSubmit = async () => {
-  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: any) {
-    useMessage().error(err.msg);
-  } finally {
-    loading.value = false;
-  }
+  console.log(state.ruleForm)
+  // 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: any) {
+  //   useMessage().error(err.msg);
+  // } finally {
+  //   loading.value = false;
+  // }
 };
 
 // 暴露变量 只有暴漏出来的变量 父组件才能使用
@@ -121,6 +267,17 @@ defineExpose({
   height: 330px !important;
 }
 .apps-form {
+  .collapse-group {
+    display: flex;
+    justify-content: start;
+    align-items:first baseline;
+  }
+  .collapse-group-name {
+    // width: 100px;
+  }
+  .tag-content {
+    flex: 1;
+  }
   .el-overlay {
     .el-overlay-dialog {
       .el-dialog {
@@ -140,5 +297,9 @@ defineExpose({
     color: rgba(18, 18, 18, 1);
     margin: 30px 0 12px;
   }
+
+  .el-collapse-item__content {
+    padding: 0;
+  }
 }
 </style>

+ 55 - 29
src/views/marketing/apps/index.vue

@@ -4,9 +4,21 @@
       <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('marketingApps.name')" prop="domain">
-            <el-input :placeholder="$t('marketingApps.inputNameTip')" clearable style="max-width: 180px"
+            <el-input :placeholder="$t('marketingApps.inputNameTip')" clearable
               v-model="state.queryForm.domain" />
           </el-form-item>
+          <el-form-item :label="'应用ID'" prop="appId">
+            <el-input :placeholder="'请输入应用ID'" clearable v-model="state.queryForm.appId" />
+          </el-form-item>
+          <el-form-item :label="'应用类型'" prop="appType">
+            <el-select placeholder="请选择应用类型" v-model="state.queryForm.appType">
+              <el-option :key="''" :label="'全部'" :value="''" />
+              <el-option v-for="item in appTypes" :key="item.value" :label="item.description" :value="item.value" />
+            </el-select>
+          </el-form-item>
+          <el-form-item :label="'备注'" prop="remark">
+            <el-input :placeholder="'请输入备注'" clearable v-model="state.queryForm.domain" />
+          </el-form-item>
           <el-form-item>
             <el-button @click="query" class="ml10" icon="search" type="primary">
               {{ $t('common.queryBtn') }}
@@ -22,6 +34,12 @@
             @queryTable="getDataList"></right-toolbar>
         </div>
       </el-row>
+
+      <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+          <el-tab-pane label="使用中" name="tab1"></el-tab-pane>
+          <el-tab-pane label="回收站" name="tab2"></el-tab-pane>
+      </el-tabs>
+
       <el-table @selection-change="handleSelectionChange" 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">
@@ -88,40 +106,48 @@
                   text-wrap: auto;
                   text-align: left;
                 ">
-              <div style="text-align: left;">名单:</div>
+              <div style="text-align: left;">名单:</div>
               <el-tooltip effect="light">
                 <template #content>
-                  <div v-for="(item, index) in row.ips" :key="index">{{ item }};</div>
+                  <div v-for="(item, index) in row.ips" :key="index">
+                    {{ item.ipType == 1 ? item.startIp + ';' : '' }}
+                  </div>
                 </template>
                 <el-text line-clamp="4">
                   <template v-for="(item, index) in row.ips" :key="index">
-                    <div>{{ item }};</div>
+                    <div>{{ item.ipType == 1 ? item.startIp + ';' : '' }}</div>
                   </template>
                 </el-text>
               </el-tooltip>
-
-              <div style="text-align: left;">白名单:</div>
+              <div style="text-align: left;">黑名单:</div>
               <el-tooltip effect="light">
                 <template #content>
-                  <div v-for="(item, index) in row.ips" :key="index">{{ item }};</div>
+                  <div v-for="(item, index) in row.ips" :key="index">
+                    {{ item.ipType == 2 ? item.startIp + ';' : '' }}
+                  </div>
                 </template>
                 <el-text line-clamp="4">
                   <template v-for="(item, index) in row.ips" :key="index">
-                    <div>{{ item }};</div>
+                    <div>{{ item.ipType == 2 ? item.startIp + ';' : '' }}</div>
                   </template>
                 </el-text>
               </el-tooltip>
             </div>
           </template>
         </el-table-column>
-        <el-table-column :label="$t('marketingApps.triggerRule')" prop="triggerRule" width="180" show-overflow-tooltip>
+        <el-table-column :label="'触发频率'" prop="triggerRule" width="120" show-overflow-tooltip>
+          <template #default="{ row }">
+            {{ Math.floor(Math.random() * 10) }}
+          </template>
+        </el-table-column>
+        <el-table-column :label="$t('marketingApps.triggerRule')" prop="triggerRule" show-overflow-tooltip>
           <template #default="{ row }">
             <el-select v-model="row.triggerRule" placeholder="" style="width: 150px">
               <el-option v-for="item in triggerRules" :key="item.value" :label="item.label" :value="item.value" />
             </el-select>
           </template>
         </el-table-column>
-        <el-table-column fixed="right" :label="$t('common.action')" width="150">
+        <el-table-column fixed="right" :label="$t('common.action')" width="80">
           <template #default="scope">
             <div class="action" style="text-align: left;">
               <!-- <el-button icon="edit-pen" @click="onOpenEditMenu('edit', scope.row, 'domain')" text type="primary"
@@ -131,12 +157,15 @@
                 v-auth="'sys_menu_edit'">IP
               </el-button>
               <br /> -->
-              <el-button icon="edit-pen" @click="onOpenEditMenu('edit', scope.row)" text type="primary"
+              <el-button style="margin-left: 0;" icon="edit-pen" @click="onOpenEditMenu('edit', scope.row)" text type="primary"
                 v-auth="'sys_menu_edit'">修改
               </el-button>
-              <el-button icon="edit-pen" @click="onOpenStatistical(scope.row)" text type="primary"
+              <el-button style="margin-left: 0;" icon="edit-pen" @click="onOpenStatistical(scope.row)" text type="primary"
                 v-auth="'sys_menu_edit'">统计
               </el-button>
+              <el-button style="margin-left: 0;" icon="edit-pen" @click="query()" text type="primary"
+                v-auth="'sys_menu_edit'">{{ activeName == 'tab1' ? '拉黑' : '还原' }}
+              </el-button>
             </div>
           </template>
         </el-table-column>
@@ -149,11 +178,21 @@
 </template>
 
 <script lang="ts" name="marketingApps" setup>
-import { delObj, pageList, update } from '/@/api/marketing/config';
+import { pageList } from '/@/api/marketing/apps';
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { useMessage, useMessageBox } from '/@/hooks/message';
 import { useI18n } from 'vue-i18n';
 import { fetchItemList } from '/@/api/admin/dict';
+import { ref } from 'vue'
+
+import type { TabsPaneContext } from 'element-plus'
+
+const activeName = ref('tab1')
+
+const handleClick = (tab: TabsPaneContext, event: Event) => {
+  console.log(tab, event);
+  query();
+}
 
 // 引入组件
 const MenuDialog = defineAsyncComponent(() => import('./form.vue'));
@@ -173,6 +212,9 @@ const state: BasicTableProps = reactive<BasicTableProps>({
   pageList: pageList, // H
   queryForm: {
     domain: '',
+    appType: '',
+    appId: '',
+    remark: '',
   }
 });
 
@@ -257,22 +299,6 @@ const onOpenStatistical = (row: any) => {
   statisticalDialogRef.value.openDialog(row);
 };
 
-// 删除操作
-const handleDelete = async (ids: string[]) => {
-  try {
-    await useMessageBox().confirm(t('common.delConfirmText'));
-  } catch {
-    return;
-  }
-
-  try {
-    await delObj(ids);
-    getDataList();
-    useMessage().success(t('common.delSuccessText'));
-  } catch (err: any) {
-    useMessage().error(err.msg);
-  }
-};
 </script>
 <style scoped lang="scss">
 :deep(.el-link__inner) {