feat: BMS 项目初始化(前端+后端)

- 前端 bms-front:基于 RuoYi-Vue3,封装 BasicForm/BasicTable 公共组件,替换 18 个业务页面表格
- 后端 bms-back:基于 RuoYi-Vue,包含 system/quartz/generator 等模块
- 项目标题由若依管理系统改为 BMS
This commit is contained in:
wuweihua
2026-08-28 11:45:11 +08:00
commit 6081a98bcc
626 changed files with 71650 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="24" class="card-box">
<el-card>
<template #header><Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">基本信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%">
<tbody>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">Redis版本</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">运行模式</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">端口</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">客户端数</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">运行时间(天)</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">使用内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">使用CPU</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">内存配置</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">AOF是否开启</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "否" : "是" }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">RDB是否成功</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">Key数量</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td>
<td class="el-table__cell is-leaf"><div class="cell">网络入口/出口</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<template #header><PieChart style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">命令统计</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="commandstats" style="height: 420px" />
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<template #header><Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="usedmemory" style="height: 420px" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup name="Cache">
import { getCache } from '@/api/monitor/cache'
import * as echarts from 'echarts'
const cache = ref([])
const commandstats = ref(null)
const usedmemory = ref(null)
const { proxy } = getCurrentInstance()
function getList() {
proxy.$modal.loading("正在加载缓存监控数据,请稍候!")
getCache().then(response => {
proxy.$modal.closeLoading()
cache.value = response.data
const commandstatsIntance = echarts.init(commandstats.value, "macarons")
commandstatsIntance.setOption({
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
series: [
{
name: "命令",
type: "pie",
roseType: "radius",
radius: [15, 95],
center: ["50%", "38%"],
data: response.data.commandStats,
animationEasing: "cubicInOut",
animationDuration: 1000
}
]
})
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons")
usedmemoryInstance.setOption({
tooltip: {
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
},
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: cache.value.info.used_memory_human
},
data: [
{
value: parseFloat(cache.value.info.used_memory_human),
name: "内存消耗"
}
]
}
]
})
window.addEventListener("resize", () => {
commandstatsIntance.resize()
usedmemoryInstance.resize()
})
})
}
getList()
</script>
+246
View File
@@ -0,0 +1,246 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="8">
<el-card style="height: calc(100vh - 125px)">
<template #header>
<Collection style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">缓存列表</span>
<el-button
style="float: right; padding: 3px 0"
link
type="primary"
icon="Refresh"
@click="refreshCacheNames()"
></el-button>
</template>
<el-table
v-loading="loading"
:data="cacheNames"
:height="tableHeight"
highlight-current-row
@row-click="getCacheKeys"
style="width: 100%"
>
<el-table-column
label="序号"
width="60"
type="index"
></el-table-column>
<el-table-column
label="缓存名称"
align="center"
prop="cacheName"
:show-overflow-tooltip="true"
:formatter="nameFormatter"
></el-table-column>
<el-table-column
label="备注"
align="center"
prop="remark"
:show-overflow-tooltip="true"
/>
<el-table-column
label="操作"
width="60"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Delete"
@click="handleClearCacheName(scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<el-col :span="8">
<el-card style="height: calc(100vh - 125px)">
<template #header>
<Key style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">键名列表</span>
<el-button
style="float: right; padding: 3px 0"
link
type="primary"
icon="Refresh"
@click="refreshCacheKeys()"
></el-button>
</template>
<el-table
v-loading="subLoading"
:data="cacheKeys"
:height="tableHeight"
highlight-current-row
@row-click="handleCacheValue"
style="width: 100%"
>
<el-table-column
label="序号"
width="60"
type="index"
></el-table-column>
<el-table-column
label="缓存键名"
align="center"
:show-overflow-tooltip="true"
:formatter="keyFormatter"
>
</el-table-column>
<el-table-column
label="操作"
width="60"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Delete"
@click="handleClearCacheKey(scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<el-col :span="8">
<el-card :bordered="false" style="height: calc(100vh - 125px)">
<template #header>
<Document style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">缓存内容</span>
<el-button
style="float: right; padding: 3px 0"
link
type="primary"
icon="Refresh"
@click="handleClearCacheAll()"
>清理全部</el-button
>
</template>
<el-form :model="cacheForm">
<el-row :gutter="32">
<el-col :offset="1" :span="22">
<el-form-item label="缓存名称:" prop="cacheName">
<el-input v-model="cacheForm.cacheName" :readOnly="true" />
</el-form-item>
</el-col>
<el-col :offset="1" :span="22">
<el-form-item label="缓存键名:" prop="cacheKey">
<el-input v-model="cacheForm.cacheKey" :readOnly="true" />
</el-form-item>
</el-col>
<el-col :offset="1" :span="22">
<el-form-item label="缓存内容:" prop="cacheValue">
<el-input
v-model="cacheForm.cacheValue"
type="textarea"
:rows="8"
:readOnly="true"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup name="CacheList">
import { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll } from "@/api/monitor/cache"
const { proxy } = getCurrentInstance()
const cacheNames = ref([])
const cacheKeys = ref([])
const cacheForm = ref({})
const loading = ref(true)
const subLoading = ref(false)
const nowCacheName = ref("")
const tableHeight = ref(window.innerHeight - 200)
/** 查询缓存名称列表 */
function getCacheNames() {
loading.value = true
listCacheName().then(response => {
cacheNames.value = response.data
loading.value = false
})
}
/** 刷新缓存名称列表 */
function refreshCacheNames() {
getCacheNames()
proxy.$modal.msgSuccess("刷新缓存列表成功")
}
/** 清理指定名称缓存 */
function handleClearCacheName(row) {
clearCacheName(row.cacheName).then(response => {
proxy.$modal.msgSuccess("清理缓存名称[" + row.cacheName + "]成功")
getCacheKeys()
})
}
/** 查询缓存键名列表 */
function getCacheKeys(row) {
const cacheName = row !== undefined ? row.cacheName : nowCacheName.value
if (cacheName === "") {
return
}
subLoading.value = true
listCacheKey(cacheName).then(response => {
cacheKeys.value = response.data
subLoading.value = false
nowCacheName.value = cacheName
})
}
/** 刷新缓存键名列表 */
function refreshCacheKeys() {
getCacheKeys()
proxy.$modal.msgSuccess("刷新键名列表成功")
}
/** 清理指定键名缓存 */
function handleClearCacheKey(cacheKey) {
clearCacheKey(cacheKey).then(response => {
proxy.$modal.msgSuccess("清理缓存键名[" + cacheKey + "]成功")
getCacheKeys()
})
}
/** 列表前缀去除 */
function nameFormatter(row) {
return row.cacheName.replace(":", "")
}
/** 键名前缀去除 */
function keyFormatter(cacheKey) {
return cacheKey.replace(nowCacheName.value, "")
}
/** 查询缓存内容详细 */
function handleCacheValue(cacheKey) {
getCacheValue(nowCacheName.value, cacheKey).then(response => {
cacheForm.value = response.data
})
}
/** 清理全部缓存 */
function handleClearCacheAll() {
clearCacheAll().then(response => {
proxy.$modal.msgSuccess("清理全部缓存成功")
})
}
getCacheNames()
</script>
@@ -0,0 +1,13 @@
<template>
<div>
<i-frame v-model:src="url"></i-frame>
</div>
</template>
<script setup>
import iFrame from '@/components/iFrame'
import { ref } from 'vue'
const url = ref(import.meta.env.VITE_APP_BASE_API + '/druid/login.html')
</script>
+217
View File
@@ -0,0 +1,217 @@
<template>
<el-dialog :title="type === 'log' ? '调度日志详细' : '任务详细'" v-model="dialogVisible" width="780px" append-to-body>
<div class="detail-wrap">
<template v-if="type === 'log'">
<!-- 基本信息 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><InfoFilled /></el-icon> 基本信息
</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">日志编号</span><span class="detail-value">{{ form.jobLogId }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">执行状态</span>
<el-tag v-if="form.status == 0" type="success" size="small">正常</el-tag>
<el-tag v-else type="danger" size="small">失败</el-tag>
</div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">开始时间</span><span class="detail-value">{{ form.startTime }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item"><span class="detail-label">结束时间</span><span class="detail-value">{{ form.endTime }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">记录时间</span><span class="detail-value">{{ form.createTime }}</span></div>
</el-col>
<el-col :span="12" v-if="form.status == 0 && form.startTime && form.endTime">
<div class="detail-item"><span class="detail-label">执行耗时</span><span class="detail-value">{{ costTime }} 毫秒</span></div>
</el-col>
</el-row>
</div>
<!-- 任务信息 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><Clock /></el-icon> 任务信息
</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">任务名称</span><span class="detail-value">{{ form.jobName }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">任务分组</span>
<dict-tag :options="sys_job_group" :value="form.jobGroup" />
</div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="24">
<div class="detail-item"><span class="detail-label">日志信息</span><span class="detail-value">{{ form.jobMessage }}</span></div>
</el-col>
</el-row>
</div>
<!-- 调用目标 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><Operation /></el-icon> 调用目标
</div>
<div class="code-body">
<div class="code-wrap"><pre class="code-pre">{{ form.invokeTarget || '(无)' }}</pre></div>
</div>
</div>
<!-- 异常信息 -->
<div class="detail-card" v-if="form.status == 1">
<div class="detail-card-title error-title">
<el-icon><Warning /></el-icon> 异常信息
</div>
<div class="error-body"><div class="error-msg">{{ form.exceptionInfo }}</div></div>
</div>
</template>
<template v-else>
<!-- 任务配置 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><Setting /></el-icon> 任务配置
</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">任务编号</span><span class="detail-value">{{ form.jobId }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item"><span class="detail-label">任务名称</span><span class="detail-value">{{ form.jobName }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">任务分组</span>
<dict-tag :options="sys_job_group" :value="form.jobGroup" />
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">执行状态</span>
<el-tag v-if="form.status == 0" type="success" size="small">正常</el-tag>
<el-tag v-else type="info" size="small">暂停</el-tag>
</div>
</el-col>
</el-row>
</div>
<!-- 调度信息 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><Calendar /></el-icon> 调度信息
</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">cron 表达式</span><span class="detail-value mono">{{ form.cronExpression }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item"><span class="detail-label">下次执行时间</span><span class="detail-value">{{ parseTime(form.nextValidTime) }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">执行策略</span>
<el-tag v-if="form.misfirePolicy == 0" type="info" size="small">默认策略</el-tag>
<el-tag v-else-if="form.misfirePolicy == 1" type="warning" size="small">立即执行</el-tag>
<el-tag v-else-if="form.misfirePolicy == 2" type="primary" size="small">执行一次</el-tag>
<el-tag v-else-if="form.misfirePolicy == 3" type="danger" size="small">放弃执行</el-tag>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">并发执行</span>
<el-tag v-if="form.concurrent == 0" type="success" size="small">允许</el-tag>
<el-tag v-else type="danger" size="small">禁止</el-tag>
</div>
</el-col>
</el-row>
</div>
<!-- 执行方法 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><Operation /></el-icon> 执行方法
</div>
<div class="code-body">
<div class="code-wrap"><pre class="code-pre">{{ form.invokeTarget || '(无)' }}</pre></div>
</div>
</div>
<!-- 元信息 -->
<div class="detail-card">
<div class="detail-card-title">
<el-icon><Document /></el-icon> 元信息
</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">创建人</span><span class="detail-value">{{ form.createBy || '-' }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item"><span class="detail-label">创建时间</span><span class="detail-value">{{ form.createTime }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">更新人</span><span class="detail-value">{{ form.updateBy || '-' }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item"><span class="detail-label">更新时间</span><span class="detail-value">{{ form.updateTime || '-' }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row" v-if="form.remark">
<el-col :span="24">
<div class="detail-item"><span class="detail-label">备注</span><span class="detail-value">{{ form.remark }}</span></div>
</el-col>
</el-row>
</div>
</template>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="JobDetail">
const props = defineProps({
visible: { type: Boolean, default: false },
row: { type: Object, default: () => ({}) },
// 'job' 任务详细 | 'log' 调度日志详细
type: { type: String, default: 'job' }
})
const emit = defineEmits(['update:visible'])
const dialogVisible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
})
const { proxy } = getCurrentInstance()
const { sys_job_group } = useDict('sys_job_group')
const form = computed(() => props.row || {})
const costTime = computed(() => {
if (!form.value.startTime || !form.value.endTime) return 0
return new Date(form.value.endTime).getTime() - new Date(form.value.startTime).getTime()
})
</script>
<style scoped>
.detail-label {
width: 80px;
}
</style>
+361
View File
@@ -0,0 +1,361 @@
<template>
<div class="app-container">
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="jobId"
>
<template #toolbar="{ selection, ids }">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['monitor:job:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="selection.length !== 1"
@click="handleUpdate(selection[0])"
v-hasPermi="['monitor:job:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="!selection.length"
@click="handleDelete(ids)"
v-hasPermi="['monitor:job:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['monitor:job:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Operation"
@click="handleJobLog()"
v-hasPermi="['monitor:job:query']"
>日志</el-button>
</el-col>
</template>
<template #col-jobName="{ row }">
<a class="link-type" style="cursor:pointer" @click="handleView(row)">{{ row.jobName }}</a>
</template>
<template #col-status="{ row }">
<el-switch
v-model="row.status"
active-value="0"
inactive-value="1"
@change="handleStatusChange(row)"
></el-switch>
</template>
<template #col-action="{ row }">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['monitor:job:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(row.jobId)" v-hasPermi="['monitor:job:remove']"></el-button>
</el-tooltip>
<el-tooltip content="执行一次" placement="top">
<el-button link type="primary" icon="CaretRight" @click="handleRun(row)" v-hasPermi="['monitor:job:changeStatus']"></el-button>
</el-tooltip>
<el-tooltip content="调度日志" placement="top">
<el-button link type="primary" icon="Operation" @click="handleJobLog(row)" v-hasPermi="['monitor:job:query']"></el-button>
</el-tooltip>
</template>
</basic-table>
<!-- 添加或修改定时任务对话框 -->
<el-dialog :title="title" v-model="open" width="820px" append-to-body>
<el-form ref="jobRef" :model="form" :rules="rules" label-width="120px">
<el-row>
<el-col :span="12">
<el-form-item label="任务名称" prop="jobName">
<el-input v-model="form.jobName" placeholder="请输入任务名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务分组" prop="jobGroup">
<el-select v-model="form.jobGroup" placeholder="请选择">
<el-option
v-for="dict in sys_job_group"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item prop="invokeTarget">
<template #label>
<span>
调用方法
<el-tooltip placement="top">
<template #content>
<div>
Bean调用示例:ryTask.ryParams('ry')
<br />Class类调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry')
<br />参数说明:支持字符串,布尔类型,长整型,浮点型,整型
</div>
</template>
<el-icon><question-filled /></el-icon>
</el-tooltip>
</span>
</template>
<el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="cron表达式" prop="cronExpression">
<el-input v-model="form.cronExpression" placeholder="请输入cron执行表达式">
<template #append>
<el-button type="primary" @click="handleShowCron">
生成表达式
<i class="el-icon-time el-icon--right"></i>
</el-button>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="24" v-if="form.jobId !== undefined">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_job_status"
:key="dict.value"
:value="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行策略" prop="misfirePolicy">
<el-radio-group v-model="form.misfirePolicy">
<el-radio-button value="1">立即执行</el-radio-button>
<el-radio-button value="2">执行一次</el-radio-button>
<el-radio-button value="3">放弃执行</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否并发" prop="concurrent">
<el-radio-group v-model="form.concurrent">
<el-radio-button value="0">允许</el-radio-button>
<el-radio-button value="1">禁止</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
<el-dialog title="Cron表达式生成器" v-model="openCron" append-to-body destroy-on-close>
<crontab ref="crontabRef" @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab>
</el-dialog>
<!-- 任务详细 -->
<job-detail v-model:visible="openView" :row="form" type="job" />
</div>
</template>
<script setup name="Job">
import Crontab from '@/components/Crontab'
import JobDetail from './detail'
import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job"
const router = useRouter()
const { proxy } = getCurrentInstance()
const { sys_job_group, sys_job_status } = useDict("sys_job_group", "sys_job_status")
const tableRef = ref()
const open = ref(false)
const title = ref("")
const openView = ref(false)
const openCron = ref(false)
const expression = ref("")
// 搜索项配置
const searchColumns = [
{ label: "任务名称", prop: "jobName", component: "input", placeholder: "请输入任务名称", width: "200px" },
{ label: "任务组名", prop: "jobGroup", component: "select", dict: "sys_job_group", placeholder: "请选择任务组名", width: "200px" },
{ label: "任务状态", prop: "status", component: "select", dict: "sys_job_status", placeholder: "请选择任务状态", width: "200px" }
]
// 表格列配置
const columns = [
{ label: "任务编号", prop: "jobId", align: "center", width: 100 },
{ label: "任务名称", prop: "jobName", align: "center" },
{ label: "任务组名", prop: "jobGroup", align: "center", dict: "sys_job_group" },
{ label: "调用目标字符串", prop: "invokeTarget", align: "center" },
{ label: "cron执行表达式", prop: "cronExpression", align: "center" },
{ label: "状态", prop: "status", align: "center" },
{ label: "操作", prop: "action", align: "center", width: 200, showInToolbar: false }
]
// 查询函数
function queryList(params) {
return listJob(params)
}
const data = reactive({
form: {},
rules: {
jobName: [{ required: true, message: "任务名称不能为空", trigger: "blur" }],
invokeTarget: [{ required: true, message: "调用目标字符串不能为空", trigger: "blur" }],
cronExpression: [{ required: true, message: "cron执行表达式不能为空", trigger: "change" }]
}
})
const { form, rules } = toRefs(data)
/** 取消按钮 */
function cancel() {
open.value = false
reset()
}
/** 表单重置 */
function reset() {
form.value = {
jobId: undefined,
jobName: undefined,
jobGroup: undefined,
invokeTarget: undefined,
cronExpression: undefined,
misfirePolicy: '1',
concurrent: '1',
status: "0"
}
proxy.resetForm("jobRef")
}
/** 新增按钮操作 */
function handleAdd() {
reset()
open.value = true
title.value = "添加任务"
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset()
const jobId = row.jobId
getJob(jobId).then(response => {
form.value = response.data
open.value = true
title.value = "修改任务"
})
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["jobRef"].validate(valid => {
if (valid) {
if (form.value.jobId != undefined) {
updateJob(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
tableRef.value.refresh()
})
} else {
addJob(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
tableRef.value.refresh()
})
}
}
})
}
/** 删除按钮操作 */
function handleDelete(jobIds) {
proxy.$modal.confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function () {
return delJob(jobIds)
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => {})
}
// 任务状态修改
function handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用"
proxy.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function () {
return changeJobStatus(row.jobId, row.status)
}).then(() => {
proxy.$modal.msgSuccess(text + "成功")
}).catch(function () {
row.status = row.status === "0" ? "1" : "0"
})
}
/* 立即执行一次 */
function handleRun(row) {
proxy.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function () {
return runJob(row.jobId, row.jobGroup)
}).then(() => {
proxy.$modal.msgSuccess("执行成功")
}).catch(() => {})
}
/** 任务详细信息 */
function handleView(row) {
getJob(row.jobId).then(response => {
form.value = response.data
openView.value = true
})
}
/** cron表达式按钮操作 */
function handleShowCron() {
expression.value = form.value.cronExpression
openCron.value = true
}
/** 确定后回传值 */
function crontabFill(value) {
form.value.cronExpression = value
}
/** 任务日志列表查询 */
function handleJobLog(row) {
const jobId = row?.jobId || 0
router.push('/monitor/job-log/index/' + jobId)
}
/** 导出按钮操作 */
function handleExport() {
proxy.download("monitor/job/export", {
...tableRef.value.getQueryParams()
}, `job_${new Date().getTime()}.xlsx`)
}
</script>
+158
View File
@@ -0,0 +1,158 @@
<template>
<div class="app-container">
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="jobLogId"
:auto-load="false"
>
<template #toolbar="{ selection, ids }">
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="!selection.length"
@click="handleDelete(ids)"
v-hasPermi="['monitor:job:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
@click="handleClean"
v-hasPermi="['monitor:job:remove']"
>清空</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['monitor:job:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Close"
@click="handleClose"
>关闭</el-button>
</el-col>
</template>
<template #col-action="{ row }">
<el-button link type="primary" icon="View" @click="handleView(row)" v-hasPermi="['monitor:job:query']">详细</el-button>
</template>
</basic-table>
<!-- 调度日志详细 -->
<job-detail v-model:visible="open" :row="form" type="log" />
</div>
</template>
<script setup name="JobLog">
import JobDetail from './detail'
import { getJob } from "@/api/monitor/job"
import { listJobLog, delJobLog, cleanJobLog } from "@/api/monitor/jobLog"
const { proxy } = getCurrentInstance()
const { sys_common_status, sys_job_group } = useDict("sys_common_status", "sys_job_group")
const route = useRoute()
const tableRef = ref()
const open = ref(false)
const data = reactive({
form: {}
})
const { form } = toRefs(data)
// 搜索项配置
const searchColumns = [
{ label: "任务名称", prop: "jobName", component: "input", placeholder: "请输入任务名称", width: "240px" },
{ label: "任务组名", prop: "jobGroup", component: "select", dict: "sys_job_group", placeholder: "请选择任务组名", width: "240px" },
{ label: "执行状态", prop: "status", component: "select", dict: "sys_common_status", placeholder: "请选择执行状态", width: "240px" },
{ label: "执行时间", prop: "dateRange", component: "daterange", width: "308px" }
]
// 表格列配置
const columns = [
{ label: "日志编号", prop: "jobLogId", align: "center", width: 80 },
{ label: "任务名称", prop: "jobName", align: "center" },
{ label: "任务组名", prop: "jobGroup", align: "center", dict: "sys_job_group" },
{ label: "调用目标字符串", prop: "invokeTarget", align: "center" },
{ label: "日志信息", prop: "jobMessage", align: "center" },
{ label: "执行状态", prop: "status", align: "center", dict: "sys_common_status" },
{ label: "执行时间", prop: "createTime", align: "center", width: 180, parseTime: true },
{ label: "操作", prop: "action", align: "center", showInToolbar: false }
]
// 查询函数
function queryList(params) {
const p = proxy.addDateRange({ ...params }, params.dateRange)
delete p.dateRange
return listJobLog(p)
}
// 返回按钮
function handleClose() {
const obj = { path: "/monitor/job" }
proxy.$tab.closeOpenPage(obj)
}
/** 详细按钮操作 */
function handleView(row) {
open.value = true
form.value = row
}
/** 删除按钮操作 */
function handleDelete(jobIds) {
proxy.$modal.confirm('是否确认删除调度日志编号为"' + jobIds + '"的数据项?').then(function () {
return delJobLog(jobIds)
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => {})
}
/** 清空按钮操作 */
function handleClean() {
proxy.$modal.confirm("是否确认清空所有调度日志数据项?").then(function () {
return cleanJobLog()
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("清空成功")
}).catch(() => {})
}
/** 导出按钮操作 */
function handleExport() {
proxy.download("monitor/jobLog/export", {
...tableRef.value.getQueryParams()
}, `job_log_${new Date().getTime()}.xlsx`)
}
// 初始化:从路由参数接收 jobId,异步获取任务名称/组名后加载列表
onMounted(() => {
const jobId = route.params?.jobId || route.query?.jobId
if (jobId !== undefined && jobId != 0) {
getJob(jobId).then(response => {
tableRef.value.setQueryParams({
jobName: response.data.jobName,
jobGroup: response.data.jobGroup
})
tableRef.value.reload()
})
} else {
tableRef.value.reload()
}
})
</script>
@@ -0,0 +1,144 @@
<template>
<div class="app-container">
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="infoId"
:default-sort="defaultSort"
:default-params="defaultParams"
@sort-change="handleSortChange"
>
<template #toolbar="{ selection, ids }">
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="!selection.length"
@click="handleDelete(ids)"
v-hasPermi="['monitor:logininfor:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
@click="handleClean"
v-hasPermi="['monitor:logininfor:remove']"
>清空</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Unlock"
:disabled="selection.length !== 1"
@click="handleUnlock(selection)"
v-hasPermi="['monitor:logininfor:unlock']"
>解锁</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['monitor:logininfor:export']"
>导出</el-button>
</el-col>
</template>
</basic-table>
</div>
</template>
<script setup name="Logininfor">
import { list, delLogininfor, cleanLogininfor, unlockLogininfor } from "@/api/monitor/logininfor"
const { proxy } = getCurrentInstance()
const { sys_common_status } = useDict("sys_common_status")
const tableRef = ref()
// 默认排序
const defaultSort = { prop: "loginTime", order: "descending" }
// 默认查询参数(含排序初始值,重置时由 BasicTable 自动恢复)
const defaultParams = {
orderByColumn: "loginTime",
isAsc: "descending"
}
// 搜索项配置
const searchColumns = [
{ label: "登录地址", prop: "ipaddr", component: "input", placeholder: "请输入登录地址", width: "240px" },
{ label: "用户名称", prop: "userName", component: "input", placeholder: "请输入用户名称", width: "240px" },
{ label: "状态", prop: "status", component: "select", dict: "sys_common_status", placeholder: "登录状态", width: "240px" },
{ label: "登录时间", prop: "dateRange", component: "daterange", width: "308px" }
]
// 表格列配置
const columns = [
{ label: "访问编号", prop: "infoId", align: "center" },
{ label: "用户名称", prop: "userName", align: "center", sortable: "custom" },
{ label: "地址", prop: "ipaddr", align: "center" },
{ label: "登录地点", prop: "loginLocation", align: "center" },
{ label: "操作系统", prop: "os", align: "center" },
{ label: "浏览器", prop: "browser", align: "center" },
{ label: "登录状态", prop: "status", align: "center", dict: "sys_common_status" },
{ label: "描述", prop: "msg", align: "center" },
{ label: "访问时间", prop: "loginTime", align: "center", width: 180, sortable: "custom", parseTime: true }
]
// 查询函数
function queryList(params) {
const p = proxy.addDateRange({ ...params }, params.dateRange)
delete p.dateRange
return list(p)
}
/** 排序触发事件 */
function handleSortChange(column) {
tableRef.value.setQueryParams({ orderByColumn: column.prop, isAsc: column.order })
tableRef.value.reload()
}
/** 删除按钮操作 */
function handleDelete(infoIds) {
proxy.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?').then(function () {
return delLogininfor(infoIds)
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => {})
}
/** 清空按钮操作 */
function handleClean() {
proxy.$modal.confirm("是否确认清空所有登录日志数据项?").then(function () {
return cleanLogininfor()
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("清空成功")
}).catch(() => {})
}
/** 解锁按钮操作 */
function handleUnlock(selection) {
const username = selection.map(item => item.userName)
proxy.$modal.confirm('是否确认解锁用户"' + username + '"数据项?').then(function () {
return unlockLogininfor(username)
}).then(() => {
proxy.$modal.msgSuccess("用户" + username + "解锁成功")
}).catch(() => {})
}
/** 导出按钮操作 */
function handleExport() {
proxy.download("monitor/logininfor/export", {
...tableRef.value.getQueryParams()
}, `logininfor_${new Date().getTime()}.xlsx`)
}
</script>
@@ -0,0 +1,64 @@
<template>
<div class="app-container">
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
:selection="false"
:show-toolbar="false"
show-index
>
<template #col-action="{ row }">
<el-button link type="primary" icon="Delete" @click="handleForceLogout(row)" v-hasPermi="['monitor:online:forceLogout']">强退</el-button>
</template>
</basic-table>
</div>
</template>
<script setup name="Online">
import { forceLogout, list as initData } from "@/api/monitor/online"
const { proxy } = getCurrentInstance()
const tableRef = ref()
// 搜索项配置
const searchColumns = [
{ label: "登录地址", prop: "ipaddr", component: "input", placeholder: "请输入登录地址", width: "200px" },
{ label: "用户名称", prop: "userName", component: "input", placeholder: "请输入用户名称", width: "200px" }
]
// 表格列配置
const columns = [
{ label: "会话编号", prop: "tokenId", align: "center" },
{ label: "登录名称", prop: "userName", align: "center" },
{ label: "所属部门", prop: "deptName", align: "center" },
{ label: "主机", prop: "ipaddr", align: "center" },
{ label: "登录地点", prop: "loginLocation", align: "center" },
{ label: "操作系统", prop: "os", align: "center" },
{ label: "浏览器", prop: "browser", align: "center" },
{ label: "登录时间", prop: "loginTime", align: "center", width: 180, parseTime: true },
{ label: "操作", prop: "action", align: "center", showInToolbar: false }
]
// 查询函数(在线用户接口返回全量数据,前端分页切片)
function queryList(params) {
return initData({ ipaddr: params.ipaddr, userName: params.userName }).then(res => {
const start = (params.pageNum - 1) * params.pageSize
return {
rows: res.rows.slice(start, start + params.pageSize),
total: res.total
}
})
}
/** 强退按钮操作 */
function handleForceLogout(row) {
proxy.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?').then(function () {
return forceLogout(row.tokenId)
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => {})
}
</script>
@@ -0,0 +1,152 @@
<template>
<el-dialog title="操作日志详细" v-model="dialogVisible" width="780px" append-to-body @close="$emit('update:visible', false)">
<div class="detail-wrap">
<!-- 基本信息 -->
<div class="detail-card">
<div class="detail-card-title"><el-icon><InfoFilled /></el-icon> 基本信息</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">操作模块</span><span class="detail-value">{{ form.title }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item"><span class="detail-label">业务类型</span><span class="detail-value">{{ typeLabel }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">操作时间</span><span class="detail-value">{{ form.operTime }}</span></div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">执行状态</span>
<el-tag v-if="form.status === 0" type="success" size="small">正常</el-tag>
<el-tag v-else type="danger" size="small">异常</el-tag>
</div>
</el-col>
</el-row>
</div>
<!-- 操作人员 -->
<div class="detail-card">
<div class="detail-card-title"><el-icon><User /></el-icon> 操作人员</div>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">操作人员</span><span class="detail-value">{{ form.operName }}</span></div>
</el-col>
<el-col :span="12" v-if="form.deptName">
<div class="detail-item"><span class="detail-label">所属部门</span><span class="detail-value">{{ form.deptName }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="24">
<div class="detail-item">
<span class="detail-label">操作地址</span>
<span class="detail-value">{{ form.operIp }}&nbsp;&nbsp;<span class="detail-location">{{ form.operLocation }}</span></span>
</div>
</el-col>
</el-row>
</div>
<!-- 请求信息 -->
<div class="detail-card">
<div class="detail-card-title"><el-icon><Sort /></el-icon> 请求信息</div>
<el-row class="detail-row">
<el-col :span="24">
<div class="detail-item">
<span class="detail-label">请求地址</span>
<span class="detail-value">
<span :class="'method-tag method-' + form.requestMethod">{{ form.requestMethod }}</span>
{{ form.operUrl }}
</span>
</div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="24">
<div class="detail-item"><span class="detail-label">操作方法</span><span class="detail-value mono">{{ form.method }}</span></div>
</el-col>
</el-row>
<el-row class="detail-row">
<el-col :span="12">
<div class="detail-item"><span class="detail-label">消耗时间</span><span class="detail-value">{{ form.costTime }} 毫秒</span></div>
</el-col>
</el-row>
</div>
<!-- 请求参数 -->
<div class="detail-card">
<div class="detail-card-title"><el-icon><Upload /></el-icon> 请求参数</div>
<div class="code-body">
<div class="code-wrap">
<div class="code-action">
<el-button size="small" :icon="CopyDocument" @click="copyText(form.operParam)">复制</el-button>
</div>
<pre class="code-pre">{{ formatJson(form.operParam) }}</pre>
</div>
</div>
</div>
<!-- 返回参数 -->
<div class="detail-card">
<div class="detail-card-title"><el-icon><Download /></el-icon> 返回参数</div>
<div class="code-body">
<div class="code-wrap">
<div class="code-action">
<el-button size="small" :icon="CopyDocument" @click="copyText(form.jsonResult)">复制</el-button>
</div>
<pre class="code-pre">{{ formatJson(form.jsonResult) }}</pre>
</div>
</div>
</div>
<!-- 异常信息 -->
<div class="detail-card" v-if="form.status !== 0">
<div class="detail-card-title error-title"><el-icon><Warning /></el-icon> 异常信息</div>
<div class="error-body">
<div class="error-msg">{{ form.errorMsg }}</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup>
const props = defineProps({
visible: { type: Boolean, default: false },
row: { type: Object, default: () => ({}) }
})
const emit = defineEmits(['update:visible'])
const dialogVisible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
})
const { sys_oper_type } = useDict('sys_oper_type')
const form = computed(() => props.row || {})
const typeLabel = computed(() => selectDictLabel(sys_oper_type.value, form.value.businessType) || '-')
function formatJson(str) {
if (!str) return '(无数据)'
try { return JSON.stringify(JSON.parse(str), null, 2) } catch { return str }
}
function copyText(str) {
const text = formatJson(str)
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(() => ElMessage({ message: '已复制', type: 'success', duration: 1500 }))
} else {
const ta = document.createElement('textarea')
ta.value = text
document.body.appendChild(ta)
ta.select()
document.execCommand('copy')
document.body.removeChild(ta)
ElMessage({ message: '已复制', type: 'success', duration: 1500 })
}
}
</script>
@@ -0,0 +1,141 @@
<template>
<div class="app-container">
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="operId"
:default-sort="defaultSort"
:default-params="defaultParams"
@sort-change="handleSortChange"
>
<template #toolbar="{ selection, ids }">
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="!selection.length"
@click="handleDelete(ids)"
v-hasPermi="['monitor:operlog:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
@click="handleClean"
v-hasPermi="['monitor:operlog:remove']"
>清空</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['monitor:operlog:export']"
>导出</el-button>
</el-col>
</template>
<template #col-action="{ row }">
<el-button link type="primary" icon="View" @click="handleDetail(row)" v-hasPermi="['monitor:operlog:query']">详细</el-button>
</template>
</basic-table>
<operlog-detail v-model:visible="detailVisible" :row="detailRow" />
</div>
</template>
<script setup name="Operlog">
import OperlogDetail from './detail'
import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog"
const { proxy } = getCurrentInstance()
const { sys_oper_type, sys_common_status } = useDict("sys_oper_type", "sys_common_status")
const tableRef = ref()
const detailVisible = ref(false)
const detailRow = ref({})
// 默认排序
const defaultSort = { prop: "operTime", order: "descending" }
// 默认查询参数(含排序初始值,重置时由 BasicTable 自动恢复)
const defaultParams = {
orderByColumn: "operTime",
isAsc: "descending"
}
// 搜索项配置
const searchColumns = [
{ label: "操作地址", prop: "operIp", component: "input", placeholder: "请输入操作地址", width: "240px" },
{ label: "系统模块", prop: "title", component: "input", placeholder: "请输入系统模块", width: "240px" },
{ label: "操作人员", prop: "operName", component: "input", placeholder: "请输入操作人员", width: "240px" },
{ label: "类型", prop: "businessType", component: "select", dict: "sys_oper_type", placeholder: "操作类型", width: "240px" },
{ label: "状态", prop: "status", component: "select", dict: "sys_common_status", placeholder: "操作状态", width: "240px" },
{ label: "操作时间", prop: "dateRange", component: "daterange", width: "308px" }
]
// 表格列配置
const columns = [
{ label: "日志编号", prop: "operId", align: "center" },
{ label: "系统模块", prop: "title", align: "center" },
{ label: "操作类型", prop: "businessType", align: "center", dict: "sys_oper_type" },
{ label: "操作人员", prop: "operName", align: "center", width: 110, sortable: "custom" },
{ label: "操作地址", prop: "operIp", align: "center", width: 130 },
{ label: "操作状态", prop: "status", align: "center", dict: "sys_common_status" },
{ label: "操作日期", prop: "operTime", align: "center", width: 180, sortable: "custom", parseTime: true },
{ label: "消耗时间", prop: "costTime", align: "center", width: 110, sortable: "custom", formatter: (row, col, val) => val + '毫秒' },
{ label: "操作", prop: "action", align: "center", showInToolbar: false }
]
// 查询函数
function queryList(params) {
const p = proxy.addDateRange({ ...params }, params.dateRange)
delete p.dateRange
return list(p)
}
/** 排序触发事件 */
function handleSortChange(column) {
tableRef.value.setQueryParams({ orderByColumn: column.prop, isAsc: column.order })
tableRef.value.reload()
}
/** 详细按钮操作 */
function handleDetail(row) {
detailRow.value = row
detailVisible.value = true
}
/** 删除按钮操作 */
function handleDelete(operIds) {
proxy.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?').then(function () {
return delOperlog(operIds)
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => {})
}
/** 清空按钮操作 */
function handleClean() {
proxy.$modal.confirm("是否确认清空所有操作日志数据项?").then(function () {
return cleanOperlog()
}).then(() => {
tableRef.value.refresh()
proxy.$modal.msgSuccess("清空成功")
}).catch(() => {})
}
/** 导出按钮操作 */
function handleExport() {
proxy.download("monitor/operlog/export", {
...tableRef.value.getQueryParams()
}, `config_${new Date().getTime()}.xlsx`)
}
</script>
@@ -0,0 +1,187 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="12" class="card-box">
<el-card>
<template #header><Cpu style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">CPU</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<tr>
<th class="el-table__cell is-leaf"><div class="cell">属性</div></th>
<th class="el-table__cell is-leaf"><div class="cell">值</div></th>
</tr>
</thead>
<tbody>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">核心数</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.cpuNum }}</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">用户使用率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.used }}%</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">系统使用率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.sys }}%</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">当前空闲率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.cpu">{{ server.cpu.free }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<template #header><Tickets style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<tr>
<th class="el-table__cell is-leaf"><div class="cell">属性</div></th>
<th class="el-table__cell is-leaf"><div class="cell">内存</div></th>
<th class="el-table__cell is-leaf"><div class="cell">JVM</div></th>
</tr>
</thead>
<tbody>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">总内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.total }}G</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.total }}M</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">已用内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.used}}G</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.used}}M</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">剩余内存</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem">{{ server.mem.free }}G</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.free }}M</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">使用率</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.mem" :class="{'text-danger': server.mem.usage > 80}">{{ server.mem.usage }}%</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<template #header><Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">服务器信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">服务器名称</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">操作系统</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">服务器IP</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.computerIp }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">系统架构</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.osArch }}</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<template #header><CoffeeCup style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">Java虚拟机信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;table-layout:fixed;">
<tbody>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">Java名称</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.name }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">Java版本</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td>
</tr>
<tr>
<td class="el-table__cell is-leaf"><div class="cell">启动时间</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.startTime }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">运行时长</div></td>
<td class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td>
</tr>
<tr>
<td colspan="1" class="el-table__cell is-leaf"><div class="cell">安装路径</div></td>
<td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.home }}</div></td>
</tr>
<tr>
<td colspan="1" class="el-table__cell is-leaf"><div class="cell">项目路径</div></td>
<td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.sys">{{ server.sys.userDir }}</div></td>
</tr>
<tr>
<td colspan="1" class="el-table__cell is-leaf"><div class="cell">运行参数</div></td>
<td colspan="3" class="el-table__cell is-leaf"><div class="cell" v-if="server.jvm">{{ server.jvm.inputArgs }}</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :span="24" class="card-box">
<el-card>
<template #header><MessageBox style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">磁盘状态</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
<tr>
<th class="el-table__cell el-table__cell is-leaf"><div class="cell">盘符路径</div></th>
<th class="el-table__cell is-leaf"><div class="cell">文件系统</div></th>
<th class="el-table__cell is-leaf"><div class="cell">盘符类型</div></th>
<th class="el-table__cell is-leaf"><div class="cell">总大小</div></th>
<th class="el-table__cell is-leaf"><div class="cell">可用大小</div></th>
<th class="el-table__cell is-leaf"><div class="cell">已用大小</div></th>
<th class="el-table__cell is-leaf"><div class="cell">已用百分比</div></th>
</tr>
</thead>
<tbody v-if="server.sysFiles">
<tr v-for="(sysFile, index) in server.sysFiles" :key="index">
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.dirName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.sysTypeName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.typeName }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.total }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.free }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell">{{ sysFile.used }}</div></td>
<td class="el-table__cell is-leaf"><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { getServer } from '@/api/monitor/server'
const server = ref([])
const { proxy } = getCurrentInstance()
function getList() {
proxy.$modal.loading("正在加载服务监控数据,请稍候!")
getServer().then(response => {
server.value = response.data
proxy.$modal.closeLoading()
})
}
getList()
</script>