feat(QueueItemManager, TimeSetManager): 增加定时列表和任务列表的拖拽功能
This commit is contained in:
@@ -11,49 +11,67 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-table
|
<!-- 使用vuedraggable替换a-table实现拖拽功能 -->
|
||||||
:columns="queueColumns"
|
<div class="draggable-table-container">
|
||||||
:data-source="queueItems"
|
<!-- 表头 -->
|
||||||
:pagination="false"
|
<div class="draggable-table-header">
|
||||||
size="middle"
|
<div class="header-cell index-cell">序号</div>
|
||||||
:scroll="{ x: false, y: false }"
|
<div class="header-cell script-cell">脚本任务</div>
|
||||||
table-layout="auto"
|
<div class="header-cell actions-cell">操作</div>
|
||||||
class="queue-table"
|
</div>
|
||||||
>
|
|
||||||
<template #emptyText>
|
<!-- 拖拽内容区域 -->
|
||||||
|
<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>
|
||||||
|
</draggable>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-if="queueItems.length === 0" class="empty-state">
|
||||||
<span>暂无任务</span>
|
<span>暂无任务</span>
|
||||||
</template>
|
</div>
|
||||||
<template #bodyCell="{ column, record, index }">
|
</div>
|
||||||
<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
|
|
||||||
/>
|
|
||||||
</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>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -61,6 +79,7 @@
|
|||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
import { Service } from '@/api'
|
import { Service } from '@/api'
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
@@ -211,6 +230,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(() => {
|
onMounted(() => {
|
||||||
loadOptions()
|
loadOptions()
|
||||||
@@ -474,6 +531,124 @@ onMounted(() => {
|
|||||||
padding: 40px 0;
|
padding: 40px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 拖拽表格样式 */
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.queue-items-grid {
|
.queue-items-grid {
|
||||||
@@ -489,6 +664,30 @@ onMounted(() => {
|
|||||||
.queue-item-card-item {
|
.queue-item-card-item {
|
||||||
padding: 12px;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标签样式 */
|
/* 标签样式 */
|
||||||
|
|||||||
@@ -16,60 +16,78 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-table
|
<!-- 使用vuedraggable替换a-table实现拖拽功能 -->
|
||||||
:columns="timeColumns"
|
<div class="draggable-table-container">
|
||||||
:data-source="timeSets"
|
<!-- 表头 -->
|
||||||
:pagination="false"
|
<div class="draggable-table-header">
|
||||||
size="middle"
|
<div class="header-cell index-cell">序号</div>
|
||||||
:scroll="{ x: false, y: false }"
|
<div class="header-cell status-cell">状态</div>
|
||||||
table-layout="auto"
|
<div class="header-cell time-cell">执行时间</div>
|
||||||
class="time-set-table"
|
<div class="header-cell actions-cell">操作</div>
|
||||||
>
|
</div>
|
||||||
<template #emptyText>
|
|
||||||
|
<!-- 拖拽内容区域 -->
|
||||||
|
<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">
|
||||||
<span>暂无定时</span>
|
<span>暂无定时</span>
|
||||||
</template>
|
</div>
|
||||||
<template #bodyCell="{ column, record }">
|
</div>
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -77,6 +95,7 @@
|
|||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
import { Service } from '@/api'
|
import { Service } from '@/api'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
@@ -159,7 +178,7 @@ const timeSets = ref([...props.timeSets])
|
|||||||
const processTimeSets = (rawTimeSets: any[]) => {
|
const processTimeSets = (rawTimeSets: any[]) => {
|
||||||
return rawTimeSets.map(item => ({
|
return rawTimeSets.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
timeValue: parseTimeString(item.time)
|
timeValue: parseTimeString(item.time),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +316,44 @@ const deleteTimeSet = async (timeSetId: string) => {
|
|||||||
message.error('删除定时项失败: ' + (error?.message || '网络错误'))
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -771,6 +828,172 @@ const deleteTimeSet = async (timeSetId: string) => {
|
|||||||
:deep(.ant-picker-time-panel-cell:hover) {
|
:deep(.ant-picker-time-panel-cell:hover) {
|
||||||
background: var(--ant-color-fill-tertiary);
|
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 {
|
||||||
|
padding: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
background: var(--ant-color-bg-container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- 全局样式 - 用于时间选择器弹出面板 -->
|
<!-- 全局样式 - 用于时间选择器弹出面板 -->
|
||||||
@@ -831,7 +1054,9 @@ const deleteTimeSet = async (timeSetId: string) => {
|
|||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='dark'] .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar-thumb:hover {
|
[data-theme='dark']
|
||||||
|
.ant-picker-dropdown
|
||||||
|
.ant-picker-time-panel-column::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: rgba(255, 255, 255, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user