浏览代码

feature:应用列表及配置中心功能完善

cmy 2 周之前
父节点
当前提交
66cd63a4cb

+ 10 - 1
src/components/JCollapse/index.vue

@@ -1,6 +1,6 @@
 
 <template>
-	<el-collapse class="coll-left mt-2">
+	<el-collapse v-model="innerActiveNames" class="coll-left mt-2">
 		<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">
@@ -20,6 +20,7 @@
 </template>
 
 <script setup name="jCollapse">
+const activeNames = ref(['1'])
 const emit = defineEmits(['update', 'delete']);
 const props = defineProps({
 	data: {
@@ -34,6 +35,10 @@ const props = defineProps({
 		type: String,
 		default: '修改',
 	},  
+	activeNames: {
+		type: Array,
+		default: () => [],
+	}
 });
 
 const handleDelete = (item) => {
@@ -42,6 +47,10 @@ const handleDelete = (item) => {
 const handleEdit = (item) => {
 	emit('update', item);
 };
+const innerActiveNames = computed({
+  get: () => props.activeNames,
+  set: (val) => emit('update:activeNames', val)
+})
 </script>
 <style lang="scss">
 .collapse-title {

+ 110 - 0
src/views/marketing/apps/components/domainEdit.vue

@@ -0,0 +1,110 @@
+<template>
+	<el-dialog
+		:title="type === 'Edit' ? '修改域名' : '添加域名'"
+		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-segmented v-model="activeName" :options="[t('marketingConfig.grouping'), t('marketingConfig.domain')]" size="large" />
+			<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" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="" prop="domain" v-else> 
+				<el-input v-model="state.ruleForm.domain" type="text" :placeholder="t('marketingConfig.domainTip')"></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 type = ref('add'); // 'add' or 'edit'
+const activeName = ref(t('marketingConfig.grouping'));
+const menuDialogFormRef = ref();
+const listSelect = ref([
+	{
+		value: 'group1',
+		label: '分组1',
+	},
+	{
+		value: 'group2',
+		label: '分组2',
+	},
+	{
+		value: 'group3',
+		label: '分组3',
+	},
+]);
+// 定义需要的数据
+const state = reactive({
+	ruleForm: {
+		list: [''],
+		https: [''], // 域名集合
+		ips: [''], // ip集合
+		// {
+		//   id: '1',
+		//   ip: '美国',
+		//   appId: '1',
+		//   appUrl: 'www.baidu.com',
+		//   appName: 'adsa'
+		// },
+	},
+	appList: [], // 应用下拉框列表
+});
+
+const props = defineProps({
+	open: {
+		type: Boolean,
+		default: false,
+	},
+});
+// // 表单校验规则
+const dataRules = reactive({
+	domain: [
+		{ 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>

+ 40 - 11
src/views/marketing/apps/form.vue → src/views/marketing/apps/components/form.vue

@@ -20,11 +20,12 @@
         :data="[
           { title: 'IP集合', id: '1'},
         ]"
-        @update="(item: any) => ipAdd = !ipAdd"
+        @update="(item: any) => listEditOpen = true"
         @delete="(item: any) => ipDeletable = !ipDeletable"
         :deleteText="t('marketingConfig.deleteText')"
         :updateText="'新增'"
         style="margin-top: 30px;"
+        :activeNames="['1']"
       >
         <template #default>
           <div class="border-b p-2 items-center flex flex-wrap collapse-group">
@@ -91,15 +92,17 @@
         :data="[
           { title: '域名集合', id: '1'},
         ]"
-        @update="(item: any) => domainAdd = !domainAdd"
+        @update="(item: any) => domainEditOpen = true"
 				@delete="(item: any) => domainDeletable = !domainDeletable"
         :deleteText="t('marketingConfig.deleteText')"
         :updateText="'新增'"
         style="margin-top: 30px;"
+        :activeNames="['1']"
       >
         <template #default>
           <div class="p-2 items-center flex flex-wrap">
-            <el-tag v-for="item in domains" :key="item" effect="light" :closable="domainDeletable"
+            <el-tag v-for="item in domains" :key="item"
+              @close="handleDelectDomain(item)" effect="light" :closable="domainDeletable"
               style="margin-bottom: 4px;" color="#f4f4f4" round class="ml-1 cursor-pointer">
               {{ item.domain }}
             </el-tag>
@@ -120,16 +123,39 @@
         </span>
       </template>
     </el-dialog>
+    <ListEdit v-model:open="listEditOpen" />
+		<DomainEdit v-model:open="domainEditOpen" />
   </div>
 </template>
 
 <script setup lang="ts" name="systemMenuDialog">
 import {useI18n} from 'vue-i18n';
-import JCollapse from '/@/components/JCollapse/index.vue';
+const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
+const ListEdit = defineAsyncComponent(() => import('./listEdit.vue'));
+const DomainEdit = defineAsyncComponent(() => import('./domainEdit.vue'));
+
+interface IpItem {
+  id: String;
+  ipMode: Number;
+  ipType: Number;
+  sourceType: Number; // 1:分组 2:具体Ip(段)
+  startIp: String;
+  endIp: String;
+  groupId: String;
+  groupName: String;
+}
 
 interface Ips {
-  blacklist: String[];
-  whitelist: String[];
+  blacklist: IpItem[];
+  whitelist: IpItem[];
+}
+
+interface DomianItem {
+  id: String;
+  domain: String;
+  sourceType: Number; // 1:分组 2:具体域名
+  groupId: String;
+  groupName: String;
 }
 
 // 定义子组件向父组件传值/事件
@@ -145,11 +171,13 @@ const ips = ref<Ips>({
   blacklist: [],
   whitelist: []
 });
-const domains = ref<string[]>([]);
+const domains = ref<DomianItem[]>([]);
 const ipDeletable = ref(false);// 控制 IP 列表项是否可删除
 const domainDeletable = ref(false);// 控制域名列表项是否可删除
 const ipAdd = ref(false);
 const domainAdd = ref(false);
+const listEditOpen = ref(false);
+const domainEditOpen = ref(false);
 
 // 定义需要的数据
 const state = reactive({
@@ -176,14 +204,15 @@ const getIpList = (detail) => {
 }
 
 // 删除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 handleDelectIp = (deleteItem: IpItem)=>{
+  ips.value.whitelist = ips.value.whitelist.filter((item: any) => item.id != deleteItem.id);
+  ips.value.blacklist = ips.value.blacklist.filter((item: any) => item.id != deleteItem.id);
 }
 
 // 删除域名
-const handleDelectDomain = (item)=>{
+const handleDelectDomain = (deleteItem: DomianItem)=>{
   console.log('delectDomain')
+  domains.value = domains.value.filter((item: any) => item.id != deleteItem.id);
 }
 
 // 打开弹窗

+ 115 - 0
src/views/marketing/apps/components/listEdit.vue

@@ -0,0 +1,115 @@
+<template>
+	<el-dialog
+		:title="type === 'Edit' ? '修改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="" prop="listType" >
+				<el-radio-group v-model="state.ruleForm.listType">
+					<el-radio value="1">白名单</el-radio>
+					<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-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" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="" prop="ip" v-else>
+				<el-input v-model="state.ruleForm.ip" type="text" :placeholder="t('marketingConfig.ipTip')"></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 type = ref('add'); // 'add' or 'edit'
+const activeName = ref(t('marketingConfig.grouping'));
+const menuDialogFormRef = ref();
+const listSelect = ref([
+	{
+		value: 'group1',
+		label: '分组1',
+	},
+	{
+		value: 'group2',
+		label: '分组2',
+	},
+	{
+		value: 'group3',
+		label: '分组3',
+	},
+]);
+// 定义需要的数据
+const state = reactive({
+	ruleForm: {
+		listType: '1', // 1: 白名单, 2: 黑名单
+		list: [''],
+		https: [''], // 域名集合
+		ips: [''], // ip集合
+		// {
+		//   id: '1',
+		//   ip: '美国',
+		//   appId: '1',
+		//   appUrl: 'www.baidu.com',
+		//   appName: 'adsa'
+		// },
+	},
+	appList: [], // 应用下拉框列表
+});
+
+const props = defineProps({
+	open: {
+		type: Boolean,
+		default: false,
+	},
+});
+// // 表单校验规则
+const dataRules = reactive({
+	domain: [{ 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>

+ 1 - 1
src/views/marketing/apps/statistical.vue → src/views/marketing/apps/components/statistical.vue

@@ -14,7 +14,7 @@
 
 <script setup lang="ts">
 import { ref } from 'vue';
-import StatisticsIndex from '../statistics/index.vue';
+const StatisticsIndex = defineAsyncComponent(() => import('/@/views/marketing/statistics/index.vue'));
 
 const visible = ref(false);
 

+ 30 - 20
src/views/marketing/apps/index.vue

@@ -99,7 +99,15 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column label="ip集合" prop="ips" width="300">
+        <el-table-column prop="ips" width="300">
+          <template #header>
+            <span style="display: inline-block; align-items: center;">
+              IP限制
+              <el-tooltip effect="light" content="鼠标悬浮查看分组ip详情" placement="top">
+                <el-icon><Warning /></el-icon>
+              </el-tooltip>
+            </span>
+          </template>
           <template #default="{ row }">
             <div style="
                   padding: 0 20px;
@@ -135,14 +143,15 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column :label="'触发频率'" prop="triggerRule" width="120" show-overflow-tooltip>
+        <el-table-column :label="'触发频率'" prop="triggerRule" width="150" show-overflow-tooltip>
           <template #default="{ row }">
-            {{ Math.floor(Math.random() * 10) }}
+            <!-- <el-input-number v-model="row.triggerNum" :max=20 /> -->
+             {{ row.triggerNum }}
           </template>
         </el-table-column>
         <el-table-column :label="$t('marketingApps.triggerRule')" width="200" prop="triggerRule" show-overflow-tooltip>
           <template #default="{ row }">
-            <el-select v-model="row.triggerRule" placeholder="" style="width: 150px">
+            <el-select v-model="row.triggerRule" placeholder="" style="width: 160px">
               <el-option v-for="item in triggerRules" :key="item.value" :label="item.label" :value="item.value" />
             </el-select>
           </template>
@@ -153,11 +162,16 @@
               <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 style="margin-left: 0;" icon="edit-pen" @click="onOpenStatistical(scope.row)" text type="primary"
+              <el-button style="margin-left: 0;" icon="TrendCharts" @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
+                :type="activeName == 'tab1' ? 'danger' : 'success'"
+                style="margin-left: 0;"
+                :icon="activeName == 'tab1' ? 'delete' : 'refresh'"
+                @click="query()" text
+                v-auth="'sys_menu_edit'">
+                  {{ activeName == 'tab1' ? '拉黑' : '还原' }}
               </el-button>
             </div>
           </template>
@@ -177,6 +191,8 @@ import { useMessage, useMessageBox } from '/@/hooks/message';
 import { useI18n } from 'vue-i18n';
 import { fetchItemList } from '/@/api/admin/dict';
 import { ref } from 'vue'
+const MenuDialog = defineAsyncComponent(() => import('./components/form.vue'));
+const StatisticalDialog = defineAsyncComponent(() => import('./components/statistical.vue'));
 
 import type { TabsPaneContext } from 'element-plus'
 
@@ -187,9 +203,6 @@ const handleClick = (tab: TabsPaneContext, event: Event) => {
   query();
 }
 
-// 引入组件
-const MenuDialog = defineAsyncComponent(() => import('./form.vue'));
-const StatisticalDialog = defineAsyncComponent(() => import('./statistical.vue'));
 const { t } = useI18n();
 // 定义变量内容
 const tableRef = ref();
@@ -221,25 +234,21 @@ const getAppTypes = async () => {
 getAppTypes();
 
 const triggerRules = [
-  {
-    label: '不弹出',
-    value: 'unset',
-  },
   {
     label: '仅一次 - URL',
-    value: 'url1',
+    value: 1,
   },
   {
-    label: '每次弹出 - URL',
-    value: 'url2',
+    label: '多次 - URL',
+    value: 2,
   },
   {
     label: '仅一次 - 视频',
-    value: 'video1',
+    value: 3,
   },
   {
-    label: '每次弹出 - 视频',
-    value: 'video2',
+    label: '多次 - 视频',
+    value: 4,
   },
 ]
 
@@ -274,6 +283,7 @@ const handleEdit = () => {
     useMessage().warning('请选择要修改的项');
     return;
   }
+  menuDialogRef.value.openDialog('', {});
   // menuDialogRef.value.openDialog('edit', selectObjs.value);
 };