优化日程增加文件上传功能

This commit is contained in:
2026-09-09 09:55:05 +08:00
parent 04fc84dde6
commit d2643a3697
7 changed files with 513 additions and 15 deletions
+103 -11
View File
@@ -132,6 +132,8 @@
<div class="day-sub">
{{ selectedItems.length }} 项日程<template v-if="pendingCount > 0"
>,{{ pendingCount }} 项未完成</template
><template v-if="selectedPendingIds.length > 0"
>,已选 {{ selectedPendingIds.length }} 项</template
>
</div>
</div>
@@ -141,7 +143,9 @@
size="small"
:icon="Promotion"
@click="handleCarryPending"
>延期未完成</el-button
>延期<template v-if="selectedPendingIds.length > 0"
>({{ selectedPendingIds.length }})</template
></el-button
>
<el-button
type="primary"
@@ -160,8 +164,11 @@
:style="{ '--item-color': item.color }"
>
<el-checkbox
:model-value="item.status === 1"
@change="toggleFinish(item)"
:model-value="selectedIds.includes(item.id)"
:disabled="item.status === 1"
:title="item.status === 1 ? '已完成的日程无需延期' : '勾选后批量延期'"
@click.stop
@change="checked => toggleSelect(item.id, checked)"
/>
<div class="day-item-main" @click="openDetail(item)">
<div class="day-item-title" :class="{ done: item.status === 1 }">
@@ -205,6 +212,9 @@
/>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="finish">{{
item.status === 1 ? "取消完成" : "标记完成"
}}</el-dropdown-item>
<el-dropdown-item
command="postpone"
:disabled="item.status === 1"
@@ -342,6 +352,7 @@
@finish="handleDetailFinish"
@revert="handleDetailRevert"
@postpone="handleDetailPostpone"
@changed="reloadAll"
/>
<!-- 延期对话框 -->
@@ -370,6 +381,7 @@ import ScheduleDetail from "./components/detail.vue";
import SchedulePostpone from "./components/postponeDialog.vue";
import {
carryPendingSchedules,
carrySchedule,
deleteSchedule,
finishSchedule,
getScheduleList,
@@ -475,6 +487,10 @@ const calendarMap = computed(() => {
}
map[item.schedule_date].push(item);
}
// 同一天内按紧急程度排序:紧急 > 重要 > 普通
for (const date of Object.keys(map)) {
map[date].sort(compareByPriority);
}
return map;
});
@@ -487,6 +503,32 @@ const pendingCount = computed(
() => selectedItems.value.filter(item => item.status !== 1).length
);
// ---------- 多选(勾选后批量延期,区别于"完成"操作) ----------
// 只记录 id,数据刷新后自动失效
const selectedIds = ref([]);
// 已勾选且仍未完成的日程,只有这些才会被延期
const selectedPendingIds = computed(() =>
selectedIds.value.filter(id => {
const item = selectedItems.value.find(i => i.id === id);
return item && item.status !== 1;
})
);
function toggleSelect(id, checked) {
const set = new Set(selectedIds.value);
if (checked) {
set.add(id);
} else {
set.delete(id);
}
selectedIds.value = [...set];
}
function clearSelection() {
selectedIds.value = [];
}
const selectedDateLabel = computed(() => {
const [y, m, d] = selectedDate.value.split("-").map(Number);
const week = "日一二三四五六"[new Date(y, m - 1, d).getDay()];
@@ -516,6 +558,8 @@ async function loadCalendarData() {
...item,
schedule_date: normDate(item.schedule_date)
}));
// 数据刷新后旧的勾选已失效(id 可能已因延期重新生成),统一清空
clearSelection();
}
function changeMonth(delta) {
@@ -650,6 +694,8 @@ function handleDetailPostpone(item) {
function handleItemCommand(cmd, item) {
if (cmd === "postpone") {
openPostpone(item);
} else if (cmd === "finish") {
toggleFinish(item);
} else if (cmd === "edit") {
openEdit(item);
} else if (cmd === "delete") {
@@ -670,23 +716,50 @@ function openPostpone(item) {
postponeVisible.value = true;
}
// 批量延期:把选中日期的全部未完成日程一次性延期到下一天
// 批量延期:优先延期勾选的日程;未勾选时延期选中日期的全部未完成日程
async function handleCarryPending() {
if (pendingCount.value === 0) {
const ids = selectedPendingIds.value;
const count = ids.length || pendingCount.value;
if (count === 0) {
return;
}
// 目标日期取"选定日期+1天",但不早于今天,避免把过去的任务再延到过去
const nextDay = addDays(selectedDate.value, 1);
const target = nextDay < todayStr() ? todayStr() : nextDay;
const tip = ids.length
? `将选中的 ${count} 条日程延期到 ${target}?当天这些日程会标记为已完成。`
: `将 ${selectedDate.value} 的 ${count} 条未完成日程延期到 ${target}?当天这些日程会标记为已完成。`;
try {
await ElMessageBox.confirm(
`将 ${selectedDate.value} 的 ${pendingCount.value} 条未完成日程延期到 ${target}?当天这些日程会标记为已完成。`,
"批量延期",
{ type: "warning", confirmButtonText: "延期", cancelButtonText: "取消" }
);
await ElMessageBox.confirm(tip, "批量延期", {
type: "warning",
confirmButtonText: "延期",
cancelButtonText: "取消"
});
} catch {
return;
}
// 有勾选:逐条调用单条延期接口(后端批量接口只支持按日期整批延期)
if (ids.length) {
let success = 0;
for (const id of ids) {
const res = await carrySchedule(id, { target_date: target });
if (res?.code === 200) {
success++;
}
}
const skipped = ids.length - success;
if (success) {
ElMessage.success(
`已延期 ${success} 条${skipped ? `,跳过 ${skipped} 条` : ""}`
);
} else {
ElMessage.warning("所选日程均无法延期");
}
await reloadAll();
return;
}
const res = await carryPendingSchedules({
date: selectedDate.value,
target_date: target
@@ -769,6 +842,25 @@ function priorityTagType(priority) {
return { 1: "warning", 2: "danger" }[priority] || "info";
}
// 排序权重:紧急(2) > 重要(1) > 普通(0)
function priorityWeight(priority) {
return { 2: 3, 1: 2 }[priority] || 1;
}
// 同天内排序:先按紧急程度降序,再按开始时间升序(全天排在最前),最后按 id
function compareByPriority(a, b) {
const diff = priorityWeight(b.priority) - priorityWeight(a.priority);
if (diff !== 0) {
return diff;
}
const ta = a.all_day === 1 ? "00:00" : a.start_time || "";
const tb = b.all_day === 1 ? "00:00" : b.start_time || "";
if (ta !== tb) {
return ta < tb ? -1 : 1;
}
return (a.id || 0) - (b.id || 0);
}
onMounted(() => {
loadStats();
loadCalendarData();
@@ -1038,7 +1130,7 @@ onMounted(() => {
/* 选中日期侧栏 */
.day-panel {
width: 320px;
width: 400px;
flex-shrink: 0;
background: #fff;
border: 1px solid #ebeef5;