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 1/4] =?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 @@ + + + + - + + + + + {{ names[0].charAt(0) }} + + {{ names[0] }} + + + + + {{ name.charAt(0) }} + + + + + {{ name.charAt(0) }} + + + +{{ names.length - (maxShow - 1) }} + + + + + + + + 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 @@ + + + + + + + + {{ (item.username || '?').charAt(0) }} + + + + {{ item.username }} + 所有者 + + + + + 所有者 + + + + + {{ item.permission === '2' ? '可编辑' : '可阅读' }} + + + + onPermissionChange(item.permissionId, key)"> + + 可编辑 + + + + 可阅读 + + + + + + + + + + + {{ item.permission === '2' ? '可编辑' : '可阅读' }} + + + + + + 暂无协作人 + + + + + + 添加协作人 + + + + + + + + 权限 + + 可编辑 + 可阅读 + + + + + + + + + + + + + + + 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 @@ + + + + + 任务清单 + + + + + + + {{ currentList?.name || '请选择清单' }} + + + + + + + + + 重命名 + + + + 删除 + + + + + + + + + + + 协作人 + + + + + + + + + {{ tab.label }} + + + + + + + + 新建任务 + + + + + + + + + + 新建分组 + + + + + + + + + + 全部清单 + 已完成 + 未完成 + + + + + {{ statusFilterLabel }} + + + + + + + + + 筛选条件 + 清空全部 + + + + onConditionFieldChange(idx, v)" + > + {{ fc.label }} + + + onConditionOperatorChange(idx, v)" + > + + {{ FILTER_OPERATOR_LABELS[op] }} + + + + + onConditionValueChange(idx, e.target.value)" + /> + onConditionValueChange(idx, v)" + > + 高 + 中 + 低 + + onConditionValueChange(idx, v)" + /> + + onConditionValueChange(idx, v)" + /> + ~ + onConditionValueEndChange(idx, v)" + /> + + + + + + + + + + + 添加条件 + + + + + + + + 筛选 + {{ activeFilterCount }} + + + + + + + + + {{ opt.label }} + + + + + + + + + + + + + + + 排序: {{ currentSortLabel }} + + + + + + + + + {{ opt.label }} + + + + + + 分组: {{ currentGroupLabel }} + + + + + + + + 字段配置 + + {{ field.label }} + + + + + + + 字段配置 + + + + + + + + + 名称 + 所有者 + 协作者 + 创建时间 + + + + + {{ list.name }} + + + + + + + + + + - + + + + + + - + + + {{ getFormattedCreateTime(list) }} + + + + + 暂无任务清单 + + + + + + + + + + 任务标题 + + + + + + + + {{ field.label }} + + + + + + + + + + + + + + + + + + {{ group.name }} + + + + {{ getPriorityLabel(group.name) }} + + {{ group.name }} + + + + + {{ group.name }} + ({{ countTopLevelTasks(group.tasks) }}) + + + + + + + + + + onGroupMenuClick(key, group.id, group.name)"> + 重命名 + 删除 + + + + + + + + + + + + + + + + + + + onTaskDragEnd(evt)" + @move="onTaskMove" + > + + + + + + + + + + + + + + + + + + + {{ ft.task.taskName }} + + + {{ ft.task.completedChildCount }}/{{ ft.task.childCount }} + + + + + + + + + + + + - + + + + + + {{ getPriorityLabel(ft.task.priority) }} + - + + + onPriorityChange(ft.task.id, key)"> + + {{ + opt.label + }} + + + + + + {{ getPriorityLabel(ft.task.priority) }} + - + + + + + + + + + + + + + + + + + + - + + + + + onDateCellChange(date, field.key)" + @open-change="onCellPickerOpenChange" + > + + + 清除 + + + + + + + {{ field.key === 'startTime' ? ft.task.startTime : ft.task.endTime }} + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + - + + + + + + + {{ ft.task.completeTime.substring(0, 10) }} + + + - + + + + + + {{ ft.task.remark.length > 20 ? ft.task.remark.substring(0, 20) + '...' : ft.task.remark }} + + - + + + + + + + + + + + + + + + + + {{ getFieldValue(ft.task, field.key) }} + + + + + + + + + + + + + + + + 新建任务 + + + + + + + + + + + + + + 新建分组 + + + + + + 请从左侧选择一个任务清单 + + + + + + + + + + + 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 @@ + + + + + + + {{ taskData.completed ? '已完成' : '完成任务' }} + + + + + + + + + + + + + + + + + + + + + + + + + 该任务的上级任务已被完成 + + + + + {{ task.taskName }} + + + + + + + + {{ taskData.taskName }} + + + + + + + + + + + 负责人 + + + + + + 添加 + + + + + + + + + + + 参与人 + + + + + + 添加 + + + + + + + + + + + 开始时间 + + + + 清除 + + + + {{ + startTimeDisplay || '未设置' + }} + + + + + + + + + + + 截止时间 + + + + 清除 + + + + {{ endTimeDisplay || '未设置' }} + + + + + + + + + + + 完成时间 + {{ + taskData.completed ? (taskData.completeTime ? formatDateStr(taskData.completeTime) : '—') : '-' + }} + + + + + + + + + + + + {{ taskData.taskDesc || '添加描述' }} + + + + + + + + + + + + {{ taskData.remark || '添加其他事项说明' }} + + + + + + + + + + 添加附件 + + + + + + + + + + + 子任务 + {{ subtaskProgress.text }} + + + + 当前最多创建{{ MAX_TASK_DEPTH }}层任务 + + + + + + + + + + + + + + {{ child.taskName }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 添加子任务 + + + + + + + + + + + + + 将通知 0 人 + + + + + + + + + + + + + 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 @@ + + + + + + 任务 + + + + + + + {{ item.label }} + + + + + + + + + 快速访问 + + + + + {{ item.label }} + + + + + + + + + + 收藏清单 + + + + + + + + + + + + + + + {{ taskList.name }} + + + + + + + 重命名 + + + + 移除 + + + + + + + + + + + + + + + + + + + + {{ group.name }} + + + + + + + 重命名 + + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + {{ taskList.name }} + + + + + + + 重命名 + + + + 移除 + + + + + + + + + + + + + + 可拖拽清单加入该分组 + + + + + + + + + + + + + + 新建分组 + + + + + + + + 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 @@ + + + + + + + + {{ (item.username || '?').charAt(0) }} + + + + {{ item.username }} + + + + + + + + + + + 暂未选择 + + + + + + {{ addPanelTitle }} + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + (statusFilter = v)" + @update:group-dimension="(v) => (groupDimension = v)" + @update:sort-field="(v) => (sortField = v)" + @update:sort-direction="(v) => (sortDirection = v)" + @update:visible-fields="(v) => (visibleFields = v)" + @update:list-group-tab="onQuickAccessTabClick" + @list-select="onListSelect" + @open-collaborator="onOpenCollaborator" + @add-to-favorites="onAddToFavorites" + @remove-from-favorites="onRemoveFromFavorites" + @move-task="onMoveTask" + @move-task-group="onMoveTaskGroup" + @rename-list="onRenameList" + @delete-list="onDeleteList" + @rename-group="onRenameTaskGroup" + @delete-group="onDeleteTaskGroup" + /> + + + + + + + + + + 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; +} From d9d5ab70cc70d7b06e211a8ae8e7708fd1408f99 Mon Sep 17 00:00:00 2001 From: zhihao <18915542763@163.com> Date: Thu, 30 Apr 2026 22:43:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?czh-20260430-=E4=BF=AE=E5=A4=8D=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=AE=A1=E7=90=86=E7=9B=B8=E5=85=B3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tasklist/components/CreateListModal.vue | 4 +- src/views/tasklist/components/TaskContent.vue | 640 +++++---- .../tasklist/components/TaskDetailDrawer.vue | 88 +- .../tasklist/components/TaskFlatView.vue | 1146 +++++++++++++++++ src/views/tasklist/components/TaskSidebar.vue | 122 +- src/views/tasklist/index.vue | 442 ++++--- src/views/tasklist/types.ts | 10 +- src/views/tasklist/utils/taskUtils.ts | 29 + 8 files changed, 1925 insertions(+), 556 deletions(-) create mode 100644 src/views/tasklist/components/TaskFlatView.vue create mode 100644 src/views/tasklist/utils/taskUtils.ts diff --git a/src/views/tasklist/components/CreateListModal.vue b/src/views/tasklist/components/CreateListModal.vue index d6f8bde..9a9c9a3 100644 --- a/src/views/tasklist/components/CreateListModal.vue +++ b/src/views/tasklist/components/CreateListModal.vue @@ -30,12 +30,12 @@ try { setModalProps({ confirmLoading: true }); const values = await validate(); - await addTaskList({ + const listId = await addTaskList({ tasklistName: values.tasklistName || values.name, pid: pendingGroupId || undefined, }); closeModal(); - emit('success'); + emit('success', listId); } finally { setModalProps({ confirmLoading: false }); } diff --git a/src/views/tasklist/components/TaskContent.vue b/src/views/tasklist/components/TaskContent.vue index 6509911..9dcef26 100644 --- a/src/views/tasklist/components/TaskContent.vue +++ b/src/views/tasklist/components/TaskContent.vue @@ -8,9 +8,9 @@ - - - {{ tab.label }} - - - - - - - 新建任务 - - - - - - - - - - 新建分组 - - - - - - @@ -282,12 +250,14 @@ - + {{ list.name }} - - - - + + + + + + @@ -344,7 +314,7 @@ - + - - - - + + + + + + + + + + + + {{ group.name }} + + + + {{ getPriorityLabel(group.name) }} + + {{ group.name }} + + + {{ group.name }} - - - - {{ getPriorityLabel(group.name) }} - - {{ group.name }} - - - - - {{ group.name }} - ({{ countTopLevelTasks(group.tasks) }}) - - - - - - - + ({{ countTopLevelTasks(group.tasks) }}) + + + - - onGroupMenuClick(key, group.id, group.name)"> - 重命名 - 删除 - - - + + + + + + onGroupMenuClick(key, group.id, group.name)"> + 重命名 + + 删除 + + 删除 + + + + - - - - - - - - - - - - + - + @@ -468,8 +426,8 @@ @@ -483,7 +441,7 @@ - + - + - + - + - + {{ ft.task.completeTime.substring(0, 10) }} @@ -657,28 +623,83 @@ - - - {{ ft.task.remark.length > 20 ? ft.task.remark.substring(0, 20) + '...' : ft.task.remark }} - - - + + + + + {{ ft.task.remark.length > 20 ? ft.task.remark.substring(0, 20) + '...' : ft.task.remark }} + + + + + - + + - + + + + + - - + + {{ ft.task.taskDesc.length > 20 ? ft.task.taskDesc.substring(0, 20) + '...' : ft.task.taskDesc }} + + + @@ -694,6 +715,29 @@ + + + + + + + + + + + 新建任务 - + @@ -717,6 +761,7 @@ + + diff --git a/src/views/tasklist/components/TaskSidebar.vue b/src/views/tasklist/components/TaskSidebar.vue index fc847af..48ab97e 100644 --- a/src/views/tasklist/components/TaskSidebar.vue +++ b/src/views/tasklist/components/TaskSidebar.vue @@ -47,22 +47,24 @@ 收藏清单 - + + + + + + + 新建清单 + + + + 新建分组 + + + + - - - - - + - + + + + + - + - + @@ -151,7 +165,7 @@ /> - + 可拖拽清单加入该分组 @@ -229,7 +243,7 @@ + + diff --git a/src/views/tasklist/components/TaskAttachmentUpload.vue b/src/views/tasklist/components/TaskAttachmentUpload.vue new file mode 100644 index 0000000..07f132a --- /dev/null +++ b/src/views/tasklist/components/TaskAttachmentUpload.vue @@ -0,0 +1,462 @@ + + + + + + + 选择文件上传 + + + + + + 共 {{ displayFileList.length }} 个文件 + + + 下载全部 + + + + + + + + + {{ file.fileName }} + + {{ getSecretLevelLabel(file.secretLevel) }} + + + 待保存 + + {{ formatFileSize(file.fileSize) }} + + + + + + 下载 + + + + 删除 + + + + + + + + + + + + + + diff --git a/src/views/tasklist/components/TaskAttachmentUploadModal.vue b/src/views/tasklist/components/TaskAttachmentUploadModal.vue new file mode 100644 index 0000000..5f5c8fc --- /dev/null +++ b/src/views/tasklist/components/TaskAttachmentUploadModal.vue @@ -0,0 +1,253 @@ + + + + + + + + + {{ option.label }} + + + + + + + + + + {{ multiple ? '选择文件(可多选)' : '选择文件' }} + + + + + + + + + {{ idx + 1}}. {{ f.name }}({{ formatFileSize(f.size) }}) + + + + + + + + + + + diff --git a/src/views/tasklist/components/TaskContent.vue b/src/views/tasklist/components/TaskContent.vue index 9dcef26..f842bc4 100644 --- a/src/views/tasklist/components/TaskContent.vue +++ b/src/views/tasklist/components/TaskContent.vue @@ -36,6 +36,9 @@ + + {{ currentList.secretText }} + 名称 所有者 协作者 + 密级 创建时间 @@ -273,6 +277,16 @@ - + + + {{ list.secretText }} + + - + {{ getFormattedCreateTime(list) }} @@ -790,6 +804,7 @@ :user-ids="userSelectModalCurrentIds" :user-names="userSelectModalCurrentNames" :multiple="true" + :secret-level="currentList?.secretLevel || 1" @confirm="onUserSelectConfirm" /> @@ -804,7 +819,7 @@ import UserSelectModal from './UserSelectModal.vue'; import { useUserStore } from '/@/store/modules/user'; import { useMessage } from '/@/hooks/web/useMessage'; - import { FIELD_CONFIG, GROUP_OPTIONS, SORT_OPTIONS, MAX_TASK_DEPTH, FILTER_FIELD_CONFIGS, FILTER_OPERATOR_LABELS } from '../types'; + import { FIELD_CONFIG, GROUP_OPTIONS, SORT_OPTIONS, MAX_TASK_DEPTH, FILTER_FIELD_CONFIGS, FILTER_OPERATOR_LABELS, SECRET_LEVEL_COLOR } from '../types'; import { PRIORITY_OPTIONS, getPriorityBg, getPriorityLabel, isOverdue } from '../utils/taskUtils'; import type { TaskList, @@ -1851,6 +1866,15 @@ gap: 8px; } + .header-secret-badge { + font-size: 11px; + padding: 0 6px; + border-radius: 3px; + border: 1px solid; + line-height: 18px; + flex-shrink: 0; + } + .collaborator-bar { display: flex; align-items: center; @@ -2150,6 +2174,9 @@ .content-body { flex: 1; overflow: auto; + display: flex; + flex-direction: column; + min-height: 0; } .task-group-header { @@ -2651,6 +2678,21 @@ padding: 0 12px; } + .list-group-col-secret { + width: 80px; + padding: 0 12px; + text-align: center; + } + + .secret-badge-inline { + font-size: 11px; + padding: 1px 6px; + border-radius: 3px; + border: 1px solid; + line-height: 16px; + white-space: nowrap; + } + .list-group-col-time { width: 160px; padding: 0 12px; diff --git a/src/views/tasklist/components/TaskDetailDrawer.vue b/src/views/tasklist/components/TaskDetailDrawer.vue index 060f609..42db2f2 100644 --- a/src/views/tasklist/components/TaskDetailDrawer.vue +++ b/src/views/tasklist/components/TaskDetailDrawer.vue @@ -76,12 +76,19 @@ 负责人 - + + + + + + + 未指定 + + - 添加 - @@ -94,12 +101,19 @@ 参与人 - + + + + + + + 未指定 + + - 添加 - @@ -222,7 +236,14 @@ - 添加附件 + 添加附件 + @@ -323,32 +344,29 @@ - + + + + + 将通知 0 人 + + 将通知 0 人 - - - + @@ -357,7 +375,8 @@ import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; import Icon from '/@/components/Icon/index'; import AvatarDisplay from './AvatarDisplay.vue'; - import UserSelectModal from './UserSelectModal.vue'; + import SzUserSelectBySecLevel from '/@/components/semri/userComponent/SzUserSelectBySecLevel.vue'; + import TaskAttachmentUpload from './TaskAttachmentUpload.vue'; import TaskDateRangePicker from './TaskDateRangePicker.vue'; import draggable from 'vuedraggable'; import dayjs from 'dayjs'; @@ -412,16 +431,20 @@ const editingSubTaskValue = ref(null); const editingSubTaskStartValue = ref(null); const editingSubTaskEndValue = ref(null); - const subTaskDatePickerOpen = ref(false); const subTaskUserSelectId = ref(''); + const subTaskDatePickerOpen = ref(false); const drawerDatePickerVisible = ref(false); + const drawerDateEditMode = ref<'start' | 'end' | null>(null); const drawerPickerEpoch = ref(0); - const drawerUserSelectRef = ref(); + const currentListSecretLevel = ref(1); + const drawerUserSelectType = ref<'assignee' | 'followers' | 'participant'>('assignee'); - const drawerUserSelectCurrentIds = ref(''); - const drawerUserSelectCurrentNames = ref(''); + const drawerUserSelectCurrentIds = ref(''); + const drawerUserSelectCurrentNames = ref(''); + const userSelectModalRef = ref | null>(null); + const fileUploadRef = ref | null>(null); const parentTask = computed(() => { if (!taskData.value || !taskData.value.pid) return null; @@ -508,6 +531,7 @@ taskData.value = data?.task ? { ...data.task } : null; allTasks.value = data?.allTasks ? [...data.allTasks] : []; isReadOnly.value = data?.readOnly === true; + currentListSecretLevel.value = data?.secretLevel || 1; showNewSubTaskInput.value = false; newSubTaskName.value = ''; editingTitle.value = false; @@ -681,62 +705,71 @@ } function openDrawerUserSelect(type: 'assignee' | 'followers' | 'participant') { + if (!taskData.value) return; drawerUserSelectType.value = type; if (type === 'assignee') { - drawerUserSelectCurrentIds.value = taskData.value?.assigneeId || ''; - drawerUserSelectCurrentNames.value = taskData.value?.assigneeName || ''; + drawerUserSelectCurrentIds.value = taskData.value.assigneeId || ''; + drawerUserSelectCurrentNames.value = taskData.value.assigneeName || ''; } else if (type === 'participant') { - drawerUserSelectCurrentIds.value = taskData.value?.participantId || ''; - drawerUserSelectCurrentNames.value = taskData.value?.participantName || ''; + drawerUserSelectCurrentIds.value = taskData.value.participantId || ''; + drawerUserSelectCurrentNames.value = taskData.value.participantName || ''; } else { - drawerUserSelectCurrentIds.value = taskData.value?.followersId || ''; - drawerUserSelectCurrentNames.value = taskData.value?.followersName || ''; + drawerUserSelectCurrentIds.value = taskData.value.followersId || ''; + drawerUserSelectCurrentNames.value = taskData.value.followersName || ''; } nextTick(() => { - drawerUserSelectRef.value?.open(); + userSelectModalRef.value?.open(); }); } - function onDrawerUserSelectConfirm(userIds: string, userNames: string) { + function onUserSelectModalConfirm(userIds: string, userNames: string) { if (subTaskUserSelectId.value) { - emit('update-sub-task-fields', subTaskUserSelectId.value, { - assigneeId: userIds, - assigneeName: userNames, - }); - const child = allTasks.value.find((t) => t.id === subTaskUserSelectId.value); - if (child) { - child.assigneeId = userIds; - child.assigneeName = userNames; - } - subTaskUserSelectId.value = ''; - return; + onSubTaskUserSelectConfirm(userIds, userNames); + } else { + onDrawerUserSelectConfirm(userIds, userNames); } + } + + function onDrawerUserSelectConfirm(userIds: string, userNames: string) { if (!taskData.value) return; const type = drawerUserSelectType.value; if (type === 'assignee') { taskData.value.assigneeId = userIds; taskData.value.assigneeName = userNames; - autoSave(); } else if (type === 'participant') { taskData.value.participantId = userIds; taskData.value.participantName = userNames; - autoSave(); } else { taskData.value.followersId = userIds; taskData.value.followersName = userNames; - autoSave(); } + autoSave(); } function openSubTaskUserSelect(child: TaskItem) { subTaskUserSelectId.value = child.id; drawerUserSelectCurrentIds.value = child.assigneeId || ''; drawerUserSelectCurrentNames.value = child.assigneeName || ''; + drawerUserSelectType.value = 'assignee'; nextTick(() => { - drawerUserSelectRef.value?.open(); + userSelectModalRef.value?.open(); }); } + function onSubTaskUserSelectConfirm(userIds: string, userNames: string) { + if (!subTaskUserSelectId.value) return; + emit('update-sub-task-fields', subTaskUserSelectId.value, { + assigneeId: userIds, + assigneeName: userNames, + }); + const child = allTasks.value.find((t) => t.id === subTaskUserSelectId.value); + if (child) { + child.assigneeId = userIds; + child.assigneeName = userNames; + } + subTaskUserSelectId.value = ''; + } + function openSubTaskDateEditor(childId: string) { editingSubTask.value = childId; editingSubTaskField.value = 'dateRange'; @@ -780,6 +813,10 @@ showNewSubTaskInput.value = false; } } + + function openFileUploadModal() { + fileUploadRef.value?.handleOpenUploadModal(); + } diff --git a/src/views/bg/xispeak/components/BgXiSpeakForm.vue b/src/views/bg/xispeak/components/BgXiSpeakForm.vue new file mode 100644 index 0000000..f470129 --- /dev/null +++ b/src/views/bg/xispeak/components/BgXiSpeakForm.vue @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 是 + 否 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 不存在 + 存在 + + + + + + + 推进中 + 已完成 + + + + + + + + + + + + + + + + + + + + 提 交 + + + + + + + + + + + diff --git a/src/views/bg/xispeak/components/BgXiSpeakModal.vue b/src/views/bg/xispeak/components/BgXiSpeakModal.vue new file mode 100644 index 0000000..df0f908 --- /dev/null +++ b/src/views/bg/xispeak/components/BgXiSpeakModal.vue @@ -0,0 +1,89 @@ + + + + + 取消 + 确认 + + + + + + + + diff --git a/src/views/super/bpm/process/processDesign/ProcessDesignList.vue b/src/views/super/bpm/process/processDesign/ProcessDesignList.vue index a55d2f7..1010cc5 100644 --- a/src/views/super/bpm/process/processDesign/ProcessDesignList.vue +++ b/src/views/super/bpm/process/processDesign/ProcessDesignList.vue @@ -2,8 +2,8 @@ - 新建流程 - 新建简流程 + 新建流程 + 新建简流程 @@ -43,7 +43,7 @@
暂无任务清单
请从左侧选择一个任务清单