394 lines
14 KiB
Vue
394 lines
14 KiB
Vue
<template>
|
||
<Layout>
|
||
<template #sidebar>
|
||
<div class="ks-sidebar-header">
|
||
<a-flex class="ks-sidebar-title">
|
||
<span class="icon"></span>
|
||
<span class="text">火力规则管理</span>
|
||
</a-flex>
|
||
<a-button class="ks-sidebar-add" size="small" @click="handleCreate">
|
||
<PlusOutlined />
|
||
新增
|
||
</a-button>
|
||
</div>
|
||
<a-list item-layout="horizontal" :data-source="datasource" class="ks-sidebar-list">
|
||
<template #renderItem="{ item }">
|
||
<a-list-item @click="()=> handleSelect(item)" :class="selectedFireRule?.id === item.id ? 'selected' : null">
|
||
<a-list-item-meta :description="substring(item.description,20)">
|
||
<template #title>
|
||
<span class="ks-algorithm-name">{{ substring(item.name, 20) }}</span>
|
||
<span class="ks-sidebar-list-type"><a-badge size="small" :count="getSceneTypeName(item)"></a-badge></span>
|
||
</template>
|
||
</a-list-item-meta>
|
||
</a-list-item>
|
||
</template>
|
||
</a-list>
|
||
|
||
<a-pagination
|
||
v-model:current="query.pageNum"
|
||
:page-size="query.pageSize"
|
||
:total="datasourceTotal"
|
||
simple size="small" @change="handleChange" />
|
||
</template>
|
||
|
||
<div class="w-full h-full">
|
||
<a-card class="ks-page-card ks-algorithm-card">
|
||
<template #title>
|
||
<a-space>
|
||
<span class="point"></span>
|
||
<span class="text">规则配置</span>
|
||
</a-space>
|
||
</template>
|
||
|
||
<div class="ks-scrollable" style="height: 80.5vh;overflow-y: auto;padding-right: 10px">
|
||
<a-row :gutter="15">
|
||
<a-col :span="16">
|
||
<a-form
|
||
ref="formRef"
|
||
:label-col="{span: 6}"
|
||
:model="selectedFireRule"
|
||
autocomplete="off"
|
||
layout="horizontal"
|
||
name="basic"
|
||
>
|
||
<a-form-item
|
||
label="规则名称"
|
||
:rules="[{ required: true, message: '请输入规则名称!', trigger: ['input', 'change'] }]"
|
||
name="name"
|
||
>
|
||
<a-input v-model:value="selectedFireRule.name" placeholder="请输入规则名称" />
|
||
</a-form-item>
|
||
|
||
<a-form-item
|
||
label="场景类型"
|
||
name="sceneType"
|
||
>
|
||
<a-select v-model:value="selectedFireRule.sceneType" placeholder="请选择场景类型">
|
||
<a-select-option :value="null">通用</a-select-option>
|
||
<a-select-option :value="0">防御</a-select-option>
|
||
<a-select-option :value="1">空降</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
|
||
<!-- 触发条件 - 传递ID,保持PlatformComponentPayload结构 -->
|
||
<a-form-item
|
||
label="触发条件"
|
||
:rules="[{ required: true, message: '请输入触发条件!', trigger: ['change'] }]"
|
||
name="conditionsArray"
|
||
>
|
||
<a-form-item-rest>
|
||
<div class="ks-sidebar-list-param-list">
|
||
<div
|
||
class="ks-sidebar-list-param-item"
|
||
v-for="(item,index) in selectedFireRule.conditionsArray"
|
||
:key="`condition-${index}-${item.platform?.id || 'null'}-${item.component?.id || 'null'}`"
|
||
>
|
||
<a-row :gutter="15">
|
||
<a-col :span="21">
|
||
<!-- 只传递ID参数 -->
|
||
<PlatformSelect
|
||
:platform-id="item.platform?.id || null"
|
||
:component-id="item.component?.id || null"
|
||
@change="(payload)=> handleUpdateCondition(payload, index)"
|
||
/>
|
||
</a-col>
|
||
<a-col :span="3">
|
||
<a-space class="ks-sidebar-list-param-actions">
|
||
<MinusCircleOutlined @click.stop="()=> handleMinusCondition(index)" />
|
||
<PlusCircleOutlined @click.stop="()=> handleAddCondition()" v-if="index === 0" />
|
||
</a-space>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</div>
|
||
</a-form-item-rest>
|
||
</a-form-item>
|
||
|
||
<!-- 响应动作 - 传递ID,保持PlatformComponentPayload结构 -->
|
||
<a-form-item
|
||
label="响应动作"
|
||
:rules="[{ required: true, message: '请输入响应动作!', trigger: ['change'] }]"
|
||
name="actionsArray"
|
||
>
|
||
<a-form-item-rest>
|
||
<div class="ks-sidebar-list-param-list">
|
||
<div
|
||
class="ks-sidebar-list-param-item"
|
||
v-for="(item,index) in selectedFireRule.actionsArray"
|
||
:key="`action-${index}-${item.platform?.id || 'null'}-${item.component?.id || 'null'}`"
|
||
>
|
||
<a-row :gutter="15">
|
||
<a-col :span="21">
|
||
<!-- 只传递ID参数 -->
|
||
<PlatformSelect
|
||
:platform-id="item.platform?.id || null"
|
||
:component-id="item.component?.id || null"
|
||
@change="(payload)=> handleUpdateAction(payload, index)"
|
||
/>
|
||
</a-col>
|
||
<a-col :span="3">
|
||
<a-space class="ks-sidebar-list-param-actions">
|
||
<MinusCircleOutlined @click.stop="()=> handleMinusAction(index)" />
|
||
<PlusCircleOutlined @click.stop="()=> handleAddAction()" v-if="index === 0" />
|
||
</a-space>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</div>
|
||
</a-form-item-rest>
|
||
</a-form-item>
|
||
|
||
<a-form-item
|
||
label="优先级(数值越小优先级越高)"
|
||
:rules="[{ required: true, message: '请输入优先级!', trigger: ['input', 'change'] }]"
|
||
name="priority"
|
||
>
|
||
<a-input-number style="width:100%;" v-model:value="selectedFireRule.priority" placeholder="请输入优先级" />
|
||
</a-form-item>
|
||
|
||
<a-form-item
|
||
label="是否启用"
|
||
name="enabled"
|
||
>
|
||
<a-switch v-model:checked="selectedFireRule.enabled" />
|
||
</a-form-item>
|
||
|
||
<a-form-item :wrapper-col="{offset: 6}">
|
||
<a-space>
|
||
<a-button @click="handleSave" type="primary">保存规则</a-button>
|
||
<a-popconfirm
|
||
v-if="selectedFireRule && selectedFireRule.id > 0"
|
||
title="确定删除?"
|
||
@confirm="handleDelete"
|
||
>
|
||
<a-button danger>删除规则</a-button>
|
||
</a-popconfirm>
|
||
</a-space>
|
||
</a-form-item>
|
||
</a-form>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</a-card>
|
||
</div>
|
||
</Layout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { nextTick, onMounted, ref } from 'vue';
|
||
import { type FormInstance, message } from 'ant-design-vue';
|
||
import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||
import Layout from '../layout.vue';
|
||
import { createFireRule, deleteFireRule, findFireRuleByQuery, updateFireRule } from './api';
|
||
import type { FireRule, FireRulePageableResponse, FireRuleRequest } from './types';
|
||
import type { PlatformComponentPayload } from '../types';
|
||
import { substring } from '@/utils/strings';
|
||
import PlatformSelect from './PlatformSelect.vue';
|
||
|
||
const query = ref<FireRuleRequest>({
|
||
id: 0,
|
||
name: '',
|
||
sceneType: null,
|
||
conditions: '',
|
||
conditionsArray: [],
|
||
actions: '',
|
||
actionsArray: [],
|
||
priority: 0,
|
||
enabled: true,
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
});
|
||
|
||
const defaultPayload: PlatformComponentPayload = {
|
||
platform: null,
|
||
component: null,
|
||
};
|
||
|
||
const defaultFireRule: FireRule = {
|
||
id: 0,
|
||
name: '',
|
||
sceneType: null,
|
||
conditions: '',
|
||
conditionsArray: [JSON.parse(JSON.stringify(defaultPayload))],
|
||
actions: '',
|
||
actionsArray: [JSON.parse(JSON.stringify(defaultPayload))],
|
||
priority: 0,
|
||
enabled: true,
|
||
};
|
||
|
||
const getSceneTypeName = (item: FireRule): string => {
|
||
if (item.sceneType === 0) return '防御';
|
||
if (item.sceneType === 1) return '空降';
|
||
return '通用';
|
||
};
|
||
|
||
const resolveItem = (item: FireRule) => {
|
||
const newItem: FireRule = JSON.parse(JSON.stringify(item)) as FireRule;
|
||
|
||
try {
|
||
newItem.conditionsArray = item.conditions
|
||
? (JSON.parse(item.conditions) as PlatformComponentPayload[])
|
||
: [JSON.parse(JSON.stringify(defaultPayload))];
|
||
} catch (e) {
|
||
newItem.conditionsArray = [JSON.parse(JSON.stringify(defaultPayload))];
|
||
}
|
||
|
||
try {
|
||
newItem.actionsArray = item.actions
|
||
? (JSON.parse(item.actions) as PlatformComponentPayload[])
|
||
: [JSON.parse(JSON.stringify(defaultPayload))];
|
||
} catch (e) {
|
||
newItem.actionsArray = [JSON.parse(JSON.stringify(defaultPayload))];
|
||
}
|
||
|
||
// 确保数组不为空
|
||
if (!Array.isArray(newItem.conditionsArray) || newItem.conditionsArray.length === 0) {
|
||
newItem.conditionsArray = [JSON.parse(JSON.stringify(defaultPayload))];
|
||
}
|
||
if (!Array.isArray(newItem.actionsArray) || newItem.actionsArray.length === 0) {
|
||
newItem.actionsArray = [JSON.parse(JSON.stringify(defaultPayload))];
|
||
}
|
||
|
||
return newItem;
|
||
};
|
||
|
||
const datasource = ref<FireRule[]>([]);
|
||
const datasourceTotal = ref<number>(0);
|
||
const selectedFireRule = ref<FireRule>(JSON.parse(JSON.stringify(defaultFireRule)));
|
||
const formRef = ref<FormInstance | null>(null);
|
||
|
||
const load = () => {
|
||
datasource.value = [];
|
||
datasourceTotal.value = 0;
|
||
if(selectedFireRule.value.id <= 0){
|
||
selectedFireRule.value = JSON.parse(JSON.stringify(defaultFireRule));
|
||
nextTick(() => {
|
||
formRef.value?.resetFields();
|
||
});
|
||
}
|
||
|
||
findFireRuleByQuery(query.value).then((r: FireRulePageableResponse) => {
|
||
datasource.value = r.rows ?? [];
|
||
datasourceTotal.value = r.total ?? 0;
|
||
});
|
||
};
|
||
|
||
const handleCreate = () => {
|
||
formRef.value?.resetFields();
|
||
selectedFireRule.value = JSON.parse(JSON.stringify(defaultFireRule));
|
||
};
|
||
|
||
const handleSelect = (item: FireRule) => {
|
||
formRef.value?.resetFields();
|
||
nextTick(() => {
|
||
selectedFireRule.value = resolveItem(item);
|
||
});
|
||
};
|
||
|
||
const handleDelete = () => {
|
||
if (selectedFireRule.value?.id > 0) {
|
||
deleteFireRule(selectedFireRule.value.id).then(r => {
|
||
if (r.code === 200) {
|
||
load();
|
||
message.success(r.msg ?? '删除成功');
|
||
} else {
|
||
message.error(r.msg ?? '删除失败');
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
const handleSave = () => {
|
||
formRef.value?.validate().then(() => {
|
||
const savedValue: FireRule = JSON.parse(JSON.stringify(selectedFireRule.value));
|
||
|
||
savedValue.conditions = JSON.stringify(savedValue.conditionsArray);
|
||
savedValue.actions = JSON.stringify(savedValue.actionsArray);
|
||
|
||
const request = savedValue.id > 0
|
||
? updateFireRule(savedValue)
|
||
: createFireRule(savedValue);
|
||
|
||
request.then(r => {
|
||
if (r.code === 200) {
|
||
load();
|
||
message.success(r.msg ?? '操作成功');
|
||
} else {
|
||
message.error(r.msg ?? '操作失败');
|
||
}
|
||
}).catch(err => {
|
||
message.error('请求失败:' + err.message);
|
||
});
|
||
}).catch(err => {
|
||
message.error('表单验证失败:' + err.message);
|
||
});
|
||
};
|
||
|
||
const handleUpdateCondition = (payload: PlatformComponentPayload, index: number) => {
|
||
if (selectedFireRule.value && selectedFireRule.value.conditionsArray[index]) {
|
||
const newArray = [...selectedFireRule.value.conditionsArray];
|
||
newArray[index] = {
|
||
platform: payload.platform,
|
||
component: payload.component,
|
||
};
|
||
selectedFireRule.value.conditionsArray = newArray;
|
||
}
|
||
};
|
||
|
||
const handleUpdateAction = (payload: PlatformComponentPayload, index: number) => {
|
||
if (selectedFireRule.value && selectedFireRule.value.actionsArray[index]) {
|
||
const newArray = [...selectedFireRule.value.actionsArray];
|
||
newArray[index] = {
|
||
platform: payload.platform,
|
||
component: payload.component,
|
||
};
|
||
selectedFireRule.value.actionsArray = newArray;
|
||
}
|
||
};
|
||
|
||
// 添加触发条件
|
||
const handleAddCondition = () => {
|
||
if (selectedFireRule.value) {
|
||
selectedFireRule.value.conditionsArray.push(JSON.parse(JSON.stringify(defaultPayload)));
|
||
}
|
||
};
|
||
|
||
// 删除触发条件
|
||
const handleMinusCondition = (index: number) => {
|
||
if (!selectedFireRule.value) return;
|
||
const list = [...selectedFireRule.value.conditionsArray];
|
||
if (list.length <= 1) {
|
||
selectedFireRule.value.conditionsArray = [JSON.parse(JSON.stringify(defaultPayload))];
|
||
} else {
|
||
list.splice(index, 1);
|
||
selectedFireRule.value.conditionsArray = list;
|
||
}
|
||
};
|
||
|
||
// 添加响应动作
|
||
const handleAddAction = () => {
|
||
if (selectedFireRule.value) {
|
||
selectedFireRule.value.actionsArray.push(JSON.parse(JSON.stringify(defaultPayload)));
|
||
}
|
||
};
|
||
|
||
// 删除响应动作
|
||
const handleMinusAction = (index: number) => {
|
||
if (!selectedFireRule.value) return;
|
||
const list = [...selectedFireRule.value.actionsArray];
|
||
if (list.length <= 1) {
|
||
selectedFireRule.value.actionsArray = [JSON.parse(JSON.stringify(defaultPayload))];
|
||
} else {
|
||
list.splice(index, 1);
|
||
selectedFireRule.value.actionsArray = list;
|
||
}
|
||
};
|
||
|
||
const handleChange = (page: number, pageSize: number) => {
|
||
query.value.pageNum = page;
|
||
query.value.pageSize = pageSize;
|
||
load();
|
||
};
|
||
|
||
onMounted(() => load());
|
||
</script> |