Compare commits

..

No commits in common. "9bfc033b41b9e0ae23baf4de0487a7823f2cdfc4" and "6685ba8d3ab92013dc2646a338f4bfc1bba61908" have entirely different histories.

5 changed files with 92 additions and 128 deletions

View File

@ -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: 'eye', // 进度相关图标 icon: 'workload', // 进度相关图标
hidden: false, hidden: false,
sort: 3.6 sort: 3.6
} }

View File

@ -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 = 40; const barHeight = 20;
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 = 40; const barHeight = 12;
return { return {
type: "rect", type: "rect",
shape: { shape: {

View File

@ -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,6 +52,10 @@
<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>
<!-- 搜索区域 --> <!-- 搜索区域 -->
@ -70,10 +74,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">
@ -130,9 +134,8 @@
<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" @click="handleView(task)">查看</button> <button class="operate-btn">查看</button>
<button class="operate-btn" @click="handleEdit(task)">编辑</button> <button class="operate-btn">编辑</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">
@ -163,34 +166,6 @@
</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>
@ -198,20 +173,14 @@
<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, Modal } from '@arco-design/web-vue'; import { Message } 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([
{ {
@ -265,39 +234,62 @@ 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.priority && task.priority !== searchForm.value.priority) return false; if (searchForm.value.name && !task.title.includes(searchForm.value.name)) {
if (searchForm.value.publisher && task.publisher !== searchForm.value.publisher) return false; 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.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; 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,
@ -307,22 +299,25 @@ 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: '',
@ -334,7 +329,6 @@ const resetSearch = () => {
}; };
}; };
//
const handleSubmit = () => { const handleSubmit = () => {
if (!taskFormRef.value?.form.taskName) { if (!taskFormRef.value?.form.taskName) {
Message.error('请填写任务名称'); Message.error('请填写任务名称');
@ -371,53 +365,6 @@ 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>
@ -638,6 +585,24 @@ 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;
@ -671,29 +636,26 @@ 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: #4ade80; background-color: #e2e8f0;
background-image: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); color: #475569;
} }
.priority-tag.medium { .priority-tag.medium {
background-color: #3b82f6; background-color: #dbeafe;
background-image: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); color: #1e40af;
} }
.priority-tag.high { .priority-tag.high {
background-color: #f59e0b; background-color: #fef3c7;
background-image: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: #92400e;
} }
.priority-tag.urgent { .priority-tag.urgent {
background-color: #ef4444; background-color: #fee2e2;
background-image: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: #b91c1c;
} }
/* 状态标签样式 */ /* 状态标签样式 */

View File

@ -34,6 +34,8 @@
</option> </option>
</select> </select>
</div> </div>
</div> </div>
</template> </template>

View File

@ -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>