refactor(project): 重构项目执行模块组件结构和数据管理

- 移除 execution-list-panel.vue 组件并将功能整合到执行区域
- 新增 execution-section.vue 组件替代原有的列表面板
- 将 task-workspace.vue 重命名为 task-workspace-comp.vue 并更新引用
- 引入 useTaskViewContext 组合式 API 进行任务视图上下文管理
- 添加跨执行任务状态统计接口调用和数据处理逻辑
- 重构执行状态筛选和任务创建权限判断逻辑
- 更新执行选择、搜索和重置功能的事件处理方式
- 调整页面布局结构,优化左右分栏的内容组织方式
- 完善执行详情获取和状态操作的业务流程
- 优化执行分配和状态变更的异步处理机制
This commit is contained in:
2026-05-23 14:22:58 +08:00
parent 13b74cfe97
commit e9214137c1
40 changed files with 4432 additions and 1419 deletions

View File

@@ -1,105 +1,107 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useRouterPush } from '@/hooks/common/router';
import { type WorkbenchMyTaskBucket, buildWorkbenchMyTaskItems, filterWorkbenchMyTaskItems } from '../homepage';
import { workbenchMyTaskMock } from '../mock';
import WorkbenchModuleCard from './workbench-module-card.vue';
defineOptions({ name: 'WorkbenchMyTask' });
interface Props {
editing?: boolean;
collapsed?: boolean;
}
withDefaults(defineProps<Props>(), { editing: false, collapsed: false });
defineEmits<{ (e: 'hide'): void; (e: 'toggle-collapse'): void; (e: 'refresh'): void }>();
defineEmits<{ (e: 'hide'): void; (e: 'toggle-collapse'): void }>();
const { routerPushByKey } = useRouterPush();
const allItems = computed(() => buildWorkbenchMyTaskItems(workbenchMyTaskMock));
const bucket = ref<WorkbenchMyTaskBucket>('today');
const visibleItems = computed(() => filterWorkbenchMyTaskItems(allItems.value, bucket.value));
function handleClickItem() {
routerPushByKey('project_list');
interface FocusItem {
id: string;
title: string;
done: boolean;
}
function handleRefresh() {
window.$message?.success('已刷新v1 mock');
}
const items = ref<FocusItem[]>([
{ id: 'f1', title: '提交昨日工时', done: true },
{ id: 'f2', title: '登录页 SSO 改造 · 18:00 截止', done: false },
{ id: 'f3', title: '迭代 24.05 关闭 · 跟交付 / QA 确认遗留', done: false }
]);
const doneCount = computed(() => items.value.filter(i => i.done).length);
const totalCount = computed(() => items.value.length);
const todayHours = 3.5;
const targetHours = 8;
</script>
<template>
<WorkbenchModuleCard
title="我的任务"
icon="mdi:checkbox-marked-circle-outline"
:badge-count="visibleItems.length"
title="我的今日"
icon="mdi:calendar-check-outline"
:editing="editing"
:collapsed="collapsed"
@hide="$emit('hide')"
@toggle-collapse="$emit('toggle-collapse')"
@navigate="handleClickItem"
@refresh="handleRefresh"
>
<ElTabs v-model="bucket" class="my-task-tabs">
<ElTabPane label="今日" name="today" />
<ElTabPane label="本周" name="week" />
<ElTabPane label="逾期" name="overdue" />
<ElTabPane label="全部" name="all" />
</ElTabs>
<ul v-if="visibleItems.length" class="my-task-list">
<li v-for="item in visibleItems" :key="item.id" class="my-task-item" @click="handleClickItem">
<ElTag size="small" :type="item.overdue ? 'danger' : 'info'">{{ item.statusLabel }}</ElTag>
<span class="my-task-title">{{ item.title }}</span>
<span class="my-task-meta">{{ item.projectName }} · {{ item.executionName }}</span>
<span class="my-task-deadline" :class="{ overdue: item.overdue }">{{ item.deadlineLabel }}</span>
<div class="today-head">
<span class="today-progress">{{ doneCount }} / {{ totalCount }} 已完成</span>
</div>
<ul class="today-list">
<li v-for="item in items" :key="item.id" class="today-row">
<ElCheckbox v-model="item.done" />
<span class="today-text" :class="{ 'today-done': item.done }">{{ item.title }}</span>
</li>
</ul>
<ElEmpty v-else description="暂无任务" :image-size="60" />
<div class="today-foot">
当日累计工时
<b>{{ todayHours }}h</b>
/ {{ targetHours }}h
</div>
</WorkbenchModuleCard>
</template>
<style scoped>
.my-task-tabs {
margin-bottom: 4px;
.today-head {
display: flex;
justify-content: flex-end;
font-size: 12px;
color: var(--el-text-color-secondary);
margin-bottom: 6px;
}
.my-task-list {
.today-progress {
padding: 2px 8px;
background: var(--el-fill-color-lighter);
border-radius: 999px;
}
.today-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
.my-task-item {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-rows: auto auto;
gap: 2px 8px;
padding: 8px 10px;
border-radius: 6px;
cursor: pointer;
background: var(--el-fill-color-lighter);
transition: background 120ms;
.today-row {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 4px;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.my-task-item:hover {
background: var(--el-color-primary-light-9);
.today-row:last-child {
border-bottom: none;
}
.my-task-title {
font-weight: 500;
.today-text {
font-size: 13px;
}
.my-task-meta {
grid-column: 2 / 3;
.today-done {
text-decoration: line-through;
color: var(--el-text-color-placeholder);
}
.today-foot {
margin-top: 10px;
padding-top: 8px;
border-top: 1px solid var(--el-border-color-lighter);
font-size: 12px;
color: var(--el-text-color-secondary);
}
.my-task-deadline {
grid-column: 3 / 4;
grid-row: 1 / 3;
align-self: center;
font-size: 12px;
color: var(--el-text-color-secondary);
}
.my-task-deadline.overdue {
color: var(--el-color-danger);
font-weight: 600;
.today-foot b {
color: var(--el-text-color-primary);
font-weight: 700;
}
</style>