style:甘特图菜单样式调整

pull/1/head
黄伟杰 2 weeks ago
parent 4f481974a5
commit 55cc7b8d67

@ -1,42 +1,45 @@
<template>
<div class="schedule-preview-wrap">
<div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
<div class="schedule-detail-panel">
<div class="schedule-detail-title">{{ t('GanttChart.GanttPanel.detailTitle') }}</div>
<div v-if="!editable" class="schedule-status-legend">
<div
v-for="item in sortedPlanStatusList"
:key="item.key"
class="legend-item"
:style="{ borderLeftColor: item.color }"
>
<div class="gantt-main-area">
<div class="schedule-status-legend">
<div v-for="item in sortedPlanStatusList" :key="item.key" class="legend-item"
:style="{ borderLeftColor: item.color }">
<span class="legend-color" :style="{ backgroundColor: item.color }"></span>
<span class="legend-label">{{ item.label }}</span>
</div>
</div>
<div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
</div>
<div class="schedule-detail-panel">
<div class="schedule-detail-title">{{ t('GanttChart.GanttPanel.detailTitle') }}</div>
<template v-if="activePreviewDevice">
<el-descriptions :column="1" border size="small">
<el-descriptions-item :label="t('GanttChart.GanttPanel.deviceNameLabel')">{{ activePreviewDevice.deviceName }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.deviceIdLabel')">{{ activePreviewDevice.deviceId }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.capacityLabel')">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dailyAverageValue' in activePreviewDevice" :label="t('GanttChart.GanttPanel.dailyAvgLabel')">{{ activePreviewDevice.dailyAverageValue ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dataCollectionCapacity' in activePreviewDevice" :label="t('GanttChart.GanttPanel.dataCollectionCapacityLabel')">{{ activePreviewDevice.dataCollectionCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.planCountLabel')">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.deviceNameLabel')">{{ activePreviewDevice.deviceName
}}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.deviceIdLabel')">{{ activePreviewDevice.deviceId
}}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.capacityLabel')">{{ activePreviewDevice.ratedCapacity
?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dailyAverageValue' in activePreviewDevice"
:label="t('GanttChart.GanttPanel.dailyAvgLabel')">{{ activePreviewDevice.dailyAverageValue ?? '-'
}}</el-descriptions-item>
<el-descriptions-item v-if="'dataCollectionCapacity' in activePreviewDevice"
:label="t('GanttChart.GanttPanel.dataCollectionCapacityLabel')">{{
activePreviewDevice.dataCollectionCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.planCountLabel')">{{ activePreviewDevice.plans?.length
?? 0 }}</el-descriptions-item>
</el-descriptions>
<div class="schedule-plan-list-title">{{ t('GanttChart.GanttPanel.planDetailTitle') }}</div>
<div class="schedule-plan-list">
<div
v-for="(plan, index) in activePreviewTask ? [activePreviewTask] : (activePreviewDevice?.plans ?? [])"
<div v-for="(plan, index) in activePreviewTask ? [activePreviewTask] : (activePreviewDevice?.plans ?? [])"
:key="`${activePreviewDevice?.deviceId}-${plan.taskDetailId}-${index}`"
:class="['schedule-plan-item', { 'schedule-plan-item-active': plan.sourceType === 'CURRENT' }]"
>
:class="['schedule-plan-item', { 'schedule-plan-item-active': plan.sourceType === 'CURRENT' }]">
<div class="schedule-plan-item-head">
<span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}</span>
<span
v-if="plan.planStatus && PLAN_STATUS_COLOR_MAP[plan.planStatus]"
class="plan-status-tag"
:style="{ backgroundColor: PLAN_STATUS_COLOR_MAP[plan.planStatus].color }"
>
<span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-'
}}</span>
<span v-if="plan.planStatus && PLAN_STATUS_COLOR_MAP[plan.planStatus]" class="plan-status-tag"
:style="{ backgroundColor: PLAN_STATUS_COLOR_MAP[plan.planStatus].color }">
{{ PLAN_STATUS_COLOR_MAP[plan.planStatus].label }}
</span>
</div>
@ -53,21 +56,18 @@
</div>
</div>
<el-dialog v-if="props.editable" v-model="taskAdjustDialogVisible" :title="t('GanttChart.GanttPanel.adjustTaskTitle')" width="420px" append-to-body>
<el-dialog v-if="props.editable" v-model="taskAdjustDialogVisible" :title="t('GanttChart.GanttPanel.adjustTaskTitle')"
width="420px" append-to-body>
<el-form label-width="90px">
<el-form-item :label="t('GanttChart.GanttPanel.deviceLabel')">
<el-select v-model="taskAdjustForm.deviceTaskId" :placeholder="t('GanttChart.GanttPanel.devicePlaceholder')" class="!w-full">
<el-select v-model="taskAdjustForm.deviceTaskId" :placeholder="t('GanttChart.GanttPanel.devicePlaceholder')"
class="!w-full">
<el-option v-for="item in previewDeviceOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('GanttChart.GanttPanel.startDateLabel')">
<el-date-picker
v-model="taskAdjustForm.startDate"
type="date"
value-format="YYYY-MM-DD"
:placeholder="t('GanttChart.GanttPanel.startDatePlaceholder')"
class="!w-full"
/>
<el-date-picker v-model="taskAdjustForm.startDate" type="date" value-format="YYYY-MM-DD"
:placeholder="t('GanttChart.GanttPanel.startDatePlaceholder')" class="!w-full" />
</el-form-item>
<el-form-item :label="t('GanttChart.GanttPanel.durationLabel')">
<el-input-number v-model="taskAdjustForm.duration" :min="1" :max="365" class="!w-full" />
@ -75,25 +75,23 @@
</el-form>
<template #footer>
<el-button @click="taskAdjustDialogVisible = false">{{ t('GanttChart.GanttPanel.buttonCancel') }}</el-button>
<el-button type="primary" @click="handleTaskAdjustSubmit">{{ t('GanttChart.GanttPanel.buttonConfirm') }}</el-button>
<el-button type="primary" @click="handleTaskAdjustSubmit">{{ t('GanttChart.GanttPanel.buttonConfirm')
}}</el-button>
</template>
</el-dialog>
<el-dialog v-if="props.editable" v-model="startDateEditorVisible" :title="t('GanttChart.GanttPanel.editStartDateTitle')" width="360px" append-to-body>
<el-dialog v-if="props.editable" v-model="startDateEditorVisible"
:title="t('GanttChart.GanttPanel.editStartDateTitle')" width="360px" append-to-body>
<el-form label-width="80px">
<el-form-item :label="t('GanttChart.GanttPanel.startTimeLabel')">
<el-date-picker
v-model="startDateEditorValue"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('GanttChart.GanttPanel.startTimePlaceholder')"
class="!w-full"
/>
<el-date-picker v-model="startDateEditorValue" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('GanttChart.GanttPanel.startTimePlaceholder')" class="!w-full" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="startDateEditorVisible = false">{{ t('GanttChart.GanttPanel.buttonCancel') }}</el-button>
<el-button type="primary" @click="handleStartDateEditorSubmit">{{ t('GanttChart.GanttPanel.buttonConfirm') }}</el-button>
<el-button type="primary" @click="handleStartDateEditorSubmit">{{ t('GanttChart.GanttPanel.buttonConfirm')
}}</el-button>
</template>
</el-dialog>
</template>
@ -702,20 +700,29 @@ const initGanttPreview = () => {
return
}
gantt.plugins({ tooltip: true, inline_edit: true,undo: true })
gantt.config.readonly = !props.editable
gantt.config.drag_move = !!props.editable
gantt.config.drag_links = false
gantt.config.drag_progress = false
gantt.config.drag_resize = !!props.editable
gantt.config.order_branch = !!props.editable
gantt.config.order_branch_free = !!props.editable
gantt.config.details_on_dblclick = false
gantt.config.show_progress = false
gantt.config.row_height = 40
gantt.config.scale_height = 44
gantt.config.xml_date = '%Y-%m-%d %H:%i'
gantt.config.task_height = 24
//
gantt.plugins({ tooltip: true, inline_edit: true, undo: true })
//
gantt.config.readonly = !props.editable //
gantt.config.drag_move = !!props.editable //
gantt.config.drag_links = false //
gantt.config.drag_progress = false //
gantt.config.drag_resize = !!props.editable //
gantt.config.order_branch = !!props.editable //
gantt.config.order_branch_free = !!props.editable //
gantt.config.details_on_dblclick = false //
gantt.config.show_progress = false //
//
gantt.config.row_height = 48 //
gantt.config.scale_height = 70 //
gantt.config.xml_date = '%Y-%m-%d %H:%i' //
gantt.config.task_height = 36 //
gantt.config.min_column_width = 80 //
gantt.config.column_width = 90 //
gantt.config.bar_height = 34 //
gantt.config.resize_step = 15 //
gantt.config.columns = [
{
@ -760,7 +767,7 @@ const initGanttPreview = () => {
gantt.templates.tooltip_text = (start, end, task: any) => buildTaskTooltipHtml(task, start, end)
gantt.templates.task_class = (_start, _end, task: any) => {
if (!task?._planData) return ''
// editable false planStatus
if (!props.editable) {
const planStatus = task._planData?.planStatus
@ -780,7 +787,7 @@ const initGanttPreview = () => {
}
return statusMap[planStatus] || 'schedule-plan-task-default'
}
// editable true 使
const sourceType = String(task?._planData?.sourceType ?? '').toUpperCase()
return sourceType === 'HISTORY' ? 'schedule-plan-task-history' : 'schedule-plan-task'
@ -794,12 +801,12 @@ const initGanttPreview = () => {
const ganttData = buildPreviewGanttData(previewScheduleList.value)
gantt.parse(ganttData)
//
gantt.eachTask((task: any) => {
gantt.refreshTask(task.id)
})
initTaskTooltips()
if (ganttData.data.length) {
@ -858,7 +865,7 @@ const initGanttPreview = () => {
startDateEditorTaskId.value = taskId
startDateEditorValue.value = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
startDateEditorVisible.value = true
} catch {}
} catch { }
}
ganttContainerRef.value.addEventListener('click', gridClickHandler, true)
tooltipCleanupFns.value.push(() => {
@ -988,11 +995,20 @@ onBeforeUnmount(() => {
.schedule-preview-wrap {
display: flex;
width: 100%;
height: 100%;
gap: 12px;
overflow: hidden;
}
.schedule-gantt-container {
.gantt-main-area {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.schedule-gantt-container {
min-width: 0;
border: 1px solid var(--el-border-color);
border-radius: 4px;
@ -1010,9 +1026,7 @@ onBeforeUnmount(() => {
}
.schedule-status-legend {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 8px;
display: flex;
margin-bottom: 12px;
padding: 8px;
background: var(--el-fill-color-light);
@ -1026,6 +1040,7 @@ onBeforeUnmount(() => {
padding: 4px 8px;
border-left: 3px solid;
font-size: 12px;
margin-right: 20px
}
.legend-color {
@ -1052,15 +1067,18 @@ onBeforeUnmount(() => {
overflow: auto;
max-height: 55vh;
border: 1px solid var(--el-border-color);
border-radius: 4px;
border-radius: 8px;
padding: 8px;
}
.schedule-plan-item {
padding: 8px;
border-radius: 4px;
border-radius: 8px;
background: var(--el-fill-color-light);
font-size: 14px;
line-height: 1.4;
}
.schedule-plan-item-head {
display: flex;
align-items: center;
@ -1068,55 +1086,135 @@ onBeforeUnmount(() => {
gap: 8px;
margin-bottom: 4px;
}
.schedule-plan-item-title {
font-weight: 600;
color: var(--el-text-color-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 16px;
}
.plan-status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 2px;
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
font-size: 10px;
color: #ffffff;
font-weight: 600;
white-space: nowrap;
flex-shrink: 0;
}
.schedule-plan-item-active {
border: 1px solid var(--el-color-success-light-5);
background: var(--el-color-success-light-9);
}
.schedule-plan-item + .schedule-plan-item {
.schedule-plan-item+.schedule-plan-item {
margin-top: 8px;
}
.schedule-gantt-container :deep(.gantt_grid_data .gantt_row) {
font-weight: 600;
background: linear-gradient(180deg, #f8fafc 0%, #f0f4f8 100%);
border-bottom: 1px solid #e2e8f0;
height: 48px;
display: flex;
align-items: flex-end;
}
.schedule-gantt-container :deep(.gantt_grid_data .gantt_row:nth-child(even)) {
background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
}
.schedule-gantt-container :deep(.gantt_grid_data .gantt_row:hover) {
background: linear-gradient(180deg, #f3f4f6 0%, #e5e7eb 100%) !important;
}
.schedule-gantt-container :deep(.gantt_grid_data .gantt_cell) {
text-overflow: clip;
font-size: 13px;
color: #4b5563;
border-right: 1px solid #e5e7eb;
padding: 0 12px;
line-height: 36px;
display: flex;
align-items: center;
justify-content: flex-start;
}
.schedule-gantt-container :deep(.gantt_scale_line) {
border-bottom: 2px solid #d1d5db;
background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
display: flex;
align-items: center;
margin-bottom: 2px;
}
.schedule-gantt-container :deep(.gantt_scale_line:last-child) {
margin-bottom: 0;
border-bottom: 1px solid #e5e7eb;
}
.schedule-gantt-container :deep(.gantt_scale_cell) {
font-size: 14px;
font-weight: 600;
color: #374151;
border-right: 1px solid #e5e7eb;
padding: 0 8px;
text-align: center;
background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
display: flex;
align-items: center;
justify-content: center;
height: 35px;
}
.schedule-gantt-container :deep(.gantt_scale_line:last-child .gantt_scale_cell) {
font-size: 13px;
font-weight: 500;
color: #6b7280;
background: linear-gradient(180deg, #f9fafb 0%, #f3f4f6 100%);
border-bottom: 1px solid #e5e7eb;
height: 35px;
}
.schedule-gantt-container :deep(.gantt_task_bg) {
background: #ffffff;
}
.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_cell) {
border-right-width: 2px;
border-right: 1px solid #f3f4f6;
border-bottom: 1px solid #f3f4f6;
}
.schedule-gantt-container :deep(.gantt-inline-editor-trigger) {
display: inline-block;
width: 100%;
overflow: visible;
cursor: pointer;
.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_row:nth-child(even)) {
background: #f9fafb;
}
.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_row:hover) {
background: #f3f4f6 !important;
}
.schedule-gantt-container :deep(.gantt_task_line) {
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border: none;
}
.schedule-gantt-container :deep(.gantt_task_content) {
font-size: 12px;
font-weight: 500;
color: #ffffff;
padding: 0 12px;
line-height: 32px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task) {
background: #67c23a;
border-color: #67c23a;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task .gantt_task_content) {
@ -1125,8 +1223,7 @@ onBeforeUnmount(() => {
/* 已排产状态 - 蓝色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-1) {
background: #409eff !important;
border-color: #409eff !important;
background: #3b82f6 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-1 .gantt_task_content) {
@ -1135,8 +1232,7 @@ onBeforeUnmount(() => {
/* 已开工状态 - 绿色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-8) {
background: #67c23a !important;
border-color: #67c23a !important;
background: #10b981 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-8 .gantt_task_content) {
@ -1145,8 +1241,7 @@ onBeforeUnmount(() => {
/* 暂停状态 - 橙色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-3) {
background: #e6a23c !important;
border-color: #e6a23c !important;
background: #f59e0b !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-3 .gantt_task_content) {
@ -1155,8 +1250,7 @@ onBeforeUnmount(() => {
/* 待入库状态 - 红色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-4) {
background: #f56c6c !important;
border-color: #f56c6c !important;
background: #ef4444 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-4 .gantt_task_content) {
@ -1165,8 +1259,7 @@ onBeforeUnmount(() => {
/* 已入库状态 - 紫色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-5) {
background: #8e7cc3 !important;
border-color: #8e7cc3 !important;
background: #8b5cf6 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-5 .gantt_task_content) {
@ -1175,8 +1268,7 @@ onBeforeUnmount(() => {
/* 其他状态 - 深灰 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-2) {
background: #606266 !important;
border-color: #606266 !important;
background: #4b5563 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-2 .gantt_task_content) {
@ -1185,17 +1277,17 @@ onBeforeUnmount(() => {
/* 默认状态 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-default) {
background: #909399;
border-color: #909399;
background: #6b7280;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-default .gantt_task_content) {
color: #ffffff;
}
/* 历史任务 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-history) {
background: #909399;
border-color: #909399;
background: #9ca3af;
opacity: 0.7;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-history .gantt_task_content) {
@ -1204,5 +1296,28 @@ onBeforeUnmount(() => {
:deep(.gantt_tooltip) {
z-index: 5000 !important;
background: rgba(255, 255, 255, 0.95) !important;
border: 1px solid #e5e7eb !important;
border-radius: 8px !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
padding: 12px !important;
font-size: 13px !important;
line-height: 1.5 !important;
}
:deep(.gantt_tooltip) div {
margin-bottom: 6px !important;
}
:deep(.gantt_tooltip) div:last-child {
margin-bottom: 0 !important;
}
:deep(.gantt_tree_content) {
line-height: 50px;
}
:deep(.gantt_tree_icon.gantt_close:before) {
margin-top: 5px;
}
</style>

Loading…
Cancel
Save