|
|
@@ -35,12 +35,9 @@
|
|
|
<el-col :span="1.5">
|
|
|
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['monitor:job:remove']">删除</el-button>
|
|
|
</el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="info" plain icon="el-icon-notebook-2" size="mini" @click="handleTaskRegistry" v-hasPermi="['monitor:job:add']">Bean注册表</el-button>
|
|
|
- </el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="primary" plain icon="el-icon-user" size="mini" @click="handleTenantJobConfig" v-hasPermi="['monitor:job:edit']">租户任务配置</el-button>
|
|
|
- </el-col>
|
|
|
+<!-- <el-col :span="1.5">-->
|
|
|
+<!-- <el-button type="primary" plain icon="el-icon-user" size="mini" @click="handleTenantJobConfig" v-hasPermi="['monitor:job:edit']">租户任务配置</el-button>-->
|
|
|
+<!-- </el-col>-->
|
|
|
<el-col :span="1.5">
|
|
|
<el-button type="info" plain icon="el-icon-s-operation" size="mini" @click="handleJobLog" v-hasPermi="['monitor:job:query']">任务日志</el-button>
|
|
|
</el-col>
|
|
|
@@ -79,6 +76,7 @@
|
|
|
<template slot-scope="scope">
|
|
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['monitor:job:edit']">修改</el-button>
|
|
|
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['monitor:job:remove']">删除</el-button>
|
|
|
+ <el-button size="mini" type="text" icon="el-icon-connection" @click="handleAssignTenant(scope.row)" v-hasPermi="['monitor:job:edit']">分配</el-button>
|
|
|
<el-button size="mini" type="text" icon="el-icon-s-operation" @click="handleRowJobLog(scope.row)" v-hasPermi="['monitor:job:query']">日志</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
@@ -121,11 +119,7 @@
|
|
|
<i class="el-icon-question"></i>
|
|
|
</el-tooltip>
|
|
|
</span>
|
|
|
- <el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串">
|
|
|
- <template slot="append">
|
|
|
- <el-button @click="openRegistry = true; initRegistry()">从注册表选择</el-button>
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
+ <el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="24">
|
|
|
@@ -191,37 +185,6 @@
|
|
|
<crontab @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab>
|
|
|
</el-dialog>
|
|
|
|
|
|
- <!-- Bean 注册表 -->
|
|
|
- <el-dialog title="可调度 Bean 方法(invoke_target 参考)" :visible.sync="openRegistry" width="960px" append-to-body @open="initRegistry">
|
|
|
- <el-form :model="registryQueryParams" ref="registryQueryForm" :inline="true" size="small">
|
|
|
- <el-form-item label="Bean" prop="beanName">
|
|
|
- <el-input v-model="registryQueryParams.beanName" placeholder="Bean 名称" clearable @keyup.enter.native="handleRegistryQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="方法" prop="methodName">
|
|
|
- <el-input v-model="registryQueryParams.methodName" placeholder="方法名" clearable @keyup.enter.native="handleRegistryQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="调用目标" prop="invokeTarget">
|
|
|
- <el-input v-model="registryQueryParams.invokeTarget" placeholder="invoke_target" clearable @keyup.enter.native="handleRegistryQuery" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" icon="el-icon-search" size="mini" @click="handleRegistryQuery">搜索</el-button>
|
|
|
- <el-button icon="el-icon-refresh" size="mini" @click="resetRegistryQuery">重置</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- <el-table v-loading="registryLoading" :data="registryList" max-height="420">
|
|
|
- <el-table-column label="Bean" prop="beanName" width="180" show-overflow-tooltip />
|
|
|
- <el-table-column label="类名" prop="className" width="160" show-overflow-tooltip />
|
|
|
- <el-table-column label="方法" prop="methodName" width="160" show-overflow-tooltip />
|
|
|
- <el-table-column label="调用目标" prop="invokeTarget" show-overflow-tooltip />
|
|
|
- <el-table-column label="操作" width="100" align="center">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-button type="text" size="mini" @click="applyInvokeTarget(scope.row)">选用</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- <pagination v-show="registryTotal > 0" :total="registryTotal" :page.sync="registryQueryParams.pageNum" :limit.sync="registryQueryParams.pageSize" @pagination="getRegistryList" />
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
<!-- 租户任务配置 -->
|
|
|
<el-dialog title="租户定时任务配置" :visible.sync="openTenantJob" width="960px" append-to-body @open="loadTenantJobDialog">
|
|
|
<el-form :inline="true" size="small">
|
|
|
@@ -256,13 +219,36 @@
|
|
|
<el-empty v-if="tenantJobForm.tenantId && !tenantJobLoading && !tenantJobConfigList.length" description="暂未同步,请点击「同步到租户库」按钮" />
|
|
|
<el-empty v-if="!tenantJobForm.tenantId" description="请先选择租户" />
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 分配租户弹窗 -->
|
|
|
+ <el-dialog :title="'分配租户 — ' + assignTemplate.jobName" :visible.sync="openAssignTenant" width="680px" append-to-body @open="loadAssignTenantDialog">
|
|
|
+ <el-form :inline="true" size="small">
|
|
|
+ <el-form-item>
|
|
|
+ <el-input v-model="assignTenantSearch" placeholder="搜索租户" clearable size="small" style="width: 220px" @input="handleAssignTenantSearch" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-checkbox v-model="selectAllTenants" @change="handleSelectAllTenants">全选</el-checkbox>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" size="mini" :loading="assignTenantSaving" @click="submitAssignTenant">保存分配</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <el-checkbox-group v-model="selectedTenantIds" v-loading="assignTenantLoading" style="display:flex; flex-wrap:wrap; gap:4px 0;">
|
|
|
+ <el-checkbox
|
|
|
+ v-for="t in filteredAssignTenants"
|
|
|
+ :key="t.id"
|
|
|
+ :label="t.id"
|
|
|
+ style="width:50%; margin-right:0;"
|
|
|
+ >{{ t.tenantName + ' (' + t.tenantCode + ')' }}</el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
+ <el-empty v-if="!assignTenantLoading && !filteredAssignTenants.length" description="无可用租户" />
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { listTemplate, getTemplate, addTemplate, updateTemplate, delTemplate } from "@/api/monitor/jobTemplate";
|
|
|
-import { listTaskRegistry } from "@/api/monitor/taskRegistry";
|
|
|
-import { getTenantJobConfig, updateTenantJobStatus, syncTenantJob, syncAllTenantJob } from "@/api/monitor/tenantJob";
|
|
|
+import { getTenantJobConfig, updateTenantJobStatus, syncTenantJob, syncAllTenantJob, saveTenantJobConfig, listTenantJobConfig } from "@/api/monitor/tenantJob";
|
|
|
import { tenantList } from "@/api/tenant/tenant";
|
|
|
import Crontab from '@/components/Crontab'
|
|
|
|
|
|
@@ -282,18 +268,22 @@ export default {
|
|
|
open: false,
|
|
|
openCron: false,
|
|
|
expression: "",
|
|
|
- // Bean 注册表
|
|
|
- openRegistry: false,
|
|
|
- registryLoading: false,
|
|
|
- registryList: [],
|
|
|
- registryTotal: 0,
|
|
|
- registryQueryParams: { pageNum: 1, pageSize: 10, beanName: undefined, methodName: undefined, invokeTarget: undefined },
|
|
|
// 租户任务配置
|
|
|
openTenantJob: false,
|
|
|
tenantJobLoading: false,
|
|
|
tenantJobConfigList: [],
|
|
|
tenantOptions: [],
|
|
|
tenantJobForm: { tenantId: undefined },
|
|
|
+ // 分配租户弹窗
|
|
|
+ openAssignTenant: false,
|
|
|
+ assignTenantLoading: false,
|
|
|
+ assignTenantSaving: false,
|
|
|
+ assignTemplate: {},
|
|
|
+ assignTenantSearch: '',
|
|
|
+ selectAllTenants: false,
|
|
|
+ selectedTenantIds: [],
|
|
|
+ previousSelectedTenantIds: [],
|
|
|
+ allTenants: [],
|
|
|
// 字典
|
|
|
jobGroupOptions: [],
|
|
|
// 查询参数
|
|
|
@@ -350,20 +340,6 @@ export default {
|
|
|
const logPath = this.$route.path.startsWith('/admin/') ? '/admin/shezhi/jobLog' : '/monitor/jobLog';
|
|
|
this.$router.push({ path: logPath, query: { jobName: row.jobName } });
|
|
|
},
|
|
|
- // Bean 注册表
|
|
|
- handleTaskRegistry() { this.openRegistry = true; },
|
|
|
- initRegistry() { this.registryQueryParams.pageNum = 1; this.getRegistryList(); },
|
|
|
- getRegistryList() {
|
|
|
- this.registryLoading = true;
|
|
|
- listTaskRegistry(this.registryQueryParams).then(res => { this.registryList = res.rows || []; this.registryTotal = res.total || 0; this.registryLoading = false; }).catch(() => { this.registryLoading = false; });
|
|
|
- },
|
|
|
- handleRegistryQuery() { this.registryQueryParams.pageNum = 1; this.getRegistryList(); },
|
|
|
- resetRegistryQuery() { this.registryQueryParams = { pageNum: 1, pageSize: 10, beanName: undefined, methodName: undefined, invokeTarget: undefined }; this.getRegistryList(); },
|
|
|
- applyInvokeTarget(row) {
|
|
|
- if (!this.open) { this.handleAdd(); }
|
|
|
- this.form.invokeTarget = row.invokeTarget;
|
|
|
- this.openRegistry = false;
|
|
|
- },
|
|
|
// 模板 CRUD
|
|
|
handleAdd() { this.reset(); this.open = true; this.title = "添加任务模板"; },
|
|
|
handleUpdate(row) {
|
|
|
@@ -415,6 +391,74 @@ export default {
|
|
|
this.$confirm("确认将全部租户的任务配置同步到各租户库?", "提示", { type: "warning" }).then(() => {
|
|
|
return syncAllTenantJob();
|
|
|
}).then(res => { const d = res.data || {}; this.msgSuccess("同步完成:成功 " + (d.successCount || 0) + ",失败 " + (d.failCount || 0)); }).catch(() => {});
|
|
|
+ },
|
|
|
+ // 分配租户
|
|
|
+ handleAssignTenant(row) {
|
|
|
+ this.assignTemplate = row
|
|
|
+ this.openAssignTenant = true
|
|
|
+ },
|
|
|
+ loadAssignTenantDialog() {
|
|
|
+ this.assignTenantLoading = true
|
|
|
+ this.assignTenantSearch = ''
|
|
|
+ this.selectAllTenants = false
|
|
|
+ this.selectedTenantIds = []
|
|
|
+ // 加载全部租户(与 tenantJobConfig 弹窗保持一致)
|
|
|
+ tenantList({}).then(res => {
|
|
|
+ this.allTenants = res.data || res.rows || []
|
|
|
+ this.assignTenantLoading = false
|
|
|
+ // 异步查询该模板已分配了哪些租户
|
|
|
+ listTenantJobConfig({ templateId: this.assignTemplate.templateId, status: 0, pageSize: 999 }).then(res2 => {
|
|
|
+ // 兼容多种响应格式:TableDataInfo { rows } / AjaxResult { data } / 直接数组
|
|
|
+ let rows = res2.rows || res2.data || (Array.isArray(res2) ? res2 : [])
|
|
|
+ console.log('分配租户 — 已分配查询结果 rows:', rows)
|
|
|
+ this.selectedTenantIds = rows.filter(r => String(r.status) === '0').map(r => Number(r.tenantId))
|
|
|
+ this.previousSelectedTenantIds = [...this.selectedTenantIds]
|
|
|
+ this.selectAllTenants = this.selectedTenantIds.length === this.allTenants.length && this.allTenants.length > 0
|
|
|
+ }).catch(() => {})
|
|
|
+ }).catch(() => { this.assignTenantLoading = false })
|
|
|
+ },
|
|
|
+ handleAssignTenantSearch() {},
|
|
|
+ handleSelectAllTenants(val) {
|
|
|
+ this.selectedTenantIds = val ? this.allTenants.map(t => t.id) : []
|
|
|
+ },
|
|
|
+ submitAssignTenant() {
|
|
|
+ this.assignTenantSaving = true
|
|
|
+ // 只处理选择状态发生变化的租户,避免覆盖其他模板的分配
|
|
|
+ const promises = []
|
|
|
+ const prevIds = this.previousSelectedTenantIds || []
|
|
|
+ this.allTenants.forEach(t => {
|
|
|
+ const isSelected = this.selectedTenantIds.includes(t.id)
|
|
|
+ const wasSelected = prevIds.includes(t.id)
|
|
|
+ if (isSelected === wasSelected) return // 无变化,跳过
|
|
|
+ const p = getTenantJobConfig(t.id).then(res => {
|
|
|
+ const currentIds = (res.data || []).map(c => c.templateId)
|
|
|
+ const newIds = isSelected
|
|
|
+ ? [...new Set([...currentIds, this.assignTemplate.templateId])]
|
|
|
+ : currentIds.filter(id => id !== this.assignTemplate.templateId)
|
|
|
+ return saveTenantJobConfig({ tenantId: t.id, templateIds: newIds, status: '0' })
|
|
|
+ })
|
|
|
+ promises.push(p)
|
|
|
+ })
|
|
|
+ if (promises.length === 0) {
|
|
|
+ this.assignTenantSaving = false
|
|
|
+ this.openAssignTenant = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ Promise.all(promises).then(() => {
|
|
|
+ this.msgSuccess('分配成功')
|
|
|
+ this.assignTenantSaving = false
|
|
|
+ this.openAssignTenant = false
|
|
|
+ }).catch(() => { this.assignTenantSaving = false })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ filteredAssignTenants() {
|
|
|
+ if (!this.assignTenantSearch) return this.allTenants
|
|
|
+ const kw = this.assignTenantSearch.toLowerCase()
|
|
|
+ return this.allTenants.filter(t =>
|
|
|
+ (t.tenantName || '').toLowerCase().includes(kw) ||
|
|
|
+ (t.tenantCode || '').toLowerCase().includes(kw)
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
};
|