UPDATE: VERSION-20260314

This commit is contained in:
libertyspy
2026-03-14 20:55:15 +08:00
parent 2198e108a4
commit 33a77428db
7 changed files with 468 additions and 125 deletions

View File

@@ -0,0 +1,35 @@
/*
* This file is part of the kernelstudio package.
*
* (c) 2014-2026 zlin <admin@kernelstudio.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
import { HttpRequestClient } from '@/utils/request';
import type { FireRule, FireRulePageableResponse, FireRuleRequest } from './types';
import type { BasicResponse } from '@/types';
const req = HttpRequestClient.create<BasicResponse>({
baseURL: '/api',
});
export const findFireRuleByQuery = (query: Partial<FireRuleRequest> = {}): Promise<FireRulePageableResponse> => {
return req.get('/system/rule/list', query);
};
export const createFireRule = (fireRule: FireRule): Promise<BasicResponse> => {
return req.postJson('/system/rule', fireRule);
};
export const updateFireRule = (fireRule: FireRule): Promise<BasicResponse> => {
return req.putJson('/system/rule', fireRule);
};
export const deleteFireRule = (id: number): Promise<BasicResponse> => {
return req.delete(`/system/rule/${id}`);
};

View File

@@ -0,0 +1,8 @@
/*
* This file is part of the kernelstudio package.
*
* (c) 2014-2026 zlin <admin@kernelstudio.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

View File

@@ -0,0 +1,254 @@
<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>
<a-form-item
label="触发条件"
:rules="[{ required: true, message: '请输入触发条件!', trigger: ['input', 'change'] }]"
:name="['conditions']"
>
<a-input v-model:value="selectedFireRule.conditions" placeholder="请输入触发条件" />
</a-form-item>
<a-form-item
label="响应动作"
:rules="[{ required: true, message: '请输入响应动作!', trigger: ['input', 'change'] }]"
:name="['actions']"
>
<a-input v-model:value="selectedFireRule.actions" placeholder="请输入响应动作" />
</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="['priority']"
>
<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 { onMounted, ref } from 'vue';
import { type FormInstance, message } from 'ant-design-vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import Layout from '../layout.vue';
import { createFireRule, deleteFireRule, findFireRuleByQuery, updateFireRule } from './api';
import type { FireRule, FireRuleRequest } from './types';
import { substring } from '@/utils/strings';
const query = ref<Partial<FireRuleRequest>>({
pageNum: 1,
pageSize: 10,
});
const defaultFireRule: FireRule = {
id: 0,
// 规则名称
name: null,
// 场景类型0-防御1-空降null表示通用
sceneType: null,
// 触发条件JSON格式
conditions: null,
// 响应动作JSON格式
actions: null,
// 优先级(数值越小优先级越高)
priority: 0,
// 是否启用0禁用1启用
enabled: true,
};
const getSceneTypeName = (item: FireRule): string => {
if (0 === item.sceneType) {
return '防御';
}
if (1 === item.sceneType) {
return '空降';
}
return '通用';
};
const resolveItem = (item: FireRule) => {
let newItem = JSON.parse(JSON.stringify(item));
return newItem;
};
const datasource = ref<FireRule[]>([]);
const datasourceTotal = ref<number>(0);
const selectedFireRule = ref<FireRule>(resolveItem(defaultFireRule));
const formRef = ref<FormInstance | null>(null);
const load = () => {
datasource.value = [];
datasourceTotal.value = 0;
formRef.value?.resetFields();
selectedFireRule.value = resolveItem(defaultFireRule);
findFireRuleByQuery(query.value).then(r => {
datasource.value = r.rows ?? [];
datasourceTotal.value = r.total ?? 0;
});
};
const handleCreate = () => {
selectedFireRule.value = resolveItem(defaultFireRule);
};
const handleSelect = (item: FireRule) => {
selectedFireRule.value = resolveItem(item);
formRef.value?.resetFields();
};
const handleDelete = () => {
if (selectedFireRule.value && selectedFireRule.value.id > 0) {
deleteFireRule(selectedFireRule.value.id).then(r => {
if (r.code === 200) {
load();
message.info(r.msg ?? '删除成功');
} else {
message.error(r.msg ?? '删除失败');
}
});
}
};
const handleSave = () => {
if (formRef.value) {
formRef.value.validate().then(() => {
let res = null;
let savedValue: FireRule = JSON.parse(JSON.stringify(selectedFireRule.value)) as any as FireRule;
if (savedValue.id > 0) {
res = updateFireRule(savedValue);
} else {
res = createFireRule(savedValue);
}
if (res) {
res.then(r => {
if (r.code === 200) {
load();
message.info(r.msg ?? '操作成功');
} else {
message.error(r.msg ?? '操作失败');
}
});
}
});
}
};
const handleChange = (page: number, pageSize: number) => {
query.value.pageNum = page;
query.value.pageSize = pageSize;
load();
};
onMounted(() => load());
</script>

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the kernelstudio package.
*
* (c) 2014-2026 zlin <admin@kernelstudio.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
import type { NullableString, PageableResponse } from '@/types';
export interface FireRule {
id: number,
// 规则名称
name: NullableString,
// 场景类型0-防御1-空降null表示通用
sceneType: Number | null,
// 触发条件JSON格式
conditions: NullableString,
// 响应动作JSON格式
actions: NullableString,
// 优先级(数值越小优先级越高)
priority: number,
// 是否启用0禁用1启用
enabled: boolean,
}
export interface FireRuleRequest extends FireRule {
pageNum: number,
pageSize: number,
}
export interface FireRulePageableResponse extends PageableResponse<FireRule> {
}