fix: 调整调度队列的空状态
This commit is contained in:
@@ -11,49 +11,69 @@
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<a-table
|
||||
:columns="queueColumns"
|
||||
:data-source="queueItems"
|
||||
:pagination="false"
|
||||
size="middle"
|
||||
:scroll="{ x: false, y: false }"
|
||||
table-layout="auto"
|
||||
class="queue-table"
|
||||
>
|
||||
<template #emptyText>
|
||||
<span>暂无任务</span>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'index'"> {{ index + 1 }} </template>
|
||||
<template v-else-if="column.key === 'script'">
|
||||
<a-select
|
||||
v-model:value="record.script"
|
||||
@change="updateQueueItemScript(record)"
|
||||
size="small"
|
||||
style="width: 200px"
|
||||
class="script-select"
|
||||
placeholder="请选择脚本"
|
||||
:options="scriptOptions"
|
||||
allow-clear
|
||||
/>
|
||||
<!-- 使用vuedraggable替换a-table实现拖拽功能 -->
|
||||
<div class="draggable-table-container">
|
||||
<!-- 表头 -->
|
||||
<div class="draggable-table-header">
|
||||
<div class="header-cell index-cell">序号</div>
|
||||
<div class="header-cell script-cell">脚本任务</div>
|
||||
<div class="header-cell actions-cell">操作</div>
|
||||
</div>
|
||||
|
||||
<!-- 拖拽内容区域 -->
|
||||
<draggable
|
||||
v-model="queueItems"
|
||||
group="queueItems"
|
||||
item-key="id"
|
||||
:animation="200"
|
||||
:disabled="loading"
|
||||
ghost-class="ghost"
|
||||
chosen-class="chosen"
|
||||
drag-class="drag"
|
||||
@end="onDragEnd"
|
||||
class="draggable-container"
|
||||
>
|
||||
<template #item="{ element: record, index }">
|
||||
<div class="draggable-row" :class="{ 'row-dragging': loading }">
|
||||
<div class="row-cell index-cell">{{ index + 1 }}</div>
|
||||
<div class="row-cell script-cell">
|
||||
<a-select
|
||||
v-model:value="record.script"
|
||||
@change="updateQueueItemScript(record)"
|
||||
size="small"
|
||||
style="width: 200px"
|
||||
class="script-select"
|
||||
placeholder="请选择脚本"
|
||||
:options="scriptOptions"
|
||||
allow-clear
|
||||
/>
|
||||
</div>
|
||||
<div class="row-cell actions-cell">
|
||||
<a-space>
|
||||
<a-popconfirm
|
||||
title="确定要删除这个任务吗?"
|
||||
@confirm="deleteQueueItem(record.id)"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
>
|
||||
<a-button size="middle" danger>
|
||||
<DeleteOutlined />
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<a-space>
|
||||
<a-popconfirm
|
||||
title="确定要删除这个任务吗?"
|
||||
@confirm="deleteQueueItem(record.id)"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
>
|
||||
<a-button size="middle" danger>
|
||||
<DeleteOutlined />
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</draggable>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-if="queueItems.length === 0" class="empty-state">
|
||||
<div class="empty-content">
|
||||
<img src="@/assets/NoData.png" alt="无数据" class="empty-image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
@@ -61,15 +81,13 @@
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import { Service } from '@/api'
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
queueId: string
|
||||
queueItems: Array<{
|
||||
id: string
|
||||
script: string | null
|
||||
}>
|
||||
queueItems: any[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
@@ -82,14 +100,8 @@ const emit = defineEmits<{
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
|
||||
// 脚本选项类型定义
|
||||
interface ScriptOption {
|
||||
label: string
|
||||
value: string | null
|
||||
}
|
||||
|
||||
// 脚本选项
|
||||
const scriptOptions = ref<ScriptOption[]>([])
|
||||
// 选项数据
|
||||
const scriptOptions = ref<Array<{ label: string; value: string | null }>>([])
|
||||
|
||||
// 表格列配置
|
||||
const queueColumns = [
|
||||
@@ -108,7 +120,7 @@ const queueColumns = [
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 180,
|
||||
width: 100,
|
||||
align: 'center',
|
||||
},
|
||||
]
|
||||
@@ -148,10 +160,9 @@ const loadOptions = async () => {
|
||||
|
||||
// 更新队列项脚本
|
||||
const updateQueueItemScript = async (record: any) => {
|
||||
const oldScript = record.script
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
|
||||
const response = await Service.updateItemApiQueueItemUpdatePost({
|
||||
queueId: props.queueId,
|
||||
queueItemId: record.id,
|
||||
@@ -164,15 +175,11 @@ const updateQueueItemScript = async (record: any) => {
|
||||
|
||||
if (response.code === 200) {
|
||||
message.success('脚本更新成功')
|
||||
// 不触发刷新,避免界面闪烁
|
||||
emit('refresh')
|
||||
} else {
|
||||
// 回滚本地变更
|
||||
record.script = oldScript
|
||||
message.error('脚本更新失败: ' + (response.message || '未知错误'))
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 发生异常时回滚
|
||||
record.script = oldScript
|
||||
console.error('更新脚本失败:', error)
|
||||
message.error('更新脚本失败: ' + (error?.message || '网络错误'))
|
||||
} finally {
|
||||
@@ -184,7 +191,7 @@ const updateQueueItemScript = async (record: any) => {
|
||||
const addQueueItem = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
|
||||
// 直接创建队列项,默认ScriptId为null(未选择)
|
||||
const createResponse = await Service.addItemApiQueueItemAddPost({
|
||||
queueId: props.queueId,
|
||||
@@ -192,7 +199,6 @@ const addQueueItem = async () => {
|
||||
|
||||
if (createResponse.code === 200 && createResponse.queueItemId) {
|
||||
message.success('任务添加成功')
|
||||
// 只在添加成功后刷新,避免不必要的闪烁
|
||||
emit('refresh')
|
||||
} else {
|
||||
message.error('任务添加失败: ' + (createResponse.message || '未知错误'))
|
||||
@@ -226,6 +232,44 @@ const deleteQueueItem = async (itemId: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 拖拽结束处理函数
|
||||
const onDragEnd = async (evt: any) => {
|
||||
// 如果位置没有变化,直接返回
|
||||
if (evt.oldIndex === evt.newIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 构造排序后的ID列表
|
||||
const sortedIds = queueItems.value.map(item => item.id)
|
||||
|
||||
// 调用排序API
|
||||
const response = await Service.reorderItemApiQueueItemOrderPost({
|
||||
queueId: props.queueId,
|
||||
indexList: sortedIds,
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
message.success('任务顺序已更新')
|
||||
// 刷新数据以确保与服务器同步
|
||||
emit('refresh')
|
||||
} else {
|
||||
message.error('更新任务顺序失败: ' + (response.message || '未知错误'))
|
||||
// 如果失败,刷新数据恢复原状态
|
||||
emit('refresh')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('拖拽排序失败:', error)
|
||||
message.error('更新任务顺序失败: ' + (error?.message || '网络错误'))
|
||||
// 如果失败,刷新数据恢复原状态
|
||||
emit('refresh')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
loadOptions()
|
||||
@@ -483,10 +527,150 @@ onMounted(() => {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 拖拽表格样式 */
|
||||
.draggable-table-container {
|
||||
width: 100%;
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.draggable-table-header {
|
||||
display: flex;
|
||||
background-color: var(--ant-color-fill-quaternary);
|
||||
border-bottom: 1px solid var(--ant-color-border);
|
||||
}
|
||||
|
||||
.header-cell {
|
||||
padding: 12px 16px;
|
||||
font-weight: 600;
|
||||
color: var(--ant-color-text);
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--ant-color-border);
|
||||
}
|
||||
|
||||
.header-cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.index-cell {
|
||||
width: 80px;
|
||||
min-width: 80px;
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
.script-cell {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.actions-cell {
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
.draggable-container {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.draggable-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--ant-color-bg-container);
|
||||
border-bottom: 1px solid var(--ant-color-border);
|
||||
transition: all 0.2s ease;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.draggable-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.draggable-row:hover {
|
||||
background-color: var(--ant-color-fill-quaternary);
|
||||
}
|
||||
|
||||
.draggable-row.row-dragging {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.row-cell {
|
||||
padding: 12px 16px;
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--ant-color-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row-cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.row-cell.index-cell {
|
||||
width: 80px;
|
||||
min-width: 80px;
|
||||
max-width: 80px;
|
||||
font-weight: 500;
|
||||
color: var(--ant-color-text-secondary);
|
||||
}
|
||||
|
||||
.row-cell.script-cell {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.row-cell.actions-cell {
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
/* 拖拽状态样式 */
|
||||
.ghost {
|
||||
opacity: 0.5;
|
||||
background: var(--ant-color-primary-bg);
|
||||
border: 2px dashed var(--ant-color-primary);
|
||||
}
|
||||
|
||||
.chosen {
|
||||
background: var(--ant-color-primary-bg-hover);
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.drag {
|
||||
transform: rotate(5deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 空状态样式 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
max-width: 200px;
|
||||
height: auto;
|
||||
opacity: 0.9;
|
||||
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.1));
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.empty-image:hover {
|
||||
transform: translateY(-4px);
|
||||
filter: drop-shadow(0 12px 32px rgba(0, 0, 0, 0.15));
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@@ -504,6 +688,30 @@ onMounted(() => {
|
||||
.queue-item-card-item {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.draggable-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.row-cell,
|
||||
.header-cell {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--ant-color-border);
|
||||
}
|
||||
|
||||
.row-cell:last-child,
|
||||
.header-cell:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.index-cell,
|
||||
.script-cell,
|
||||
.actions-cell {
|
||||
width: 100% !important;
|
||||
min-width: auto !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
@@ -604,4 +812,4 @@ onMounted(() => {
|
||||
.script-select :deep(.ant-select-item-option-content) {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-card title="定时列表" class="time-set-card" :loading="loading">
|
||||
<a-card title="定时列表" class="time-set-card">
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button
|
||||
@@ -16,60 +16,80 @@
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<a-table
|
||||
:columns="timeColumns"
|
||||
:data-source="timeSets"
|
||||
:pagination="false"
|
||||
size="middle"
|
||||
:scroll="{ x: false, y: false }"
|
||||
table-layout="auto"
|
||||
class="time-set-table"
|
||||
>
|
||||
<template #emptyText>
|
||||
<span>暂无定时</span>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'enabled'">
|
||||
<a-select
|
||||
v-model:value="record.enabled"
|
||||
@change="updateTimeSetStatus(record)"
|
||||
size="small"
|
||||
style="width: 80px"
|
||||
class="status-select"
|
||||
>
|
||||
<a-select-option :value="true">启用</a-select-option>
|
||||
<a-select-option :value="false">禁用</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'time'">
|
||||
<a-time-picker
|
||||
v-model:value="record.timeValue"
|
||||
format="HH:mm"
|
||||
placeholder="请选择时间"
|
||||
size="small"
|
||||
@change="updateTimeSetTime(record)"
|
||||
:disabled="loading"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<a-space>
|
||||
<a-popconfirm
|
||||
title="确定要删除这个定时吗?"
|
||||
@confirm="deleteTimeSet(record.id)"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
>
|
||||
<a-button size="middle" danger>
|
||||
<DeleteOutlined />
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 使用vuedraggable替换a-table实现拖拽功能 -->
|
||||
<div class="draggable-table-container">
|
||||
<!-- 表头 -->
|
||||
<div class="draggable-table-header">
|
||||
<div class="header-cell index-cell">序号</div>
|
||||
<div class="header-cell status-cell">状态</div>
|
||||
<div class="header-cell time-cell">执行时间</div>
|
||||
<div class="header-cell actions-cell">操作</div>
|
||||
</div>
|
||||
|
||||
<!-- 拖拽内容区域 -->
|
||||
<draggable
|
||||
v-model="timeSets"
|
||||
group="timeSets"
|
||||
item-key="id"
|
||||
:animation="200"
|
||||
:disabled="loading"
|
||||
ghost-class="ghost"
|
||||
chosen-class="chosen"
|
||||
drag-class="drag"
|
||||
@end="onDragEnd"
|
||||
class="draggable-container"
|
||||
>
|
||||
<template #item="{ element: record, index }">
|
||||
<div class="draggable-row" :class="{ 'row-dragging': loading }">
|
||||
<div class="row-cell index-cell">{{ index + 1 }}</div>
|
||||
<div class="row-cell status-cell">
|
||||
<a-select
|
||||
v-model:value="record.enabled"
|
||||
@change="updateTimeSetStatus(record)"
|
||||
size="small"
|
||||
style="width: 80px"
|
||||
class="status-select"
|
||||
>
|
||||
<a-select-option :value="true">启用</a-select-option>
|
||||
<a-select-option :value="false">禁用</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="row-cell time-cell">
|
||||
<a-time-picker
|
||||
v-model:value="record.timeValue"
|
||||
format="HH:mm"
|
||||
placeholder="请选择时间"
|
||||
size="small"
|
||||
@change="updateTimeSetTime(record)"
|
||||
:disabled="loading"
|
||||
/>
|
||||
</div>
|
||||
<div class="row-cell actions-cell">
|
||||
<a-space>
|
||||
<a-popconfirm
|
||||
title="确定要删除这个定时吗?"
|
||||
@confirm="deleteTimeSet(record.id)"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
>
|
||||
<a-button size="middle" danger>
|
||||
<DeleteOutlined />
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-if="timeSets.length === 0" class="empty-state">
|
||||
<div class="empty-content">
|
||||
<img src="@/assets/NoData.png" alt="无数据" class="empty-image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
@@ -77,6 +97,7 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import { Service } from '@/api'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
@@ -159,7 +180,7 @@ const timeSets = ref([...props.timeSets])
|
||||
const processTimeSets = (rawTimeSets: any[]) => {
|
||||
return rawTimeSets.map(item => ({
|
||||
...item,
|
||||
timeValue: parseTimeString(item.time)
|
||||
timeValue: parseTimeString(item.time),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -221,7 +242,7 @@ const addTimeSet = async () => {
|
||||
const updateTimeSetTime = async (timeSet: any) => {
|
||||
try {
|
||||
const timeString = formatTimeValue(timeSet.timeValue)
|
||||
|
||||
|
||||
const response = await Service.updateTimeSetApiQueueTimeUpdatePost({
|
||||
queueId: props.queueId,
|
||||
timeSetId: timeSet.id,
|
||||
@@ -297,6 +318,44 @@ const deleteTimeSet = async (timeSetId: string) => {
|
||||
message.error('删除定时项失败: ' + (error?.message || '网络错误'))
|
||||
}
|
||||
}
|
||||
|
||||
// 拖拽结束处理函数
|
||||
const onDragEnd = async (evt: any) => {
|
||||
// 如果位置没有变化,直接返回
|
||||
if (evt.oldIndex === evt.newIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 构造排序后的ID列表
|
||||
const sortedIds = timeSets.value.map(item => item.id)
|
||||
|
||||
// 调用排序API
|
||||
const response = await Service.reorderTimeSetApiQueueTimeOrderPost({
|
||||
queueId: props.queueId,
|
||||
indexList: sortedIds,
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
message.success('定时顺序已更新')
|
||||
// 刷新数据以确保与服务器同步
|
||||
emit('refresh')
|
||||
} else {
|
||||
message.error('更新定时顺序失败: ' + (response.message || '未知错误'))
|
||||
// 如果失败,刷新数据恢复原状态
|
||||
emit('refresh')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('拖拽排序失败:', error)
|
||||
message.error('更新定时顺序失败: ' + (error?.message || '网络错误'))
|
||||
// 如果失败,刷新数据恢复原状态
|
||||
emit('refresh')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -768,6 +827,221 @@ const deleteTimeSet = async (timeSetId: string) => {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.ant-picker-time-panel-cell:hover) {
|
||||
background: var(--ant-color-fill-tertiary);
|
||||
}
|
||||
|
||||
/* 拖拽表格样式 */
|
||||
.draggable-table-container {
|
||||
width: 100%;
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.draggable-table-header {
|
||||
display: flex;
|
||||
background-color: var(--ant-color-fill-quaternary);
|
||||
border-bottom: 1px solid var(--ant-color-border);
|
||||
}
|
||||
|
||||
.header-cell {
|
||||
padding: 12px 16px;
|
||||
font-weight: 600;
|
||||
color: var(--ant-color-text);
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--ant-color-border);
|
||||
}
|
||||
|
||||
.header-cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.index-cell {
|
||||
width: 80px;
|
||||
min-width: 80px;
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
.status-cell {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.time-cell {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.actions-cell {
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
.draggable-container {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.draggable-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--ant-color-bg-container);
|
||||
border-bottom: 1px solid var(--ant-color-border);
|
||||
transition: all 0.2s ease;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.draggable-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.draggable-row:hover {
|
||||
background-color: var(--ant-color-fill-quaternary);
|
||||
}
|
||||
|
||||
.draggable-row.row-dragging {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.row-cell {
|
||||
padding: 12px 16px;
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--ant-color-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row-cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.row-cell.index-cell {
|
||||
width: 80px;
|
||||
min-width: 80px;
|
||||
max-width: 80px;
|
||||
font-weight: 500;
|
||||
color: var(--ant-color-text-secondary);
|
||||
}
|
||||
|
||||
.row-cell.status-cell {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.row-cell.time-cell {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.row-cell.actions-cell {
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
/* 拖拽状态样式 */
|
||||
.ghost {
|
||||
opacity: 0.5;
|
||||
background: var(--ant-color-primary-bg);
|
||||
border: 2px dashed var(--ant-color-primary);
|
||||
}
|
||||
|
||||
.chosen {
|
||||
background: var(--ant-color-primary-bg-hover);
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.drag {
|
||||
transform: rotate(5deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 空状态样式 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
max-width: 200px;
|
||||
height: auto;
|
||||
opacity: 0.9;
|
||||
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.1));
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.empty-image:hover {
|
||||
transform: translateY(-4px);
|
||||
filter: drop-shadow(0 12px 32px rgba(0, 0, 0, 0.15));
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.draggable-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.row-cell,
|
||||
.header-cell {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--ant-color-border);
|
||||
}
|
||||
|
||||
.row-cell:last-child,
|
||||
.header-cell:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.index-cell,
|
||||
.status-cell,
|
||||
.time-cell,
|
||||
.actions-cell {
|
||||
width: 100% !important;
|
||||
min-width: auto !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色主题滚动条样式 - 时间选择器 */
|
||||
.dark :deep(.ant-picker-time-panel-column)::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.dark :deep(.ant-picker-time-panel-column)::-webkit-scrollbar-track {
|
||||
background: var(--ant-color-bg-layout);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.dark :deep(.ant-picker-time-panel-column)::-webkit-scrollbar-thumb {
|
||||
background: var(--ant-color-border);
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.dark :deep(.ant-picker-time-panel-column)::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--ant-color-text-tertiary);
|
||||
}
|
||||
|
||||
/* 时间选择器面板项目样式优化 */
|
||||
:deep(.ant-picker-time-panel-cell) {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.ant-picker-time-panel-cell:hover) {
|
||||
background: var(--ant-color-fill-tertiary);
|
||||
}
|
||||
@@ -834,45 +1108,4 @@ const deleteTimeSet = async (timeSetId: string) => {
|
||||
[data-theme='dark'] .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
/* body类名方式的深色模式 */
|
||||
body.dark .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
body.dark .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar-track {
|
||||
background: #000000;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
body.dark .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar-thumb {
|
||||
background: #424242;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
body.dark .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
/* Firefox 支持 */
|
||||
.ant-picker-dropdown .ant-picker-time-panel-column {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #d9d9d9 #f5f5f5;
|
||||
}
|
||||
|
||||
:root.dark .ant-picker-dropdown .ant-picker-time-panel-column {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #424242 #000000;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .ant-picker-dropdown .ant-picker-time-panel-column {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #424242 #000000;
|
||||
}
|
||||
|
||||
body.dark .ant-picker-dropdown .ant-picker-time-panel-column {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #424242 #000000;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -362,7 +362,7 @@ const refreshTimeSets = async () => {
|
||||
|
||||
if (response.code !== 200) {
|
||||
console.error('获取定时项数据失败:', response)
|
||||
currentTimeSets.value = []
|
||||
// 不清空数组,避免骨架屏闪现
|
||||
return
|
||||
}
|
||||
|
||||
@@ -401,19 +401,19 @@ const refreshTimeSets = async () => {
|
||||
|
||||
// 使用nextTick确保数据更新不会导致渲染问题
|
||||
await nextTick()
|
||||
currentTimeSets.value = [...timeSets]
|
||||
// 直接替换数组内容,而不是清空再赋值,避免骨架屏闪现
|
||||
currentTimeSets.value.splice(0, currentTimeSets.value.length, ...timeSets)
|
||||
console.log('刷新后的定时项数据:', timeSets) // 调试日志
|
||||
} catch (error) {
|
||||
console.error('刷新定时项列表失败:', error)
|
||||
currentTimeSets.value = []
|
||||
// 不显示错误消息,避免干扰用户
|
||||
// 不清空数组,避免骨架屏闪现
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新队列项数据
|
||||
const refreshQueueItems = async () => {
|
||||
if (!activeQueueId.value) {
|
||||
currentQueueItems.value = []
|
||||
// 不清空数组,避免骨架屏闪现
|
||||
return
|
||||
}
|
||||
|
||||
@@ -425,7 +425,7 @@ const refreshQueueItems = async () => {
|
||||
|
||||
if (response.code !== 200) {
|
||||
console.error('获取队列项数据失败:', response)
|
||||
currentQueueItems.value = []
|
||||
// 不清空数组,避免骨架屏闪现
|
||||
return
|
||||
}
|
||||
|
||||
@@ -451,19 +451,14 @@ const refreshQueueItems = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 只在数据真正发生变化时才更新,避免不必要的界面闪烁
|
||||
const oldItemsString = JSON.stringify(currentQueueItems.value)
|
||||
const newItemsString = JSON.stringify(queueItems)
|
||||
|
||||
if (oldItemsString !== newItemsString) {
|
||||
currentQueueItems.value = [...queueItems]
|
||||
}
|
||||
|
||||
// 使用nextTick确保数据更新不会导致渲染问题
|
||||
await nextTick()
|
||||
// 直接替换数组内容,而不是清空再赋值,避免骨架屏闪现
|
||||
currentQueueItems.value.splice(0, currentQueueItems.value.length, ...queueItems)
|
||||
console.log('刷新后的队列项数据:', queueItems) // 调试日志
|
||||
} catch (error) {
|
||||
console.error('刷新队列项列表失败:', error)
|
||||
currentQueueItems.value = []
|
||||
// 不显示错误消息,避免干扰用户
|
||||
// 不清空数组,避免骨架屏闪现
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,19 +626,6 @@ const saveQueueData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 监听队列项变化,但避免频繁刷新导致的界面闪烁
|
||||
watch(
|
||||
() => currentQueueItems.value,
|
||||
(newItems, oldItems) => {
|
||||
// 深度比较避免不必要的更新
|
||||
if (JSON.stringify(newItems) !== JSON.stringify(oldItems)) {
|
||||
// 只有当数据真正改变时才触发更新
|
||||
console.log('队列项数据变化,触发更新')
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 自动保存功能
|
||||
watch(
|
||||
() => [
|
||||
|
||||
@@ -109,15 +109,15 @@ const clearLogs = () => {
|
||||
|
||||
.log-content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
background: var(--ant-color-bg-container);
|
||||
padding: 12px;
|
||||
background: var(--ant-color-bg-layout);
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.empty-state-mini {
|
||||
@@ -125,13 +125,13 @@ const clearLogs = () => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
height: 200px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
}
|
||||
|
||||
.empty-image-mini {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
max-width: 64px;
|
||||
height: auto;
|
||||
opacity: 0.5;
|
||||
margin-bottom: 8px;
|
||||
filter: var(--ant-color-scheme-dark, brightness(0.8));
|
||||
@@ -145,8 +145,8 @@ const clearLogs = () => {
|
||||
|
||||
.log-line {
|
||||
margin-bottom: 2px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
border-radius: 2px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@@ -198,9 +198,8 @@ const clearLogs = () => {
|
||||
}
|
||||
|
||||
.log-content {
|
||||
background: var(--ant-color-bg-container, #1f1f1f);
|
||||
background: var(--ant-color-bg-layout, #141414);
|
||||
border: 1px solid var(--ant-color-border, #424242);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.empty-state-mini {
|
||||
@@ -251,11 +250,3 @@ const clearLogs = () => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
@media (max-width: 768px) {
|
||||
.log-content {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -74,11 +74,6 @@ const getStatusColor = (status: string) => getQueueStatusColor(status)
|
||||
.queue-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 12px;
|
||||
background: var(--ant-color-bg-layout);
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.empty-state-mini {
|
||||
@@ -86,13 +81,13 @@ const getStatusColor = (status: string) => getQueueStatusColor(status)
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
height: 200px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
}
|
||||
|
||||
.empty-image-mini {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
max-width: 48px;
|
||||
height: auto;
|
||||
opacity: 0.5;
|
||||
margin-bottom: 8px;
|
||||
filter: var(--ant-color-scheme-dark, brightness(0.8));
|
||||
@@ -114,13 +109,11 @@ const getStatusColor = (status: string) => getQueueStatusColor(status)
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
background-color: var(--ant-color-bg-container);
|
||||
border: 1px solid var(--ant-color-border);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
border-color: var(--ant-color-border);
|
||||
}
|
||||
|
||||
.queue-card:hover {
|
||||
box-shadow: 0 2px 8px var(--ant-color-shadow);
|
||||
border-color: var(--ant-color-primary);
|
||||
}
|
||||
|
||||
.running-card {
|
||||
@@ -151,12 +144,6 @@ const getStatusColor = (status: string) => getQueueStatusColor(status)
|
||||
color: var(--ant-color-text-heading, #ffffff);
|
||||
}
|
||||
|
||||
.queue-content {
|
||||
background: var(--ant-color-bg-layout, #141414);
|
||||
border: 1px solid var(--ant-color-border, #424242);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.empty-state-mini {
|
||||
color: var(--ant-color-text-tertiary, #8c8c8c);
|
||||
}
|
||||
@@ -172,17 +159,19 @@ const getStatusColor = (status: string) => getQueueStatusColor(status)
|
||||
.queue-card {
|
||||
background-color: var(--ant-color-bg-container, #1f1f1f);
|
||||
border-color: var(--ant-color-border, #424242);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.queue-card:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.running-card {
|
||||
border-color: var(--ant-color-primary, #1890ff);
|
||||
box-shadow: 0 0 0 1px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.item-name {
|
||||
color: var(--ant-color-text, #ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.queue-content {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -131,70 +131,33 @@ const filterTaskOption = (input: string, option: any) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
background: var(--ant-color-bg-container);
|
||||
padding: 16px;
|
||||
background: var(--ant-color-bg-layout);
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.control-row:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.control-label {
|
||||
font-weight: 500;
|
||||
color: var(--ant-color-text);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.control-select {
|
||||
min-width: 200px;
|
||||
/* 暗色模式适配 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.control-row {
|
||||
background: var(--ant-color-bg-layout, #141414);
|
||||
border: 1px solid var(--ant-color-border, #424242);
|
||||
}
|
||||
}
|
||||
|
||||
.control-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
/* 暗色模式适配 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.control-row {
|
||||
background: var(--ant-color-bg-container, #1f1f1f);
|
||||
border: 1px solid var(--ant-color-border, #424242);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.control-row:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.control-label {
|
||||
color: var(--ant-color-text, #ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.control-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.control-select {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.control-spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -225,21 +225,22 @@ onUnmounted(() => {
|
||||
<style scoped>
|
||||
/* 页面容器 */
|
||||
.scheduler-page {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background: var(--ant-color-bg-layout);
|
||||
padding: 24px;
|
||||
background-color: var(--ant-color-bg-container);
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
|
||||
/* 页面头部样式 */
|
||||
.scheduler-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 24px;
|
||||
padding: 0 4px;
|
||||
align-items: center;
|
||||
padding: 0 4px 24px;
|
||||
flex-shrink: 0;
|
||||
background-color: var(--ant-color-bg-layout);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
@@ -247,7 +248,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0 0 8px 0;
|
||||
margin: 0;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--ant-color-text);
|
||||
@@ -276,9 +277,6 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background-color: var(--ant-color-bg-container);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.scheduler-tabs :deep(.ant-tabs) {
|
||||
@@ -303,12 +301,10 @@ onUnmounted(() => {
|
||||
.scheduler-tabs :deep(.ant-tabs-tab) {
|
||||
background-color: var(--ant-color-bg-layout);
|
||||
border-color: var(--ant-color-border);
|
||||
border-radius: 6px 6px 0 0;
|
||||
}
|
||||
|
||||
.scheduler-tabs :deep(.ant-tabs-tab-active) {
|
||||
background-color: var(--ant-color-bg-container);
|
||||
border-bottom: 1px solid var(--ant-color-bg-container);
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
@@ -337,7 +333,6 @@ onUnmounted(() => {
|
||||
.status-row {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.status-row :deep(.ant-col) {
|
||||
@@ -371,10 +366,14 @@ onUnmounted(() => {
|
||||
/* 暗色模式适配 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.scheduler-page {
|
||||
background-color: var(--ant-color-bg-layout, #141414);
|
||||
background-color: var(--ant-color-bg-container, #1f1f1f);
|
||||
color: var(--ant-color-text, #ffffff);
|
||||
}
|
||||
|
||||
.scheduler-header {
|
||||
background-color: var(--ant-color-bg-layout, #141414);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--ant-color-text, #ffffff);
|
||||
}
|
||||
@@ -385,7 +384,6 @@ onUnmounted(() => {
|
||||
|
||||
.scheduler-tabs {
|
||||
background-color: var(--ant-color-bg-container, #1f1f1f);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.scheduler-tabs :deep(.ant-tabs) {
|
||||
@@ -409,7 +407,6 @@ onUnmounted(() => {
|
||||
.scheduler-tabs :deep(.ant-tabs-tab-active) {
|
||||
background-color: var(--ant-color-bg-container, #1f1f1f);
|
||||
color: var(--ant-color-text, #ffffff);
|
||||
border-bottom: 1px solid var(--ant-color-bg-container, #1f1f1f);
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
@@ -449,25 +446,16 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.scheduler-page {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.scheduler-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.scheduler-tabs {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.status-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ export const TAB_STATUS_COLOR: Record<SchedulerStatus, string> = {
|
||||
|
||||
// 队列状态 -> 颜色
|
||||
export const getQueueStatusColor = (status: string): string => {
|
||||
if (/成功|完成|已完成/.test(status)) return 'success'
|
||||
if (/失败|错误|异常/.test(status)) return 'error'
|
||||
if (/等待|排队|挂起/.test(status)) return 'warning'
|
||||
if (/进行|执行|运行/.test(status)) return 'processing'
|
||||
if (/成功|完成|已完成/.test(status)) return 'green'
|
||||
if (/失败|错误|异常/.test(status)) return 'red'
|
||||
if (/等待|排队|挂起/.test(status)) return 'orange'
|
||||
if (/进行|执行|运行/.test(status)) return 'blue'
|
||||
return 'default'
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export const POWER_ACTION_TEXT: Record<PowerIn.signal, string> = {
|
||||
[PowerIn.signal.SHUTDOWN_FORCE]: '强制关机',
|
||||
}
|
||||
|
||||
export const getPowerActionText = (action: PowerIn.signal) => POWER_ACTION_TEXT[action] || '无动作'
|
||||
export const getPowerActionText = (action: PowerIn.signal) => POWER_ACTION_TEXT[action] || '无动<EFBFBD><EFBFBD>'
|
||||
|
||||
// 日志相关
|
||||
export const LOG_MAX_LENGTH = 2000 // 最多保留日志条数
|
||||
@@ -77,4 +77,4 @@ export interface TaskMessage {
|
||||
needInput: boolean
|
||||
messageId?: string
|
||||
taskId?: string
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user