feat(cdss): upgrade rule engine with priority, category, execution history and stats
This commit is contained in:
@@ -8,6 +8,29 @@ export function getCdssRuleList(query) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getCdssRuleListEnhanced(query) {
|
||||
return request({
|
||||
url: '/infection/cdss/rules/enhanced',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getCdssRuleStats() {
|
||||
return request({
|
||||
url: '/infection/cdss/rules/stats',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getCdssRuleHistory(query) {
|
||||
return request({
|
||||
url: '/infection/cdss/rules/history',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addCdssRule(data) {
|
||||
return request({
|
||||
url: '/infection/cdss/rules',
|
||||
|
||||
@@ -18,16 +18,71 @@
|
||||
<el-option label="INFO" value="INFO" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="优先级" prop="priority">
|
||||
<el-select v-model="queryParams.priority" placeholder="请选择" clearable style="width: 120px">
|
||||
<el-option label="最高" :value="2" />
|
||||
<el-option label="紧急" :value="1" />
|
||||
<el-option label="普通" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类" prop="category">
|
||||
<el-input v-model="queryParams.category" placeholder="规则分类" clearable style="width: 140px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规则名称" prop="keyword">
|
||||
<el-input v-model="queryParams.keyword" placeholder="搜索规则名称" clearable style="width: 180px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button type="info" icon="DataAnalysis" @click="handleToggleStats">{{ showStats ? '返回列表' : '统计概览' }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<vxe-table :data="ruleList" :loading="loading" border stripe height="auto">
|
||||
<div v-if="showStats" class="stats-panel">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-value">{{ ruleStats.totalRules || 0 }}</div>
|
||||
<div class="stat-label">规则总数</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card stat-active">
|
||||
<div class="stat-value">{{ ruleStats.activeRules || 0 }}</div>
|
||||
<div class="stat-label">启用规则</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card stat-exec">
|
||||
<div class="stat-value">{{ ruleStats.totalExecutions || 0 }}</div>
|
||||
<div class="stat-label">执行总次数</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card stat-hit">
|
||||
<div class="stat-value">{{ ruleStats.hitRate || 0 }}%</div>
|
||||
<div class="stat-label">命中率</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">执行历史</el-divider>
|
||||
<vxe-table :data="executionHistory" :loading="historyLoading" border stripe height="auto" size="small">
|
||||
<vxe-column type="seq" title="序号" width="60" />
|
||||
<vxe-column field="ruleCode" title="规则编码" width="120" />
|
||||
<vxe-column field="encounterId" title="就诊ID" width="100" />
|
||||
<vxe-column field="matched" title="是否命中" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.matched ? 'success' : 'info'" size="small">{{ row.matched ? '命中' : '未命中' }}</el-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="executionResult" title="执行结果" min-width="150" show-overflow />
|
||||
<vxe-column field="durationMs" title="耗时(ms)" width="100" align="center" />
|
||||
<vxe-column field="executionTime" title="执行时间" width="170" />
|
||||
</vxe-table>
|
||||
</div>
|
||||
|
||||
<vxe-table v-else :data="ruleList" :loading="loading" border stripe height="auto">
|
||||
<vxe-column type="seq" title="序号" width="70" />
|
||||
<vxe-column field="ruleCode" title="规则编码" width="120" />
|
||||
<vxe-column field="ruleName" title="规则名称" min-width="180" show-overflow />
|
||||
@@ -41,6 +96,12 @@
|
||||
<el-tag :type="severityTagType(row.severity)" effect="dark">{{ row.severity }}</el-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="priority" title="优先级" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="priorityTagType(row.priority)" size="small">{{ priorityLabel(row.priority) }}</el-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="category" title="分类" width="110" show-overflow />
|
||||
<vxe-column field="conditionExpr" title="条件表达式" min-width="200" show-overflow />
|
||||
<vxe-column field="actionExpr" title="执行动作" min-width="200" show-overflow />
|
||||
<vxe-column field="status" title="状态" width="80" align="center">
|
||||
@@ -55,16 +116,22 @@
|
||||
|
||||
<script setup name="CdssRules">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { getCdssRuleList } from '@/api/cdss/cdssRule'
|
||||
import { getCdssRuleListEnhanced, getCdssRuleStats, getCdssRuleHistory } from '@/api/cdss/cdssRule'
|
||||
|
||||
const ruleList = ref([])
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const showStats = ref(false)
|
||||
const ruleStats = ref({})
|
||||
const executionHistory = ref([])
|
||||
const historyLoading = ref(false)
|
||||
|
||||
const queryParams = reactive({
|
||||
ruleType: '',
|
||||
severity: '',
|
||||
keyword: ''
|
||||
keyword: '',
|
||||
category: '',
|
||||
priority: undefined
|
||||
})
|
||||
|
||||
const severityTagType = (severity) => {
|
||||
@@ -72,6 +139,16 @@ const severityTagType = (severity) => {
|
||||
return map[severity] || 'info'
|
||||
}
|
||||
|
||||
const priorityTagType = (priority) => {
|
||||
const map = { 2: 'danger', 1: 'warning', 0: 'info' }
|
||||
return map[priority] || 'info'
|
||||
}
|
||||
|
||||
const priorityLabel = (priority) => {
|
||||
const map = { 2: '最高', 1: '紧急', 0: '普通' }
|
||||
return map[priority] || '普通'
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -79,7 +156,9 @@ const getList = async () => {
|
||||
if (queryParams.ruleType) params.ruleType = queryParams.ruleType
|
||||
if (queryParams.severity) params.severity = queryParams.severity
|
||||
if (queryParams.keyword) params.keyword = queryParams.keyword
|
||||
const res = await getCdssRuleList(params)
|
||||
if (queryParams.category) params.category = queryParams.category
|
||||
if (queryParams.priority !== undefined && queryParams.priority !== '') params.priority = queryParams.priority
|
||||
const res = await getCdssRuleListEnhanced(params)
|
||||
if (res.code === 200) {
|
||||
ruleList.value = res.data || []
|
||||
}
|
||||
@@ -88,6 +167,29 @@ const getList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const getStats = async () => {
|
||||
try {
|
||||
const res = await getCdssRuleStats()
|
||||
if (res.code === 200) {
|
||||
ruleStats.value = res.data || {}
|
||||
}
|
||||
} catch (e) {
|
||||
ruleStats.value = {}
|
||||
}
|
||||
}
|
||||
|
||||
const getHistory = async () => {
|
||||
historyLoading.value = true
|
||||
try {
|
||||
const res = await getCdssRuleHistory({ page: 1, size: 50 })
|
||||
if (res.code === 200) {
|
||||
executionHistory.value = res.data || []
|
||||
}
|
||||
} finally {
|
||||
historyLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
getList()
|
||||
}
|
||||
@@ -96,10 +198,43 @@ const resetQuery = () => {
|
||||
queryParams.ruleType = ''
|
||||
queryParams.severity = ''
|
||||
queryParams.keyword = ''
|
||||
queryParams.category = ''
|
||||
queryParams.priority = undefined
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const handleToggleStats = () => {
|
||||
showStats.value = !showStats.value
|
||||
if (showStats.value) {
|
||||
getStats()
|
||||
getHistory()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.stats-panel {
|
||||
padding: 16px 0;
|
||||
}
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
}
|
||||
.stat-card .stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.stat-card .stat-label {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.stat-active .stat-value { color: #67c23a; }
|
||||
.stat-exec .stat-value { color: #409eff; }
|
||||
.stat-hit .stat-value { color: #e6a23c; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user