Version: 0.8.3.dev.260328
后端: 1.彻底删除原agent文件夹,并将现agent2文件夹全量重命名为agent(包括全部涉及到的文件以及文档、注释),迁移工作完美结束 2.修复了重试消息的相关逻辑问题 前端: 1.改善了一些交互体验,修复了一些bug,现在只剩少的功能了,现存的bug基本都修复完毕 全仓库: 1.更新了决策记录和README文档
This commit is contained in:
@@ -72,7 +72,6 @@ function resolveDetailPanelStyle(items: TaskClassDetail['items']) {
|
||||
// 2. 条目超过“当前屏幕可安全展示的最大条数”后,立即锁住高度并进入内部滚动。
|
||||
// 3. 这样像 8 条 task_item 这类中等长度列表会稳定触发滚动,不会再因为估算过大而失效。
|
||||
return {
|
||||
height: `${finalHeight}px`,
|
||||
maxHeight: `${finalHeight}px`,
|
||||
}
|
||||
}
|
||||
@@ -299,13 +298,15 @@ watch(
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
align-content: start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 14px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.task-class-sidebar__skeleton-item {
|
||||
flex: 0 0 auto;
|
||||
height: 120px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(90deg, rgba(234, 239, 246, 0.9), rgba(248, 251, 255, 1), rgba(234, 239, 246, 0.9));
|
||||
@@ -314,6 +315,7 @@ watch(
|
||||
}
|
||||
|
||||
.task-class-card {
|
||||
flex: 0 0 auto;
|
||||
min-width: 0;
|
||||
border-radius: 24px;
|
||||
border: 1px solid rgba(216, 225, 238, 0.9);
|
||||
@@ -330,6 +332,7 @@ watch(
|
||||
.task-class-card__summary {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 92px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 18px 20px 18px 18px;
|
||||
@@ -394,7 +397,9 @@ watch(
|
||||
}
|
||||
|
||||
.task-class-card__detail {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 0 14px 14px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@@ -490,6 +495,7 @@ watch(
|
||||
}
|
||||
|
||||
.task-class-sidebar__create {
|
||||
flex: 0 0 auto;
|
||||
min-width: 0;
|
||||
min-height: 108px;
|
||||
border: 1px dashed rgba(204, 216, 232, 0.92);
|
||||
@@ -542,6 +548,7 @@ watch(
|
||||
|
||||
.task-class-card__summary {
|
||||
padding: 16px 16px 16px 15px;
|
||||
min-height: 84px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,6 +597,7 @@ watch(
|
||||
|
||||
.task-class-card__summary {
|
||||
padding: 14px 14px 14px 13px;
|
||||
min-height: 76px;
|
||||
}
|
||||
|
||||
.task-class-card__content {
|
||||
@@ -616,6 +624,7 @@ watch(
|
||||
|
||||
.task-class-card__summary {
|
||||
padding: 12px;
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
.task-class-card__corner {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import type { ScheduleWeekData, ScheduleWeekEvent } from '@/types/schedule'
|
||||
|
||||
@@ -15,18 +15,31 @@ interface SectionSlot {
|
||||
timeRange: string
|
||||
}
|
||||
|
||||
interface PreviewMovePayload {
|
||||
week: number
|
||||
sourceDayOfWeek: number
|
||||
sourceOrder: number
|
||||
targetDayOfWeek: number
|
||||
targetOrder: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
weekLabel: string
|
||||
weekHeaders: WeekDayHeader[]
|
||||
weekData: ScheduleWeekData | null
|
||||
scheduleSelectionMode: boolean
|
||||
selectedScheduleEventIds: number[]
|
||||
previewDragEnabled: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleScheduleEvent: [eventId: number]
|
||||
movePreviewEvent: [payload: PreviewMovePayload]
|
||||
}>()
|
||||
|
||||
const draggingCellKey = ref<string | null>(null)
|
||||
const dragOverCellKey = ref<string | null>(null)
|
||||
|
||||
const sectionSlots: SectionSlot[] = [
|
||||
{ order: 1, title: '1-2', timeRange: '08:00\n09:40' },
|
||||
{ order: 2, title: '3-4', timeRange: '10:15\n11:55' },
|
||||
@@ -54,11 +67,24 @@ function isSelected(eventId: number) {
|
||||
return props.selectedScheduleEventIds.includes(eventId)
|
||||
}
|
||||
|
||||
function hasEmbeddedTask(event?: ScheduleWeekEvent) {
|
||||
return Boolean(
|
||||
event &&
|
||||
event.type === 'course' &&
|
||||
event.embedded_task_info &&
|
||||
event.embedded_task_info.id > 0,
|
||||
)
|
||||
}
|
||||
|
||||
function resolveEventTone(event?: ScheduleWeekEvent) {
|
||||
if (!event || event.type === 'empty') {
|
||||
return 'empty'
|
||||
}
|
||||
|
||||
if (hasEmbeddedTask(event)) {
|
||||
return 'course-embedded'
|
||||
}
|
||||
|
||||
if (event.type === 'course') {
|
||||
return 'course'
|
||||
}
|
||||
@@ -88,6 +114,149 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
}
|
||||
return event.location || '未定'
|
||||
}
|
||||
|
||||
function resolveEmbeddedTaskName(event?: ScheduleWeekEvent) {
|
||||
if (!hasEmbeddedTask(event)) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return event!.embedded_task_info.name
|
||||
}
|
||||
|
||||
// isSuggestedPreviewEvent 负责判断当前格子是否允许作为“拖拽源”。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 这里只判断前端交互条件,不负责真正改写 preview JSON。
|
||||
// 2. 只有 preview 模式下的 suggested 条目才允许拖拽,正式课表与普通课程保持只读。
|
||||
function isSuggestedPreviewEvent(event?: ScheduleWeekEvent) {
|
||||
return Boolean(
|
||||
props.previewDragEnabled &&
|
||||
!props.scheduleSelectionMode &&
|
||||
event &&
|
||||
event.status === 'suggested',
|
||||
)
|
||||
}
|
||||
|
||||
function isEmbeddedSuggestedPreviewEvent(event?: ScheduleWeekEvent) {
|
||||
return Boolean(
|
||||
isSuggestedPreviewEvent(event) &&
|
||||
event &&
|
||||
event.type === 'course' &&
|
||||
hasEmbeddedTask(event),
|
||||
)
|
||||
}
|
||||
|
||||
function isWholeCellDraggable(event?: ScheduleWeekEvent) {
|
||||
return Boolean(isSuggestedPreviewEvent(event) && !isEmbeddedSuggestedPreviewEvent(event))
|
||||
}
|
||||
|
||||
// canDropPreviewEvent 负责判断当前格子是否允许作为“拖拽目标”。
|
||||
//
|
||||
// 设计说明:
|
||||
// 1. 空白格允许放置 suggested 任务。
|
||||
// 2. 课程格允许接收 suggested 任务,父组件会把它转换成“嵌入课程”的预览结构。
|
||||
// 3. suggested 格本身也允许作为目标,用于交换两个建议任务的位置。
|
||||
function canDropPreviewEvent(event?: ScheduleWeekEvent) {
|
||||
if (!props.previewDragEnabled || props.scheduleSelectionMode) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!event || event.type === 'empty') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.status === 'suggested') {
|
||||
return true
|
||||
}
|
||||
|
||||
return event.type === 'course'
|
||||
}
|
||||
|
||||
function buildCellKey(dayOfWeek: number, order: number) {
|
||||
return `${dayOfWeek}-${order}`
|
||||
}
|
||||
|
||||
function handlePreviewDragStart(dayOfWeek: number, order: number, dragEvent: DragEvent) {
|
||||
const event = resolveEvent(dayOfWeek, order)
|
||||
if (!isSuggestedPreviewEvent(event) || !props.weekData) {
|
||||
dragEvent.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
draggingCellKey.value = buildCellKey(dayOfWeek, order)
|
||||
dragOverCellKey.value = null
|
||||
|
||||
dragEvent.dataTransfer?.setData(
|
||||
'application/json',
|
||||
JSON.stringify({
|
||||
week: props.weekData.week,
|
||||
sourceDayOfWeek: dayOfWeek,
|
||||
sourceOrder: order,
|
||||
}),
|
||||
)
|
||||
if (dragEvent.dataTransfer) {
|
||||
dragEvent.dataTransfer.effectAllowed = 'move'
|
||||
}
|
||||
}
|
||||
|
||||
function handlePreviewDragOver(dayOfWeek: number, order: number, dragEvent: DragEvent) {
|
||||
if (!draggingCellKey.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const cellKey = buildCellKey(dayOfWeek, order)
|
||||
if (cellKey === draggingCellKey.value || !canDropPreviewEvent(resolveEvent(dayOfWeek, order))) {
|
||||
return
|
||||
}
|
||||
|
||||
dragEvent.preventDefault()
|
||||
dragOverCellKey.value = cellKey
|
||||
if (dragEvent.dataTransfer) {
|
||||
dragEvent.dataTransfer.dropEffect = 'move'
|
||||
}
|
||||
}
|
||||
|
||||
function handlePreviewDrop(dayOfWeek: number, order: number, dragEvent: DragEvent) {
|
||||
if (!draggingCellKey.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const cellKey = buildCellKey(dayOfWeek, order)
|
||||
const payloadText = dragEvent.dataTransfer?.getData('application/json')
|
||||
if (!payloadText || cellKey === draggingCellKey.value || !canDropPreviewEvent(resolveEvent(dayOfWeek, order))) {
|
||||
draggingCellKey.value = null
|
||||
dragOverCellKey.value = null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = JSON.parse(payloadText) as Partial<PreviewMovePayload>
|
||||
if (
|
||||
typeof payload.week !== 'number' ||
|
||||
typeof payload.sourceDayOfWeek !== 'number' ||
|
||||
typeof payload.sourceOrder !== 'number'
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
dragEvent.preventDefault()
|
||||
emit('movePreviewEvent', {
|
||||
week: payload.week,
|
||||
sourceDayOfWeek: payload.sourceDayOfWeek,
|
||||
sourceOrder: payload.sourceOrder,
|
||||
targetDayOfWeek: dayOfWeek,
|
||||
targetOrder: order,
|
||||
})
|
||||
} finally {
|
||||
draggingCellKey.value = null
|
||||
dragOverCellKey.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function handlePreviewDragEnd() {
|
||||
draggingCellKey.value = null
|
||||
dragOverCellKey.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -119,8 +288,16 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
{
|
||||
'planning-board__cell--selectable': scheduleSelectionMode && resolveEvent(header.dayOfWeek, slot.order)?.type !== 'empty',
|
||||
'planning-board__cell--selected': resolveEvent(header.dayOfWeek, slot.order) && isSelected(resolveEvent(header.dayOfWeek, slot.order)!.id),
|
||||
'planning-board__cell--draggable': isWholeCellDraggable(resolveEvent(header.dayOfWeek, slot.order)),
|
||||
'planning-board__cell--dragging': draggingCellKey === buildCellKey(header.dayOfWeek, slot.order),
|
||||
'planning-board__cell--dragover': dragOverCellKey === buildCellKey(header.dayOfWeek, slot.order),
|
||||
},
|
||||
]"
|
||||
:draggable="isWholeCellDraggable(resolveEvent(header.dayOfWeek, slot.order))"
|
||||
@dragstart="handlePreviewDragStart(header.dayOfWeek, slot.order, $event)"
|
||||
@dragover="handlePreviewDragOver(header.dayOfWeek, slot.order, $event)"
|
||||
@drop="handlePreviewDrop(header.dayOfWeek, slot.order, $event)"
|
||||
@dragend="handlePreviewDragEnd"
|
||||
>
|
||||
<button
|
||||
v-if="scheduleSelectionMode && resolveEvent(header.dayOfWeek, slot.order)?.type !== 'empty'"
|
||||
@@ -131,7 +308,33 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
/>
|
||||
|
||||
<template v-if="resolveEvent(header.dayOfWeek, slot.order)">
|
||||
<div class="planning-board__cell-main">
|
||||
<div
|
||||
v-if="hasEmbeddedTask(resolveEvent(header.dayOfWeek, slot.order))"
|
||||
class="planning-board__embedded-shell"
|
||||
>
|
||||
<div class="planning-board__embedded-course">
|
||||
<strong>{{ resolveCellTitle(resolveEvent(header.dayOfWeek, slot.order)) }}</strong>
|
||||
</div>
|
||||
|
||||
<div class="planning-board__embedded-task">
|
||||
<strong
|
||||
class="planning-board__embedded-task-dragger"
|
||||
:class="{
|
||||
'planning-board__embedded-task-dragger--active': isEmbeddedSuggestedPreviewEvent(resolveEvent(header.dayOfWeek, slot.order)),
|
||||
}"
|
||||
:draggable="isEmbeddedSuggestedPreviewEvent(resolveEvent(header.dayOfWeek, slot.order))"
|
||||
@dragstart.stop="handlePreviewDragStart(header.dayOfWeek, slot.order, $event)"
|
||||
@dragend.stop="handlePreviewDragEnd"
|
||||
>
|
||||
{{ resolveEmbeddedTaskName(resolveEvent(header.dayOfWeek, slot.order)) }}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="planning-board__cell-main"
|
||||
>
|
||||
<strong>{{ resolveCellTitle(resolveEvent(header.dayOfWeek, slot.order)) }}</strong>
|
||||
<span>{{ resolveCellMeta(resolveEvent(header.dayOfWeek, slot.order)) }}</span>
|
||||
</div>
|
||||
@@ -261,11 +464,85 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
background: #acd6f4;
|
||||
}
|
||||
|
||||
.planning-board__cell--course-embedded {
|
||||
background: linear-gradient(180deg, rgba(121, 187, 239, 0.96) 0%, rgba(88, 161, 225, 0.96) 100%);
|
||||
align-items: stretch;
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
.planning-board__cell--course .planning-board__cell-main strong,
|
||||
.planning-board__cell--course .planning-board__cell-main span {
|
||||
color: #2576cc;
|
||||
}
|
||||
|
||||
.planning-board__embedded-shell {
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course,
|
||||
.planning-board__embedded-task {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course {
|
||||
padding: 6px 4px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course strong,
|
||||
.planning-board__embedded-task strong {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course strong {
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
line-height: 1.28;
|
||||
font-weight: 800;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.planning-board__embedded-task {
|
||||
padding: 6px 8px;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
box-shadow: 0 10px 18px rgba(31, 82, 145, 0.14);
|
||||
}
|
||||
|
||||
.planning-board__embedded-task strong {
|
||||
color: #1f5db3;
|
||||
font-size: 11px;
|
||||
line-height: 1.24;
|
||||
font-weight: 800;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.planning-board__embedded-task-dragger {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.planning-board__embedded-task-dragger--active {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.planning-board__cell--amber {
|
||||
background: #ffe58b;
|
||||
}
|
||||
@@ -319,6 +596,20 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
box-shadow: inset 0 0 0 2px rgba(32, 102, 212, 0.52);
|
||||
}
|
||||
|
||||
.planning-board__cell--draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.planning-board__cell--dragging {
|
||||
opacity: 0.42;
|
||||
}
|
||||
|
||||
.planning-board__cell--dragover {
|
||||
box-shadow:
|
||||
inset 0 0 0 2px rgba(20, 92, 192, 0.58),
|
||||
0 0 0 4px rgba(33, 109, 215, 0.1);
|
||||
}
|
||||
|
||||
.planning-board__checkbox {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
@@ -378,6 +669,14 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
.planning-board__cell-main strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course strong {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.planning-board__cell--course-embedded {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
@@ -433,5 +732,23 @@ function resolveCellMeta(event?: ScheduleWeekEvent) {
|
||||
.planning-board__cell {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
.planning-board__embedded-task {
|
||||
padding: 5px 7px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course {
|
||||
padding: 4px 2px;
|
||||
}
|
||||
|
||||
.planning-board__embedded-course strong,
|
||||
.planning-board__embedded-task strong {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.planning-board__cell--course-embedded {
|
||||
padding: 7px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user