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/bg/xispeak/BgXiSpeak.api.ts b/src/views/bg/xispeak/BgXiSpeak.api.ts new file mode 100644 index 0000000..6c02d29 --- /dev/null +++ b/src/views/bg/xispeak/BgXiSpeak.api.ts @@ -0,0 +1,154 @@ +import { defHttp } from "/@/utils/http/axios"; +import { useMessage } from "/@/hooks/web/useMessage"; + +const { createConfirm } = useMessage(); + +enum Api { + list = '/bg/xispeak/bgXiSpeak/rootList', + save='/bg/xispeak/bgXiSpeak/add', + edit='/bg/xispeak/bgXiSpeak/edit', + deleteBgXiSpeak = '/bg/xispeak/bgXiSpeak/delete', + importExcel = '/bg/xispeak/bgXiSpeak/importExcel', + exportXls = '/bg/xispeak/bgXiSpeak/exportXls', + loadTreeData = '/bg/xispeak/bgXiSpeak/loadTreeRoot', + getChildList = '/bg/xispeak/bgXiSpeak/childList', + getChildListBatch = '/bg/xispeak/bgXiSpeak/getChildListBatch', +} + +/** + * 导出api + * @param params + */ +export const getExportUrl = Api.exportXls; + +/** + * 导入api + * @param params + */ +export const getImportUrl = Api.importExcel; + +/** + * 列表接口 + * @param params + */ +export const list = (params) => defHttp.get({ url: Api.list, params }); + +/** + * 删除 + * @param params + * @param handleSuccess + */ +export const deleteBgXiSpeak = (params,handleSuccess) => { + return defHttp.delete({ url: Api.deleteBgXiSpeak, params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); +} + +/** + * 批量删除 + * @param params + * @param handleSuccess + */ +export const batchDeleteBgXiSpeak = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({ url: Api.deleteBgXiSpeak, data: params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); + } + }); +} + +/** + * 保存或者更新 + * @param params + * @param isUpdate + */ +export const saveOrUpdateDict = (params, isUpdate) => { + let url = isUpdate ? Api.edit : Api.save; + return defHttp.post({ url: url, params },{ isTransformResponse:false }); +} + +/** + * 查询全部树形节点数据 + * @param params + */ +export const loadTreeData = (params) => defHttp.get({ url: Api.loadTreeData,params }); + +/** + * 查询子节点数据 + * @param params + */ +export const getChildList = (params) => defHttp.get({ url: Api.getChildList, params }); + +/** + * 批量查询子节点数据 + * @param params + */ +export const getChildListBatch = (params) => defHttp.get({ url: Api.getChildListBatch, params },{ isTransformResponse:false }); + +// ====================== 子表反馈相关API ====================== + +/** + * 子表列表接口 + * @param params + */ +export const bgXiSpeakFeedbackList = (params) => { + if (params['mainId']) { + return defHttp.get({ url: '/bg/xispeak/bgXiSpeak/listBgXiSpeakFeedbackByMainId', params }); + } + return Promise.resolve({}); +}; + +/** + * 子表删除单个 + */ +export const bgXiSpeakFeedbackDelete = (params, handleSuccess) => { + return defHttp.delete({ url: '/bg/xispeak/bgXiSpeak/deleteBgXiSpeakFeedback', params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); +}; + +/** + * 子表批量删除 + * @param params + */ +export const bgXiSpeakFeedbackDeleteBatch = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({ url: '/bg/xispeak/bgXiSpeak/deleteBatchBgXiSpeakFeedback', data: params }, { joinParamsToUrl: true }).then(() => { + handleSuccess(); + }); + }, + }); +}; + +/** + * 子表保存或者更新 + * @param params + * @param isUpdate + */ +export const bgXiSpeakFeedbackSaveOrUpdate = (params, isUpdate) => { + let url = isUpdate ? '/bg/xispeak/bgXiSpeak/editBgXiSpeakFeedback' : '/bg/xispeak/bgXiSpeak/addBgXiSpeakFeedback'; + return defHttp.post({ url: url, params }, { isTransformResponse: false }); +}; + +/** + * 子表导入 + */ +export const bgXiSpeakFeedbackImportUrl = '/bg/xispeak/bgXiSpeak/importBgXiSpeakFeedback'; + +/** + * 子表导出 + */ +export const bgXiSpeakFeedbackExportXlsUrl = '/bg/xispeak/bgXiSpeak/exportBgXiSpeakFeedback'; diff --git a/src/views/bg/xispeak/BgXiSpeak.data.ts b/src/views/bg/xispeak/BgXiSpeak.data.ts new file mode 100644 index 0000000..ec63aa0 --- /dev/null +++ b/src/views/bg/xispeak/BgXiSpeak.data.ts @@ -0,0 +1,190 @@ +import {BasicColumn} from '/@/components/Table'; +import {FormSchema} from '/@/components/Table'; +import { rules} from '/@/utils/helper/validator'; +import { render } from '/@/utils/common/renderUtils'; +import { getWeekMonthQuarterYear } from '/@/utils'; +//列表数据 +export const columns: BasicColumn[] = [ + { + title: '习近平总书记重要讲话指示批示情况', + align: 'center', + dataIndex: 'speakStatus' + }, + { + title: '时间', + align: 'center', + dataIndex: 'time', + customRender:({text}) =>{ + text = !text ? "" : (text.length > 10 ? text.substr(0,10) : text); + return text; + }, + }, + { + title: '企业负责同志批示情况', + align: 'center', + dataIndex: 'elmComment' + }, + { + title: '学习传达研究部署情况', + align: 'center', + dataIndex: 'status' + }, + { + title: '责任人', + align: 'center', + dataIndex: 'responsiblePerson_dictText' + }, + { + title: '落实方案', + align: 'center', + dataIndex: 'implPlan' + }, + { + title: '牵头部门', + align: 'center', + dataIndex: 'leadDepartment_dictText' + }, + { + title: '贯彻落实措施', + align: 'center', + dataIndex: 'implMeasures' + }, + { + title: '会议决策数', + align: 'center', + dataIndex: 'numberOfResolutions' + }, + { + title: '落实情况', + align: 'left', + dataIndex: 'implStatus' + }, + { + title: '落实条数', + align: 'center', + dataIndex: 'implCount' + }, + { + title: '已闭环数量', + align: 'center', + dataIndex: 'closedCount' + }, + { + title: '报告反馈情况', + align: 'center', + dataIndex: 'reportFeedbackStatus' + }, + { + title: '是否存在完成风险(0不存在,1存在)', + align: 'center', + dataIndex: 'isCompletionRisk' + }, + { + title: '拖期风险应对措施', + align: 'center', + dataIndex: 'delayRiskMitigation' + }, + { + title: '完成状态(0推进中,1已完成)', + align: 'center', + dataIndex: 'completionStatus' + }, + { + title: '落实部门id', + align: 'center', + dataIndex: 'implDept_dictText' + }, + { + title: '层深(最顶层节点层深为0)', + align: 'center', + dataIndex: 'treeDepth' + }, + { + title: '同级数据排序号', + align: 'center', + dataIndex: 'sortOrder' + }, + { + title: '流程引擎状态字段', + align: 'center', + dataIndex: 'bpmStatus' + }, + { + title: '督办次数', + align: 'center', + dataIndex: 'supervisionCount' + }, +]; + +//子表列表数据 +export const bgXiSpeakFeedbackColumns: BasicColumn[] = [ + { + title: '反馈部门id', + align: 'center', + dataIndex: 'responDeptid', + }, + { + title: '反馈部门名称', + align: 'center', + dataIndex: 'responDeptname', + }, + { + title: '反馈人id', + align: 'center', + dataIndex: 'responPersonid', + }, + { + title: '反馈人姓名', + align: 'center', + dataIndex: 'responPersonname', + }, + { + title: '反馈时间', + align: 'center', + dataIndex: 'responTime', + customRender: ({ text }) => { + text = !text ? '' : text.length > 10 ? text.substr(0, 10) : text; + return text; + }, + }, + { + title: '落实情况', + align: 'center', + dataIndex: 'implStatus', + }, + { + title: '完成状态', + align: 'center', + dataIndex: 'completionStatus', + }, + { + title: '外键', + align: 'center', + dataIndex: 'mainId', + }, +]; + +// 高级查询数据 +export const superQuerySchema = { + speakStatus: {title: '习近平总书记重要讲话指示批示情况',order: 1,view: 'text', type: 'string',}, + time: {title: '时间',order: 2,view: 'date', type: 'string',}, + elmComment: {title: '企业负责同志批示情况',order: 3,view: 'text', type: 'string',}, + status: {title: '学习传达研究部署情况',order: 4,view: 'text', type: 'string',}, + responsiblePerson: {title: '责任人',order: 5,view: 'sel_user', type: 'string',}, + implPlan: {title: '落实方案',order: 6,view: 'text', type: 'string',}, + leadDepartment: {title: '牵头部门',order: 7,view: 'sel_depart', type: 'string',}, + implMeasures: {title: '贯彻落实措施',order: 8,view: 'text', type: 'string',}, + numberOfResolutions: {title: '会议决策数',order: 9,view: 'number', type: 'number',}, + implStatus: {title: '落实情况',order: 10,view: 'text', type: 'string',}, + implCount: {title: '落实条数',order: 11,view: 'number', type: 'number',}, + closedCount: {title: '已闭环数量',order: 12,view: 'number', type: 'number',}, + reportFeedbackStatus: {title: '报告反馈情况',order: 13,view: 'text', type: 'string',}, + isCompletionRisk: {title: '是否存在完成风险(0不存在,1存在)',order: 14,view: 'number', type: 'number',}, + delayRiskMitigation: {title: '拖期风险应对措施',order: 15,view: 'text', type: 'string',}, + completionStatus: {title: '完成状态(0推进中,1已完成)',order: 16,view: 'number', type: 'number',}, + implDept: {title: '落实部门id',order: 17,view: 'sel_depart', type: 'string',}, + treeDepth: {title: '层深(最顶层节点层深为0)',order: 18,view: 'number', type: 'number',}, + sortOrder: {title: '同级数据排序号',order: 19,view: 'number', type: 'number',}, + bpmStatus: {title: '流程引擎状态字段',order: 21,view: 'number', type: 'number',}, + supervisionCount: {title: '督办次数',order: 22,view: 'number', type: 'number',}, +}; diff --git a/src/views/bg/xispeak/BgXiSpeakList.vue b/src/views/bg/xispeak/BgXiSpeakList.vue new file mode 100644 index 0000000..f4dee24 --- /dev/null +++ b/src/views/bg/xispeak/BgXiSpeakList.vue @@ -0,0 +1,639 @@ + + + + + + + + 新增 + 导出 + 导入 + + + + + + 删除 + + + + 批量操作 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ diff --git a/src/views/tasklist/components/CollaboratorModal.vue b/src/views/tasklist/components/CollaboratorModal.vue new file mode 100644 index 0000000..a3f536b --- /dev/null +++ b/src/views/tasklist/components/CollaboratorModal.vue @@ -0,0 +1,506 @@ + + + + + + + + {{ (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..90178df --- /dev/null +++ b/src/views/tasklist/components/CreateListModal.vue @@ -0,0 +1,49 @@ + + + + + + + diff --git a/src/views/tasklist/components/CreateTaskModal.vue b/src/views/tasklist/components/CreateTaskModal.vue new file mode 100644 index 0000000..eeebcb3 --- /dev/null +++ b/src/views/tasklist/components/CreateTaskModal.vue @@ -0,0 +1,115 @@ + + + + + 附件 + 添加附件 + + + + + + + + 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 new file mode 100644 index 0000000..f842bc4 --- /dev/null +++ b/src/views/tasklist/components/TaskContent.vue @@ -0,0 +1,2781 @@ + + + + + 任务清单 + + + + + + + {{ currentList?.name || '请选择清单' }} + + + + + + + + + 重命名 + + + + 删除 + + + + + + + + {{ currentList.secretText }} + + + + + + 协作人 + + + + + + + + + + + 全部清单 + 已完成 + 未完成 + + + + + {{ 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 }} + + + + + + + + + + + + - + + + + + + - + + + + {{ list.secretText }} + + - + + + {{ 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 }} + + + + + + + + + + + + + - + + + + + + + + {{ ft.task.taskDesc.length > 20 ? ft.task.taskDesc.substring(0, 20) + '...' : ft.task.taskDesc }} + + + + + + + + + + {{ 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..42db2f2 --- /dev/null +++ b/src/views/tasklist/components/TaskDetailDrawer.vue @@ -0,0 +1,1432 @@ + + + + + + + {{ 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 人 + + + + + 将通知 0 人 + + + + + + + + + + + diff --git a/src/views/tasklist/components/TaskFlatView.vue b/src/views/tasklist/components/TaskFlatView.vue new file mode 100644 index 0000000..b9f05f0 --- /dev/null +++ b/src/views/tasklist/components/TaskFlatView.vue @@ -0,0 +1,1146 @@ + + + + {{ title }} + ({{ flattenedTasks.length }}) + + + + + + + 全部任务 + 已完成 + 未完成 + + + + + {{ statusFilterLabel }} + + + + + + + + + + {{ opt.label }} + + + + + + + + + + + + + + + 排序: {{ currentSortLabel }} + + + + + + + + 字段配置 + + {{ field.label }} + + + + + + + 字段配置 + + + + + + + + + + 任务标题 + + + + + + + + {{ field.label }} + + + + + + + + + + + + + + + + + {{ 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(ft.task.id, 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) }} + - + + + + + + {{ getFieldValue(ft.task, field.key) }} + + + + + + + + + 暂无任务 + + + + + + + + + + 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..93d1755 --- /dev/null +++ b/src/views/tasklist/components/TaskSidebar.vue @@ -0,0 +1,820 @@ + + + + + + 任务 + + + + + + + {{ 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..267e03c --- /dev/null +++ b/src/views/tasklist/components/UserSelectModal.vue @@ -0,0 +1,383 @@ + + + + + + + + {{ (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..0448cef --- /dev/null +++ b/src/views/tasklist/index.vue @@ -0,0 +1,1128 @@ + + + + + (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)" + @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" + /> + (statusFilter = v)" + @update:sort-field="(v) => (sortField = v)" + @update:sort-direction="(v) => (sortDirection = v)" + @update:visible-fields="(v) => (visibleFields = v)" + @load-subtasks="onLoadSubtasks" + /> + + + + + + + + + + diff --git a/src/views/tasklist/types.ts b/src/views/tasklist/types.ts new file mode 100644 index 0000000..1790c1a --- /dev/null +++ b/src/views/tasklist/types.ts @@ -0,0 +1,275 @@ +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; + isDefault?: number; + listName: string; + mainId: string; + myPermission?: string; +} + +export interface TaskGroup { + id: string; + name: string; + taskListId: string; + collapsed: boolean; + tasks: TaskItem[]; + isDefault?: number; + sortOrder?: number; +} + +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; + secretLevel?: number; + secretText?: 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 = + | 'listName' + | '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: false }, + { key: 'listName', 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; +} + +export const SECRET_LEVEL_MAP = [ + { value: 1, label: '非密' }, + { value: 2, label: '内部' }, + { value: 3, label: '秘密' }, + { value: 4, label: '机密' }, +] as const; + +export const SECRET_LEVEL_COLOR: Record = { + 1: '#8c8c8c', + 2: '#1677ff', + 3: '#fa8c16', + 4: '#f5222d', +}; + +export function getAvailableSecretLevels(userSecurityLevel: number) { + if (!userSecurityLevel) userSecurityLevel = 3; + return SECRET_LEVEL_MAP.filter((item) => userSecurityLevel > item.value); +} + +export function getSecretText(level?: number | null): string { + if (level == null) return '非密'; + const found = SECRET_LEVEL_MAP.find((item) => item.value === level); + return found ? found.label : '非密'; +} diff --git a/src/views/tasklist/utils/taskUtils.ts b/src/views/tasklist/utils/taskUtils.ts new file mode 100644 index 0000000..97eac4b --- /dev/null +++ b/src/views/tasklist/utils/taskUtils.ts @@ -0,0 +1,60 @@ +export const PRIORITY_OPTIONS = [ + { label: '高', value: 'high' }, + { label: '中', value: 'medium' }, + { label: '低', value: 'low' }, +]; + +const PRIORITY_COLOR_MAP: Record = { + high: '#ffccc7', + medium: '#ffe58f', + low: '#91d5ff', +}; + +export function getPriorityBg(value: string): string { + return PRIORITY_COLOR_MAP[value] || '#f5f5f5'; +} + +export function getPriorityLabel(value: string): string { + const opt = PRIORITY_OPTIONS.find((o) => o.value === value); + return opt?.label || value; +} + +export function isOverdue(task: { endTime?: string; completed?: boolean }): boolean { + if (!task.endTime || task.completed) return false; + const end = new Date(task.endTime); + const now = new Date(); + now.setHours(0, 0, 0, 0); + end.setHours(0, 0, 0, 0); + return now > end; +} + +export async function checkUserSecLevel( + userIds: string, + secretLevel: number +): Promise<{ ok: boolean; invalidNames: string[] }> { + if (!userIds || !userIds.trim()) return { ok: true, invalidNames: [] }; + const { getUserList } = await import('/@/api/common/api'); + const ids = userIds.split(',').map((id) => id.trim()).filter(Boolean); + if (ids.length === 0) return { ok: true, invalidNames: [] }; + const level = secretLevel || 1; + try { + const res = await getUserList({ id: ids.join(','), pageNo: 1, pageSize: ids.length }); + const records: any[] = res?.records || []; + const nameMap: Record = {}; + const levelMap: Record = {}; + for (const r of records) { + nameMap[r.id] = r.realname || r.id; + levelMap[r.id] = r.userSecurityLevel ?? 3; + } + const invalidNames: string[] = []; + for (const id of ids) { + const userSecLevel = levelMap[id] ?? 3; + if (userSecLevel <= level) { + invalidNames.push(nameMap[id] || id); + } + } + return { ok: invalidNames.length === 0, invalidNames }; + } catch { + return { ok: true, invalidNames: [] }; + } +} diff --git a/src/views/three-admin/role/config/rolePermession.ts b/src/views/three-admin/role/config/rolePermession.ts index 45b0ae4..29d0afd 100644 --- a/src/views/three-admin/role/config/rolePermession.ts +++ b/src/views/three-admin/role/config/rolePermession.ts @@ -1,3 +1,3 @@ export const ROLE_SCOPE_CONFIG = { - DEFAULT_PERMESSION_IDS: '2035916880060223489', + DEFAULT_PERMESSION_IDS: '2035916880060223489,2026041610467690430', };
暂无任务清单
请从左侧选择一个任务清单
暂无任务