UPDATE: VERSION-20260314
This commit is contained in:
@@ -42,4 +42,12 @@ export const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
component: () => import('@/views/decision/algorithm/management.vue'),
|
||||
},
|
||||
{
|
||||
name: 'decision-fire-rule',
|
||||
path: '/app/decision/fire-rule',
|
||||
meta: {
|
||||
title: '活力规则',
|
||||
},
|
||||
component: () => import('@/views/decision/rule/management.vue'),
|
||||
},
|
||||
]
|
||||
@@ -1648,3 +1648,123 @@
|
||||
color: #eeeeee;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ant-switch {
|
||||
background: rgb(8 30 59);
|
||||
}
|
||||
|
||||
|
||||
.ks-algorithm-card {
|
||||
.ant-card-head-title {
|
||||
span.text {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-header {
|
||||
line-height: 40px;
|
||||
background: #081d36;
|
||||
min-height: 40px;
|
||||
background: url(@/assets/icons/card-head.png) left / 180% 100%;
|
||||
padding: 0 10px;
|
||||
|
||||
.ks-sidebar-title {
|
||||
color: #7ae8fc;
|
||||
font-size: 16px;
|
||||
.icon {
|
||||
background: url(@/assets/icons/list.png) center / 100% 100%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
}
|
||||
.text{
|
||||
margin-left: 40px;
|
||||
font-size: 16px;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-add {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 8px;
|
||||
font-size: 12px;
|
||||
|
||||
.anticon {
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list {
|
||||
&.ks-sidebar-list {
|
||||
.ant-list-item {
|
||||
cursor: pointer;
|
||||
transition: all 0.5s;
|
||||
border-left: 2px solid transparent;
|
||||
position: relative;
|
||||
|
||||
&.selected,
|
||||
&:hover {
|
||||
background: #0a1b3c;
|
||||
border-left: 2px solid #11377e;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-list-type {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
.ant-badge {
|
||||
.ant-badge-count {
|
||||
color: #c3c2c2;
|
||||
background: #333f7d;
|
||||
box-shadow: 0 0 0 1px #325478;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list-item-meta {
|
||||
.ant-list-item-meta-title {
|
||||
color: #7ae8fc;
|
||||
}
|
||||
|
||||
.ant-list-item-meta-description {
|
||||
color: #4d8c98;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-list-param-list {
|
||||
padding: 15px;
|
||||
border: 1px solid #475f71;
|
||||
border-radius: 2px;
|
||||
|
||||
.ks-sidebar-list-param-item {
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ks-sidebar-list-param-actions {
|
||||
.anticon {
|
||||
color: #7ae8fc;
|
||||
font-size: 20px;
|
||||
display: block;
|
||||
line-height: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,13 @@
|
||||
新增
|
||||
</a-button>
|
||||
</div>
|
||||
<a-list item-layout="horizontal" :data-source="algorithms" class="ks-algorithm-list">
|
||||
<a-list item-layout="horizontal" :data-source="algorithms" class="ks-sidebar-list">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item @click="()=> handleSelect(item)" :class="selectedAlgorithm?.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-algorithm-type"><a-badge size="small" :count="getAlgorithmTypeName(item.type)"></a-badge></span>
|
||||
<span class="ks-sidebar-list-type"><a-badge size="small" :count="getAlgorithmTypeName(item.type)"></a-badge></span>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
@@ -95,8 +95,8 @@
|
||||
:name="['algorithmParamList']"
|
||||
>
|
||||
<a-form-item-rest>
|
||||
<div class="ks-algorithm-param-list">
|
||||
<div class="ks-algorithm-param-item" v-for="(item,index) in selectedAlgorithm.algorithmParamList">
|
||||
<div class="ks-sidebar-list-param-list">
|
||||
<div class="ks-sidebar-list-param-item" v-for="(item,index) in selectedAlgorithm.algorithmParamList">
|
||||
<a-row :gutter="15">
|
||||
<a-col :span="7">
|
||||
<a-input v-model:value="item.paramName" placeholder="请输入参数名" />
|
||||
@@ -108,7 +108,7 @@
|
||||
<a-input v-model:value="item.description" placeholder="请输入描述" />
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-space class="ks-algorithm-param-actions">
|
||||
<a-space class="ks-sidebar-list-param-actions">
|
||||
<MinusCircleOutlined @click="()=> handleMinus(index)" />
|
||||
<PlusCircleOutlined @click="handleAdd" v-if="index === 0" />
|
||||
</a-space>
|
||||
@@ -125,8 +125,8 @@
|
||||
:name="['algoConfig']"
|
||||
>
|
||||
<a-form-item-rest>
|
||||
<div class="ks-algorithm-param-list">
|
||||
<div class="ks-algorithm-param-item" v-for="(t,index) in selectedAlgorithm.algoConfigList">
|
||||
<div class="ks-sidebar-list-param-list">
|
||||
<div class="ks-sidebar-list-param-item" v-for="(t,index) in selectedAlgorithm.algoConfigList">
|
||||
<a-row :gutter="15">
|
||||
<a-col :span="7">
|
||||
<a-select placeholder="请选择参数" v-model:value="t.name" @change="(v: any)=> t.name = v">
|
||||
@@ -139,7 +139,7 @@
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-space class="ks-algorithm-param-actions">
|
||||
<a-space class="ks-sidebar-list-param-actions">
|
||||
<MinusCircleOutlined @click="()=> handleMinusConfig(index)" />
|
||||
<PlusCircleOutlined @click="handleAddConfig" v-if="index === 0" />
|
||||
</a-space>
|
||||
@@ -290,6 +290,7 @@ const handleCreate = () => {
|
||||
|
||||
const handleSelect = (item: Algorithm) => {
|
||||
selectedAlgorithm.value = resolveItem(item);
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
@@ -394,120 +395,3 @@ const handleChange = (page: number, pageSize: number) => {
|
||||
onMounted(() => load());
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.ks-algorithm-card {
|
||||
.ant-card-head-title {
|
||||
span.text {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-header {
|
||||
line-height: 40px;
|
||||
padding: 5px 15px;
|
||||
background: #081d36;
|
||||
min-height: 40px;
|
||||
background: url(@/assets/icons/card-head.png) left / 180% 100%;
|
||||
padding: 0 10px;
|
||||
|
||||
.ks-sidebar-title {
|
||||
color: #7ae8fc;
|
||||
font-size: 16px;
|
||||
.icon {
|
||||
background: url(@/assets/icons/list.png) center / 100% 100%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
}
|
||||
.text{
|
||||
margin-left: 40px;
|
||||
font-size: 16px;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-add {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 8px;
|
||||
font-size: 12px;
|
||||
|
||||
.anticon {
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list {
|
||||
&.ks-algorithm-list {
|
||||
.ant-list-item {
|
||||
cursor: pointer;
|
||||
transition: all 0.5s;
|
||||
border-left: 2px solid transparent;
|
||||
position: relative;
|
||||
|
||||
&.selected,
|
||||
&:hover {
|
||||
background: #0a1b3c;
|
||||
border-left: 2px solid #11377e;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-algorithm-type {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
.ant-badge {
|
||||
.ant-badge-count {
|
||||
color: #c3c2c2;
|
||||
background: #333f7d;
|
||||
box-shadow: 0 0 0 1px #325478;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list-item-meta {
|
||||
.ant-list-item-meta-title {
|
||||
color: #7ae8fc;
|
||||
}
|
||||
|
||||
.ant-list-item-meta-description {
|
||||
color: #4d8c98;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ks-algorithm-param-list {
|
||||
padding: 15px;
|
||||
border: 1px solid #475f71;
|
||||
border-radius: 2px;
|
||||
|
||||
.ks-algorithm-param-item {
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ks-algorithm-param-actions {
|
||||
.anticon {
|
||||
color: #7ae8fc;
|
||||
font-size: 20px;
|
||||
display: block;
|
||||
line-height: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
35
modeler/src/views/decision/rule/api.ts
Normal file
35
modeler/src/views/decision/rule/api.ts
Normal 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}`);
|
||||
};
|
||||
|
||||
|
||||
|
||||
8
modeler/src/views/decision/rule/config.ts
Normal file
8
modeler/src/views/decision/rule/config.ts
Normal 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.
|
||||
*/
|
||||
254
modeler/src/views/decision/rule/management.vue
Normal file
254
modeler/src/views/decision/rule/management.vue
Normal 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>
|
||||
34
modeler/src/views/decision/rule/types.ts
Normal file
34
modeler/src/views/decision/rule/types.ts
Normal 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> {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user