任务中心

This commit is contained in:
iqudoo
2026-06-07 11:10:48 +08:00
parent 72207bb1bf
commit 93798e05ec
3 changed files with 123 additions and 58 deletions

View File

@@ -1,18 +1,36 @@
<template> <template>
<div class="log-content-preview"> <div class="log-content-preview">
<div class="log-content-preview__toolbar"> <div class="log-content-preview__panel" :class="{ 'is-expanded': hasContent && expanded }">
<a-button size="small" type="link" :disabled="!hasContent" @click="copyLog"> <div class="log-content-preview__toolbar">
复制日志 <a-button
</a-button> v-if="hasContent"
</div> size="small"
<div class="log-content-preview__viewport"> type="link"
<pre class="log-content-preview__text">{{ displayText }}</pre> @click="toggleExpanded"
>
{{ expanded ? "收起" : "展开" }}
</a-button>
<a-button size="small" type="link" :disabled="!hasContent" @click="copyLog">
复制
</a-button>
</div>
<div
class="log-content-preview__content"
:class="{ 'is-collapsed': hasContent && !expanded }"
>
<pre class="log-content-preview__text" :class="{ 'is-empty': !hasContent }">{{ displayText }}</pre>
<div
v-if="hasContent && !expanded"
class="log-content-preview__fade"
aria-hidden="true"
/>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { computed } from "vue"; import { computed, ref } from "vue";
import { showErrorTips, showSuccessTips } from "@/iview/utils/messageTips"; import { showErrorTips, showSuccessTips } from "@/iview/utils/messageTips";
const props = defineProps({ const props = defineProps({
@@ -26,6 +44,8 @@ const props = defineProps({
}, },
}); });
const expanded = ref(false);
const hasContent = computed(() => !!`${props.content || ""}`.trim()); const hasContent = computed(() => !!`${props.content || ""}`.trim());
const displayText = computed(() => { const displayText = computed(() => {
@@ -33,6 +53,10 @@ const displayText = computed(() => {
return props.emptyText; return props.emptyText;
}); });
function toggleExpanded() {
expanded.value = !expanded.value;
}
async function copyLog() { async function copyLog() {
if (!hasContent.value) return; if (!hasContent.value) return;
const text = `${props.content || ""}`; const text = `${props.content || ""}`;
@@ -74,26 +98,45 @@ function copyByTextarea(text) {
min-width: 0; min-width: 0;
} }
.log-content-preview__toolbar { .log-content-preview__panel {
display: flex; position: relative;
justify-content: flex-end; overflow: hidden;
margin-bottom: 4px;
}
.log-content-preview__viewport {
/* overflow-x 非 visible 时overflow-y: visible 会被浏览器算成 auto 而出现纵向滚动条 */
overflow-x: auto;
overflow-y: clip;
max-width: 100%;
padding: 10px;
background-color: #f5f5f5; background-color: #f5f5f5;
border-radius: 5px; border-radius: 5px;
}
.log-content-preview__panel.is-expanded {
overflow: visible;
}
.log-content-preview__toolbar {
position: sticky;
top: 0;
z-index: 2;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 8px;
padding: 6px 10px;
background-color: #f5f5f5;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.log-content-preview__content {
position: relative;
overflow-x: auto;
padding: 10px;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
.log-content-preview__content.is-collapsed {
max-height: 300px;
overflow: hidden;
}
.log-content-preview__text { .log-content-preview__text {
margin: 0; margin: 0;
color: #999; color: #666;
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
@@ -103,6 +146,23 @@ function copyByTextarea(text) {
overflow-wrap: normal; overflow-wrap: normal;
width: max-content; width: max-content;
min-width: 100%; min-width: 100%;
}
.log-content-preview__text.is-empty {
min-height: 200px; min-height: 200px;
} }
.log-content-preview__fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 48px;
background: linear-gradient(
to bottom,
rgba(245, 245, 245, 0),
rgba(245, 245, 245, 0.95)
);
pointer-events: none;
}
</style> </style>

View File

@@ -11,6 +11,10 @@
<template #icon><reload-outlined /></template> <template #icon><reload-outlined /></template>
刷新 刷新
</a-button> </a-button>
<a-button type="default" @click="clearTask" :loading="clearTaskLoading">
<template #icon><delete-outlined /></template>
清除任务
</a-button>
</div> </div>
</div> </div>
</template> </template>
@@ -20,7 +24,7 @@
<div> <div>
<div :class="type !== 'detail' ? 'name-click' : 'name-text'" <div :class="type !== 'detail' ? 'name-click' : 'name-text'"
@click="type !== 'detail' ? detailClick(record) : null"> @click="type !== 'detail' ? detailClick(record) : null">
{{ record.taskType }} {{ record.transId }}
</div> </div>
<div class="item-detail" v-if="displayBizInfo(record)"> <div class="item-detail" v-if="displayBizInfo(record)">
<span class="item-title">业务编号</span> <span class="item-title">业务编号</span>
@@ -58,7 +62,7 @@
<span class="item-title">执行用时</span> <span class="item-title">执行用时</span>
<span class="item-value">{{ displayUseTime(record.useTime) }}</span> <span class="item-value">{{ displayUseTime(record.useTime) }}</span>
</div> </div>
<div v-if="type === 'detail'" style="margin: 10px 0px;"> <div v-if="type === 'detail' && record.failureCount > 0" style="margin: 10px 0px;">
<IViewConfirm importance="normal" :title="`确认要重新执行异步任务吗?`" @confirm="() => restartClick(record)"> <IViewConfirm importance="normal" :title="`确认要重新执行异步任务吗?`" @confirm="() => restartClick(record)">
<a-button size="small" type="default"> 重新执行 </a-button> <a-button size="small" type="default"> 重新执行 </a-button>
</IViewConfirm> </IViewConfirm>
@@ -69,7 +73,7 @@
<a-button size="small" type="default" @click="detailClick(record)"> <a-button size="small" type="default" @click="detailClick(record)">
详情 详情
</a-button> </a-button>
<IViewConfirm importance="normal" :title="`确认要重新执行异步任务吗?`" @confirm="() => restartClick(record)"> <IViewConfirm v-if="record.failureCount > 0" importance="normal" :title="`确认要重新执行异步任务吗?`" @confirm="() => restartClick(record)">
<a-button size="small" type="default"> 重新执行 </a-button> <a-button size="small" type="default"> 重新执行 </a-button>
</IViewConfirm> </IViewConfirm>
</template> </template>
@@ -86,7 +90,7 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { ReloadOutlined } from "@ant-design/icons-vue"; import { ReloadOutlined, DeleteOutlined } from "@ant-design/icons-vue";
import { showErrorTips, showSuccessTips } from "@/iview/utils/messageTips"; import { showErrorTips, showSuccessTips } from "@/iview/utils/messageTips";
import { apiRequest } from "@/iview/utils/request"; import { apiRequest } from "@/iview/utils/request";
import IViewConfirm from "@/iview/IViewConfirm.vue"; import IViewConfirm from "@/iview/IViewConfirm.vue";
@@ -94,6 +98,7 @@ import ILayoutPage from "@/iview/ILayoutPage.vue";
import TaskLogs from "./TaskLogs.vue"; import TaskLogs from "./TaskLogs.vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
const clearTaskLoading = ref(false);
const pageRef = ref(null); const pageRef = ref(null);
@@ -182,6 +187,19 @@ const detailClick = (record) => {
pageRef.value.showDetailPage("detail", record); pageRef.value.showDetailPage("detail", record);
}; };
const clearTask = async () => {
try {
clearTaskLoading.value = true;
await apiRequest("api.system.async.task.info.clear", {});
showSuccessTips("异步任务清除成功");
refreshPage();
} catch (error) {
showErrorTips(error);
} finally {
clearTaskLoading.value = false;
}
};
const restartClick = async (record) => { const restartClick = async (record) => {
try { try {
record.actionLoading = true; record.actionLoading = true;
@@ -226,7 +244,7 @@ const config = ref({
isDefault: true, isDefault: true,
pagination: { pagination: {
pageSize: 10, pageSize: 10,
showTotal: (total) => total > 0 ? `${total}` : "暂无数据", showTotal: (total) => total > 0 ? `${total}` : "",
}, },
columns: recordColumns.value, columns: recordColumns.value,
filters: { filters: {
@@ -255,7 +273,6 @@ const config = ref({
recordMap: (record) => { recordMap: (record) => {
return record; return record;
}, },
hiddenLabel: true,
columns: recordColumns.value, columns: recordColumns.value,
tabs: [ tabs: [
{ {
@@ -313,13 +330,12 @@ const config = ref({
color: rgba(0, 0, 0, 0.75); color: rgba(0, 0, 0, 0.75);
font-size: 13px; font-size: 13px;
line-height: 20px; line-height: 20px;
word-break: break-all; word-break: break-word;
min-width: 0; min-width: 0;
} }
.name-click { .name-click {
font-size: 14px; font-size: 14px;
font-weight: 500;
color: #333333; color: #333333;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
@@ -329,7 +345,6 @@ const config = ref({
.name-text { .name-text {
font-size: 14px; font-size: 14px;
font-weight: 500;
color: #333333; color: #333333;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -1,9 +1,8 @@
<template> <template>
<div class="record-page"> <div class="record-page">
<ILayoutPage ref="pageRef" :config="config"> <ILayoutPage :config="config">
<template #bodyCell="{ column, record, type }"> <template #bodyCell="{ column, record, type }">
<template v-if="column.dataIndex === 'recordDetail'"> <template v-if="column.dataIndex === 'recordDetail'">
<pre class="log-params">{{ displayParams(taskInfo.taskParams) }}</pre>
<LogContentPreview :content="record.logsContent" /> <LogContentPreview :content="record.logsContent" />
</template> </template>
</template> </template>
@@ -23,21 +22,6 @@ const props = defineProps({
}, },
}); });
const pageRef = ref(null);
const refreshPage = () => {
pageRef.value.refreshPage();
};
const displayParams = (params) => {
try {
let paramsObj = JSON.parse(params);
return JSON.stringify(paramsObj, null, 2);
} catch (_) {
return params;
}
};
const recordColumns = ref([ const recordColumns = ref([
{ {
title: "执行日志", title: "执行日志",
@@ -47,6 +31,23 @@ const recordColumns = ref([
}, },
]); ]);
function formatTaskParams(params) {
if (params == null || params === "") return "";
if (typeof params === "object") {
try {
return JSON.stringify(params, null, 2);
} catch (_) {
return String(params);
}
}
try {
const paramsObj = JSON.parse(params);
return JSON.stringify(paramsObj, null, 2);
} catch (_) {
return String(params);
}
}
const config = ref({ const config = ref({
infoPages: [ infoPages: [
{ {
@@ -77,15 +78,4 @@ const config = ref({
height: 100%; height: 100%;
min-height: 100%; min-height: 100%;
} }
.log-params {
color: #999;
padding: 10px;
font-size: 12px;
white-space: pre-wrap;
word-break: break-all;
background-color: #f5f5f5;
border-radius: 5px;
}
</style> </style>