From 2641522dcd5cca138a65d2a2feaaf7bbe1c3c9d0 Mon Sep 17 00:00:00 2001 From: zhihao <18915542763@163.com> Date: Thu, 30 Apr 2026 09:24:45 +0800 Subject: [PATCH] =?UTF-8?q?czh-20260429-=E5=AE=9E=E7=8E=B0=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=AE=A1=E7=90=86=E7=9A=84=E5=89=8D=E7=AB=AF=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/tasklist/add-user.svg | 3 + src/assets/icons/tasklist/arrow-down.svg | 3 + src/assets/icons/tasklist/attachment.svg | 3 + src/assets/icons/tasklist/calendar.svg | 4 + src/assets/icons/tasklist/desc.svg | 3 + src/assets/icons/tasklist/drag-handle.svg | 8 + src/assets/icons/tasklist/followers.svg | 3 + .../icons/tasklist/list-drag-handle.svg | 3 + src/assets/icons/tasklist/list-file.svg | 3 + src/assets/icons/tasklist/priority.svg | 4 + src/assets/icons/tasklist/quick-access.svg | 3 + src/assets/icons/tasklist/remark.svg | 3 + src/assets/icons/tasklist/star-filled.svg | 3 + src/assets/icons/tasklist/star.svg | 3 + src/assets/icons/tasklist/subtask-drag.svg | 3 + src/assets/icons/tasklist/subtask.svg | 3 + src/assets/icons/tasklist/task-list.svg | 3 + src/assets/icons/tasklist/time.svg | 4 + src/assets/icons/tasklist/title.svg | 3 + src/assets/icons/tasklist/user.svg | 3 + src/views/tasklist/TaskList.api.ts | 152 + src/views/tasklist/TaskList.data.ts | 587 ++++ .../tasklist/components/AvatarDisplay.vue | 155 + .../tasklist/components/CollaboratorModal.vue | 430 +++ .../tasklist/components/CreateListModal.vue | 43 + .../tasklist/components/CreateTaskModal.vue | 55 + src/views/tasklist/components/TaskContent.vue | 2761 +++++++++++++++++ .../components/TaskDateRangePicker.vue | 123 + .../tasklist/components/TaskDetailDrawer.vue | 1357 ++++++++ .../tasklist/components/TaskListForm.vue | 123 + .../tasklist/components/TaskListModal.vue | 119 + src/views/tasklist/components/TaskSidebar.vue | 794 +++++ .../tasklist/components/UserSelectModal.vue | 366 +++ src/views/tasklist/index.vue | 982 ++++++ src/views/tasklist/types.ts | 240 ++ 35 files changed, 8355 insertions(+) create mode 100644 src/assets/icons/tasklist/add-user.svg create mode 100644 src/assets/icons/tasklist/arrow-down.svg create mode 100644 src/assets/icons/tasklist/attachment.svg create mode 100644 src/assets/icons/tasklist/calendar.svg create mode 100644 src/assets/icons/tasklist/desc.svg create mode 100644 src/assets/icons/tasklist/drag-handle.svg create mode 100644 src/assets/icons/tasklist/followers.svg create mode 100644 src/assets/icons/tasklist/list-drag-handle.svg create mode 100644 src/assets/icons/tasklist/list-file.svg create mode 100644 src/assets/icons/tasklist/priority.svg create mode 100644 src/assets/icons/tasklist/quick-access.svg create mode 100644 src/assets/icons/tasklist/remark.svg create mode 100644 src/assets/icons/tasklist/star-filled.svg create mode 100644 src/assets/icons/tasklist/star.svg create mode 100644 src/assets/icons/tasklist/subtask-drag.svg create mode 100644 src/assets/icons/tasklist/subtask.svg create mode 100644 src/assets/icons/tasklist/task-list.svg create mode 100644 src/assets/icons/tasklist/time.svg create mode 100644 src/assets/icons/tasklist/title.svg create mode 100644 src/assets/icons/tasklist/user.svg create mode 100644 src/views/tasklist/TaskList.api.ts create mode 100644 src/views/tasklist/TaskList.data.ts create mode 100644 src/views/tasklist/components/AvatarDisplay.vue create mode 100644 src/views/tasklist/components/CollaboratorModal.vue create mode 100644 src/views/tasklist/components/CreateListModal.vue create mode 100644 src/views/tasklist/components/CreateTaskModal.vue create mode 100644 src/views/tasklist/components/TaskContent.vue create mode 100644 src/views/tasklist/components/TaskDateRangePicker.vue create mode 100644 src/views/tasklist/components/TaskDetailDrawer.vue create mode 100644 src/views/tasklist/components/TaskListForm.vue create mode 100644 src/views/tasklist/components/TaskListModal.vue create mode 100644 src/views/tasklist/components/TaskSidebar.vue create mode 100644 src/views/tasklist/components/UserSelectModal.vue create mode 100644 src/views/tasklist/index.vue create mode 100644 src/views/tasklist/types.ts diff --git a/src/assets/icons/tasklist/add-user.svg b/src/assets/icons/tasklist/add-user.svg new file mode 100644 index 0000000..b2d6a81 --- /dev/null +++ b/src/assets/icons/tasklist/add-user.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/arrow-down.svg b/src/assets/icons/tasklist/arrow-down.svg new file mode 100644 index 0000000..7c6b1ca --- /dev/null +++ b/src/assets/icons/tasklist/arrow-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/attachment.svg b/src/assets/icons/tasklist/attachment.svg new file mode 100644 index 0000000..7dc0051 --- /dev/null +++ b/src/assets/icons/tasklist/attachment.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/calendar.svg b/src/assets/icons/tasklist/calendar.svg new file mode 100644 index 0000000..f8d54d5 --- /dev/null +++ b/src/assets/icons/tasklist/calendar.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/desc.svg b/src/assets/icons/tasklist/desc.svg new file mode 100644 index 0000000..d50aa5e --- /dev/null +++ b/src/assets/icons/tasklist/desc.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/drag-handle.svg b/src/assets/icons/tasklist/drag-handle.svg new file mode 100644 index 0000000..78bd50c --- /dev/null +++ b/src/assets/icons/tasklist/drag-handle.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/followers.svg b/src/assets/icons/tasklist/followers.svg new file mode 100644 index 0000000..fef41fb --- /dev/null +++ b/src/assets/icons/tasklist/followers.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/list-drag-handle.svg b/src/assets/icons/tasklist/list-drag-handle.svg new file mode 100644 index 0000000..fa657cd --- /dev/null +++ b/src/assets/icons/tasklist/list-drag-handle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/list-file.svg b/src/assets/icons/tasklist/list-file.svg new file mode 100644 index 0000000..cfb7562 --- /dev/null +++ b/src/assets/icons/tasklist/list-file.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/priority.svg b/src/assets/icons/tasklist/priority.svg new file mode 100644 index 0000000..fc309fb --- /dev/null +++ b/src/assets/icons/tasklist/priority.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/quick-access.svg b/src/assets/icons/tasklist/quick-access.svg new file mode 100644 index 0000000..26b83ca --- /dev/null +++ b/src/assets/icons/tasklist/quick-access.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/remark.svg b/src/assets/icons/tasklist/remark.svg new file mode 100644 index 0000000..88c054c --- /dev/null +++ b/src/assets/icons/tasklist/remark.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/star-filled.svg b/src/assets/icons/tasklist/star-filled.svg new file mode 100644 index 0000000..e93a029 --- /dev/null +++ b/src/assets/icons/tasklist/star-filled.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/star.svg b/src/assets/icons/tasklist/star.svg new file mode 100644 index 0000000..d909e67 --- /dev/null +++ b/src/assets/icons/tasklist/star.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/subtask-drag.svg b/src/assets/icons/tasklist/subtask-drag.svg new file mode 100644 index 0000000..fa657cd --- /dev/null +++ b/src/assets/icons/tasklist/subtask-drag.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/subtask.svg b/src/assets/icons/tasklist/subtask.svg new file mode 100644 index 0000000..5f8ccfd --- /dev/null +++ b/src/assets/icons/tasklist/subtask.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/task-list.svg b/src/assets/icons/tasklist/task-list.svg new file mode 100644 index 0000000..88c054c --- /dev/null +++ b/src/assets/icons/tasklist/task-list.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/time.svg b/src/assets/icons/tasklist/time.svg new file mode 100644 index 0000000..5014780 --- /dev/null +++ b/src/assets/icons/tasklist/time.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/title.svg b/src/assets/icons/tasklist/title.svg new file mode 100644 index 0000000..a59e2a3 --- /dev/null +++ b/src/assets/icons/tasklist/title.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/tasklist/user.svg b/src/assets/icons/tasklist/user.svg new file mode 100644 index 0000000..fef41fb --- /dev/null +++ b/src/assets/icons/tasklist/user.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/views/tasklist/TaskList.api.ts b/src/views/tasklist/TaskList.api.ts new file mode 100644 index 0000000..c503aaf --- /dev/null +++ b/src/views/tasklist/TaskList.api.ts @@ -0,0 +1,152 @@ +import { defHttp } from '/@/utils/http/axios'; +import { useMessage } from '/@/hooks/web/useMessage'; + +const { createConfirm } = useMessage(); + +enum Api { + list = '/tasklist/taskList/list', + save = '/tasklist/taskList/add', + edit = '/tasklist/taskList/edit', + deleteOne = '/tasklist/taskList/delete', + deleteBatch = '/tasklist/taskList/deleteBatch', + importExcel = '/tasklist/taskList/importExcel', + exportXls = '/tasklist/taskList/exportXls', + taskListDetialList = '/tasklist/taskList/queryTaskListDetialByMainId', + treeList = '/tasklist/taskList/treeList', + myFavorites = '/tasklist/taskList/myFavorites', + addTaskList = '/tasklist/taskList/addTaskList', + addTaskListGroup = '/tasklist/taskList/addTaskListGroup', + moveTaskList = '/tasklist/taskList/moveTaskList', + moveGroup = '/tasklist/taskList/moveGroup', + deleteTaskList = '/tasklist/taskList/deleteTaskList', + removeFavorite = '/tasklist/taskList/removeFavorite', + removeFavoriteGroup = '/tasklist/taskList/removeFavoriteGroup', + addToFavorites = '/tasklist/taskList/addToFavorites', + renameGroup = '/tasklist/taskList/renameGroup', + renameTaskList = '/tasklist/taskList/renameTaskList', + myOwnLists = '/tasklist/taskList/myOwnLists', + allLists = '/tasklist/taskList/allLists', + myCollabLists = '/tasklist/taskList/myCollabLists', + addTask = '/tasklist/taskListDetial/add', + editTask = '/tasklist/taskListDetial/edit', + deleteTask = '/tasklist/taskListDetial/delete', + toggleTaskStatus = '/tasklist/taskListDetial/toggleStatus', + getCollaborators = '/tasklist/taskList/getCollaborators', + addCollaborator = '/tasklist/taskList/addCollaborator', + removeCollaborator = '/tasklist/taskList/removeCollaborator', + updateCollaboratorPermission = '/tasklist/taskList/updateCollaboratorPermission', + getMyPermission = '/tasklist/taskList/getMyPermission', + moveTask = '/tasklist/taskListDetial/moveTask', + moveTaskGroup = '/tasklist/taskListDetial/moveTaskGroup', + followTask = '/tasklist/taskListDetial/follow', + unfollowTask = '/tasklist/taskListDetial/unfollow', + loadSubTasks = '/tasklist/taskListDetial/loadSubTasks', + listByMainId = '/tasklist/taskListDetial/listByMainId', + listAllByMainId = '/tasklist/taskListDetial/listAllByMainId', + myResponsibleTasks = '/tasklist/taskList/myResponsibleTasks', + myFollowedTasks = '/tasklist/taskList/myFollowedTasks', +} + +export const getExportUrl = Api.exportXls; +export const getImportUrl = Api.importExcel; +export const taskListDetialList = Api.taskListDetialList; + +export const list = (params) => defHttp.get({ url: Api.list, params }); + +export const treeList = (params) => defHttp.get({ url: Api.treeList, params }); + +export const deleteOne = (params, handleSuccess) => { + return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); +}; + +export const batchDelete = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); + }, + }); +}; + +export const saveOrUpdate = (params, isUpdate) => { + const url = isUpdate ? Api.edit : Api.save; + return defHttp.post({ url: url, params }); +}; + +export const addGroup = (params) => defHttp.post({ url: Api.addTaskListGroup, params }); + +export const addTaskList = (params) => defHttp.post({ url: Api.addTaskList, params }); + +export const addTaskListGroup = (params) => defHttp.post({ url: Api.addTaskListGroup, params }); + +export const moveTaskList = (params) => defHttp.post({ url: Api.moveTaskList, params }); + +export const moveGroup = (params) => defHttp.post({ url: Api.moveGroup, params }); + +export const getMyFavorites = () => defHttp.get({ url: Api.myFavorites }); + +export const deleteTaskListApi = (params) => defHttp.post({ url: Api.deleteTaskList, params }); + +export const removeFavorite = (params) => defHttp.post({ url: Api.removeFavorite, params }); + +export const addToFavorites = (params) => defHttp.post({ url: Api.addToFavorites, params }); + +export const removeFavoriteGroup = (params) => defHttp.post({ url: Api.removeFavoriteGroup, params }); + +export const renameGroup = (params) => defHttp.post({ url: Api.renameGroup, params }); + +export const renameTaskList = (params) => defHttp.post({ url: Api.renameTaskList, params }); + +export const getMyOwnLists = () => defHttp.get({ url: Api.myOwnLists }); + +export const getAllLists = () => defHttp.get({ url: Api.allLists }); + +export const getMyCollabLists = () => defHttp.get({ url: Api.myCollabLists }); + +export const addTask = (params) => defHttp.post({ url: Api.addTask, params }); + +export const editTask = (params) => defHttp.post({ url: Api.editTask, params }); + +export const deleteTask = (params, handleSuccess) => { + return defHttp.delete({ url: Api.deleteTask, params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); +}; + +export const toggleTaskStatus = (params) => defHttp.post({ url: Api.toggleTaskStatus, params }); + +export const getCollaborators = (params) => defHttp.get({ url: Api.getCollaborators, params }); + +export const addCollaborator = (params) => defHttp.post({ url: Api.addCollaborator, params }); + +export const removeCollaborator = (params) => defHttp.post({ url: Api.removeCollaborator, params }); + +export const updateCollaboratorPermission = (params) => defHttp.post({ url: Api.updateCollaboratorPermission, params }); + +export const getMyPermission = (params) => defHttp.get({ url: Api.getMyPermission, params }); + +export const moveTask = (params) => defHttp.post({ url: Api.moveTask, params }); + +export const moveTaskGroup = (params) => defHttp.post({ url: Api.moveTaskGroup, params }); + +export const followTask = (params) => defHttp.post({ url: Api.followTask, params }); + +export const unfollowTask = (params) => defHttp.post({ url: Api.unfollowTask, params }); + +export const loadSubTasks = (params) => defHttp.get({ url: Api.loadSubTasks, params }); + +export const listByMainId = (params) => defHttp.get({ url: Api.listByMainId, params }); + +export const listAllByMainId = (params) => defHttp.get({ url: Api.listAllByMainId, params }); + +export const myResponsibleTasks = () => defHttp.get({ url: Api.myResponsibleTasks }); + +export const myFollowedTasks = () => defHttp.get({ url: Api.myFollowedTasks }); diff --git a/src/views/tasklist/TaskList.data.ts b/src/views/tasklist/TaskList.data.ts new file mode 100644 index 0000000..7d2894e --- /dev/null +++ b/src/views/tasklist/TaskList.data.ts @@ -0,0 +1,587 @@ +import { BasicColumn } from '/@/components/Table'; +import { FormSchema } from '/@/components/Table'; +import { JVxeTypes, JVxeColumn } from '/@/components/jeecg/JVxeTable/types'; +import type { TaskListGroup } from './types'; + +export const columns: BasicColumn[] = [ + { + title: '任务清单名称', + align: 'center', + dataIndex: 'tasklistName', + }, + { + title: '父级节点', + align: 'center', + dataIndex: 'pid', + }, + { + title: '是否有子节点', + align: 'center', + dataIndex: 'hasChild', + }, + { + title: '排序号', + align: 'center', + dataIndex: 'sortOrder', + }, + { + title: '类型', + align: 'center', + dataIndex: 'type', + }, +]; +export const searchFormSchema: FormSchema[] = []; + +export const formSchema: FormSchema[] = [ + { + label: '任务清单名称', + field: 'tasklistName', + component: 'Input', + }, + { + label: '父级节点', + field: 'pid', + component: 'Input', + }, + { + label: '是否有子节点', + field: 'hasChild', + component: 'Input', + }, + { + label: '排序号', + field: 'sortOrder', + component: 'InputNumber', + }, + { + label: '类型', + field: 'type', + component: 'Input', + }, + { + label: '', + field: 'id', + component: 'Input', + show: false, + }, +]; + +export const taskListDetialColumns: JVxeColumn[] = [ + { + title: '父节点ID', + key: 'pid', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '是否有子节点', + key: 'hasChild', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '排序号', + key: 'sortOrder', + type: JVxeTypes.inputNumber, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '任务名称', + key: 'taskName', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '任务描述', + key: 'taskDesc', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '优先级', + key: 'priority', + type: JVxeTypes.select, + options: [], + dictCode: 'task_priority', + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '完成状态', + key: 'taskStatus', + type: JVxeTypes.inputNumber, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '负责人ID', + key: 'assigneeId', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '关注人ID', + key: 'followersId', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '分配人ID', + key: 'assignId', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '开始时间', + key: 'startTime', + type: JVxeTypes.datetime, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '结束时间', + key: 'endTime', + type: JVxeTypes.datetime, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '完成时间', + key: 'completeTime', + type: JVxeTypes.datetime, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '类型', + key: 'type', + type: JVxeTypes.input, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '子任务数', + key: 'subTaskCount', + type: JVxeTypes.inputNumber, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, + { + title: '子任务完成数', + key: 'completedSubTaskCount', + type: JVxeTypes.inputNumber, + width: '200px', + placeholder: '请输入${title}', + defaultValue: '', + }, +]; + +export const superQuerySchema = { + tasklistName: { title: '任务清单名称', order: 0, view: 'text', type: 'string' }, + pid: { title: '父级节点', order: 1, view: 'text', type: 'string' }, + hasChild: { title: '是否有子节点', order: 2, view: 'text', type: 'string' }, + sortOrder: { title: '排序号', order: 3, view: 'number', type: 'number' }, + type: { title: '类型', order: 4, view: 'text', type: 'string' }, + taskListDetial: { + title: '工作任务清单详情', + view: 'table', + fields: { + mainId: { title: '外键', order: 0, view: 'text', type: 'string' }, + pid: { title: '父节点ID', order: 1, view: 'text', type: 'string' }, + hasChild: { title: '是否有子节点', order: 2, view: 'text', type: 'string' }, + sortOrder: { title: '排序号', order: 3, view: 'number', type: 'number' }, + taskName: { title: '任务名称', order: 4, view: 'text', type: 'string' }, + taskDesc: { title: '任务描述', order: 5, view: 'text', type: 'string' }, + priority: { title: '优先级', order: 6, view: 'list', type: 'string', dictCode: 'task_priority' }, + taskStatus: { title: '完成状态', order: 7, view: 'number', type: 'number' }, + assigneeId: { title: '负责人ID', order: 8, view: 'text', type: 'string' }, + followersId: { title: '关注人ID', order: 9, view: 'text', type: 'string' }, + assignId: { title: '分配人ID', order: 10, view: 'text', type: 'string' }, + startTime: { title: '开始时间', order: 11, view: 'datetime', type: 'string' }, + endTime: { title: '结束时间', order: 12, view: 'datetime', type: 'string' }, + completeTime: { title: '完成时间', order: 13, view: 'datetime', type: 'string' }, + type: { title: '类型', order: 14, view: 'text', type: 'string' }, + subTaskCount: { title: '子任务数', order: 15, view: 'number', type: 'number' }, + completedSubTaskCount: { title: '子任务完成数', order: 16, view: 'number', type: 'number' }, + }, + }, +}; + +export function getBpmFormSchema(_formData): FormSchema[] { + return formSchema; +} + +export const createTaskFormSchema: FormSchema[] = [ + { + label: '任务标题', + field: 'taskName', + component: 'Input', + required: true, + }, + { + label: '任务描述', + field: 'taskDesc', + component: 'InputTextArea', + componentProps: { rows: 3 }, + }, + { + label: '优先级', + field: 'priority', + component: 'JDictSelectTag', + componentProps: { + dictCode: 'task_priority', + placeholder: '请选择优先级', + }, + }, + { + label: '负责人', + field: 'assigneeId', + component: 'JSelectUser', + componentProps: { + labelKey: 'realname', + rowKey: 'id', + isRadioSelection: true, + }, + }, + { + label: '分配人', + field: 'assignId', + component: 'JSelectUser', + componentProps: { + labelKey: 'realname', + rowKey: 'id', + isRadioSelection: true, + }, + }, + { + label: '开始日期', + field: 'startTime', + component: 'DatePicker', + componentProps: { + style: { width: '100%' }, + }, + }, + { + label: '截止日期', + field: 'endTime', + component: 'DatePicker', + componentProps: { + style: { width: '100%' }, + }, + }, + { + label: '', + field: 'groupId', + component: 'Input', + show: false, + }, +]; + +export const createListFormSchema: FormSchema[] = [ + { + label: '清单名称', + field: 'tasklistName', + component: 'Input', + required: true, + }, +]; + +export const mockTaskListGroups: TaskListGroup[] = [ + { + id: 'tlg-1', + name: '任务清单', + pid: '0', + hasChild: '1', + type: 0, + sortOrder: 1, + delFlag: 0, + taskLists: [ + { + id: 'tl-1', + name: '测试', + pid: 'tlg-1', + hasChild: '0', + type: 1, + sortOrder: 1, + delFlag: 0, + groups: [ + { + id: 'g-1', + name: '默认分组', + taskListId: 'tl-1', + collapsed: false, + tasks: [ + { + id: 't-1', + taskName: '导入数据、说明书编写、bug调试', + taskDesc: '', + completed: true, + priority: 'high', + assigneeId: 'u1', + assigneeName: '惠灵佳', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-12-15', + endTime: '2025-12-19', + completeTime: '2025-12-18', + createTime: '2025-12-15', + updateTime: '2025-12-18', + groupId: 'g-1', + groupName: '默认分组', + childCount: 2, + completedChildCount: 1, + pid: '', + hasChild: '1', + }, + { + id: 't-1-1', + taskName: '数据导入脚本', + taskDesc: '编写Python导入脚本', + completed: true, + priority: 'high', + assigneeId: 'u1', + assigneeName: '惠灵佳', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-12-15', + endTime: '2025-12-16', + completeTime: '2025-12-16', + createTime: '2025-12-15', + updateTime: '2025-12-16', + groupId: 'g-1', + groupName: '默认分组', + childCount: 0, + completedChildCount: 0, + pid: 't-1', + hasChild: '0', + }, + { + id: 't-1-2', + taskName: '说明书编写', + taskDesc: '', + completed: false, + priority: 'medium', + assigneeId: 'u1', + assigneeName: '惠灵佳', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-12-17', + endTime: '2025-12-19', + completeTime: '', + createTime: '2025-12-15', + updateTime: '2025-12-17', + groupId: 'g-1', + groupName: '默认分组', + childCount: 0, + completedChildCount: 0, + pid: 't-1', + hasChild: '0', + }, + { + id: 't-2', + taskName: '完成新能体开发与调试', + taskDesc: '', + completed: true, + priority: 'medium', + assigneeId: 'u1', + assigneeName: '惠灵佳,张三,李四,王五,赵六,钱七', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-12-11', + endTime: '2025-12-12', + completeTime: '2025-12-12', + createTime: '2025-12-09', + updateTime: '2025-12-12', + groupId: 'g-1', + groupName: '默认分组', + childCount: 0, + completedChildCount: 0, + pid: '', + hasChild: '0', + }, + { + id: 't-3', + taskName: '完成其他数据的导入与知识库构建', + taskDesc: '', + completed: false, + priority: 'high', + assigneeId: 'u1', + assigneeName: '惠灵佳,张三,李四,王五,赵六,钱七,孙八,周九,吴十,郑十一', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-12-10', + endTime: '2025-12-20', + completeTime: '', + createTime: '2025-12-09', + updateTime: '2025-12-10', + groupId: 'g-1', + groupName: '默认分组', + childCount: 0, + completedChildCount: 0, + pid: '', + hasChild: '0', + }, + { + id: 't-4', + taskName: '简历筛选脚本编写', + taskDesc: '', + completed: false, + priority: 'low', + assigneeId: 'u3', + assigneeName: '张三,惠灵佳', + assignId: 'u2', + assignName: '管理员', + followersId: 'u1', + followersName: '惠灵佳', + createBy: '张三', + startTime: '2025-11-27', + endTime: '2025-12-27', + completeTime: '', + createTime: '2025-11-27', + updateTime: '2025-11-27', + groupId: 'g-1', + groupName: '默认分组', + childCount: 0, + completedChildCount: 0, + pid: '', + hasChild: '0', + }, + { + id: 't-5', + taskName: '使用HiAgent开发AI应用', + taskDesc: '保罗办需求', + completed: false, + priority: 'high', + assigneeId: 'u1', + assigneeName: '惠灵佳,张三,李四,王五', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-10-20', + endTime: '2025-12-31', + completeTime: '', + createTime: '2025-10-03', + updateTime: '2025-10-20', + groupId: 'g-1', + groupName: '默认分组', + childCount: 0, + completedChildCount: 0, + pid: '', + hasChild: '0', + }, + ], + }, + { + id: 'g-2', + name: '高优先级', + taskListId: 'tl-1', + collapsed: false, + tasks: [ + { + id: 't-6', + taskName: '考研关系梳理配置', + taskDesc: '', + completed: false, + priority: 'high', + assigneeId: 'u1', + assigneeName: '惠灵佳,李四,王五,赵六,钱七,孙八', + assignId: 'u2', + assignName: '管理员', + followersId: '', + followersName: '', + createBy: '惠灵佳', + startTime: '2025-12-11', + endTime: '2025-12-24', + completeTime: '', + createTime: '2025-11-27', + updateTime: '2025-12-11', + groupId: 'g-2', + groupName: '高优先级', + childCount: 0, + completedChildCount: 0, + pid: '', + hasChild: '0', + }, + ], + }, + ], + }, + { + id: 'tl-2', + name: '2025年所有工作登记', + pid: 'tlg-1', + hasChild: '0', + type: 1, + sortOrder: 2, + delFlag: 0, + groups: [ + { + id: 'g-tl2-1', + name: '默认分组', + taskListId: 'tl-2', + collapsed: false, + tasks: [], + }, + ], + }, + { + id: 'tl-3', + name: 'AI应用开发与实施', + pid: 'tlg-1', + hasChild: '0', + type: 1, + sortOrder: 3, + delFlag: 0, + groups: [ + { + id: 'g-tl3-1', + name: '默认分组', + taskListId: 'tl-3', + collapsed: false, + tasks: [], + }, + ], + }, + ], + }, +]; diff --git a/src/views/tasklist/components/AvatarDisplay.vue b/src/views/tasklist/components/AvatarDisplay.vue new file mode 100644 index 0000000..627c222 --- /dev/null +++ b/src/views/tasklist/components/AvatarDisplay.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/src/views/tasklist/components/CollaboratorModal.vue b/src/views/tasklist/components/CollaboratorModal.vue new file mode 100644 index 0000000..2e6788d --- /dev/null +++ b/src/views/tasklist/components/CollaboratorModal.vue @@ -0,0 +1,430 @@ + + + + + diff --git a/src/views/tasklist/components/CreateListModal.vue b/src/views/tasklist/components/CreateListModal.vue new file mode 100644 index 0000000..d6f8bde --- /dev/null +++ b/src/views/tasklist/components/CreateListModal.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/views/tasklist/components/CreateTaskModal.vue b/src/views/tasklist/components/CreateTaskModal.vue new file mode 100644 index 0000000..9db2f3c --- /dev/null +++ b/src/views/tasklist/components/CreateTaskModal.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/views/tasklist/components/TaskContent.vue b/src/views/tasklist/components/TaskContent.vue new file mode 100644 index 0000000..6509911 --- /dev/null +++ b/src/views/tasklist/components/TaskContent.vue @@ -0,0 +1,2761 @@ + + + + + diff --git a/src/views/tasklist/components/TaskDateRangePicker.vue b/src/views/tasklist/components/TaskDateRangePicker.vue new file mode 100644 index 0000000..d7d57dc --- /dev/null +++ b/src/views/tasklist/components/TaskDateRangePicker.vue @@ -0,0 +1,123 @@ + + + diff --git a/src/views/tasklist/components/TaskDetailDrawer.vue b/src/views/tasklist/components/TaskDetailDrawer.vue new file mode 100644 index 0000000..15f7975 --- /dev/null +++ b/src/views/tasklist/components/TaskDetailDrawer.vue @@ -0,0 +1,1357 @@ + + + + + diff --git a/src/views/tasklist/components/TaskListForm.vue b/src/views/tasklist/components/TaskListForm.vue new file mode 100644 index 0000000..9725e65 --- /dev/null +++ b/src/views/tasklist/components/TaskListForm.vue @@ -0,0 +1,123 @@ + + + diff --git a/src/views/tasklist/components/TaskListModal.vue b/src/views/tasklist/components/TaskListModal.vue new file mode 100644 index 0000000..cda3e6c --- /dev/null +++ b/src/views/tasklist/components/TaskListModal.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/src/views/tasklist/components/TaskSidebar.vue b/src/views/tasklist/components/TaskSidebar.vue new file mode 100644 index 0000000..fc847af --- /dev/null +++ b/src/views/tasklist/components/TaskSidebar.vue @@ -0,0 +1,794 @@ + + + + + diff --git a/src/views/tasklist/components/UserSelectModal.vue b/src/views/tasklist/components/UserSelectModal.vue new file mode 100644 index 0000000..dacf848 --- /dev/null +++ b/src/views/tasklist/components/UserSelectModal.vue @@ -0,0 +1,366 @@ + + + + + diff --git a/src/views/tasklist/index.vue b/src/views/tasklist/index.vue new file mode 100644 index 0000000..ccdf926 --- /dev/null +++ b/src/views/tasklist/index.vue @@ -0,0 +1,982 @@ + + + + + + diff --git a/src/views/tasklist/types.ts b/src/views/tasklist/types.ts new file mode 100644 index 0000000..a1d8215 --- /dev/null +++ b/src/views/tasklist/types.ts @@ -0,0 +1,240 @@ +export type ViewType = 'my-tasks' | 'followed' | 'all-tasks' | 'created' | 'assigned' | ''; + +export interface NavItem { + id: ViewType; + label: string; + icon: string; +} + +export const NAV_ITEMS: NavItem[] = [ + { id: 'my-tasks', label: '我负责的', icon: 'ant-design:user-outlined' }, + { id: 'followed', label: '我关注的', icon: 'ant-design:star-outlined' }, +]; + +export const QUICK_ACCESS_ITEMS: NavItem[] = [ + { id: 'all-tasks', label: '全部清单', icon: 'ant-design:unordered-list-outlined' }, + { id: 'created', label: '我所有的', icon: 'ant-design:edit-outlined' }, + { id: 'assigned', label: '我协作的', icon: 'ant-design:send-outlined' }, +]; + +export const QUICK_ACCESS_TAB_MAP: Record = { + 'all-tasks': 'all', + created: 'own', + assigned: 'collab', +}; + +export const MAX_TASK_DEPTH = 5; + +const AVATAR_COLORS = ['#3370ff', '#14c9c9', '#f77234', '#722ed1', '#eb2f96', '#f53f3f', '#0fc6c2', '#3491fa', '#9fdb1d', '#f7ba1e']; + +export function getAvatarColor(name: string): string { + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length]; +} + +export interface TaskItem { + id: string; + taskName: string; + taskDesc: string; + completed: boolean; + priority: string; + assigneeId: string; + assigneeName: string; + assignId: string; + assignName: string; + participantId: string; + participantName: string; + followersId: string; + followersName: string; + createBy: string; + startTime: string; + endTime: string; + completeTime: string; + createTime: string; + updateTime: string; + type: string; + subTaskCount: number; + completedSubTaskCount: number; + remark: string; + groupId: string; + groupName: string; + childCount: number; + completedChildCount: number; + pid: string; + hasChild: string; + sortOrder: number; +} + +export interface TaskGroup { + id: string; + name: string; + taskListId: string; + collapsed: boolean; + tasks: TaskItem[]; +} + +export interface TaskList { + id: string; + name: string; + pid: string; + hasChild: string; + type: number; + sortOrder: number; + delFlag: number; + icon?: string; + groups: TaskGroup[]; + ownerName?: string; + collaboratorNames?: string; + createTimeStr?: string; + myPermission?: string; +} + +export interface TaskListGroup { + id: string; + name: string; + pid: string; + hasChild: string; + type: number; + sortOrder: number; + delFlag: number; + taskLists: TaskList[]; +} + +export type StatusFilter = 'all' | 'completed' | 'uncompleted'; + +export type GroupDimension = 'custom' | 'assignee' | 'startTime' | 'endTime' | 'createBy' | 'priority' | 'none'; + +export type SortField = 'sortOrder' | 'startTime' | 'endTime' | 'createTime' | 'completeTime' | 'updateTime' | 'priority'; + +export type SortDirection = 'asc' | 'desc'; + +export type FieldKey = + | 'taskDesc' + | 'assigneeName' + | 'participantName' + | 'startTime' + | 'endTime' + | 'subTaskProgress' + | 'createBy' + | 'assignName' + | 'followersName' + | 'createTime' + | 'completeTime' + | 'updateTime' + | 'priority' + | 'remark'; + +export interface FieldConfig { + key: FieldKey; + label: string; + defaultVisible: boolean; +} + +export const FIELD_CONFIG: FieldConfig[] = [ + { key: 'taskDesc', label: '任务描述', defaultVisible: false }, + { key: 'assigneeName', label: '负责人', defaultVisible: true }, + { key: 'participantName', label: '参与人', defaultVisible: true }, + { key: 'startTime', label: '开始时间', defaultVisible: true }, + { key: 'endTime', label: '截止时间', defaultVisible: true }, + { key: 'subTaskProgress', label: '子任务进度', defaultVisible: true }, + { key: 'createBy', label: '创建人', defaultVisible: true }, + { key: 'assignName', label: '分配人', defaultVisible: false }, + { key: 'followersName', label: '关注人', defaultVisible: false }, + { key: 'createTime', label: '创建时间', defaultVisible: false }, + { key: 'completeTime', label: '完成时间', defaultVisible: true }, + { key: 'updateTime', label: '更新时间', defaultVisible: false }, + { key: 'priority', label: '优先级', defaultVisible: true }, + { key: 'remark', label: '其他事项说明', defaultVisible: true }, +]; + +export const DEFAULT_VISIBLE_FIELDS: FieldKey[] = FIELD_CONFIG.filter((f) => f.defaultVisible).map((f) => f.key); + +export interface GroupOption { + value: GroupDimension; + label: string; +} + +export const GROUP_OPTIONS: GroupOption[] = [ + { value: 'custom', label: '自定义分组' }, + { value: 'assignee', label: '按负责人' }, + { value: 'startTime', label: '按开始时间' }, + { value: 'endTime', label: '按截止时间' }, + { value: 'createBy', label: '按创建人' }, + { value: 'priority', label: '按优先级' }, + { value: 'none', label: '无分组' }, +]; + +export interface SortOption { + value: SortField; + label: string; +} + +export const SORT_OPTIONS: SortOption[] = [ + { value: 'sortOrder', label: '拖拽自定义' }, + { value: 'createTime', label: '创建时间' }, + { value: 'startTime', label: '开始时间' }, + { value: 'endTime', label: '截止时间' }, + { value: 'completeTime', label: '完成时间' }, + { value: 'updateTime', label: '更新时间' }, + { value: 'priority', label: '优先级' }, +]; + +export type FilterFieldType = + | 'assigneeName' + | 'createBy' + | 'assignName' + | 'followersName' + | 'priority' + | 'startTime' + | 'endTime' + | 'createTime' + | 'completeTime' + | 'updateTime'; + +export type FilterOperator = 'contains' | 'equals' | 'before' | 'after' | 'between' | 'isEmpty' | 'isNotEmpty'; + +export interface FilterFieldConfig { + value: FilterFieldType; + label: string; + type: 'text' | 'select' | 'date'; + operators: FilterOperator[]; +} + +export const FILTER_FIELD_CONFIGS: FilterFieldConfig[] = [ + { value: 'assigneeName', label: '负责人', type: 'text', operators: ['contains', 'isEmpty', 'isNotEmpty'] }, + { value: 'createBy', label: '创建人', type: 'text', operators: ['contains', 'isEmpty', 'isNotEmpty'] }, + { value: 'assignName', label: '分配人', type: 'text', operators: ['contains', 'isEmpty', 'isNotEmpty'] }, + { value: 'followersName', label: '关注人', type: 'text', operators: ['contains', 'isEmpty', 'isNotEmpty'] }, + { value: 'priority', label: '优先级', type: 'select', operators: ['equals', 'isEmpty', 'isNotEmpty'] }, + { value: 'startTime', label: '开始时间', type: 'date', operators: ['before', 'after', 'between', 'isEmpty', 'isNotEmpty'] }, + { value: 'endTime', label: '截止时间', type: 'date', operators: ['before', 'after', 'between', 'isEmpty', 'isNotEmpty'] }, + { value: 'createTime', label: '创建时间', type: 'date', operators: ['before', 'after', 'between', 'isEmpty', 'isNotEmpty'] }, + { value: 'completeTime', label: '完成时间', type: 'date', operators: ['before', 'after', 'between', 'isEmpty', 'isNotEmpty'] }, + { value: 'updateTime', label: '更新时间', type: 'date', operators: ['before', 'after', 'between', 'isEmpty', 'isNotEmpty'] }, +]; + +export const FILTER_OPERATOR_LABELS: Record = { + contains: '包含', + equals: '等于', + before: '早于', + after: '晚于', + between: '介于', + isEmpty: '为空', + isNotEmpty: '不为空', +}; + +export interface FilterCondition { + id: string; + field: FilterFieldType; + operator: FilterOperator; + value: any; + valueEnd?: any; +} + +export interface FlatTaskItem { + task: TaskItem; + level: number; +}