Sfoglia il codice sorgente

fix: 全局配置结束

jcq 1 settimana fa
parent
commit
2107e79653

+ 16 - 5
src/api/marketing/config.ts

@@ -3,7 +3,6 @@ import request from '/@/utils/request';
 export const pageListDomain = () => {
 	return request({
 		url: '/marketing/config/getDomainGroup',
-		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
 		method: 'get',
 	});
 };
@@ -11,7 +10,14 @@ export const pageListDomain = () => {
 export const pageListIp = (params?: Object) => {
 	return request({
 		url: '/marketing/config/getIpGroup',
-		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
+		method: 'get',
+		params,
+	});
+};
+//获取全局配置
+export const getConfigDetail = (params?: Object) => {
+	return request({
+		url: '/marketing/config/getConfig',
 		method: 'get',
 		params,
 	});
@@ -20,7 +26,6 @@ export const pageListIp = (params?: Object) => {
 export const getConfigIpList = (params?: Object) => {
 	return request({
 		url: '/marketing/config/ipList',
-		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
 		method: 'get',
 		params,
 	});
@@ -29,7 +34,6 @@ export const getConfigIpList = (params?: Object) => {
 export const getConfigDomainList = (params?: Object) => {
 	return request({
 		url: '/marketing/config/domainList',
-		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
 		method: 'get',
 		params,
 	});
@@ -38,7 +42,6 @@ export const getConfigDomainList = (params?: Object) => {
 export const getGroupDetail = (params?: Object) => {
 	return request({
 		url: '/marketing/config/group/detail',
-		// url: 'https://m1.apifoxmock.com/m1/6687089-6396408-default/marketing/config/page',
 		method: 'get',
 		params,
 	});
@@ -51,6 +54,14 @@ export const saveIpList = (data: Object) => {
 		data: data,
 	});
 };
+//保存全局配置
+export const saveConfigDetail = (data: Object) => {
+	return request({
+		url: '/marketing/config/setConfig',
+		method: 'post',
+		data: data,
+	});
+};
 //删除配置ip集合
 export const delIpList = (id?: string) => {
 	return request({

+ 104 - 0
src/components/JDictSelect/index.vue

@@ -0,0 +1,104 @@
+<template>
+	<el-select :class="styleClass" :placeholder="prompt" v-model="state.value" :loading="state.loading" @change="onChange" @focus="onFocus">
+		<el-option v-for="item in state.data" :key="item.value" :label="item.label" :value="item.value" />
+	</el-select>
+</template>
+
+<script setup>
+import { reactive, watch, onMounted } from 'vue';
+import { fetchItemList } from '/@/api/admin/dict';
+
+const state = reactive({
+	data: [],
+	loading: false,
+});
+const emit = defineEmits(['change', 'update:value']);
+
+const props = defineProps({
+	value: {
+		type: [String, Number, Array],
+		default: '',
+	},
+	valueKey: {
+		type: String,
+		default: 'value',
+	},
+	trigger: {
+		desc: '请求数据时机init:初始化/focus:聚焦',
+		type: String,
+		default: 'init',
+	},
+	dictType: {
+		type: String,
+		default: '',
+	},
+	isSelectAll: {
+		desc: '是否需要全部选项',
+		type: Boolean,
+		default: false,
+	},
+	selectFirst: {
+		desc: '是否选择第一个',
+		type: Boolean,
+		default: false,
+	},
+	prompt: {
+		type: String,
+		default: '请选择',
+	},
+	styleClass: {
+		type: String,
+		default: 'min-w-[160px]',
+	},
+});
+watch(
+	() => props.value,
+	() => {
+		state.value = props.value || null;
+		if (!props.value && props.selectFirst && state.data[0]) {
+			state.value = state.data[0].value;
+			emit('update:value', state.value);
+		}
+	},
+	{
+		immediate: true,
+	}
+);
+const onChange = (record, option) => {
+	emit('change', record, option);
+	emit('update:value', record);
+};
+//获取数据
+const getData = async () => {
+	state.data = [];
+	state.loading = true;
+	const { data = [], code } = await fetchItemList({ dictType: props.dictType });
+		
+	if (code === 0) {
+		state.data = data.records.map((item) => {
+			return {
+				label: item.label,
+				value: item[props.valueKey],
+			};
+		});
+		if(props.isSelectAll){
+			state.data.unshift({
+				label: '全部',
+				value: '',
+			});
+		}
+		state.loading = false;
+		if (props.selectFirst) {
+			state.value = state.data[0].value;
+			emit('update:value', state.value);
+		}
+	}
+};
+const onFocus = () => {
+	props.trigger === 'focus' && getData();
+};
+onMounted(() => {	
+	props.trigger === 'init' && getData();
+});
+</script>
+<style lang="less" scoped></style>

+ 99 - 77
src/views/marketing/config/index.vue

@@ -5,21 +5,23 @@
 				<Title class="ml-4" :title="t('marketingConfig.ipList')" />
 				<div class="p-4 rounded">
 					<el-button type="primary" @click="onClickAdd('ip')">{{ t('marketingConfig.addIpList') }}</el-button>
-					<JCollapse
-						@update="(item) => onClickEdit(item, 'ip')"
-						@delete="(item) => onOpenDelete(item, 'ip')"
-						:data="ipData"
-						:activeNames="ipActiveId"
-						:deleteText="t('marketingConfig.deleteListText')"
-						:updateText="t('marketingConfig.updateText')"
-					/>
+					<div v-if="ipData.length > 0" class="overflow-y-auto mt-2" style="height: calc(100vh - 250px)">
+						<JCollapse
+							@update="(item) => onClickEdit(item, 'ip')"
+							@delete="(item) => onOpenDelete(item, 'ip')"
+							:data="ipData"
+							:activeNames="ipActiveId"
+							:deleteText="t('marketingConfig.deleteListText')"
+							:updateText="t('marketingConfig.updateText')"
+						/>
+					</div>
 				</div>
 			</el-tab-pane>
 			<el-tab-pane :label="t('marketingConfig.domainList')" name="域名分组" class="layout-padding-auto layout-padding-view">
 				<Title class="ml-4" :title="t('marketingConfig.domainList')" />
 				<div class="p-4 rounded">
 					<el-button type="primary" @click="onClickAdd('domain')">{{ t('marketingConfig.addDomainList') }}</el-button>
-					<div v-if="domainData.length > 0">
+					<div v-if="domainData.length > 0" class="overflow-y-auto mt-2" style="height: calc(100vh - 250px)">
 						<JCollapse
 							@update="(item) => onClickEdit(item, 'domain')"
 							:data="domainData"
@@ -34,7 +36,7 @@
 			</el-tab-pane>
 			<el-tab-pane :label="t('marketingConfig.disposition')" name="全局配置" class="layout-padding-auto layout-padding-view">
 				<Title class="ml-4" :title="t('marketingConfig.disposition')" />
-				<div class="p-4 rounded">
+				<div class="p-4 rounded overflow-y-auto" style="max-height: calc(100vh - 350px)">
 					<JCollapse
 						:data="[{ title: 'IP集合', id: '1' }]"
 						:activeNames="['1']"
@@ -82,7 +84,7 @@
 							</div>
 							<div class="p-2 items-center flex flex-wrap">
 								<span class="mr-2">黑名单</span>
-								
+
 								<span v-for="item in ipBlackList" :key="item.id">
 									<el-popover v-if="item?.sourceType === 1" width="280" @show="onLoadDetail(item, ipList)" trigger="hover" placement="top">
 										<div v-if="item.list.length > 0" class="flex flex-wrap">
@@ -119,13 +121,12 @@
 							</div>
 						</template>
 					</JCollapse>
-
 					<JCollapse
 						class="mt-4"
 						:data="[{ title: '域名集合', id: '1' }]"
 						:activeNames="['1']"
-						@update="(item) => (domainEditOpen = true)"
-						@delete="(item) => (closeDomainTags = !closeDomainTags)"
+						@update="() => (domainEditOpen = true)"
+						@delete="() => (closeDomainTags = !closeDomainTags)"
 						:deleteText="closeDomainTags ? t('marketingConfig.cancel') : t('marketingConfig.deleteDomain')"
 						:updateText="t('marketingConfig.addDomain')"
 					>
@@ -172,41 +173,49 @@
 					</JCollapse>
 				</div>
 				<div class="w-[66%] ml-[-8px] mt-5">
-					<el-form ref="menuDialogFormRef" :model="formData" :rules="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')">
-								<el-option v-for="item in listSelect" :key="item.value" :label="item.label" :value="item.value" />
-							</el-select>
+					<el-form ref="ruleFormRef" :model="formData" :rules="dataRules" label-width="90px" class="flex flex-wrap">
+						<el-form-item :label="t('marketingConfig.jumpMode')" prop="triggerMode" class="w-1/3">
+							<JDictSelect
+								v-model:value="formData.triggerMode"
+								:placeholder="t('marketingConfig.jumpModeTip')"
+								:dictType="'triggerMode'"
+								:selectFirst="true"
+								:styleClass="'w-full'"
+							/>
 						</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')">
-								<el-option v-for="item in triggerSelect" :key="item.value" :label="item.label" :value="item.value" />
-							</el-select>
+						<el-form-item :label="t('marketingConfig.triggerType')" prop="triggerRule" class="w-1/3">
+							<JDictSelect
+								v-model:value="formData.triggerRule"
+								:placeholder="t('marketingConfig.triggerTypeTip')"
+								:dictType="'triggerRule'"
+								:selectFirst="true"
+								:styleClass="'w-full'"
+							/>
 						</el-form-item>
-						<el-form-item :label="t('marketingConfig.triggerFrequency')" prop="triggerFrequency" class="w-1/3">
-							<el-input v-model="formData.triggerFrequency" type="text" :placeholder="t('marketingConfig.triggerFrequencyTip')" />
+						<el-form-item :label="t('marketingConfig.triggerFrequency')" prop="triggerNum" class="w-1/3">
+							<el-input v-model="formData.triggerNum" type="text" :placeholder="t('marketingConfig.triggerFrequencyTip')" />
 						</el-form-item>
 
 						<div class="w-full mb-[18px]">
 							<el-form-item
-								v-if="formData.jumpMode == '2' || formData.jumpMode == '3'"
+								v-if="formData.triggerMode == '2' || formData.triggerMode == '3'"
 								:label="t('marketingConfig.prompt')"
-								prop="prompt"
+								prop="promptMsg"
 								class="w-1/3"
 							>
-								<el-input v-model="formData.prompt" type="text" :placeholder="t('marketingConfig.promptTip')"></el-input>
+								<el-input v-model="formData.promptMsg" type="text" :placeholder="t('marketingConfig.promptTip')"></el-input>
 							</el-form-item>
 							<el-form-item
-								v-if="formData.jumpMode == '1' || formData.jumpMode == '3'"
+								v-if="formData.triggerMode == '1' || formData.triggerMode == '3'"
 								:label="t('marketingConfig.jumpLink')"
-								prop="jumpLink"
+								prop="url"
 								class="w-1/3"
 							>
-								<el-input v-model="formData.jumpLink" type="text" :placeholder="t('marketingConfig.jumpLinkTip')"></el-input>
+								<el-input v-model="formData.url" 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>
+							<el-button type="primary" @click="onSubmit(ruleFormRef)" class="w-[80px] ml-5">{{ t('common.saveBtn') }}</el-button>
 						</div>
 					</el-form>
 				</div>
@@ -238,11 +247,14 @@ import {
 	delIpList,
 	delDomainList,
 	getGroupDetail,
+	getConfigDetail,
+	saveConfigDetail,
 } from '/@/api/marketing/config';
 import { useI18n } from 'vue-i18n';
 import { useMessage } from '/@/hooks/message';
 import { rule } from '/@/utils/validate';
 import { ipSplicing } from '/@/utils/ipUpdate';
+import type { FormInstance } from 'element-plus';
 
 // 引入组件
 const JCollapse = defineAsyncComponent(() => import('/@/components/JCollapse/index.vue'));
@@ -251,9 +263,10 @@ const DomainEdit = defineAsyncComponent(() => import('./components/domainEdit.vu
 const ListEdit = defineAsyncComponent(() => import('./components/listEdit.vue'));
 const GroupingEdit = defineAsyncComponent(() => import('./components/ipGroupingEdit.vue'));
 const IpListEdit = defineAsyncComponent(() => import('./components/ipListEdit.vue'));
+const JDictSelect = defineAsyncComponent(() => import('/@/components/JDictSelect/index.vue'));
 const { t } = useI18n();
 // 定义变量内容
-const activeName = ref('全局配置');
+const activeName = ref('IP分组');
 //关闭或打开tabs的关闭按钮
 const closeDomainTags = ref(false);
 const closeIpTags = ref(false);
@@ -277,38 +290,13 @@ const openType = ref('ip'); // 'ip' or 'domain'
 const ipWhiteList = ref([]); // ip白名单
 const ipBlackList = ref([]); // ip黑名单
 const domainList = ref([]);
-
-const listSelect = ref([
-	{
-		value: '1',
-		label: '跳转链接',
-	},
-	{
-		value: '2',
-		label: '提示信息',
-	},
-	{
-		value: '3',
-		label: '提示并跳转',
-	},
-]);
-const triggerSelect = ref([
-	{
-		value: '1',
-		label: '仅一次跳转',
-	},
-	{
-		value: '2',
-		label: '多次跳转',
-	},
-]);
-
+const ruleFormRef = ref();
 const formData = ref({
-	jumpMode: '1',
-	triggerType: '',
-	jumpLink: '',
-	triggerFrequency: '',
-	prompt: '',
+	promptMsg: '',
+	triggerMode: '',
+	triggerRule: '',
+	url: '',
+	triggerNum: '',
 });
 const onOpenDelete = (item: any, type: any) => {
 	console.log(item);
@@ -322,22 +310,22 @@ const getData = () => {
 	openType.value === 'ip' ? getIpData() : getDomainData();
 };
 const onLoadDetail = async (item: any) => {
-	if(item.list.length !== 0) return;
+	if (item.list.length !== 0) return;
 	await getGroupDetail({ id: item.groupId }).then((val) => {
-		item.list = val.data.ips.map((item)=>{
-			return ipSplicing(item.startIp, item.endIp)
+		item.list = val.data.ips.map((item) => {
+			return ipSplicing(item.startIp, item.endIp);
 		});
-	});	
+	});
 };
 
 // // 表单校验规则
 const dataRules = reactive({
-	jumpLink: [
+	url: [
 		{ required: true, message: '跳转连接不能为空', trigger: 'blur' },
 		{ validator: rule.url, trigger: 'blur' },
 	],
-	triggerFrequency: [
-		{ required: true, message: '域名不能为空', trigger: 'blur' },
+	triggerNum: [
+		{ required: true, message: '触发频率不能为空', trigger: 'blur' },
 		{
 			pattern: /^(10000|[1-9]\d{0,3}|0)$|^(100%|[1-9]?\d%|0%)$/,
 			message: '请输入 0-10000 的正整数或 0%-100% 的百分比',
@@ -346,8 +334,29 @@ const dataRules = reactive({
 	],
 });
 
-const onSubmit = () => {
-	useMessage().success(t(formData.value ? 'common.editSuccessText' : 'common.addSuccessText'));
+const onSubmit = async () => {
+	let isValid = true;
+	if (formData.value.triggerMode === '1' || formData.value.triggerMode === '3') {
+		try {
+			await ruleFormRef.value.validateField('url');
+		} catch (error) {
+			// 验证失败,阻止后续逻辑执行
+			return;
+		}
+	}
+	// 如果验证失败,直接返回,不执行后续逻辑
+	if (!isValid) {
+		return;
+	}
+	try {
+		loading.value = true;
+		await saveConfigDetail(formData.value);
+		useMessage().success(t('common.editSuccessText'));
+	} catch (err) {
+		useMessage().error(err.msg);
+	} finally {
+		getConfig();
+	}
 };
 const handleClick = (data: any) => {
 	if (data.props.label === 'IP分组') {
@@ -416,6 +425,7 @@ const onOpenEditMenu = (type: string, row: any) => {
 const getConfig = () => {
 	configIp();
 	configDomain();
+	getConfigDetail();
 };
 const configDomain = async () => {
 	await getConfigDomainList().then((val) => {
@@ -434,11 +444,11 @@ const configIp = async () => {
 			.map((item) => {
 				return { ...item, list: [] };
 			});
-		ipBlackList.value = val.data.filter((item) => item.ipType === 2).map((item) => {
+		ipBlackList.value = val.data
+			.filter((item) => item.ipType === 2)
+			.map((item) => {
 				return { ...item, list: [] };
-			});;
-		console.log(ipBlackList.value);
-		
+			});
 	});
 };
 const getDomainData = async () => {
@@ -480,6 +490,18 @@ const getIpData = async () => {
 			};
 		});
 	});
+
+	await getConfigDetail().then((val) => {
+		formData.value = {
+			promptMsg: '',
+			triggerMode: '',
+			triggerRule: '',
+			url: '',
+			triggerNum: '',
+		};
+		formData.value = { ...val.data, triggerMode: val.data?.triggerMode.toString(), triggerRule: val.data?.triggerRule.toString() };
+		console.log(val);
+	});
 };
 
 onMounted(() => {