|
|
|
@ -19,7 +19,7 @@
|
|
|
|
|
@click="handleStatClick('low')"
|
|
|
|
|
:class="{ 'active': searchForm.priority === 'low' }"
|
|
|
|
|
>
|
|
|
|
|
<div class="stat-text">不重要不紧急</div>
|
|
|
|
|
<div class="stat-text">低优先级</div>
|
|
|
|
|
<div class="stat-number">{{ priorityStats.low }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
@ -27,7 +27,7 @@
|
|
|
|
|
@click="handleStatClick('medium')"
|
|
|
|
|
:class="{ 'active': searchForm.priority === 'medium' }"
|
|
|
|
|
>
|
|
|
|
|
<div class="stat-text">重要不紧急</div>
|
|
|
|
|
<div class="stat-text">中优先级</div>
|
|
|
|
|
<div class="stat-number">{{ priorityStats.medium }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
@ -35,7 +35,7 @@
|
|
|
|
|
@click="handleStatClick('high')"
|
|
|
|
|
:class="{ 'active': searchForm.priority === 'high' }"
|
|
|
|
|
>
|
|
|
|
|
<div class="stat-text">紧急不重要</div>
|
|
|
|
|
<div class="stat-text">高优先级</div>
|
|
|
|
|
<div class="stat-number">{{ priorityStats.high }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
@ -43,7 +43,7 @@
|
|
|
|
|
@click="handleStatClick('urgent')"
|
|
|
|
|
:class="{ 'active': searchForm.priority === 'urgent' }"
|
|
|
|
|
>
|
|
|
|
|
<div class="stat-text">重要紧急</div>
|
|
|
|
|
<div class="stat-text">紧急</div>
|
|
|
|
|
<div class="stat-number">{{ priorityStats.urgent }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@ -52,6 +52,10 @@
|
|
|
|
|
<div class="history-tasks">
|
|
|
|
|
<div class="table-controls">
|
|
|
|
|
<h3>历史任务</h3>
|
|
|
|
|
<!-- <div class="table-actions">
|
|
|
|
|
<button class="refresh-btn" @click="resetSearch">刷新</button>
|
|
|
|
|
<button class="export-btn">导出</button>
|
|
|
|
|
</div> -->
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
@ -70,10 +74,10 @@
|
|
|
|
|
<label>优先级:</label>
|
|
|
|
|
<select v-model="searchForm.priority" class="search-select">
|
|
|
|
|
<option value="">全部</option>
|
|
|
|
|
<option value="low">不重要不紧急</option>
|
|
|
|
|
<option value="medium">重要不紧急</option>
|
|
|
|
|
<option value="high">紧急不重要</option>
|
|
|
|
|
<option value="urgent">重要紧急</option>
|
|
|
|
|
<option value="low">低</option>
|
|
|
|
|
<option value="medium">中</option>
|
|
|
|
|
<option value="high">高</option>
|
|
|
|
|
<option value="urgent">紧急</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="search-item">
|
|
|
|
@ -130,9 +134,8 @@
|
|
|
|
|
<td><span class="priority-tag" :class="task.priority">{{ getPriorityText(task.priority) }}</span></td>
|
|
|
|
|
<td><span class="task-status" :class="task.status">{{ task.statusText }}</span></td>
|
|
|
|
|
<td class="operation-btns">
|
|
|
|
|
<button class="operate-btn" @click="handleView(task)">查看</button>
|
|
|
|
|
<button class="operate-btn" @click="handleEdit(task)">编辑</button>
|
|
|
|
|
<button class="operate-btn" @click="handleDelete(task.id)">删除</button>
|
|
|
|
|
<button class="operate-btn">查看</button>
|
|
|
|
|
<button class="operate-btn">编辑</button>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr v-if="filteredTasks.length === 0">
|
|
|
|
@ -163,34 +166,6 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 查看/编辑任务弹窗(居中,表单只读/可编辑) -->
|
|
|
|
|
<div class="modal-overlay" v-if="showTaskModal" @click.self="showTaskModal = false">
|
|
|
|
|
<div class="publish-modal">
|
|
|
|
|
<button class="modal-close" @click="showTaskModal = false">
|
|
|
|
|
<i class="icon-close"></i>
|
|
|
|
|
</button>
|
|
|
|
|
<h3>{{ taskModalMode === 'view' ? '查看任务' : '编辑任务' }}</h3>
|
|
|
|
|
<div class="modal-content" style="display: block;">
|
|
|
|
|
<div style="margin-bottom: 10px;">
|
|
|
|
|
<label>任务名称:</label>
|
|
|
|
|
<input type="text" v-model="currentTask.title" :disabled="taskModalMode === 'view'" class="search-input">
|
|
|
|
|
</div>
|
|
|
|
|
<div style="margin-bottom: 10px;">
|
|
|
|
|
<label>任务描述:</label>
|
|
|
|
|
<textarea v-model="currentTask.description" :disabled="taskModalMode === 'view'" class="search-input" style="width: 100%; height: 80px;"></textarea>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="margin-bottom: 10px;">
|
|
|
|
|
<label>截止日期:</label>
|
|
|
|
|
<input type="date" v-model="currentTask.dueDate" :disabled="taskModalMode === 'view'" class="search-input">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-actions">
|
|
|
|
|
<button class="cancel-btn" @click="showTaskModal = false">关闭</button>
|
|
|
|
|
<button v-if="taskModalMode === 'edit'" class="submit-btn" @click="handleSaveTask">保存</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</GiPageLayout>
|
|
|
|
|
</template>
|
|
|
|
@ -198,20 +173,14 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, computed } from 'vue';
|
|
|
|
|
import { useRouter } from 'vue-router';
|
|
|
|
|
import { Message, Modal } from '@arco-design/web-vue';
|
|
|
|
|
import { Message } from '@arco-design/web-vue';
|
|
|
|
|
import TaskForm from './components/TaskForm.vue';
|
|
|
|
|
import AssigneeSelector from './components/AssigneeSelector.vue';
|
|
|
|
|
|
|
|
|
|
const taskFormRef = ref<InstanceType<typeof TaskForm> | null>(null);
|
|
|
|
|
const assigneeRef = ref<InstanceType<typeof AssigneeSelector> | null>(null);
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const showPublishModal = ref(false);
|
|
|
|
|
|
|
|
|
|
// 查看/编辑弹窗状态
|
|
|
|
|
const showTaskModal = ref(false);
|
|
|
|
|
const taskModalMode = ref<'view' | 'edit'>('view');
|
|
|
|
|
const currentTask = ref<any>({});
|
|
|
|
|
|
|
|
|
|
// 模拟历史任务数据
|
|
|
|
|
const historyTasks = ref([
|
|
|
|
|
{
|
|
|
|
@ -265,39 +234,62 @@ const searchForm = ref({
|
|
|
|
|
// 计算过滤后的任务列表
|
|
|
|
|
const filteredTasks = computed(() => {
|
|
|
|
|
return historyTasks.value.filter(task => {
|
|
|
|
|
if (searchForm.value.name && !task.title.includes(searchForm.value.name)) return false;
|
|
|
|
|
if (searchForm.value.priority && task.priority !== searchForm.value.priority) return false;
|
|
|
|
|
if (searchForm.value.publisher && task.publisher !== searchForm.value.publisher) return false;
|
|
|
|
|
if (searchForm.value.assignee && task.assignee !== searchForm.value.assignee) return false;
|
|
|
|
|
if (searchForm.value.startDate && task.publishDate < searchForm.value.startDate) return false;
|
|
|
|
|
if (searchForm.value.endDate && task.publishDate > searchForm.value.endDate) return false;
|
|
|
|
|
// 任务名称筛选
|
|
|
|
|
if (searchForm.value.name && !task.title.includes(searchForm.value.name)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 优先级筛选(统计方框点击会设置此值)
|
|
|
|
|
if (searchForm.value.priority && task.priority !== searchForm.value.priority) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 发布人筛选
|
|
|
|
|
if (searchForm.value.publisher && task.publisher !== searchForm.value.publisher) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 负责人筛选
|
|
|
|
|
if (searchForm.value.assignee && task.assignee !== searchForm.value.assignee) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 开始日期筛选
|
|
|
|
|
if (searchForm.value.startDate && task.publishDate < searchForm.value.startDate) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 结束日期筛选
|
|
|
|
|
if (searchForm.value.endDate && task.publishDate > searchForm.value.endDate) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 统计方框点击事件处理
|
|
|
|
|
const handleStatClick = (priority: string) => {
|
|
|
|
|
// 如果点击的是当前已选中的优先级,则清除筛选
|
|
|
|
|
if (searchForm.value.priority === priority) {
|
|
|
|
|
searchForm.value.priority = '';
|
|
|
|
|
} else {
|
|
|
|
|
// 否则设置为对应优先级筛选条件
|
|
|
|
|
searchForm.value.priority = priority;
|
|
|
|
|
}
|
|
|
|
|
// 触发搜索逻辑
|
|
|
|
|
handleSearch();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取发布人/负责人列表
|
|
|
|
|
// 获取所有发布人列表(去重)
|
|
|
|
|
const allPublishers = computed(() => {
|
|
|
|
|
const publishers = new Set<string>();
|
|
|
|
|
historyTasks.value.forEach(task => publishers.add(task.publisher));
|
|
|
|
|
return Array.from(publishers);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 获取所有负责人列表(去重)
|
|
|
|
|
const allAssignees = computed(() => {
|
|
|
|
|
const assignees = new Set<string>();
|
|
|
|
|
historyTasks.value.forEach(task => assignees.add(task.assignee));
|
|
|
|
|
return Array.from(assignees);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 各优先级任务数量
|
|
|
|
|
// 计算各优先级任务数量
|
|
|
|
|
const priorityStats = computed(() => {
|
|
|
|
|
return {
|
|
|
|
|
low: historyTasks.value.filter(task => task.priority === 'low').length,
|
|
|
|
@ -307,22 +299,25 @@ const priorityStats = computed(() => {
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 优先级文本
|
|
|
|
|
// 获取优先级文本
|
|
|
|
|
const getPriorityText = (priority: string) => {
|
|
|
|
|
const map: Record<string, string> = {
|
|
|
|
|
low: '不重要不紧急',
|
|
|
|
|
medium: '重要不紧急',
|
|
|
|
|
high: '紧急不重要',
|
|
|
|
|
urgent: '重要紧急'
|
|
|
|
|
low: '低',
|
|
|
|
|
medium: '中',
|
|
|
|
|
high: '高',
|
|
|
|
|
urgent: '紧急'
|
|
|
|
|
};
|
|
|
|
|
return map[priority] || '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 搜索、重置
|
|
|
|
|
// 搜索处理
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
|
console.log('搜索条件:', searchForm.value);
|
|
|
|
|
// 滚动到表格顶部,提升用户体验
|
|
|
|
|
document.querySelector('.task-table')?.scrollIntoView({ behavior: 'smooth' });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 重置搜索条件
|
|
|
|
|
const resetSearch = () => {
|
|
|
|
|
searchForm.value = {
|
|
|
|
|
name: '',
|
|
|
|
@ -334,7 +329,6 @@ const resetSearch = () => {
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 发布新任务
|
|
|
|
|
const handleSubmit = () => {
|
|
|
|
|
if (!taskFormRef.value?.form.taskName) {
|
|
|
|
|
Message.error('请填写任务名称');
|
|
|
|
@ -371,53 +365,6 @@ const handleSubmit = () => {
|
|
|
|
|
Message.success('任务发布成功!');
|
|
|
|
|
showPublishModal.value = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 查看
|
|
|
|
|
const handleView = (task: any) => {
|
|
|
|
|
currentTask.value = { ...task };
|
|
|
|
|
taskModalMode.value = 'view';
|
|
|
|
|
showTaskModal.value = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 编辑
|
|
|
|
|
const handleEdit = (task: any) => {
|
|
|
|
|
currentTask.value = { ...task };
|
|
|
|
|
taskModalMode.value = 'edit';
|
|
|
|
|
showTaskModal.value = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 删除(使用 Arco Modal.confirm,只有确认时才删除)
|
|
|
|
|
const handleDelete = (taskId: string) => {
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
title: '确认删除',
|
|
|
|
|
content: '删除后将无法恢复,是否确认删除该任务?',
|
|
|
|
|
okText: '确认删除',
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
closable: true,
|
|
|
|
|
maskClosable: true,
|
|
|
|
|
onOk: () => {
|
|
|
|
|
historyTasks.value = historyTasks.value.filter(t => t.id !== taskId);
|
|
|
|
|
Message.success('任务已删除');
|
|
|
|
|
},
|
|
|
|
|
onCancel: () => {
|
|
|
|
|
Message.info('已取消删除');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 保存编辑
|
|
|
|
|
const handleSaveTask = () => {
|
|
|
|
|
if (!currentTask.value.title) {
|
|
|
|
|
Message.error('任务名称不能为空');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const index = historyTasks.value.findIndex(t => t.id === currentTask.value.id);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
historyTasks.value[index] = { ...currentTask.value };
|
|
|
|
|
Message.success('任务更新成功');
|
|
|
|
|
showTaskModal.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
@ -638,6 +585,24 @@ h3 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* .table-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.refresh-btn, .export-btn {
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
border: 1px solid #d1d5db;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.refresh-btn:hover, .export-btn:hover {
|
|
|
|
|
background-color: #f3f4f6;
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
.task-table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
@ -671,29 +636,26 @@ h3 {
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.priority-tag.low {
|
|
|
|
|
background-color: #4ade80;
|
|
|
|
|
background-image: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
|
|
|
|
|
background-color: #e2e8f0;
|
|
|
|
|
color: #475569;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.priority-tag.medium {
|
|
|
|
|
background-color: #3b82f6;
|
|
|
|
|
background-image: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
|
|
|
|
background-color: #dbeafe;
|
|
|
|
|
color: #1e40af;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.priority-tag.high {
|
|
|
|
|
background-color: #f59e0b;
|
|
|
|
|
background-image: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
|
|
|
|
background-color: #fef3c7;
|
|
|
|
|
color: #92400e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.priority-tag.urgent {
|
|
|
|
|
background-color: #ef4444;
|
|
|
|
|
background-image: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
|
|
|
background-color: #fee2e2;
|
|
|
|
|
color: #b91c1c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 状态标签样式 */
|
|
|
|
|