Compare commits
2 Commits
6685ba8d3a
...
9bfc033b41
Author | SHA1 | Date |
---|---|---|
|
9bfc033b41 | |
|
3e999e95e7 |
|
@ -167,7 +167,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
component: () => import('@/views/task/task-gantt/TaskGantt.vue'),
|
component: () => import('@/views/task/task-gantt/TaskGantt.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '人力甘特图',
|
title: '人力甘特图',
|
||||||
icon: 'workload', // 进度相关图标
|
icon: 'eye', // 进度相关图标
|
||||||
hidden: false,
|
hidden: false,
|
||||||
sort: 3.6
|
sort: 3.6
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,7 @@ const initChart = (personIndex: number) => {
|
||||||
const categoryIndex = api.value(3);
|
const categoryIndex = api.value(3);
|
||||||
const startCoord = api.coord([api.value(0), categoryIndex]);
|
const startCoord = api.coord([api.value(0), categoryIndex]);
|
||||||
const endCoord = api.coord([api.value(1), categoryIndex]);
|
const endCoord = api.coord([api.value(1), categoryIndex]);
|
||||||
const barHeight = 20;
|
const barHeight = 40;
|
||||||
return {
|
return {
|
||||||
type: "rect",
|
type: "rect",
|
||||||
shape: {
|
shape: {
|
||||||
|
@ -220,7 +220,7 @@ const initChart = (personIndex: number) => {
|
||||||
const categoryIndex = api.value(3);
|
const categoryIndex = api.value(3);
|
||||||
const startCoord = api.coord([api.value(0), categoryIndex]);
|
const startCoord = api.coord([api.value(0), categoryIndex]);
|
||||||
const endCoord = api.coord([api.value(1), categoryIndex]);
|
const endCoord = api.coord([api.value(1), categoryIndex]);
|
||||||
const barHeight = 12;
|
const barHeight = 40;
|
||||||
return {
|
return {
|
||||||
type: "rect",
|
type: "rect",
|
||||||
shape: {
|
shape: {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
@click="handleStatClick('low')"
|
@click="handleStatClick('low')"
|
||||||
:class="{ 'active': searchForm.priority === 'low' }"
|
:class="{ 'active': searchForm.priority === 'low' }"
|
||||||
>
|
>
|
||||||
<div class="stat-text">低优先级</div>
|
<div class="stat-text">不重要不紧急</div>
|
||||||
<div class="stat-number">{{ priorityStats.low }}</div>
|
<div class="stat-number">{{ priorityStats.low }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
@click="handleStatClick('medium')"
|
@click="handleStatClick('medium')"
|
||||||
:class="{ 'active': searchForm.priority === 'medium' }"
|
:class="{ 'active': searchForm.priority === 'medium' }"
|
||||||
>
|
>
|
||||||
<div class="stat-text">中优先级</div>
|
<div class="stat-text">重要不紧急</div>
|
||||||
<div class="stat-number">{{ priorityStats.medium }}</div>
|
<div class="stat-number">{{ priorityStats.medium }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
@click="handleStatClick('high')"
|
@click="handleStatClick('high')"
|
||||||
:class="{ 'active': searchForm.priority === 'high' }"
|
:class="{ 'active': searchForm.priority === 'high' }"
|
||||||
>
|
>
|
||||||
<div class="stat-text">高优先级</div>
|
<div class="stat-text">紧急不重要</div>
|
||||||
<div class="stat-number">{{ priorityStats.high }}</div>
|
<div class="stat-number">{{ priorityStats.high }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
@click="handleStatClick('urgent')"
|
@click="handleStatClick('urgent')"
|
||||||
:class="{ 'active': searchForm.priority === 'urgent' }"
|
:class="{ 'active': searchForm.priority === 'urgent' }"
|
||||||
>
|
>
|
||||||
<div class="stat-text">紧急</div>
|
<div class="stat-text">重要紧急</div>
|
||||||
<div class="stat-number">{{ priorityStats.urgent }}</div>
|
<div class="stat-number">{{ priorityStats.urgent }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,10 +52,6 @@
|
||||||
<div class="history-tasks">
|
<div class="history-tasks">
|
||||||
<div class="table-controls">
|
<div class="table-controls">
|
||||||
<h3>历史任务</h3>
|
<h3>历史任务</h3>
|
||||||
<!-- <div class="table-actions">
|
|
||||||
<button class="refresh-btn" @click="resetSearch">刷新</button>
|
|
||||||
<button class="export-btn">导出</button>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 搜索区域 -->
|
<!-- 搜索区域 -->
|
||||||
|
@ -74,10 +70,10 @@
|
||||||
<label>优先级:</label>
|
<label>优先级:</label>
|
||||||
<select v-model="searchForm.priority" class="search-select">
|
<select v-model="searchForm.priority" class="search-select">
|
||||||
<option value="">全部</option>
|
<option value="">全部</option>
|
||||||
<option value="low">低</option>
|
<option value="low">不重要不紧急</option>
|
||||||
<option value="medium">中</option>
|
<option value="medium">重要不紧急</option>
|
||||||
<option value="high">高</option>
|
<option value="high">紧急不重要</option>
|
||||||
<option value="urgent">紧急</option>
|
<option value="urgent">重要紧急</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="search-item">
|
<div class="search-item">
|
||||||
|
@ -134,8 +130,9 @@
|
||||||
<td><span class="priority-tag" :class="task.priority">{{ getPriorityText(task.priority) }}</span></td>
|
<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><span class="task-status" :class="task.status">{{ task.statusText }}</span></td>
|
||||||
<td class="operation-btns">
|
<td class="operation-btns">
|
||||||
<button class="operate-btn">查看</button>
|
<button class="operate-btn" @click="handleView(task)">查看</button>
|
||||||
<button class="operate-btn">编辑</button>
|
<button class="operate-btn" @click="handleEdit(task)">编辑</button>
|
||||||
|
<button class="operate-btn" @click="handleDelete(task.id)">删除</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="filteredTasks.length === 0">
|
<tr v-if="filteredTasks.length === 0">
|
||||||
|
@ -166,6 +163,34 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</GiPageLayout>
|
</GiPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
@ -173,14 +198,20 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message, Modal } from '@arco-design/web-vue';
|
||||||
import TaskForm from './components/TaskForm.vue';
|
import TaskForm from './components/TaskForm.vue';
|
||||||
import AssigneeSelector from './components/AssigneeSelector.vue';
|
import AssigneeSelector from './components/AssigneeSelector.vue';
|
||||||
|
|
||||||
const taskFormRef = ref<InstanceType<typeof TaskForm> | null>(null);
|
const taskFormRef = ref<InstanceType<typeof TaskForm> | null>(null);
|
||||||
const assigneeRef = ref<InstanceType<typeof AssigneeSelector> | null>(null);
|
const assigneeRef = ref<InstanceType<typeof AssigneeSelector> | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const showPublishModal = ref(false);
|
const showPublishModal = ref(false);
|
||||||
|
|
||||||
|
// 查看/编辑弹窗状态
|
||||||
|
const showTaskModal = ref(false);
|
||||||
|
const taskModalMode = ref<'view' | 'edit'>('view');
|
||||||
|
const currentTask = ref<any>({});
|
||||||
|
|
||||||
// 模拟历史任务数据
|
// 模拟历史任务数据
|
||||||
const historyTasks = ref([
|
const historyTasks = ref([
|
||||||
{
|
{
|
||||||
|
@ -234,62 +265,39 @@ const searchForm = ref({
|
||||||
// 计算过滤后的任务列表
|
// 计算过滤后的任务列表
|
||||||
const filteredTasks = computed(() => {
|
const filteredTasks = computed(() => {
|
||||||
return historyTasks.value.filter(task => {
|
return historyTasks.value.filter(task => {
|
||||||
// 任务名称筛选
|
if (searchForm.value.name && !task.title.includes(searchForm.value.name)) return false;
|
||||||
if (searchForm.value.name && !task.title.includes(searchForm.value.name)) {
|
if (searchForm.value.priority && task.priority !== searchForm.value.priority) return false;
|
||||||
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.priority && task.priority !== searchForm.value.priority) {
|
if (searchForm.value.endDate && task.publishDate > searchForm.value.endDate) return false;
|
||||||
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;
|
return true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 统计方框点击事件处理
|
// 统计方框点击事件处理
|
||||||
const handleStatClick = (priority: string) => {
|
const handleStatClick = (priority: string) => {
|
||||||
// 如果点击的是当前已选中的优先级,则清除筛选
|
|
||||||
if (searchForm.value.priority === priority) {
|
if (searchForm.value.priority === priority) {
|
||||||
searchForm.value.priority = '';
|
searchForm.value.priority = '';
|
||||||
} else {
|
} else {
|
||||||
// 否则设置为对应优先级筛选条件
|
|
||||||
searchForm.value.priority = priority;
|
searchForm.value.priority = priority;
|
||||||
}
|
}
|
||||||
// 触发搜索逻辑
|
|
||||||
handleSearch();
|
handleSearch();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取所有发布人列表(去重)
|
// 获取发布人/负责人列表
|
||||||
const allPublishers = computed(() => {
|
const allPublishers = computed(() => {
|
||||||
const publishers = new Set<string>();
|
const publishers = new Set<string>();
|
||||||
historyTasks.value.forEach(task => publishers.add(task.publisher));
|
historyTasks.value.forEach(task => publishers.add(task.publisher));
|
||||||
return Array.from(publishers);
|
return Array.from(publishers);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取所有负责人列表(去重)
|
|
||||||
const allAssignees = computed(() => {
|
const allAssignees = computed(() => {
|
||||||
const assignees = new Set<string>();
|
const assignees = new Set<string>();
|
||||||
historyTasks.value.forEach(task => assignees.add(task.assignee));
|
historyTasks.value.forEach(task => assignees.add(task.assignee));
|
||||||
return Array.from(assignees);
|
return Array.from(assignees);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算各优先级任务数量
|
// 各优先级任务数量
|
||||||
const priorityStats = computed(() => {
|
const priorityStats = computed(() => {
|
||||||
return {
|
return {
|
||||||
low: historyTasks.value.filter(task => task.priority === 'low').length,
|
low: historyTasks.value.filter(task => task.priority === 'low').length,
|
||||||
|
@ -299,25 +307,22 @@ const priorityStats = computed(() => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取优先级文本
|
// 优先级文本
|
||||||
const getPriorityText = (priority: string) => {
|
const getPriorityText = (priority: string) => {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
low: '低',
|
low: '不重要不紧急',
|
||||||
medium: '中',
|
medium: '重要不紧急',
|
||||||
high: '高',
|
high: '紧急不重要',
|
||||||
urgent: '紧急'
|
urgent: '重要紧急'
|
||||||
};
|
};
|
||||||
return map[priority] || '';
|
return map[priority] || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 搜索处理
|
// 搜索、重置
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
console.log('搜索条件:', searchForm.value);
|
console.log('搜索条件:', searchForm.value);
|
||||||
// 滚动到表格顶部,提升用户体验
|
|
||||||
document.querySelector('.task-table')?.scrollIntoView({ behavior: 'smooth' });
|
document.querySelector('.task-table')?.scrollIntoView({ behavior: 'smooth' });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置搜索条件
|
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
searchForm.value = {
|
searchForm.value = {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -329,6 +334,7 @@ const resetSearch = () => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 发布新任务
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (!taskFormRef.value?.form.taskName) {
|
if (!taskFormRef.value?.form.taskName) {
|
||||||
Message.error('请填写任务名称');
|
Message.error('请填写任务名称');
|
||||||
|
@ -365,6 +371,53 @@ const handleSubmit = () => {
|
||||||
Message.success('任务发布成功!');
|
Message.success('任务发布成功!');
|
||||||
showPublishModal.value = false;
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -585,24 +638,6 @@ h3 {
|
||||||
margin: 0;
|
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 {
|
.task-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
@ -636,26 +671,29 @@ h3 {
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.priority-tag.low {
|
.priority-tag.low {
|
||||||
background-color: #e2e8f0;
|
background-color: #4ade80;
|
||||||
color: #475569;
|
background-image: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.priority-tag.medium {
|
.priority-tag.medium {
|
||||||
background-color: #dbeafe;
|
background-color: #3b82f6;
|
||||||
color: #1e40af;
|
background-image: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.priority-tag.high {
|
.priority-tag.high {
|
||||||
background-color: #fef3c7;
|
background-color: #f59e0b;
|
||||||
color: #92400e;
|
background-image: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.priority-tag.urgent {
|
.priority-tag.urgent {
|
||||||
background-color: #fee2e2;
|
background-color: #ef4444;
|
||||||
color: #b91c1c;
|
background-image: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 状态标签样式 */
|
/* 状态标签样式 */
|
||||||
|
|
|
@ -34,8 +34,6 @@
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,10 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="priority">优先级</label>
|
<label for="priority">优先级</label>
|
||||||
<select id="priority" v-model="form.priority">
|
<select id="priority" v-model="form.priority">
|
||||||
<option value="low">低</option>
|
<option value="low">不重要不紧急</option>
|
||||||
<option value="medium" selected>中</option>
|
<option value="medium" selected>重要不紧急</option>
|
||||||
<option value="high">高</option>
|
<option value="high">紧急不重要</option>
|
||||||
<option value="urgent">紧急</option>
|
<option value="urgent">重要紧急</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue