Merge branch 'master' into feature/20260519
This commit is contained in:
@@ -20,6 +20,16 @@
|
|||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-if="showDeadlineField" label="事项截止时间">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="formData.deadline"
|
||||||
|
valueFormat="YYYY-MM-DD"
|
||||||
|
placeholder="请选择事项截止时间"
|
||||||
|
style="width: 100%"
|
||||||
|
:getPopupContainer="(node) => node?.parentNode"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<template v-if="showSuoleaderFields">
|
<template v-if="showSuoleaderFields">
|
||||||
<a-form-item label="是否所党委审批">
|
<a-form-item label="是否所党委审批">
|
||||||
<j-switch v-model:value="formData.isNeedAppro" />
|
<j-switch v-model:value="formData.isNeedAppro" />
|
||||||
@@ -79,6 +89,8 @@
|
|||||||
showSuoleaderFields?: boolean;
|
showSuoleaderFields?: boolean;
|
||||||
isNeedAppro?: string | number;
|
isNeedAppro?: string | number;
|
||||||
supDeptleaderid?: string | string[];
|
supDeptleaderid?: string | string[];
|
||||||
|
showDeadlineField?: boolean;
|
||||||
|
deadline?: string | Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeptOption {
|
interface DeptOption {
|
||||||
@@ -94,6 +106,7 @@
|
|||||||
const contextData = ref<Record<string, any>>({});
|
const contextData = ref<Record<string, any>>({});
|
||||||
const deptOptions = ref<DeptOption[]>([]);
|
const deptOptions = ref<DeptOption[]>([]);
|
||||||
const showSuoleaderFields = ref(false);
|
const showSuoleaderFields = ref(false);
|
||||||
|
const showDeadlineField = ref(false);
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
triggerMode: 'immediate',
|
triggerMode: 'immediate',
|
||||||
intervalType: undefined as number | string | undefined,
|
intervalType: undefined as number | string | undefined,
|
||||||
@@ -102,6 +115,7 @@
|
|||||||
deptIds: [] as string[],
|
deptIds: [] as string[],
|
||||||
isNeedAppro: '0',
|
isNeedAppro: '0',
|
||||||
supDeptleaderid: '' as string | string[],
|
supDeptleaderid: '' as string | string[],
|
||||||
|
deadline: undefined as string | undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
@@ -112,8 +126,10 @@
|
|||||||
formData.deptIds = [];
|
formData.deptIds = [];
|
||||||
formData.isNeedAppro = '0';
|
formData.isNeedAppro = '0';
|
||||||
formData.supDeptleaderid = '';
|
formData.supDeptleaderid = '';
|
||||||
|
formData.deadline = undefined;
|
||||||
deptOptions.value = [];
|
deptOptions.value = [];
|
||||||
showSuoleaderFields.value = false;
|
showSuoleaderFields.value = false;
|
||||||
|
showDeadlineField.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeDeptOptions(options: DeptOption[] = []) {
|
function normalizeDeptOptions(options: DeptOption[] = []) {
|
||||||
@@ -131,6 +147,19 @@
|
|||||||
return Array.from(optionMap.values());
|
return Array.from(optionMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDeadlineValue(value?: string | Date) {
|
||||||
|
if (!value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value.length >= 10 ? value.substring(0, 10) : value;
|
||||||
|
}
|
||||||
|
const year = value.getFullYear();
|
||||||
|
const month = String(value.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(value.getDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
function open(data: ModalPayload = {}) {
|
function open(data: ModalPayload = {}) {
|
||||||
resetForm();
|
resetForm();
|
||||||
title.value = data.title || '发起流程';
|
title.value = data.title || '发起流程';
|
||||||
@@ -140,6 +169,8 @@
|
|||||||
showSuoleaderFields.value = !!data.showSuoleaderFields;
|
showSuoleaderFields.value = !!data.showSuoleaderFields;
|
||||||
formData.isNeedAppro = String(data.isNeedAppro ?? '0');
|
formData.isNeedAppro = String(data.isNeedAppro ?? '0');
|
||||||
formData.supDeptleaderid = data.supDeptleaderid || '';
|
formData.supDeptleaderid = data.supDeptleaderid || '';
|
||||||
|
showDeadlineField.value = !!data.showDeadlineField;
|
||||||
|
formData.deadline = normalizeDeadlineValue(data.deadline);
|
||||||
if (String(formData.isNeedAppro) !== '1') {
|
if (String(formData.isNeedAppro) !== '1') {
|
||||||
formData.supDeptleaderid = '';
|
formData.supDeptleaderid = '';
|
||||||
}
|
}
|
||||||
@@ -170,6 +201,11 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showDeadlineField.value && !formData.deadline) {
|
||||||
|
createMessage.warning('请选择事项截止时间');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
emit('confirm', {
|
emit('confirm', {
|
||||||
payload: contextData.value,
|
payload: contextData.value,
|
||||||
options: {
|
options: {
|
||||||
@@ -181,6 +217,7 @@
|
|||||||
deptIds: [...formData.deptIds],
|
deptIds: [...formData.deptIds],
|
||||||
isNeedAppro: showSuoleaderFields.value ? formData.isNeedAppro : undefined,
|
isNeedAppro: showSuoleaderFields.value ? formData.isNeedAppro : undefined,
|
||||||
supDeptleaderid: showSuoleaderFields.value ? (String(formData.isNeedAppro) === '1' ? formData.supDeptleaderid : '') : undefined,
|
supDeptleaderid: showSuoleaderFields.value ? (String(formData.isNeedAppro) === '1' ? formData.supDeptleaderid : '') : undefined,
|
||||||
|
deadline: showDeadlineField.value ? formData.deadline : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
handleCancel();
|
handleCancel();
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ const dashboard: AppRouteModule = {
|
|||||||
title: t('routes.dashboard.workbench'),
|
title: t('routes.dashboard.workbench'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'processPortal',
|
||||||
|
name: 'ProcessPortal',
|
||||||
|
component: () => import('/@/views/dashboard/ProcessPortal/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '流程门户',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
|||||||
// authenticationScheme: 'Bearer',
|
// authenticationScheme: 'Bearer',
|
||||||
authenticationScheme: '',
|
authenticationScheme: '',
|
||||||
//接口超时设置
|
//接口超时设置
|
||||||
timeout: 10 * 1000,
|
timeout: 20 * 1000,
|
||||||
// 基础接口地址
|
// 基础接口地址
|
||||||
// baseURL: globSetting.apiUrl,
|
// baseURL: globSetting.apiUrl,
|
||||||
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||||
|
|||||||
@@ -6,16 +6,10 @@ import { getWeekMonthQuarterYear } from '/@/utils';
|
|||||||
//列表数据
|
//列表数据
|
||||||
export const columns: BasicColumn[] = [
|
export const columns: BasicColumn[] = [
|
||||||
{
|
{
|
||||||
title: '序号',
|
title: '年份',
|
||||||
align:"center",
|
align:"center",
|
||||||
dataIndex: 'number',
|
dataIndex: 'year',
|
||||||
sorter: true,
|
width: '60px',
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '督办次数',
|
|
||||||
align:"center",
|
|
||||||
dataIndex: 'superviseCount',
|
|
||||||
width: '100px',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '事项名称',
|
title: '事项名称',
|
||||||
@@ -23,20 +17,9 @@ export const columns: BasicColumn[] = [
|
|||||||
dataIndex: 'title'
|
dataIndex: 'title'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '年份',
|
title: '序号',
|
||||||
align:"center",
|
align:"center",
|
||||||
dataIndex: 'year',
|
dataIndex: 'number',
|
||||||
width: '60px',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: '会议时间',
|
|
||||||
align:"center",
|
|
||||||
dataIndex: 'matterTime',
|
|
||||||
customRender:({text}) =>{
|
|
||||||
text = !text ? "" : (text.length > 10 ? text.substr(0,10) : text);
|
|
||||||
return text;
|
|
||||||
},
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,10 +33,50 @@ export const columns: BasicColumn[] = [
|
|||||||
dataIndex: 'matterCode'
|
dataIndex: 'matterCode'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '责任部门',
|
title: '督办次数',
|
||||||
|
align:"center",
|
||||||
|
dataIndex: 'superviseCount',
|
||||||
|
width: '100px',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '会议时间',
|
||||||
|
align:"center",
|
||||||
|
dataIndex: 'matterTime',
|
||||||
|
customRender:({text}) =>{
|
||||||
|
text = !text ? "" : (text.length > 10 ? text.substr(0,10) : text);
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '议题序号',
|
||||||
|
align:"center",
|
||||||
|
dataIndex: 'itemNumber',
|
||||||
|
sorter: true,
|
||||||
|
width: '100px',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '抄告单编号',
|
||||||
|
align:"center",
|
||||||
|
dataIndex: 'noticeNumber'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否为三重一大决策',
|
||||||
|
align:"center",
|
||||||
|
dataIndex: 'isImportant_dictText'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '主办部门',
|
||||||
align:"center",
|
align:"center",
|
||||||
dataIndex: 'responDeptid_dictText'
|
dataIndex: 'responDeptid_dictText'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '协办部门',
|
||||||
|
align:"center",
|
||||||
|
dataIndex: 'assistDeptid_dictText'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '实际执行情况',
|
title: '实际执行情况',
|
||||||
align:"center",
|
align:"center",
|
||||||
@@ -69,7 +92,7 @@ export const columns: BasicColumn[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '紧急程度',
|
title: '是否急件',
|
||||||
align:"center",
|
align:"center",
|
||||||
dataIndex: 'urgencyLevel_dictText',
|
dataIndex: 'urgencyLevel_dictText',
|
||||||
},
|
},
|
||||||
@@ -146,7 +169,8 @@ export const bgPartymatterFeedbackColumns: BasicColumn[] = [
|
|||||||
{
|
{
|
||||||
title: '完成状态',
|
title: '完成状态',
|
||||||
align:"center",
|
align:"center",
|
||||||
dataIndex: 'bpmStatus'
|
dataIndex: 'bpmStatus',
|
||||||
|
customRender: ({ record }) => record?.bpmStatus_dictText || record?.bpmStatus
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// title: '外键',
|
// title: '外键',
|
||||||
@@ -159,18 +183,21 @@ export const bgPartymatterFeedbackColumns: BasicColumn[] = [
|
|||||||
export const superQuerySchema = {
|
export const superQuerySchema = {
|
||||||
year: {title: '年份',order: 0,view: 'text', type: 'string',},
|
year: {title: '年份',order: 0,view: 'text', type: 'string',},
|
||||||
number: {title: '序号',order: 1,view: 'text', type: 'string',},
|
number: {title: '序号',order: 1,view: 'text', type: 'string',},
|
||||||
matterTime: {title: '会议时间',order: 2,view: 'date', type: 'string',},
|
itemNumber: {title: '议题序号',order: 2,view: 'number', type: 'number',},
|
||||||
title: {title: '事项名称',order: 3,view: 'text', type: 'string',},
|
matterTime: {title: '会议时间',order: 3,view: 'date', type: 'string',},
|
||||||
content: {title: '会议决议',order: 4,view: 'textarea', type: 'string',},
|
title: {title: '事项名称',order: 4,view: 'text', type: 'string',},
|
||||||
matterCode: {title: '事项目录编码',order: 5,view: 'text', type: 'string',},
|
content: {title: '会议决议',order: 5,view: 'textarea', type: 'string',},
|
||||||
responDeptid: {title: '责任部门',order: 6,view: 'sel_depart', type: 'string',},
|
matterCode: {title: '事项目录编码',order: 6,view: 'text', type: 'string',},
|
||||||
actualExect: {title: '实际执行情况',order: 7,view: 'text', type: 'string',},
|
noticeNumber: {title: '抄告单编号',order: 7,view: 'text', type: 'string',},
|
||||||
bpmStatus: {title: '完成状态',order: 8,view: 'text', type: 'string',},
|
isImportant: {title: '是否为三重一大决策',order: 8,view: 'list', type: 'string', dictCode: 'yn',},
|
||||||
superviseCount: {title: '督办次数',order: 9,view: 'text', type: 'string',},
|
responDeptid: {title: '责任部门',order: 9,view: 'sel_depart', type: 'string',},
|
||||||
deadline: {title: '要求完成日期',order: 10,view: 'date', type: 'string',},
|
actualExect: {title: '实际执行情况',order: 10,view: 'text', type: 'string',},
|
||||||
urgencyLevel: {title: '紧急程度',order: 11,view: 'text', type: 'string',},
|
bpmStatus: {title: '完成状态',order: 11,view: 'text', type: 'string',},
|
||||||
isNeedAppro: {title: '是否需要所办领导审批',order: 12,view: 'switch', type: 'string',},
|
superviseCount: {title: '督办次数',order: 12,view: 'text', type: 'string',},
|
||||||
supDeptleaderid: {title: '所办领导',order: 13,view: 'sel_user', type: 'string',},
|
deadline: {title: '要求完成日期',order: 13,view: 'date', type: 'string',},
|
||||||
isNeedSuoleader: {title: '是否抄送所领导',order: 14,view: 'switch', type: 'string',},
|
urgencyLevel: {title: '是否急件',order: 14,view: 'list', type: 'string', dictCode: 'yn',},
|
||||||
suoleaderid: {title: '抄送所领导',order: 15,view: 'sel_user', type: 'string',},
|
isNeedAppro: {title: '是否需要所办领导审批',order: 15,view: 'switch', type: 'string',},
|
||||||
|
supDeptleaderid: {title: '所办领导',order: 16,view: 'sel_user', type: 'string',},
|
||||||
|
isNeedSuoleader: {title: '是否抄送所领导',order: 17,view: 'switch', type: 'string',},
|
||||||
|
suoleaderid: {title: '抄送所领导',order: 18,view: 'sel_user', type: 'string',},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
<JDictSelectTag v-model:value="queryParam.year" dictCode="super_year" placeholder="请选择年份" allow-clear @change="searchQuery" />
|
<JDictSelectTag v-model:value="queryParam.year" dictCode="super_year" placeholder="请选择年份" allow-clear @change="searchQuery" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="5" :md="6" :sm="8">
|
<a-col :lg="5" :md="8" :sm="12">
|
||||||
<a-form-item label="完成状态" name="bpmStatus">
|
<a-form-item label="事项名称" name="title">
|
||||||
<JDictSelectTag v-model:value="queryParam.bpmStatus" dictCode="super_status" placeholder="请选择完成状态" allow-clear @change="searchQuery" />
|
<JInput v-model:value="queryParam.title" type="like" placeholder="请输入事项名称" allow-clear trim />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="5" :md="8" :sm="12">
|
<a-col :lg="5" :md="8" :sm="12">
|
||||||
@@ -19,9 +19,14 @@
|
|||||||
<JInput v-model:value="queryParam.content" type="like" placeholder="请输入会议决议" allow-clear trim />
|
<JInput v-model:value="queryParam.content" type="like" placeholder="请输入会议决议" allow-clear trim />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="6" :md="8" :sm="12">
|
<!-- <a-col :lg="6" :md="8" :sm="12">-->
|
||||||
<a-form-item label="事项目录编码" name="matterCode">
|
<!-- <a-form-item label="事项目录编码" name="matterCode">-->
|
||||||
<JInput v-model:value="queryParam.matterCode" type="like" placeholder="请输入事项目录编码" allow-clear trim />
|
<!-- <JInput v-model:value="queryParam.matterCode" type="like" placeholder="请输入事项目录编码" allow-clear trim />-->
|
||||||
|
<!-- </a-form-item>-->
|
||||||
|
<!-- </a-col>-->
|
||||||
|
<a-col :lg="5" :md="6" :sm="8">
|
||||||
|
<a-form-item label="完成状态" name="bpmStatus">
|
||||||
|
<JDictSelectTag v-model:value="queryParam.bpmStatus" dictCode="super_status" placeholder="请选择完成状态" allow-clear @change="searchQuery" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="8" :sm="12">
|
<a-col :lg="4" :md="8" :sm="12">
|
||||||
@@ -139,7 +144,7 @@
|
|||||||
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, bgPartymatterFeedbackList, saveOrUpdate } from './BgPartymatter.api';
|
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, bgPartymatterFeedbackList, saveOrUpdate } from './BgPartymatter.api';
|
||||||
|
|
||||||
const FLOW_CODE = 'dev_task_task_001';
|
const FLOW_CODE = 'dev_task_task_001';
|
||||||
const FLOW_NAME = '党委会决议事项';
|
const FLOW_NAME = '党委会决议事项督办';
|
||||||
const BUSINESS_TABLE_NAME = 'bg_partymatter';
|
const BUSINESS_TABLE_NAME = 'bg_partymatter';
|
||||||
const FORM_URL = 'bg/bgpartymatter/components/BgPartymatterBPMForm';
|
const FORM_URL = 'bg/bgpartymatter/components/BgPartymatterBPMForm';
|
||||||
|
|
||||||
@@ -433,6 +438,8 @@
|
|||||||
showSuoleaderFields: true,
|
showSuoleaderFields: true,
|
||||||
isNeedAppro: record?.isNeedAppro,
|
isNeedAppro: record?.isNeedAppro,
|
||||||
supDeptleaderid: record?.supDeptleaderid,
|
supDeptleaderid: record?.supDeptleaderid,
|
||||||
|
showDeadlineField: true,
|
||||||
|
deadline: record?.deadline,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,12 +454,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getResponsibleDeptOptions(record): DeptOption[] {
|
function getResponsibleDeptOptions(record): DeptOption[] {
|
||||||
const deptIds = splitDeptValue(record?.responDeptid);
|
const optionMap = new Map<string, DeptOption>();
|
||||||
const deptNames = splitDeptValue(record?.responDeptid_dictText || record?.responDeptname);
|
[
|
||||||
return deptIds.map((deptId, index) => ({
|
{
|
||||||
|
ids: splitDeptValue(record?.responDeptid),
|
||||||
|
names: splitDeptValue(record?.responDeptid_dictText || record?.responDeptname),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ids: splitDeptValue(record?.assistDeptid),
|
||||||
|
names: splitDeptValue(record?.assistDeptid_dictText || record?.assistDeptname),
|
||||||
|
},
|
||||||
|
].forEach(({ ids, names }) => {
|
||||||
|
ids.forEach((deptId, index) => {
|
||||||
|
if (optionMap.has(deptId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
optionMap.set(deptId, {
|
||||||
value: deptId,
|
value: deptId,
|
||||||
label: deptNames[index] || deptId,
|
label: names[index] || deptId,
|
||||||
}));
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return Array.from(optionMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResponsibleDeptIds(record) {
|
||||||
|
return getResponsibleDeptOptions(record)
|
||||||
|
.map((item) => item.value)
|
||||||
|
.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextSuperviseCount(value) {
|
function getNextSuperviseCount(value) {
|
||||||
@@ -474,12 +503,19 @@
|
|||||||
const formatDate = (value, format) => {
|
const formatDate = (value, format) => {
|
||||||
return value ? dateUtil(value).format(format) : undefined;
|
return value ? dateUtil(value).format(format) : undefined;
|
||||||
};
|
};
|
||||||
|
const formatDeadlineDateTime = (value) => {
|
||||||
|
const deadlineDate = formatDate(value, 'YYYY-MM-DD');
|
||||||
|
return deadlineDate ? `${deadlineDate} 00:00:00` : undefined;
|
||||||
|
};
|
||||||
const nextSuperviseCount = getNextSuperviseCount(record.superviseCount);
|
const nextSuperviseCount = getNextSuperviseCount(record.superviseCount);
|
||||||
|
const selectedDeadline = options.deadline || record.deadline;
|
||||||
|
const defaultDeptIds = getResponsibleDeptIds(record);
|
||||||
const flowJsonData = {
|
const flowJsonData = {
|
||||||
...record,
|
...record,
|
||||||
isNeedAppro: options.isNeedAppro ?? record.isNeedAppro ?? '',
|
isNeedAppro: options.isNeedAppro ?? record.isNeedAppro ?? '',
|
||||||
supDeptleaderid: options.supDeptleaderid ?? record.supDeptleaderid ?? '',
|
supDeptleaderid: options.supDeptleaderid ?? record.supDeptleaderid ?? '',
|
||||||
superviseCount: nextSuperviseCount,
|
superviseCount: nextSuperviseCount,
|
||||||
|
deadline: selectedDeadline,
|
||||||
};
|
};
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@@ -500,10 +536,10 @@
|
|||||||
title: record.title,
|
title: record.title,
|
||||||
content: record.content,
|
content: record.content,
|
||||||
urgencyLevel: record.urgencyLevel,
|
urgencyLevel: record.urgencyLevel,
|
||||||
deptId: options.deptId || record.responDeptid,
|
deptId: options.deptId || defaultDeptIds,
|
||||||
deptLeaderId: record.responDeptid,
|
deptLeaderId: record.responDeptid,
|
||||||
requireFinishDate: formatDate(record.deadline, 'YYYY-MM-DD'),
|
requireFinishDate: formatDate(selectedDeadline, 'YYYY-MM-DD'),
|
||||||
deadline: formatDate(record.deadline, 'YYYY-MM-DD HH:mm:ss'),
|
deadline: formatDeadlineDateTime(selectedDeadline),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<JModal
|
<JModal
|
||||||
title="新增反馈"
|
:title="modalTitle"
|
||||||
:width="820"
|
:width="820"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:confirmLoading="saving"
|
:confirmLoading="saving"
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<!-- </a-col>-->
|
<!-- </a-col>-->
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="完成状态" name="bpmStatus">
|
<a-form-item label="完成状态" name="bpmStatus">
|
||||||
<a-input v-model:value="formData.bpmStatus" allow-clear />
|
<JDictSelectTag v-model:value="formData.bpmStatus" dictCode="feedback_status" placeholder="请选择完成状态" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
@@ -78,6 +78,7 @@
|
|||||||
import { useUserStore } from '/@/store/modules/user';
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
import { buildUUID } from '/@/utils/uuid';
|
import { buildUUID } from '/@/utils/uuid';
|
||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
@@ -85,6 +86,7 @@
|
|||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
|
const modalTitle = ref('新增反馈');
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const uploadFileRef = ref<InstanceType<typeof SUploadFile> | null>(null);
|
const uploadFileRef = ref<InstanceType<typeof SUploadFile> | null>(null);
|
||||||
const uploadSecretLevel = ref(1);
|
const uploadSecretLevel = ref(1);
|
||||||
@@ -121,10 +123,22 @@
|
|||||||
nextTick(() => formRef.value?.clearValidate?.());
|
nextTick(() => formRef.value?.clearValidate?.());
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(mainId: string, secretLevel?: string | number, bpmProcessId = '') {
|
function open(mainId: string, secretLevel?: string | number, bpmProcessId = '', record?: Record<string, any>) {
|
||||||
resetForm(mainId, bpmProcessId);
|
resetForm(mainId, bpmProcessId);
|
||||||
uploadSecretLevel.value = normalizeSecretLevel(secretLevel);
|
uploadSecretLevel.value = normalizeSecretLevel(secretLevel);
|
||||||
|
if (record?.id) {
|
||||||
|
modalTitle.value = '编辑反馈';
|
||||||
|
Object.keys(formData).forEach((key) => {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
||||||
|
formData[key] = record[key] ?? '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
formData.mainId = formData.mainId || mainId;
|
||||||
|
formData.bpmProcessId = formData.bpmProcessId || bpmProcessId;
|
||||||
|
} else {
|
||||||
|
modalTitle.value = '新增反馈';
|
||||||
void applyCurrentUserFeedbackInfo();
|
void applyCurrentUserFeedbackInfo();
|
||||||
|
}
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,11 +200,14 @@
|
|||||||
}
|
}
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
try {
|
try {
|
||||||
|
const isUpdate = !!formData.id;
|
||||||
|
if (!isUpdate) {
|
||||||
await applyCurrentUserFeedbackInfo();
|
await applyCurrentUserFeedbackInfo();
|
||||||
if (!formData.id && uploadFileRef.value?.hasPendingFiles?.()) {
|
}
|
||||||
|
if (!isUpdate && uploadFileRef.value?.hasPendingFiles?.()) {
|
||||||
formData.id = buildUUID();
|
formData.id = buildUUID();
|
||||||
}
|
}
|
||||||
const res = await bgPartymatterFeedbackSaveOrUpdate({ ...formData }, false);
|
const res = await bgPartymatterFeedbackSaveOrUpdate({ ...formData }, isUpdate);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
if (formData.id && uploadFileRef.value?.hasPendingFiles?.()) {
|
if (formData.id && uploadFileRef.value?.hasPendingFiles?.()) {
|
||||||
await uploadFileRef.value.bindBussinessId(formData.id);
|
await uploadFileRef.value.bindBussinessId(formData.id);
|
||||||
|
|||||||
@@ -40,7 +40,12 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="序号" name="number">
|
<a-form-item label="序号" name="number">
|
||||||
<a-input v-model:value="mainForm.number" :disabled="mainDisabled" allow-clear />
|
<a-input v-model:value="mainForm.number" :disabled="mainDisabled" :readonly="true" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="议题序号" name="itemNumber">
|
||||||
|
<a-input-number v-model:value="mainForm.itemNumber" :disabled="mainDisabled" style="width: 100%" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
@@ -54,19 +59,34 @@
|
|||||||
<a-input v-model:value="mainForm.matterCode" :disabled="mainDisabled" allow-clear />
|
<a-input v-model:value="mainForm.matterCode" :disabled="mainDisabled" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="抄告单编号" name="noticeNumber">
|
||||||
|
<a-input v-model:value="mainForm.noticeNumber" :disabled="mainDisabled" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="是否为三重一大决策" name="isImportant">
|
||||||
|
<j-dict-select-tag v-model:value="mainForm.isImportant" :disabled="mainDisabled" dictCode="yn" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="责任部门" name="responDeptid">
|
<a-form-item label="责任部门" name="responDeptid">
|
||||||
<j-select-dept v-model:value="mainForm.responDeptid" :disabled="mainDisabled" :multiple="true" checkStrictly allow-clear />
|
<j-select-dept v-model:value="mainForm.responDeptid" :disabled="mainDisabled" :multiple="true" checkStrictly allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="协办部门" name="assistDeptid">
|
||||||
|
<j-select-dept v-model:value="mainForm.assistDeptid" :disabled="mainDisabled" :multiple="true" checkStrictly allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="要求完成日期" name="deadline">
|
<a-form-item label="要求完成日期" name="deadline">
|
||||||
<a-date-picker v-model:value="mainForm.deadline" value-format="YYYY-MM-DD" :disabled="mainDisabled" style="width: 100%" allow-clear />
|
<a-date-picker v-model:value="mainForm.deadline" value-format="YYYY-MM-DD" :disabled="mainDisabled" style="width: 100%" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="紧急程度" name="urgencyLevel">
|
<a-form-item label="是否急件" name="urgencyLevel">
|
||||||
<j-dict-select-tag v-model:value="mainForm.urgencyLevel" :disabled="mainDisabled" dictCode="urgency_level" placeholder="请选择紧急程度" allow-clear />
|
<j-dict-select-tag v-model:value="mainForm.urgencyLevel" :disabled="mainDisabled" dictCode="yn" placeholder="请选择是否急件" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
@@ -104,12 +124,13 @@
|
|||||||
:columns="feedbackColumns"
|
:columns="feedbackColumns"
|
||||||
:data-source="feedbackList"
|
:data-source="feedbackList"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1220 }"
|
:scroll="{ x: feedbackTableScrollX }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'attachments'">
|
<template v-if="column.key === 'attachments'">
|
||||||
<SUploadFile
|
<SUploadFile
|
||||||
v-if="record.id"
|
v-if="record.id"
|
||||||
|
:key="getFeedbackAttachmentKey(record)"
|
||||||
class="feedback-attachment-list"
|
class="feedback-attachment-list"
|
||||||
:bussiness-id="record.id"
|
:bussiness-id="record.id"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
@@ -117,6 +138,14 @@
|
|||||||
/>
|
/>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="canManageFeedback && column.key === 'action'">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="link" size="small" @click="openFeedbackEditModal(record)">编辑</a-button>
|
||||||
|
<a-popconfirm title="是否确认删除" @confirm="handleFeedbackDelete(record)">
|
||||||
|
<a-button type="link" size="small" danger>删除</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
|
|
||||||
@@ -173,7 +202,7 @@
|
|||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
|
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
|
||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import { bgPartymatterFeedbackList, saveBpmForm, setProcessVariable } from '../BgPartymatter.api';
|
import { bgPartymatterFeedbackDelete, bgPartymatterFeedbackList, saveBpmForm, setProcessVariable } from '../BgPartymatter.api';
|
||||||
import { usePermission } from '/@/hooks/web/usePermission';
|
import { usePermission } from '/@/hooks/web/usePermission';
|
||||||
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
||||||
import JDictSelectTag from "../../../../components/Form/src/jeecg/components/JDictSelectTag.vue";
|
import JDictSelectTag from "../../../../components/Form/src/jeecg/components/JDictSelectTag.vue";
|
||||||
@@ -211,19 +240,25 @@
|
|||||||
const savingSubApproveUser = ref(false);
|
const savingSubApproveUser = ref(false);
|
||||||
const showBaseInfoDetail = ref(false);
|
const showBaseInfoDetail = ref(false);
|
||||||
const feedbackList = ref<any[]>([]);
|
const feedbackList = ref<any[]>([]);
|
||||||
|
const feedbackAttachmentRefreshKey = ref(0);
|
||||||
const subApproveUser = ref('');
|
const subApproveUser = ref('');
|
||||||
const isEnd = ref(0);
|
const isEnd = ref(0);
|
||||||
|
const lastGeneratedNumber = ref('');
|
||||||
|
|
||||||
const mainForm = reactive<Record<string, any>>({
|
const mainForm = reactive<Record<string, any>>({
|
||||||
id: '',
|
id: '',
|
||||||
year: '',
|
year: '',
|
||||||
secretLevel: '',
|
secretLevel: '',
|
||||||
number: '',
|
number: '',
|
||||||
|
itemNumber: undefined,
|
||||||
matterTime: '',
|
matterTime: '',
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
matterCode: '',
|
matterCode: '',
|
||||||
|
noticeNumber: '',
|
||||||
|
isImportant: '',
|
||||||
responDeptid: '',
|
responDeptid: '',
|
||||||
|
assistDeptid: '',
|
||||||
actualExect: '',
|
actualExect: '',
|
||||||
bpmStatus: '',
|
bpmStatus: '',
|
||||||
superviseCount: '',
|
superviseCount: '',
|
||||||
@@ -239,14 +274,21 @@
|
|||||||
intervalStartTime: '',
|
intervalStartTime: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const feedbackColumns = [
|
const feedbackBaseColumns = [
|
||||||
{ title: '责任部门名称', dataIndex: 'responDeptname', align: 'center', width: 160 },
|
{ title: '责任部门名称', dataIndex: 'responDeptname', align: 'center', width: 160 },
|
||||||
{ title: '反馈人姓名', dataIndex: 'responPersonname', align: 'center', width: 140 },
|
{ title: '反馈人姓名', dataIndex: 'responPersonname', align: 'center', width: 140 },
|
||||||
{ title: '实际执行情况', dataIndex: 'actualExect', align: 'left', width: 280 },
|
{ title: '实际执行情况', dataIndex: 'actualExect', align: 'left', width: 280 },
|
||||||
{ title: '实际完成时间', dataIndex: 'actualTime', align: 'center', width: 180 },
|
{ title: '实际完成时间', dataIndex: 'actualTime', align: 'center', width: 180 },
|
||||||
{ title: '完成状态', dataIndex: 'bpmStatus', align: 'center', width: 120 },
|
{ title: '完成状态', dataIndex: 'bpmStatus', align: 'center', width: 120, customRender: ({ record }) => record?.bpmStatus_dictText || record?.bpmStatus },
|
||||||
{ title: '附件', dataIndex: 'id', key: 'attachments', align: 'left', width: 340 },
|
{ title: '附件', dataIndex: 'id', key: 'attachments', align: 'left', width: 340 },
|
||||||
];
|
];
|
||||||
|
const feedbackActionColumn = { title: '操作', dataIndex: 'action', key: 'action', align: 'center', width: 120, fixed: 'right' };
|
||||||
|
const feedbackColumns = computed(() => {
|
||||||
|
return canManageFeedback.value ? [...feedbackBaseColumns, feedbackActionColumn] : feedbackBaseColumns;
|
||||||
|
});
|
||||||
|
const feedbackTableScrollX = computed(() => {
|
||||||
|
return canManageFeedback.value ? 1340 : 1220;
|
||||||
|
});
|
||||||
|
|
||||||
const mainDisabled = computed(() => {
|
const mainDisabled = computed(() => {
|
||||||
if (props.formBpm) {
|
if (props.formBpm) {
|
||||||
@@ -254,7 +296,9 @@
|
|||||||
}
|
}
|
||||||
return props.formDisabled;
|
return props.formDisabled;
|
||||||
});
|
});
|
||||||
const showProcessHandle = computed(() => props.formBpm && !!props.formData?.taskId);
|
// 只有待办办理入口展示流程办理区,历史/抄送查看即使有 taskId 也只展示业务表单。
|
||||||
|
const showProcessHandle = computed(() => props.formBpm && props.formData?.PROCESS_TAB_TYPE === 'run' && !!props.formData?.taskId);
|
||||||
|
const canManageFeedback = computed(() => props.formBpm && props.formData?.PROCESS_TAB_TYPE === 'run' && !!props.formData?.taskId);
|
||||||
const showAddFeedback = computed(() => String(props.formData?.extendUrlParams?.addfeedback ?? '') === '1');
|
const showAddFeedback = computed(() => String(props.formData?.extendUrlParams?.addfeedback ?? '') === '1');
|
||||||
const showSubApproveUser = computed(() => String(props.formData?.extendUrlParams?.selectuser ?? '') === '1');
|
const showSubApproveUser = computed(() => String(props.formData?.extendUrlParams?.selectuser ?? '') === '1');
|
||||||
const showSubApproveUserSelector = computed(() => showSubApproveUser.value && String(isEnd.value) !== '1');
|
const showSubApproveUserSelector = computed(() => showSubApproveUser.value && String(isEnd.value) !== '1');
|
||||||
@@ -291,6 +335,14 @@
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 根据会议时间和议题序号自动生成业务序号,例如:2026-05-20 + 2 => 20260520-2。
|
||||||
|
watch(
|
||||||
|
() => [mainForm.matterTime, mainForm.itemNumber],
|
||||||
|
() => {
|
||||||
|
syncGeneratedNumber();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
async function initFormData() {
|
async function initFormData() {
|
||||||
const dataId = props.formData?.dataId;
|
const dataId = props.formData?.dataId;
|
||||||
if (!dataId) {
|
if (!dataId) {
|
||||||
@@ -313,14 +365,62 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncGeneratedNumber() {
|
||||||
|
const generatedNumber = buildGeneratedNumber(mainForm.matterTime, mainForm.itemNumber);
|
||||||
|
if (generatedNumber) {
|
||||||
|
mainForm.number = generatedNumber;
|
||||||
|
lastGeneratedNumber.value = generatedNumber;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lastGeneratedNumber.value && mainForm.number === lastGeneratedNumber.value) {
|
||||||
|
mainForm.number = '';
|
||||||
|
lastGeneratedNumber.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只在两个必要字段都填写后生成,避免用户未填完整时写入半截序号。
|
||||||
|
function buildGeneratedNumber(matterTime, itemNumber) {
|
||||||
|
const dateText = formatMatterTime(matterTime);
|
||||||
|
if (!dateText || itemNumber === undefined || itemNumber === null || itemNumber === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `${dateText}-${itemNumber}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容日期选择器返回的字符串、Date、dayjs/moment 对象。
|
||||||
|
function formatMatterTime(value) {
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value.slice(0, 10).replace(/-/g, '');
|
||||||
|
}
|
||||||
|
if (value instanceof Date) {
|
||||||
|
const year = value.getFullYear();
|
||||||
|
const month = String(value.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(value.getDate()).padStart(2, '0');
|
||||||
|
return `${year}${month}${day}`;
|
||||||
|
}
|
||||||
|
if (typeof value.format === 'function') {
|
||||||
|
return value.format('YYYYMMDD');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
async function loadFeedbackList() {
|
async function loadFeedbackList() {
|
||||||
const bpmProcessId = processInfo.value.processInstanceId;
|
const bpmProcessId = processInfo.value.processInstanceId;
|
||||||
if (!mainForm.id || !bpmProcessId) {
|
if (!mainForm.id || !bpmProcessId) {
|
||||||
feedbackList.value = [];
|
feedbackList.value = [];
|
||||||
|
feedbackAttachmentRefreshKey.value += 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await bgPartymatterFeedbackList({ mainId: mainForm.id, bpmProcessId, pageNo: 1, pageSize: 999 });
|
const res = await bgPartymatterFeedbackList({ mainId: mainForm.id, bpmProcessId, pageNo: 1, pageSize: 999 });
|
||||||
feedbackList.value = res?.records || res?.result?.records || [];
|
feedbackList.value = res?.records || res?.result?.records || [];
|
||||||
|
feedbackAttachmentRefreshKey.value += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFeedbackAttachmentKey(record: Recordable) {
|
||||||
|
return `${record?.id || ''}-${feedbackAttachmentRefreshKey.value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitForm() {
|
async function submitForm() {
|
||||||
@@ -383,6 +483,36 @@
|
|||||||
feedbackModalRef.value?.open(mainForm.id, mainForm.secretLevel, processInstanceId);
|
feedbackModalRef.value?.open(mainForm.id, mainForm.secretLevel, processInstanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openFeedbackEditModal(record: Recordable) {
|
||||||
|
if (!canManageFeedback.value) {
|
||||||
|
createMessage.warning('当前流程状态不允许编辑反馈');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!record?.id) {
|
||||||
|
createMessage.warning('反馈记录不存在,无法编辑');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
feedbackModalRef.value?.open(mainForm.id, mainForm.secretLevel, processInfo.value.processInstanceId, record);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFeedbackDelete(record: Recordable) {
|
||||||
|
if (!canManageFeedback.value) {
|
||||||
|
createMessage.warning('当前流程状态不允许删除反馈');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!record?.id) {
|
||||||
|
createMessage.warning('反馈记录不存在,无法删除');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await bgPartymatterFeedbackDelete({ id: record.id }, () => undefined);
|
||||||
|
createMessage.success('删除成功');
|
||||||
|
await loadFeedbackList();
|
||||||
|
} catch (error) {
|
||||||
|
createMessage.warning('删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function saveSubApproveUserVariable(options: { showSuccessMessage?: boolean } = {}) {
|
async function saveSubApproveUserVariable(options: { showSuccessMessage?: boolean } = {}) {
|
||||||
const { showSuccessMessage = true } = options;
|
const { showSuccessMessage = true } = options;
|
||||||
const processInstanceId = processInfo.value.processInstanceId;
|
const processInstanceId = processInfo.value.processInstanceId;
|
||||||
@@ -556,6 +686,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-input),
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-input-number),
|
||||||
:deep(.ant-picker),
|
:deep(.ant-picker),
|
||||||
:deep(.ant-select-selector) {
|
:deep(.ant-select-selector) {
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
@@ -572,6 +703,9 @@
|
|||||||
:deep(.ant-input-affix-wrapper-disabled-focused),
|
:deep(.ant-input-affix-wrapper-disabled-focused),
|
||||||
:deep(.ant-input-outlined[disabled]),
|
:deep(.ant-input-outlined[disabled]),
|
||||||
:deep(.ant-input-outlined.ant-input-disabled),
|
:deep(.ant-input-outlined.ant-input-disabled),
|
||||||
|
:deep(.ant-input-number-disabled),
|
||||||
|
:deep(.ant-input-number-disabled:hover),
|
||||||
|
:deep(.ant-input-number-disabled:focus),
|
||||||
:deep(textarea.ant-input[disabled]),
|
:deep(textarea.ant-input[disabled]),
|
||||||
:deep(.ant-picker-disabled),
|
:deep(.ant-picker-disabled),
|
||||||
:deep(.ant-select-disabled .ant-select-selector) {
|
:deep(.ant-select-disabled .ant-select-selector) {
|
||||||
@@ -585,6 +719,7 @@
|
|||||||
|
|
||||||
:deep(.ant-input[disabled]),
|
:deep(.ant-input[disabled]),
|
||||||
:deep(.ant-input-disabled),
|
:deep(.ant-input-disabled),
|
||||||
|
:deep(.ant-input-number-disabled),
|
||||||
:deep(.ant-input-affix-wrapper-disabled),
|
:deep(.ant-input-affix-wrapper-disabled),
|
||||||
:deep(textarea.ant-input[disabled]) {
|
:deep(textarea.ant-input[disabled]) {
|
||||||
padding-left: 0 !important;
|
padding-left: 0 !important;
|
||||||
@@ -597,6 +732,13 @@
|
|||||||
cursor: default !important;
|
cursor: default !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.ant-input-number-disabled .ant-input-number-input) {
|
||||||
|
background: transparent !important;
|
||||||
|
color: #1f2937 !important;
|
||||||
|
-webkit-text-fill-color: #1f2937 !important;
|
||||||
|
cursor: default !important;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.ant-input[disabled]::placeholder),
|
:deep(.ant-input[disabled]::placeholder),
|
||||||
:deep(.ant-input-disabled::placeholder),
|
:deep(.ant-input-disabled::placeholder),
|
||||||
:deep(.ant-input-affix-wrapper-disabled .ant-input::placeholder) {
|
:deep(.ant-input-affix-wrapper-disabled .ant-input::placeholder) {
|
||||||
@@ -631,6 +773,7 @@
|
|||||||
:deep(.ant-picker-disabled .ant-picker-suffix),
|
:deep(.ant-picker-disabled .ant-picker-suffix),
|
||||||
:deep(.ant-input-disabled .ant-input-clear-icon),
|
:deep(.ant-input-disabled .ant-input-clear-icon),
|
||||||
:deep(.ant-input-affix-wrapper-disabled .ant-input-clear-icon),
|
:deep(.ant-input-affix-wrapper-disabled .ant-input-clear-icon),
|
||||||
|
:deep(.ant-input-number-disabled .ant-input-number-handler-wrap),
|
||||||
:deep(.ant-select-disabled .ant-select-clear) {
|
:deep(.ant-select-disabled .ant-select-clear) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,18 +10,23 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="密级" v-bind="validateInfos.secretLevel" id="BgPartymatterForm-secretLevel" name="secretLevel">
|
<a-form-item label="会议时间" v-bind="validateInfos.matterTime" id="BgPartymatterForm-matterTime" name="matterTime">
|
||||||
<j-dict-select-tag v-model:value="formData.secretLevel" dictCode="secret_level" placeholder="请选择密级" allow-clear />
|
<a-date-picker placeholder="请选择会议时间" v-model:value="formData.matterTime" value-format="YYYY-MM-DD" style="width: 100%" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="序号" v-bind="validateInfos.number" id="BgPartymatterForm-number" name="number">
|
<a-form-item label="序号" v-bind="validateInfos.number" id="BgPartymatterForm-number" name="number">
|
||||||
<a-input v-model:value="formData.number" placeholder="请输入序号" allow-clear ></a-input>
|
<a-input v-model:value="formData.number" placeholder="填写会议时间和议题序号后自动生成" :readonly="true" ></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="会议时间" v-bind="validateInfos.matterTime" id="BgPartymatterForm-matterTime" name="matterTime">
|
<a-form-item label="议题序号" v-bind="validateInfos.itemNumber" id="BgPartymatterForm-itemNumber" name="itemNumber">
|
||||||
<a-date-picker placeholder="请选择会议时间" v-model:value="formData.matterTime" value-format="YYYY-MM-DD" style="width: 100%" allow-clear />
|
<a-input-number v-model:value="formData.itemNumber" placeholder="请输入议题序号" style="width: 100%" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="密级" v-bind="validateInfos.secretLevel" id="BgPartymatterForm-secretLevel" name="secretLevel">
|
||||||
|
<j-dict-select-tag v-model:value="formData.secretLevel" dictCode="secret_level" placeholder="请选择密级" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
@@ -40,10 +45,25 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="责任部门" v-bind="validateInfos.responDeptid" id="BgPartymatterForm-responDeptid" name="responDeptid">
|
<a-form-item label="抄告单编号" v-bind="validateInfos.noticeNumber" id="BgPartymatterForm-noticeNumber" name="noticeNumber">
|
||||||
|
<a-input v-model:value="formData.noticeNumber" placeholder="请输入抄告单编号" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="是否为三重一大决策" v-bind="validateInfos.isImportant" id="BgPartymatterForm-isImportant" name="isImportant">
|
||||||
|
<j-dict-select-tag v-model:value="formData.isImportant" dictCode="yn" placeholder="请选择是否为三重一大决策" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="主办部门" v-bind="validateInfos.responDeptid" id="BgPartymatterForm-responDeptid" name="responDeptid">
|
||||||
<j-select-dept v-model:value="formData.responDeptid" :multiple="true" checkStrictly allow-clear />
|
<j-select-dept v-model:value="formData.responDeptid" :multiple="true" checkStrictly allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="协办部门" v-bind="validateInfos.assistDeptid" id="BgPartymatterForm-assistDeptid" name="assistDeptid">
|
||||||
|
<j-select-dept v-model:value="formData.assistDeptid" :multiple="true" checkStrictly allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="实际执行情况" v-bind="validateInfos.actualExect" id="BgPartymatterForm-actualExect" name="actualExect">
|
<a-form-item label="实际执行情况" v-bind="validateInfos.actualExect" id="BgPartymatterForm-actualExect" name="actualExect">
|
||||||
<a-input v-model:value="formData.actualExect" placeholder="请输入实际执行情况" allow-clear ></a-input>
|
<a-input v-model:value="formData.actualExect" placeholder="请输入实际执行情况" allow-clear ></a-input>
|
||||||
@@ -65,8 +85,8 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="紧急程度" v-bind="validateInfos.urgencyLevel" id="BgPartymatterForm-urgencyLevel" name="urgencyLevel">
|
<a-form-item label="是否急件" v-bind="validateInfos.urgencyLevel" id="BgPartymatterForm-urgencyLevel" name="urgencyLevel">
|
||||||
<j-dict-select-tag v-model:value="formData.urgencyLevel" dictCode="urgency_level" placeholder="请选择紧急程度" allow-clear />
|
<j-dict-select-tag v-model:value="formData.urgencyLevel" dictCode="yn" placeholder="请选择是否急件" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
@@ -144,16 +164,20 @@
|
|||||||
year: '',
|
year: '',
|
||||||
secretLevel: '',
|
secretLevel: '',
|
||||||
number: '',
|
number: '',
|
||||||
|
itemNumber: undefined,
|
||||||
matterTime: '',
|
matterTime: '',
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
matterCode: '',
|
matterCode: '',
|
||||||
|
noticeNumber: '',
|
||||||
|
isImportant: '',
|
||||||
responDeptid: '',
|
responDeptid: '',
|
||||||
|
assistDeptid: '',
|
||||||
actualExect: '',
|
actualExect: '',
|
||||||
bpmStatus: '1',
|
bpmStatus: '1',
|
||||||
superviseCount: '0',
|
superviseCount: '0',
|
||||||
deadline: '',
|
deadline: '',
|
||||||
urgencyLevel: '1',
|
urgencyLevel: '0',
|
||||||
isNeedAppro: '',
|
isNeedAppro: '',
|
||||||
supDeptleaderid: '',
|
supDeptleaderid: '',
|
||||||
isNeedSuoleader: '',
|
isNeedSuoleader: '',
|
||||||
@@ -214,6 +238,58 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lastGeneratedNumber = ref('');
|
||||||
|
|
||||||
|
// 根据会议时间和议题序号自动生成业务序号,例如:2026-05-20 + 2 => 20260520-2。
|
||||||
|
watch(
|
||||||
|
() => [formData.matterTime, formData.itemNumber],
|
||||||
|
() => {
|
||||||
|
syncGeneratedNumber();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function syncGeneratedNumber() {
|
||||||
|
const generatedNumber = buildGeneratedNumber(formData.matterTime, formData.itemNumber);
|
||||||
|
if (generatedNumber) {
|
||||||
|
formData.number = generatedNumber;
|
||||||
|
lastGeneratedNumber.value = generatedNumber;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lastGeneratedNumber.value && formData.number === lastGeneratedNumber.value) {
|
||||||
|
formData.number = '';
|
||||||
|
lastGeneratedNumber.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只在两个必要字段都填写后生成,避免用户未填完整时写入半截序号。
|
||||||
|
function buildGeneratedNumber(matterTime, itemNumber) {
|
||||||
|
const dateText = formatMatterTime(matterTime);
|
||||||
|
if (!dateText || itemNumber === undefined || itemNumber === null || itemNumber === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `${dateText}-${itemNumber}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容日期选择器返回的字符串、Date、dayjs/moment 对象。
|
||||||
|
function formatMatterTime(value) {
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value.slice(0, 10).replace(/-/g, '');
|
||||||
|
}
|
||||||
|
if (value instanceof Date) {
|
||||||
|
const year = value.getFullYear();
|
||||||
|
const month = String(value.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(value.getDate()).padStart(2, '0');
|
||||||
|
return `${year}${month}${day}`;
|
||||||
|
}
|
||||||
|
if (typeof value.format === 'function') {
|
||||||
|
return value.format('YYYYMMDD');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
initFormData();
|
initFormData();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,342 @@
|
|||||||
|
<template>
|
||||||
|
<div class="my-work">
|
||||||
|
<div class="my-work__header">
|
||||||
|
<h2 class="my-work__title">我的工作</h2>
|
||||||
|
<div class="my-work__actions">
|
||||||
|
<div class="my-work__tabs">
|
||||||
|
<div class="my-work__tab" :class="{ 'my-work__tab--active': activeTab === 'todo' }" @click="activeTab = 'todo'">
|
||||||
|
待办
|
||||||
|
<span class="my-work__badge my-work__badge--danger">{{ todoTotal }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="my-work__tab" :class="{ 'my-work__tab--active': activeTab === 'cc' }" @click="activeTab = 'cc'">我的抄送</div>
|
||||||
|
</div>
|
||||||
|
<a class="my-work__more" @click="goMore">
|
||||||
|
更多
|
||||||
|
<RightOutlined class="my-work__more-icon" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-work__body">
|
||||||
|
<template v-if="activeTab === 'todo'">
|
||||||
|
<BasicTable @register="registerTodoTable">
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTodoTableAction(record)" />
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</template>
|
||||||
|
<template v-if="activeTab === 'cc'">
|
||||||
|
<BasicTable @register="registerCcTable">
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getCcTableAction(record)" />
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<task-handle-modal @register="registerHandleModal" @success="reloadTodo" />
|
||||||
|
<select-entruster-modal @register="registerEntrusterModal" @selected="selectedEntruster" />
|
||||||
|
<select-entruster-modal @register="registerComplaintModal" @selected="selectedComplaint" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { BasicTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { useModal } from '/@/components/Modal';
|
||||||
|
import { RightOutlined } from '@ant-design/icons-vue';
|
||||||
|
import TaskHandleModal from '/@/views/super/bpm/process/personalOffice/myHandleTask/modal/TaskHandleModal.vue';
|
||||||
|
import SelectEntrusterModal from '/@/views/super/bpm/process/personalOffice/myHandleTask/modal/SelectEntrusterModal.vue';
|
||||||
|
import { getTaskInfoForHistory } from '/@/views/super/bpm/process/personalOffice/myHandleTask/useTaskList';
|
||||||
|
import { todoColumns } from '../data/todo.data';
|
||||||
|
import { ccColumns } from '../data/cc.data';
|
||||||
|
import {
|
||||||
|
list as todoList,
|
||||||
|
taskNodeInfo,
|
||||||
|
taskEntrust,
|
||||||
|
taskClaim,
|
||||||
|
taskComplaint,
|
||||||
|
} from '/@/views/super/bpm/process/personalOffice/myHandleTask/task.handle.api';
|
||||||
|
import { list as ccList } from '/@/views/super/bpm/process/personalOffice/myCcTask/task.cc.api';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const activeTab = ref<'todo' | 'cc'>('todo');
|
||||||
|
const todoTotal = ref(0);
|
||||||
|
|
||||||
|
const { tableContext: todoContext } = useListPage({
|
||||||
|
designScope: 'process-portal-todo',
|
||||||
|
pagination: true,
|
||||||
|
tableProps: {
|
||||||
|
api: (params) => todoList('run', params),
|
||||||
|
columns: todoColumns,
|
||||||
|
showIndexColumn: true,
|
||||||
|
showTableSetting: false,
|
||||||
|
canResize: false,
|
||||||
|
scroll: { x: 900 },
|
||||||
|
actionColumn: { dataIndex: 'action', fixed: 'right', width: 180 },
|
||||||
|
useSearchForm: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerTodoTable, { reload: reloadTodo }] = todoContext;
|
||||||
|
|
||||||
|
const { tableContext: ccContext } = useListPage({
|
||||||
|
designScope: 'process-portal-cc',
|
||||||
|
pagination: true,
|
||||||
|
tableProps: {
|
||||||
|
api: ccList,
|
||||||
|
columns: ccColumns,
|
||||||
|
showIndexColumn: true,
|
||||||
|
showTableSetting: false,
|
||||||
|
canResize: false,
|
||||||
|
scroll: { x: 900 },
|
||||||
|
actionColumn: { dataIndex: 'action', fixed: 'right', width: 120 },
|
||||||
|
useSearchForm: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerCcTable] = ccContext;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchTodoTotal();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchTodoTotal() {
|
||||||
|
try {
|
||||||
|
const res = await todoList('run', { pageNo: 1, pageSize: 1 });
|
||||||
|
todoTotal.value = res?.total ?? 0;
|
||||||
|
} catch (e) {
|
||||||
|
todoTotal.value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goMore() {
|
||||||
|
if (activeTab.value === 'todo') {
|
||||||
|
router.push('/task/myHandleTaskInfo');
|
||||||
|
} else {
|
||||||
|
router.push('/task/myCcTaskList');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [registerHandleModal, { openModal: openHandleModal }] = useModal();
|
||||||
|
const [registerEntrusterModal, { openModal: openEntrusterModal }] = useModal();
|
||||||
|
const [registerComplaintModal, { openModal: openComplaintModal }] = useModal();
|
||||||
|
|
||||||
|
async function handleProcess(record) {
|
||||||
|
let { formData, formUrl } = await getTaskNodeInfo(record);
|
||||||
|
formData['PROCESS_TAB_TYPE'] = 'run';
|
||||||
|
openHandleModal(true, { formData, formUrl, title: '流程办理' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTaskNodeInfo(record) {
|
||||||
|
let params = { taskId: record.id };
|
||||||
|
const result = await taskNodeInfo('run', params);
|
||||||
|
let procInsId = record.processInstanceId || (result.records ? result.records.BPM_INST_ID : '');
|
||||||
|
let realDataId = (result.records && result.records.business_id) || result.dataId;
|
||||||
|
let realTableName = (result.records && result.records.business_tablename) || result.tableName;
|
||||||
|
let formData: any = {
|
||||||
|
taskId: record.id,
|
||||||
|
taskDefKey: result.taskDefKey,
|
||||||
|
procInsId: procInsId,
|
||||||
|
dataId: realDataId,
|
||||||
|
tableName: realTableName,
|
||||||
|
processDataId: result.dataId,
|
||||||
|
processTableName: result.tableName,
|
||||||
|
permissionList: result.permissionList,
|
||||||
|
subPermissionList: result.subPermissionList,
|
||||||
|
vars: result.records,
|
||||||
|
};
|
||||||
|
let tempFormUrl = result.formUrl;
|
||||||
|
if (tempFormUrl && tempFormUrl.indexOf('?') != -1 && !/^http[s]?:\/\/.*/.test(tempFormUrl) && tempFormUrl.indexOf('{{DOMAIN_URL}}') == -1) {
|
||||||
|
tempFormUrl = result.formUrl.split('?')[0];
|
||||||
|
}
|
||||||
|
return { formData, formUrl: tempFormUrl };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleClaim(record) {
|
||||||
|
await taskClaim({ taskId: record.id });
|
||||||
|
await reloadTodo();
|
||||||
|
await fetchTodoTotal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelectEntruster(record) {
|
||||||
|
openEntrusterModal(true, { taskId: record.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleComplaintEntruster(record) {
|
||||||
|
openComplaintModal(true, { taskId: record.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectedEntruster(params) {
|
||||||
|
await taskEntrust(params);
|
||||||
|
await reloadTodo();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectedComplaint(params) {
|
||||||
|
await taskComplaint(params);
|
||||||
|
await reloadTodo();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showCcHistory(record) {
|
||||||
|
let { formData, formUrl } = await getTaskInfoForHistory(record);
|
||||||
|
formData['PROCESS_TAB_TYPE'] = 'history';
|
||||||
|
openHandleModal(true, { formData, formUrl, title: '流程历史' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTodoTableAction(record) {
|
||||||
|
let arr = [];
|
||||||
|
if (record.taskAssigneeId && record.taskAssigneeId != '') {
|
||||||
|
arr.push({ label: '办理', onClick: handleProcess.bind(null, record) });
|
||||||
|
} else {
|
||||||
|
arr.push({
|
||||||
|
label: '签收',
|
||||||
|
popConfirm: {
|
||||||
|
title: '确定签收吗?',
|
||||||
|
placement: 'left',
|
||||||
|
confirm: handleClaim.bind(null, record),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
arr.push({ label: '转办', onClick: handleComplaintEntruster.bind(null, record) });
|
||||||
|
arr.push({ label: '委托', onClick: handleSelectEntruster.bind(null, record) });
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCcTableAction(record) {
|
||||||
|
return [{ label: '查看审批', onClick: showCcHistory.bind(null, record) }];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.my-work {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 3px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tab {
|
||||||
|
padding: 5px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background: #fff;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 9px;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background: #ff4d4f;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__more {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1890ff;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__more-icon {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 12px 20px;
|
||||||
|
|
||||||
|
:deep(.ant-table) {
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-thead > tr > th {
|
||||||
|
background: #fafafa;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
padding: 10px 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody > tr > td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody > tr:hover > td {
|
||||||
|
background: #f0f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-cell {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
<template>
|
||||||
|
<a-row :gutter="16" class="stat-cards">
|
||||||
|
<a-col :xs="24" :sm="12" :lg="6" v-for="item in statList" :key="item.title">
|
||||||
|
<div class="stat-card" hoverable>
|
||||||
|
<div class="stat-card__header">
|
||||||
|
<span class="stat-card__label">{{ item.title }}</span>
|
||||||
|
<div class="stat-card__icon-wrap" :style="{ background: item.bgColor }">
|
||||||
|
<component :is="item.icon" :style="{ color: item.iconColor, fontSize: '18px' }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card__footer">
|
||||||
|
<div class="stat-card__value-group">
|
||||||
|
<span class="stat-card__value">{{ item.value }}</span>
|
||||||
|
<span class="stat-card__change" :class="'stat-card__change--' + item.trend">
|
||||||
|
<CaretUpOutlined v-if="item.trend === 'up'" />
|
||||||
|
<CaretDownOutlined v-if="item.trend === 'down'" />
|
||||||
|
{{ item.change }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<svg class="stat-card__sparkline" width="80" height="32" :viewBox="'0 0 80 32'">
|
||||||
|
<defs>
|
||||||
|
<linearGradient :id="'grad-' + item.key" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0%" :stop-color="item.sparkColor" stop-opacity="0.3" />
|
||||||
|
<stop offset="100%" :stop-color="item.sparkColor" stop-opacity="0.05" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<polygon :points="item.fillPoints" :fill="'url(#grad-' + item.key + ')'" />
|
||||||
|
<path :d="item.pathD" fill="none" :stroke="item.sparkColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
FileTextOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
WarningOutlined,
|
||||||
|
CaretUpOutlined,
|
||||||
|
CaretDownOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
function buildSparkline(data: number[]) {
|
||||||
|
const max = Math.max(...data);
|
||||||
|
const min = Math.min(...data);
|
||||||
|
const range = max - min || 1;
|
||||||
|
const width = 80;
|
||||||
|
const height = 32;
|
||||||
|
const pad = 2;
|
||||||
|
const points = data.map((v, i) => {
|
||||||
|
const x = pad + (i / (data.length - 1)) * (width - pad * 2);
|
||||||
|
const y = height - pad - ((v - min) / range) * (height - pad * 2);
|
||||||
|
return `${x},${y}`;
|
||||||
|
});
|
||||||
|
const pathD = `M ${points.join(' L ')}`;
|
||||||
|
const fillPoints = [`${pad},${height - pad}`, ...points, `${width - pad},${height - pad}`].join(' ');
|
||||||
|
return { pathD, fillPoints };
|
||||||
|
}
|
||||||
|
|
||||||
|
const statList = [
|
||||||
|
{
|
||||||
|
key: 'todo',
|
||||||
|
title: '待办事项',
|
||||||
|
value: '28',
|
||||||
|
change: '+4',
|
||||||
|
trend: 'up',
|
||||||
|
icon: FileTextOutlined,
|
||||||
|
bgColor: 'rgba(24, 144, 255, 0.1)',
|
||||||
|
iconColor: '#1890ff',
|
||||||
|
sparkColor: '#3b82f6',
|
||||||
|
...buildSparkline([20, 22, 18, 24, 21, 26, 28]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'done',
|
||||||
|
title: '本月已办',
|
||||||
|
value: '342',
|
||||||
|
change: '+18.2%',
|
||||||
|
trend: 'up',
|
||||||
|
icon: CheckCircleOutlined,
|
||||||
|
bgColor: 'rgba(82, 196, 26, 0.1)',
|
||||||
|
iconColor: '#52c41a',
|
||||||
|
sparkColor: '#22c55e',
|
||||||
|
...buildSparkline([280, 295, 310, 305, 320, 335, 342]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'avg',
|
||||||
|
title: '平均时长',
|
||||||
|
value: '2.4h',
|
||||||
|
change: '-0.6h',
|
||||||
|
trend: 'down',
|
||||||
|
icon: ClockCircleOutlined,
|
||||||
|
bgColor: 'rgba(168, 85, 247, 0.1)',
|
||||||
|
iconColor: '#a855f7',
|
||||||
|
sparkColor: '#a855f7',
|
||||||
|
...buildSparkline([3.2, 3.0, 2.8, 2.9, 2.6, 2.5, 2.4]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'warn',
|
||||||
|
title: '超时预警',
|
||||||
|
value: '6',
|
||||||
|
change: '+2',
|
||||||
|
trend: 'up',
|
||||||
|
icon: WarningOutlined,
|
||||||
|
bgColor: 'rgba(255, 77, 79, 0.1)',
|
||||||
|
iconColor: '#ff4d4f',
|
||||||
|
sparkColor: '#ef4444',
|
||||||
|
...buildSparkline([3, 4, 2, 5, 4, 5, 6]),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.stat-cards {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon-wrap {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__change {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
&--up {
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--down {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sparkline {
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<div class="welcome-banner">
|
||||||
|
<div class="welcome-banner__deco welcome-banner__deco--top"></div>
|
||||||
|
<div class="welcome-banner__deco welcome-banner__deco--bottom"></div>
|
||||||
|
<div class="welcome-banner__content">
|
||||||
|
<div class="welcome-banner__left">
|
||||||
|
<h2 class="welcome-banner__title">{{ greeting }},{{ userinfo.realname }}<span class="welcome-banner__wave">👋</span></h2>
|
||||||
|
<p class="welcome-banner__desc">
|
||||||
|
今天有 <span class="welcome-banner__highlight">8 项待办</span>、
|
||||||
|
<span class="welcome-banner__warn">2 项即将超时</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="welcome-banner__right">
|
||||||
|
<div class="welcome-banner__date-btn">
|
||||||
|
<CalendarOutlined />
|
||||||
|
<span>{{ formattedDate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="welcome-banner__time-btn">
|
||||||
|
<ClockCircleOutlined />
|
||||||
|
<span>{{ formattedTime }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { CalendarOutlined, ClockCircleOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const userinfo = computed(() => userStore.getUserInfo);
|
||||||
|
|
||||||
|
const now = ref(new Date());
|
||||||
|
let timer: ReturnType<typeof setInterval>;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
timer = setInterval(() => {
|
||||||
|
now.value = new Date();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer);
|
||||||
|
});
|
||||||
|
|
||||||
|
const greeting = computed(() => {
|
||||||
|
const hour = now.value.getHours();
|
||||||
|
if (hour < 6) return '凌晨好';
|
||||||
|
if (hour < 9) return '早上好';
|
||||||
|
if (hour < 12) return '上午好';
|
||||||
|
if (hour < 14) return '中午好';
|
||||||
|
if (hour < 18) return '下午好';
|
||||||
|
return '晚上好';
|
||||||
|
});
|
||||||
|
|
||||||
|
const formattedDate = computed(() => {
|
||||||
|
const d = now.value;
|
||||||
|
const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} 周${weekDays[d.getDay()]}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const formattedTime = computed(() => {
|
||||||
|
const d = now.value;
|
||||||
|
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.welcome-banner {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 28px 32px;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.25);
|
||||||
|
|
||||||
|
&__deco {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
&--top {
|
||||||
|
top: -60px;
|
||||||
|
right: -40px;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
filter: blur(40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--bottom {
|
||||||
|
bottom: -40px;
|
||||||
|
left: -30px;
|
||||||
|
width: 160px;
|
||||||
|
height: 160px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
filter: blur(30px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__wave {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__highlight {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warn {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fde68a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__date-btn,
|
||||||
|
&__time-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
cursor: default;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { BasicColumn } from '/@/components/Table';
|
||||||
|
|
||||||
|
export const ccColumns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '业务标题',
|
||||||
|
align: 'center',
|
||||||
|
dataIndex: 'bpmBizTitle',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务名称',
|
||||||
|
align: 'center',
|
||||||
|
width: 260,
|
||||||
|
dataIndex: 'taskName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '发起人',
|
||||||
|
align: 'center',
|
||||||
|
width: 160,
|
||||||
|
dataIndex: 'processApplyUserName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理人',
|
||||||
|
align: 'center',
|
||||||
|
width: 160,
|
||||||
|
dataIndex: 'taskAssigneeName',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { BasicColumn } from '/@/components/Table';
|
||||||
|
import { Tag } from 'ant-design-vue';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
export const todoColumns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '业务标题',
|
||||||
|
align: 'center',
|
||||||
|
dataIndex: 'bpmBizTitle',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '当前环节',
|
||||||
|
align: 'center',
|
||||||
|
width: 200,
|
||||||
|
dataIndex: 'taskName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '转入时间',
|
||||||
|
align: 'center',
|
||||||
|
width: 160,
|
||||||
|
dataIndex: 'taskBeginTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '事项截止时间',
|
||||||
|
align: 'center',
|
||||||
|
width: 160,
|
||||||
|
dataIndex: 'deadline',
|
||||||
|
customRender: ({ text, record }) => {
|
||||||
|
const value = !text ? '' : text.length > 10 ? text.substr(0, 10) : text;
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (record.deadlineOverdue) {
|
||||||
|
return h(Tag, { color: 'error' }, () => (record.deadlineWarnText ? `${value} ${record.deadlineWarnText}` : value));
|
||||||
|
}
|
||||||
|
if (record.deadlineSoon) {
|
||||||
|
return h(Tag, { color: 'warning' }, () => (record.deadlineWarnText ? `${value} ${record.deadlineWarnText}` : value));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<!-- eslint-disable vue/multi-word-component-names -->
|
||||||
|
<template>
|
||||||
|
<div class="process-portal">
|
||||||
|
<WelcomeBanner />
|
||||||
|
<StatCards />
|
||||||
|
<MyWork />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import WelcomeBanner from './components/WelcomeBanner.vue';
|
||||||
|
import StatCards from './components/StatCards.vue';
|
||||||
|
import MyWork from './components/MyWork.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.process-portal {
|
||||||
|
padding: 12px 16px;
|
||||||
|
max-width: 1600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
//办理
|
//办理
|
||||||
const [registerHandleModal, { openModal: openHandleModal }] = useModal();
|
const [registerHandleModal, { openModal: openHandleModal }] = useModal();
|
||||||
async function handleProcess(record) {
|
async function handleProcess(record) {
|
||||||
debugger
|
//debugger
|
||||||
let { formData, formUrl } = await getTaskNodeInfo(record);
|
let { formData, formUrl } = await getTaskNodeInfo(record);
|
||||||
formData['PROCESS_TAB_TYPE'] = 'run';
|
formData['PROCESS_TAB_TYPE'] = 'run';
|
||||||
openHandleModal(true, {
|
openHandleModal(true, {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export function useTaskList(type) {
|
|||||||
*/
|
*/
|
||||||
async function getTaskNodeInfo(record) {
|
async function getTaskNodeInfo(record) {
|
||||||
//查询条件
|
//查询条件
|
||||||
debugger;
|
// debugger;
|
||||||
let params = { taskId: record.id };
|
let params = { taskId: record.id };
|
||||||
const result = await taskNodeInfo(type, params);
|
const result = await taskNodeInfo(type, params);
|
||||||
console.log('获取流程节点信息', result);
|
console.log('获取流程节点信息', result);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const columns: BasicColumn[] = [
|
|||||||
title: '#',
|
title: '#',
|
||||||
dataIndex: '',
|
dataIndex: '',
|
||||||
key: 'rowIndex',
|
key: 'rowIndex',
|
||||||
width: 60,
|
width: 30,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
customRender: function ({ index }) {
|
customRender: function ({ index }) {
|
||||||
return parseInt(index) + 1;
|
return parseInt(index) + 1;
|
||||||
@@ -17,7 +17,7 @@ export const columns: BasicColumn[] = [
|
|||||||
{
|
{
|
||||||
title: '节点名称',
|
title: '节点名称',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 120,
|
width: 140,
|
||||||
dataIndex: 'processNodeName',
|
dataIndex: 'processNodeName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -30,14 +30,14 @@ export const columns: BasicColumn[] = [
|
|||||||
title: 'PC表单地址',
|
title: 'PC表单地址',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
dataIndex: 'modelAndView',
|
dataIndex: 'modelAndView',
|
||||||
width: 240,
|
width: 400,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '移动表单地址',
|
title: '移动表单地址',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
dataIndex: 'modelAndViewMobile',
|
dataIndex: 'modelAndViewMobile',
|
||||||
width: 240,
|
width: 30,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from "/@/hooks/web/useMessage";
|
||||||
|
|
||||||
|
const { createConfirm } = useMessage();
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/taskapprovalopinion/taskApprovalOpinion/list',
|
||||||
|
save='/taskapprovalopinion/taskApprovalOpinion/add',
|
||||||
|
edit='/taskapprovalopinion/taskApprovalOpinion/edit',
|
||||||
|
deleteOne = '/taskapprovalopinion/taskApprovalOpinion/delete',
|
||||||
|
deleteBatch = '/taskapprovalopinion/taskApprovalOpinion/deleteBatch',
|
||||||
|
importExcel = '/taskapprovalopinion/taskApprovalOpinion/importExcel',
|
||||||
|
exportXls = '/taskapprovalopinion/taskApprovalOpinion/exportXls',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出api
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getExportUrl = Api.exportXls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入api
|
||||||
|
*/
|
||||||
|
export const getImportUrl = Api.importExcel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单个
|
||||||
|
* @param params
|
||||||
|
* @param handleSuccess
|
||||||
|
*/
|
||||||
|
export const deleteOne = (params,handleSuccess) => {
|
||||||
|
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
|
||||||
|
handleSuccess();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除
|
||||||
|
* @param params
|
||||||
|
* @param 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存或者更新
|
||||||
|
* @param params
|
||||||
|
* @param isUpdate
|
||||||
|
*/
|
||||||
|
export const saveOrUpdate = (params, isUpdate) => {
|
||||||
|
let url = isUpdate ? Api.edit : Api.save;
|
||||||
|
return defHttp.post({ url: url, params }, { isTransformResponse: false });
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
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: '记录类型:PROCESS流程意见、SUPPLEMENT补充反馈、SYSTEM系统记录',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'recordType'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '事项任务ID,对应task_task.id',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'taskTaskId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '关联业务ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'businessId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '流程编码',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'flowCode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '流程实例ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'processInstId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Flowable任务ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'flowTaskId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '流程节点Key',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'taskDefKey'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '流程节点名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'taskName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作类型:START发起、APPROVE办理、REJECT驳回、CALLBACK取回、INVALID作废、FEEDBACK反馈',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'actionType'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作结果,如同意、不同意、驳回、发起流程、补充反馈',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'actionResult'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '审批意见或反馈内容',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opinion'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理人账号',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opUserId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理人姓名',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opUserName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理人部门ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opDeptId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理人部门名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opDeptName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理人部门编码',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opDeptCode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办理时间',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'opTime'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 高级查询数据
|
||||||
|
export const superQuerySchema = {
|
||||||
|
recordType: {title: '记录类型:PROCESS流程意见、SUPPLEMENT补充反馈、SYSTEM系统记录',order: 0,view: 'text', type: 'string',},
|
||||||
|
taskTaskId: {title: '事项任务ID,对应task_task.id',order: 1,view: 'text', type: 'string',},
|
||||||
|
businessId: {title: '关联业务ID',order: 2,view: 'text', type: 'string',},
|
||||||
|
flowCode: {title: '流程编码',order: 3,view: 'text', type: 'string',},
|
||||||
|
processInstId: {title: '流程实例ID',order: 4,view: 'text', type: 'string',},
|
||||||
|
flowTaskId: {title: 'Flowable任务ID',order: 5,view: 'text', type: 'string',},
|
||||||
|
taskDefKey: {title: '流程节点Key',order: 6,view: 'text', type: 'string',},
|
||||||
|
taskName: {title: '流程节点名称',order: 7,view: 'text', type: 'string',},
|
||||||
|
actionType: {title: '操作类型:START发起、APPROVE办理、REJECT驳回、CALLBACK取回、INVALID作废、FEEDBACK反馈',order: 8,view: 'text', type: 'string',},
|
||||||
|
actionResult: {title: '操作结果,如同意、不同意、驳回、发起流程、补充反馈',order: 9,view: 'text', type: 'string',},
|
||||||
|
opinion: {title: '审批意见或反馈内容',order: 10,view: 'textarea', type: 'string',},
|
||||||
|
opUserId: {title: '办理人账号',order: 11,view: 'text', type: 'string',},
|
||||||
|
opUserName: {title: '办理人姓名',order: 12,view: 'text', type: 'string',},
|
||||||
|
opDeptId: {title: '办理人部门ID',order: 13,view: 'text', type: 'string',},
|
||||||
|
opDeptName: {title: '办理人部门名称',order: 14,view: 'text', type: 'string',},
|
||||||
|
opDeptCode: {title: '办理人部门编码',order: 15,view: 'text', type: 'string',},
|
||||||
|
opTime: {title: '办理时间',order: 16,view: 'datetime', type: 'string',},
|
||||||
|
};
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-2">
|
||||||
|
<!--查询区域-->
|
||||||
|
<div class="jeecg-basic-table-form-container">
|
||||||
|
<a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<!--引用表格-->
|
||||||
|
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||||
|
<!--插槽:table标题-->
|
||||||
|
<template #tableTitle>
|
||||||
|
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
||||||
|
<a-button type="primary" v-auth="'taskapprovalopinion:task_approval_opinion:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||||
|
<j-upload-button type="primary" v-auth="'taskapprovalopinion:task_approval_opinion:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||||
|
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item key="1" @click="batchHandleDelete">
|
||||||
|
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||||
|
删除
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button v-auth="'taskapprovalopinion:task_approval_opinion:deleteBatch'">批量操作
|
||||||
|
<Icon icon="mdi:chevron-down"></Icon>
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<!-- 高级查询 -->
|
||||||
|
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
||||||
|
</template>
|
||||||
|
<!--操作栏-->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
|
||||||
|
</template>
|
||||||
|
<template v-slot:bodyCell="{ column, record, index, text }">
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
<TaskApprovalOpinionModal ref="registerModal" @success="handleSuccess"></TaskApprovalOpinionModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="taskapprovalopinion-taskApprovalOpinion" setup>
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { columns, superQuerySchema } from './TaskApprovalOpinion.data';
|
||||||
|
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './TaskApprovalOpinion.api';
|
||||||
|
import { downloadFile } from '/@/utils/common/renderUtils';
|
||||||
|
import TaskApprovalOpinionModal from './components/TaskApprovalOpinionModal.vue'
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import {useModal} from '/@/components/Modal';
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const queryParam = reactive<any>({});
|
||||||
|
const toggleSearchStatus = ref<boolean>(false);
|
||||||
|
const registerModal = ref();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
//注册table数据
|
||||||
|
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: 'task_approval_opinion',
|
||||||
|
api: list,
|
||||||
|
columns,
|
||||||
|
canResize:false,
|
||||||
|
useSearchForm: false,
|
||||||
|
actionColumn: {
|
||||||
|
width: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
beforeFetch: async (params) => {
|
||||||
|
return Object.assign(params, queryParam);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exportConfig: {
|
||||||
|
name: "task_approval_opinion",
|
||||||
|
url: getExportUrl,
|
||||||
|
params: queryParam,
|
||||||
|
},
|
||||||
|
importConfig: {
|
||||||
|
url: getImportUrl,
|
||||||
|
success: handleSuccess
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||||
|
const labelCol = reactive({
|
||||||
|
xs:24,
|
||||||
|
sm:4,
|
||||||
|
xl:6,
|
||||||
|
xxl:4
|
||||||
|
});
|
||||||
|
const wrapperCol = reactive({
|
||||||
|
xs: 24,
|
||||||
|
sm: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 高级查询配置
|
||||||
|
const superQueryConfig = reactive(superQuerySchema);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高级查询事件
|
||||||
|
*/
|
||||||
|
function handleSuperQuery(params) {
|
||||||
|
Object.keys(params).map((k) => {
|
||||||
|
queryParam[k] = params[k];
|
||||||
|
});
|
||||||
|
searchQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增事件
|
||||||
|
*/
|
||||||
|
function handleAdd() {
|
||||||
|
registerModal.value.disableSubmit = false;
|
||||||
|
registerModal.value.add();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑事件
|
||||||
|
*/
|
||||||
|
function handleEdit(record: Recordable) {
|
||||||
|
registerModal.value.disableSubmit = false;
|
||||||
|
registerModal.value.edit(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情
|
||||||
|
*/
|
||||||
|
function handleDetail(record: Recordable) {
|
||||||
|
registerModal.value.disableSubmit = true;
|
||||||
|
registerModal.value.edit(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除事件
|
||||||
|
*/
|
||||||
|
async function handleDelete(record) {
|
||||||
|
await deleteOne({ id: record.id }, handleSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除事件
|
||||||
|
*/
|
||||||
|
async function batchHandleDelete() {
|
||||||
|
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功回调
|
||||||
|
*/
|
||||||
|
function handleSuccess() {
|
||||||
|
(selectedRowKeys.value = []) && reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作栏
|
||||||
|
*/
|
||||||
|
function getTableAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
auth: 'taskapprovalopinion:task_approval_opinion:edit'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下拉操作栏
|
||||||
|
*/
|
||||||
|
function getDropDownAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '详情',
|
||||||
|
onClick: handleDetail.bind(null, record),
|
||||||
|
}, {
|
||||||
|
label: '删除',
|
||||||
|
popConfirm: {
|
||||||
|
title: '是否确认删除',
|
||||||
|
confirm: handleDelete.bind(null, record),
|
||||||
|
placement: 'topLeft',
|
||||||
|
},
|
||||||
|
auth: 'taskapprovalopinion:task_approval_opinion:delete'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询
|
||||||
|
*/
|
||||||
|
function searchQuery() {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置
|
||||||
|
*/
|
||||||
|
function searchReset() {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
//刷新数据
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.jeecg-basic-table-form-container {
|
||||||
|
padding: 0;
|
||||||
|
.table-page-search-submitButtons {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.query-group-cust{
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
|
.query-group-split-cust{
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
.ant-form-item:not(.ant-form-item-with-help){
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
:deep(.ant-picker),:deep(.ant-input-number){
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="TaskApprovalOpinionForm">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="记录类型:PROCESS流程意见、SUPPLEMENT补充反馈、SYSTEM系统记录" v-bind="validateInfos.recordType" id="TaskApprovalOpinionForm-recordType" name="recordType">
|
||||||
|
<a-input v-model:value="formData.recordType" placeholder="请输入记录类型:PROCESS流程意见、SUPPLEMENT补充反馈、SYSTEM系统记录" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="事项任务ID,对应task_task.id" v-bind="validateInfos.taskTaskId" id="TaskApprovalOpinionForm-taskTaskId" name="taskTaskId">
|
||||||
|
<a-input v-model:value="formData.taskTaskId" placeholder="请输入事项任务ID,对应task_task.id" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="关联业务ID" v-bind="validateInfos.businessId" id="TaskApprovalOpinionForm-businessId" name="businessId">
|
||||||
|
<a-input v-model:value="formData.businessId" placeholder="请输入关联业务ID" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="流程编码" v-bind="validateInfos.flowCode" id="TaskApprovalOpinionForm-flowCode" name="flowCode">
|
||||||
|
<a-input v-model:value="formData.flowCode" placeholder="请输入流程编码" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="流程实例ID" v-bind="validateInfos.processInstId" id="TaskApprovalOpinionForm-processInstId" name="processInstId">
|
||||||
|
<a-input v-model:value="formData.processInstId" placeholder="请输入流程实例ID" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="Flowable任务ID" v-bind="validateInfos.flowTaskId" id="TaskApprovalOpinionForm-flowTaskId" name="flowTaskId">
|
||||||
|
<a-input v-model:value="formData.flowTaskId" placeholder="请输入Flowable任务ID" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="流程节点Key" v-bind="validateInfos.taskDefKey" id="TaskApprovalOpinionForm-taskDefKey" name="taskDefKey">
|
||||||
|
<a-input v-model:value="formData.taskDefKey" placeholder="请输入流程节点Key" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="流程节点名称" v-bind="validateInfos.taskName" id="TaskApprovalOpinionForm-taskName" name="taskName">
|
||||||
|
<a-input v-model:value="formData.taskName" placeholder="请输入流程节点名称" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="操作类型:START发起、APPROVE办理、REJECT驳回、CALLBACK取回、INVALID作废、FEEDBACK反馈" v-bind="validateInfos.actionType" id="TaskApprovalOpinionForm-actionType" name="actionType">
|
||||||
|
<a-input v-model:value="formData.actionType" placeholder="请输入操作类型:START发起、APPROVE办理、REJECT驳回、CALLBACK取回、INVALID作废、FEEDBACK反馈" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="操作结果,如同意、不同意、驳回、发起流程、补充反馈" v-bind="validateInfos.actionResult" id="TaskApprovalOpinionForm-actionResult" name="actionResult">
|
||||||
|
<a-input v-model:value="formData.actionResult" placeholder="请输入操作结果,如同意、不同意、驳回、发起流程、补充反馈" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="审批意见或反馈内容" v-bind="validateInfos.opinion" id="TaskApprovalOpinionForm-opinion" name="opinion">
|
||||||
|
<a-textarea v-model:value="formData.opinion" :rows="4" placeholder="请输入审批意见或反馈内容" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="办理人账号" v-bind="validateInfos.opUserId" id="TaskApprovalOpinionForm-opUserId" name="opUserId">
|
||||||
|
<a-input v-model:value="formData.opUserId" placeholder="请输入办理人账号" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="办理人姓名" v-bind="validateInfos.opUserName" id="TaskApprovalOpinionForm-opUserName" name="opUserName">
|
||||||
|
<a-input v-model:value="formData.opUserName" placeholder="请输入办理人姓名" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="办理人部门ID" v-bind="validateInfos.opDeptId" id="TaskApprovalOpinionForm-opDeptId" name="opDeptId">
|
||||||
|
<a-input v-model:value="formData.opDeptId" placeholder="请输入办理人部门ID" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="办理人部门名称" v-bind="validateInfos.opDeptName" id="TaskApprovalOpinionForm-opDeptName" name="opDeptName">
|
||||||
|
<a-input v-model:value="formData.opDeptName" placeholder="请输入办理人部门名称" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="办理人部门编码" v-bind="validateInfos.opDeptCode" id="TaskApprovalOpinionForm-opDeptCode" name="opDeptCode">
|
||||||
|
<a-input v-model:value="formData.opDeptCode" placeholder="请输入办理人部门编码" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="办理时间" v-bind="validateInfos.opTime" id="TaskApprovalOpinionForm-opTime" name="opTime">
|
||||||
|
<a-date-picker placeholder="请选择办理时间" v-model:value="formData.opTime" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { saveOrUpdate } from '../TaskApprovalOpinion.api';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
const props = defineProps({
|
||||||
|
formDisabled: { type: Boolean, default: false },
|
||||||
|
formData: { type: Object, default: () => ({})},
|
||||||
|
formBpm: { type: Boolean, default: true }
|
||||||
|
});
|
||||||
|
const formRef = ref();
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
const emit = defineEmits(['register', 'ok']);
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
id: '',
|
||||||
|
recordType: '',
|
||||||
|
taskTaskId: '',
|
||||||
|
businessId: '',
|
||||||
|
flowCode: '',
|
||||||
|
processInstId: '',
|
||||||
|
flowTaskId: '',
|
||||||
|
taskDefKey: '',
|
||||||
|
taskName: '',
|
||||||
|
actionType: '',
|
||||||
|
actionResult: '',
|
||||||
|
opinion: '',
|
||||||
|
opUserId: '',
|
||||||
|
opUserName: '',
|
||||||
|
opDeptId: '',
|
||||||
|
opDeptName: '',
|
||||||
|
opDeptCode: '',
|
||||||
|
opTime: '',
|
||||||
|
delFlag: '',
|
||||||
|
});
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||||
|
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
//表单验证
|
||||||
|
const validatorRules = reactive({
|
||||||
|
recordType: [{ required: true, message: '请输入记录类型:PROCESS流程意见、SUPPLEMENT补充反馈、SYSTEM系统记录!'},],
|
||||||
|
});
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
|
||||||
|
|
||||||
|
// 表单禁用
|
||||||
|
const disabled = computed(()=>{
|
||||||
|
if(props.formBpm === true){
|
||||||
|
if(props.formData.disabled === false){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.formDisabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*/
|
||||||
|
function add() {
|
||||||
|
edit({});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
*/
|
||||||
|
function edit(record) {
|
||||||
|
nextTick(() => {
|
||||||
|
resetFields();
|
||||||
|
const tmpData = {};
|
||||||
|
Object.keys(formData).forEach((key) => {
|
||||||
|
if(record.hasOwnProperty(key)){
|
||||||
|
tmpData[key] = record[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//赋值
|
||||||
|
Object.assign(formData, tmpData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交数据
|
||||||
|
*/
|
||||||
|
async function submitForm() {
|
||||||
|
try {
|
||||||
|
// 触发表单验证
|
||||||
|
await validate();
|
||||||
|
} catch ({ errorFields }) {
|
||||||
|
if (errorFields) {
|
||||||
|
const firstField = errorFields[0];
|
||||||
|
if (firstField) {
|
||||||
|
formRef.value.scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(errorFields);
|
||||||
|
}
|
||||||
|
confirmLoading.value = true;
|
||||||
|
const isUpdate = ref<boolean>(false);
|
||||||
|
//时间格式化
|
||||||
|
let model = formData;
|
||||||
|
if (model.id) {
|
||||||
|
isUpdate.value = true;
|
||||||
|
}
|
||||||
|
//循环数据
|
||||||
|
for (let data in model) {
|
||||||
|
//如果该数据是数组并且是字符串类型
|
||||||
|
if (model[data] instanceof Array) {
|
||||||
|
let valueType = getValueType(formRef.value.getProps, data);
|
||||||
|
//如果是字符串类型的需要变成以逗号分割的字符串
|
||||||
|
if (valueType === 'string') {
|
||||||
|
model[data] = model[data].join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await saveOrUpdate(model, isUpdate.value)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
createMessage.success(res.message);
|
||||||
|
emit('ok');
|
||||||
|
} else {
|
||||||
|
createMessage.warning(res.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
add,
|
||||||
|
edit,
|
||||||
|
submitForm,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
|
||||||
|
<TaskApprovalOpinionForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></TaskApprovalOpinionForm>
|
||||||
|
<template #footer>
|
||||||
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
|
<a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk">确认</a-button>
|
||||||
|
</template>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, nextTick, defineExpose } from 'vue';
|
||||||
|
import TaskApprovalOpinionForm from './TaskApprovalOpinionForm.vue'
|
||||||
|
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const title = ref<string>('');
|
||||||
|
const width = ref<number>(800);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const disableSubmit = ref<boolean>(false);
|
||||||
|
const registerForm = ref();
|
||||||
|
const emit = defineEmits(['register', 'success']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*/
|
||||||
|
function add() {
|
||||||
|
title.value = '新增';
|
||||||
|
visible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
registerForm.value.add();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
* @param record
|
||||||
|
*/
|
||||||
|
function edit(record) {
|
||||||
|
title.value = disableSubmit.value ? '详情' : '编辑';
|
||||||
|
visible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
registerForm.value.edit(record);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定按钮点击事件
|
||||||
|
*/
|
||||||
|
function handleOk() {
|
||||||
|
registerForm.value.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* form保存回调事件
|
||||||
|
*/
|
||||||
|
function submitCallback() {
|
||||||
|
handleCancel();
|
||||||
|
emit('success');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消按钮回调事件
|
||||||
|
*/
|
||||||
|
function handleCancel() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
add,
|
||||||
|
edit,
|
||||||
|
disableSubmit,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/**隐藏样式-modal确定按钮 */
|
||||||
|
.jee-hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -124,15 +124,15 @@ export const deleteTask = (params, handleSuccess) => {
|
|||||||
|
|
||||||
export const toggleTaskStatus = (params) => defHttp.post({ url: Api.toggleTaskStatus, params });
|
export const toggleTaskStatus = (params) => defHttp.post({ url: Api.toggleTaskStatus, params });
|
||||||
|
|
||||||
export const getCollaborators = (params) => defHttp.get({ url: Api.getCollaborators, params });
|
export const getCollaborators = (params, options?: Record<string, any>) => defHttp.get({ url: Api.getCollaborators, params }, options);
|
||||||
|
|
||||||
export const addCollaborator = (params) => defHttp.post({ url: Api.addCollaborator, params });
|
export const addCollaborator = (params, options?) => defHttp.post({ url: Api.addCollaborator, params }, options);
|
||||||
|
|
||||||
export const removeCollaborator = (params) => defHttp.post({ url: Api.removeCollaborator, params });
|
export const removeCollaborator = (params, options?) => defHttp.post({ url: Api.removeCollaborator, params }, options);
|
||||||
|
|
||||||
export const updateCollaboratorPermission = (params) => defHttp.post({ url: Api.updateCollaboratorPermission, params });
|
export const updateCollaboratorPermission = (params, options?) => defHttp.post({ url: Api.updateCollaboratorPermission, params }, options);
|
||||||
|
|
||||||
export const getMyPermission = (params) => defHttp.get({ url: Api.getMyPermission, params });
|
export const getMyPermission = (params, options?) => defHttp.get({ url: Api.getMyPermission, params }, options);
|
||||||
|
|
||||||
export const moveTask = (params) => defHttp.post({ url: Api.moveTask, params });
|
export const moveTask = (params) => defHttp.post({ url: Api.moveTask, params });
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getAvailableSecretLevels } from './types';
|
|||||||
|
|
||||||
export const columns: BasicColumn[] = [
|
export const columns: BasicColumn[] = [
|
||||||
{
|
{
|
||||||
title: '任务清单名称',
|
title: '计划名称',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
dataIndex: 'tasklistName',
|
dataIndex: 'tasklistName',
|
||||||
},
|
},
|
||||||
@@ -35,7 +35,7 @@ export const searchFormSchema: FormSchema[] = [];
|
|||||||
|
|
||||||
export const formSchema: FormSchema[] = [
|
export const formSchema: FormSchema[] = [
|
||||||
{
|
{
|
||||||
label: '任务清单名称',
|
label: '计划名称',
|
||||||
field: 'tasklistName',
|
field: 'tasklistName',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
},
|
},
|
||||||
@@ -93,7 +93,7 @@ export const taskListDetialColumns: JVxeColumn[] = [
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '任务名称',
|
title: '事项名称',
|
||||||
key: 'taskName',
|
key: 'taskName',
|
||||||
type: JVxeTypes.input,
|
type: JVxeTypes.input,
|
||||||
width: '200px',
|
width: '200px',
|
||||||
@@ -101,7 +101,7 @@ export const taskListDetialColumns: JVxeColumn[] = [
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '任务描述',
|
title: '事项描述',
|
||||||
key: 'taskDesc',
|
key: 'taskDesc',
|
||||||
type: JVxeTypes.input,
|
type: JVxeTypes.input,
|
||||||
width: '200px',
|
width: '200px',
|
||||||
@@ -183,7 +183,7 @@ export const taskListDetialColumns: JVxeColumn[] = [
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '子任务数',
|
title: '子事项数',
|
||||||
key: 'subTaskCount',
|
key: 'subTaskCount',
|
||||||
type: JVxeTypes.inputNumber,
|
type: JVxeTypes.inputNumber,
|
||||||
width: '200px',
|
width: '200px',
|
||||||
@@ -191,7 +191,7 @@ export const taskListDetialColumns: JVxeColumn[] = [
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '子任务完成数',
|
title: '子事项完成数',
|
||||||
key: 'completedSubTaskCount',
|
key: 'completedSubTaskCount',
|
||||||
type: JVxeTypes.inputNumber,
|
type: JVxeTypes.inputNumber,
|
||||||
width: '200px',
|
width: '200px',
|
||||||
@@ -201,21 +201,21 @@ export const taskListDetialColumns: JVxeColumn[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const superQuerySchema = {
|
export const superQuerySchema = {
|
||||||
tasklistName: { title: '任务清单名称', order: 0, view: 'text', type: 'string' },
|
tasklistName: { title: '计划名称', order: 0, view: 'text', type: 'string' },
|
||||||
pid: { title: '父级节点', order: 1, view: 'text', type: 'string' },
|
pid: { title: '父级节点', order: 1, view: 'text', type: 'string' },
|
||||||
hasChild: { title: '是否有子节点', order: 2, view: 'text', type: 'string' },
|
hasChild: { title: '是否有子节点', order: 2, view: 'text', type: 'string' },
|
||||||
sortOrder: { title: '排序号', order: 3, view: 'number', type: 'number' },
|
sortOrder: { title: '排序号', order: 3, view: 'number', type: 'number' },
|
||||||
type: { title: '类型', order: 4, view: 'text', type: 'string' },
|
type: { title: '类型', order: 4, view: 'text', type: 'string' },
|
||||||
taskListDetial: {
|
taskListDetial: {
|
||||||
title: '工作任务清单详情',
|
title: '计划详情',
|
||||||
view: 'table',
|
view: 'table',
|
||||||
fields: {
|
fields: {
|
||||||
mainId: { title: '外键', order: 0, view: 'text', type: 'string' },
|
mainId: { title: '外键', order: 0, view: 'text', type: 'string' },
|
||||||
pid: { title: '父节点ID', order: 1, view: 'text', type: 'string' },
|
pid: { title: '父节点ID', order: 1, view: 'text', type: 'string' },
|
||||||
hasChild: { title: '是否有子节点', order: 2, view: 'text', type: 'string' },
|
hasChild: { title: '是否有子节点', order: 2, view: 'text', type: 'string' },
|
||||||
sortOrder: { title: '排序号', order: 3, view: 'number', type: 'number' },
|
sortOrder: { title: '排序号', order: 3, view: 'number', type: 'number' },
|
||||||
taskName: { title: '任务名称', order: 4, view: 'text', type: 'string' },
|
taskName: { title: '事项名称', order: 4, view: 'text', type: 'string' },
|
||||||
taskDesc: { title: '任务描述', order: 5, view: 'text', type: 'string' },
|
taskDesc: { title: '事项描述', order: 5, view: 'text', type: 'string' },
|
||||||
priority: { title: '优先级', order: 6, view: 'list', type: 'string', dictCode: 'task_priority' },
|
priority: { title: '优先级', order: 6, view: 'list', type: 'string', dictCode: 'task_priority' },
|
||||||
taskStatus: { title: '完成状态', order: 7, view: 'number', type: 'number' },
|
taskStatus: { title: '完成状态', order: 7, view: 'number', type: 'number' },
|
||||||
assigneeId: { title: '负责人ID', order: 8, view: 'text', type: 'string' },
|
assigneeId: { title: '负责人ID', order: 8, view: 'text', type: 'string' },
|
||||||
@@ -225,8 +225,8 @@ export const superQuerySchema = {
|
|||||||
endTime: { title: '结束时间', order: 12, view: 'datetime', type: 'string' },
|
endTime: { title: '结束时间', order: 12, view: 'datetime', type: 'string' },
|
||||||
completeTime: { title: '完成时间', order: 13, view: 'datetime', type: 'string' },
|
completeTime: { title: '完成时间', order: 13, view: 'datetime', type: 'string' },
|
||||||
type: { title: '类型', order: 14, view: 'text', type: 'string' },
|
type: { title: '类型', order: 14, view: 'text', type: 'string' },
|
||||||
subTaskCount: { title: '子任务数', order: 15, view: 'number', type: 'number' },
|
subTaskCount: { title: '子事项数', order: 15, view: 'number', type: 'number' },
|
||||||
completedSubTaskCount: { title: '子任务完成数', order: 16, view: 'number', type: 'number' },
|
completedSubTaskCount: { title: '子事项完成数', order: 16, view: 'number', type: 'number' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -237,13 +237,13 @@ export function getBpmFormSchema(_formData): FormSchema[] {
|
|||||||
|
|
||||||
export const createTaskFormSchema: FormSchema[] = [
|
export const createTaskFormSchema: FormSchema[] = [
|
||||||
{
|
{
|
||||||
label: '任务标题',
|
label: '事项标题',
|
||||||
field: 'taskName',
|
field: 'taskName',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '任务描述',
|
label: '事项描述',
|
||||||
field: 'taskDesc',
|
field: 'taskDesc',
|
||||||
component: 'InputTextArea',
|
component: 'InputTextArea',
|
||||||
componentProps: { rows: 3 },
|
componentProps: { rows: 3 },
|
||||||
@@ -305,12 +305,12 @@ export function getCreateListFormSchema(userSecurityLevel: number): FormSchema[]
|
|||||||
if (!userSecurityLevel) userSecurityLevel = 3;
|
if (!userSecurityLevel) userSecurityLevel = 3;
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: '清单名称',
|
label: '计划名称',
|
||||||
field: 'tasklistName',
|
field: 'tasklistName',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
required: true,
|
required: true,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
placeholder: '请输入清单名称',
|
placeholder: '请输入计划名称',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -331,7 +331,7 @@ export function getCreateListFormSchema(userSecurityLevel: number): FormSchema[]
|
|||||||
export const mockTaskListGroups: TaskListGroup[] = [
|
export const mockTaskListGroups: TaskListGroup[] = [
|
||||||
{
|
{
|
||||||
id: 'tlg-1',
|
id: 'tlg-1',
|
||||||
name: '任务清单',
|
name: '计划管理',
|
||||||
pid: '0',
|
pid: '0',
|
||||||
hasChild: '1',
|
hasChild: '1',
|
||||||
type: 0,
|
type: 0,
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed } from 'vue';
|
import { ref, reactive, computed, nextTick } from 'vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import SzUserSelect from '/@/components/semri/userComponent/SzUserSelect.vue';
|
import SzUserSelect from '/@/components/semri/userComponent/SzUserSelect.vue';
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
changed: [];
|
changed: [collaboratorNames: string];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
@@ -147,6 +147,8 @@
|
|||||||
permission: '2',
|
permission: '2',
|
||||||
});
|
});
|
||||||
const pendingAddList = ref<PendingAddItem[]>([]);
|
const pendingAddList = ref<PendingAddItem[]>([]);
|
||||||
|
const pendingRemoveIds = ref<Set<string>>(new Set());
|
||||||
|
const pendingPermissionMap = ref<Map<string, string>>(new Map());
|
||||||
|
|
||||||
const allMembers = computed(() => {
|
const allMembers = computed(() => {
|
||||||
const pendingAsCollab: CollaboratorItem[] = pendingAddList.value.map((p) => ({
|
const pendingAsCollab: CollaboratorItem[] = pendingAddList.value.map((p) => ({
|
||||||
@@ -155,7 +157,22 @@
|
|||||||
username: p.username,
|
username: p.username,
|
||||||
permission: p.permission,
|
permission: p.permission,
|
||||||
}));
|
}));
|
||||||
return [...collaborators.value, ...pendingAsCollab];
|
return [...collaborators.value, ...pendingAsCollab]
|
||||||
|
.filter((m) => (m.permission !== '1' ? !pendingRemoveIds.value.has(m.permissionId) : true))
|
||||||
|
.map((m) => {
|
||||||
|
if (m.permissionId && pendingPermissionMap.value.has(m.permissionId)) {
|
||||||
|
return { ...m, permission: pendingPermissionMap.value.get(m.permissionId)! };
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const collaboratorNamesSnapshot = computed(() => {
|
||||||
|
return allMembers.value
|
||||||
|
.filter((m) => m.permission !== '1')
|
||||||
|
.map((m) => m.username)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(',');
|
||||||
});
|
});
|
||||||
|
|
||||||
function getColor(name: string): string {
|
function getColor(name: string): string {
|
||||||
@@ -163,11 +180,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function open() {
|
async function open() {
|
||||||
visible.value = true;
|
collaborators.value = [];
|
||||||
showAddPanel.value = false;
|
showAddPanel.value = false;
|
||||||
pendingAddList.value = [];
|
pendingAddList.value = [];
|
||||||
|
pendingRemoveIds.value.clear();
|
||||||
|
pendingPermissionMap.value.clear();
|
||||||
addForm.userIds = '';
|
addForm.userIds = '';
|
||||||
addForm.permission = '2';
|
addForm.permission = '2';
|
||||||
|
visible.value = true;
|
||||||
|
await nextTick();
|
||||||
await loadCollaborators();
|
await loadCollaborators();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,11 +198,14 @@
|
|||||||
addForm.userIds = '';
|
addForm.userIds = '';
|
||||||
addForm.permission = '2';
|
addForm.permission = '2';
|
||||||
pendingAddList.value = [];
|
pendingAddList.value = [];
|
||||||
|
pendingRemoveIds.value.clear();
|
||||||
|
pendingPermissionMap.value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadCollaborators() {
|
async function loadCollaborators() {
|
||||||
try {
|
try {
|
||||||
const data: any[] = await getCollaborators({ taskListId: props.taskListId });
|
const options = { successMessageMode: 'none' as const };
|
||||||
|
const data: any[] = await getCollaborators({ taskListId: props.taskListId }, options);
|
||||||
collaborators.value = data;
|
collaborators.value = data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[CollaboratorModal] load failed', e);
|
console.error('[CollaboratorModal] load failed', e);
|
||||||
@@ -196,7 +220,7 @@
|
|||||||
const secretLevel = props.secretLevel || 1;
|
const secretLevel = props.secretLevel || 1;
|
||||||
const { ok, invalidNames } = await checkUserSecLevel(addForm.userIds, secretLevel);
|
const { ok, invalidNames } = await checkUserSecLevel(addForm.userIds, secretLevel);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
message.warning(`以下人员的密级不满足当前清单(${props.secretText || '非密'})要求:${invalidNames.join('、')}`);
|
message.warning(`以下人员的密级不满足当前计划(${props.secretText || '非密'})要求:${invalidNames.join('、')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +240,6 @@
|
|||||||
const existingIds = new Set(collaborators.value.map((c) => c.userId));
|
const existingIds = new Set(collaborators.value.map((c) => c.userId));
|
||||||
const pendingIds = new Set(pendingAddList.value.map((p) => p.userId));
|
const pendingIds = new Set(pendingAddList.value.map((p) => p.userId));
|
||||||
|
|
||||||
let addedCount = 0;
|
|
||||||
for (const uid of ids) {
|
for (const uid of ids) {
|
||||||
if (!existingIds.has(uid) && !pendingIds.has(uid)) {
|
if (!existingIds.has(uid) && !pendingIds.has(uid)) {
|
||||||
pendingAddList.value.push({
|
pendingAddList.value.push({
|
||||||
@@ -224,74 +247,84 @@
|
|||||||
username: nameMap[uid] || uid,
|
username: nameMap[uid] || uid,
|
||||||
permission: addForm.permission,
|
permission: addForm.permission,
|
||||||
});
|
});
|
||||||
addedCount++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addedCount > 0) {
|
|
||||||
createMessage.success(`已添加 ${addedCount} 位协作人`);
|
|
||||||
} else {
|
|
||||||
createMessage.warning('所选用户均已是协作人');
|
|
||||||
}
|
|
||||||
|
|
||||||
addForm.userIds = '';
|
addForm.userIds = '';
|
||||||
addForm.permission = '2';
|
addForm.permission = '2';
|
||||||
showAddPanel.value = false;
|
showAddPanel.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onDone() {
|
function onDone() {
|
||||||
if (pendingAddList.value.length > 0) {
|
const hasChanges =
|
||||||
let successCount = 0;
|
pendingAddList.value.length > 0 || pendingRemoveIds.value.size > 0 || pendingPermissionMap.value.size > 0;
|
||||||
let failCount = 0;
|
|
||||||
for (const item of pendingAddList.value) {
|
|
||||||
try {
|
|
||||||
await addCollaborator({
|
|
||||||
taskListId: props.taskListId,
|
|
||||||
userId: item.userId,
|
|
||||||
permission: item.permission,
|
|
||||||
});
|
|
||||||
successCount++;
|
|
||||||
} catch {
|
|
||||||
failCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (successCount > 0) {
|
const itemsToAdd = [...pendingAddList.value];
|
||||||
createMessage.success(`成功添加 ${successCount} 位协作人${failCount > 0 ? `,${failCount} 位失败` : ''}`);
|
const idsToRemove = [...pendingRemoveIds.value];
|
||||||
} else {
|
const permsToChange = [...pendingPermissionMap.value];
|
||||||
createMessage.warning('添加协作人失败');
|
const listId = props.taskListId;
|
||||||
}
|
|
||||||
|
|
||||||
pendingAddList.value = [];
|
pendingAddList.value = [];
|
||||||
emit('changed');
|
pendingRemoveIds.value.clear();
|
||||||
}
|
pendingPermissionMap.value.clear();
|
||||||
|
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
showAddPanel.value = false;
|
showAddPanel.value = false;
|
||||||
addForm.userIds = '';
|
addForm.userIds = '';
|
||||||
addForm.permission = '2';
|
addForm.permission = '2';
|
||||||
}
|
|
||||||
|
|
||||||
async function onRemove(permissionId: string) {
|
if (!hasChanges) return;
|
||||||
|
|
||||||
|
const suppressOptions = { successMessageMode: 'none' as const };
|
||||||
|
let errorMsg = '';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
for (const item of itemsToAdd) {
|
||||||
try {
|
try {
|
||||||
await removeCollaborator({ permissionId });
|
await addCollaborator(
|
||||||
await loadCollaborators();
|
{ taskListId: listId, userId: item.userId, permission: item.permission },
|
||||||
emit('changed');
|
suppressOptions,
|
||||||
} catch (e: any) {
|
);
|
||||||
createMessage.error(e?.message || '移除失败');
|
} catch {
|
||||||
|
errorMsg = '部分协作人添加失败';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPermissionChange(permissionId: string, newPermission: string) {
|
for (const permissionId of idsToRemove) {
|
||||||
try {
|
try {
|
||||||
await updateCollaboratorPermission({ permissionId, permission: newPermission });
|
await removeCollaborator({ permissionId }, suppressOptions);
|
||||||
await loadCollaborators();
|
} catch {
|
||||||
emit('changed');
|
errorMsg = '部分协作人移除失败';
|
||||||
} catch (e: any) {
|
|
||||||
createMessage.error(e?.message || '修改失败');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const [permissionId, newPermission] of permsToChange) {
|
||||||
|
try {
|
||||||
|
await updateCollaboratorPermission({ permissionId, permission: newPermission }, suppressOptions);
|
||||||
|
} catch {
|
||||||
|
errorMsg = '部分权限修改失败';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadCollaborators();
|
||||||
|
emit('changed', collaboratorNamesSnapshot.value);
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
createMessage.warning(errorMsg);
|
||||||
|
} else {
|
||||||
|
createMessage.success('编辑成功');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRemove(permissionId: string) {
|
||||||
|
pendingRemoveIds.value.add(permissionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPermissionChange(permissionId: string, newPermission: string) {
|
||||||
|
pendingPermissionMap.value.set(permissionId, newPermission);
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ open });
|
defineExpose({ open });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicModal v-bind="$attrs" @register="registerModal" destroy-on-close title="创建清单" :width="500" @ok="handleSubmit">
|
<BasicModal v-bind="$attrs" @register="registerModal" destroy-on-close title="创建计划" :width="500" @ok="handleSubmit">
|
||||||
<BasicForm @register="registerForm" />
|
<BasicForm @register="registerForm" />
|
||||||
</BasicModal>
|
</BasicModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicModal v-bind="$attrs" @register="registerModal" destroy-on-close title="创建任务" :width="600" @ok="handleSubmit">
|
<BasicModal v-bind="$attrs" @register="registerModal" destroy-on-close title="创建事项" :width="600" @ok="handleSubmit">
|
||||||
<BasicForm @register="registerForm" />
|
<BasicForm @register="registerForm" />
|
||||||
<div class="attachment-row">
|
<div class="attachment-row">
|
||||||
<span class="attachment-label">附件</span>
|
<span class="attachment-label">附件</span>
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
if (values.assigneeId) {
|
if (values.assigneeId) {
|
||||||
const { ok, invalidNames } = await checkUserSecLevel(values.assigneeId, currentSecretLevel.value);
|
const { ok, invalidNames } = await checkUserSecLevel(values.assigneeId, currentSecretLevel.value);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
message.warning(`以下人员的密级不满足当前清单(${currentSecretText.value})要求:${invalidNames.join('、')}`);
|
message.warning(`以下人员的密级不满足当前计划(${currentSecretText.value})要求:${invalidNames.join('、')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="task-content">
|
<div class="task-content">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<template v-if="showListGroupView">
|
<template v-if="showListGroupView">
|
||||||
<h2 class="content-title">任务清单</h2>
|
<h2 class="content-title">计划列表</h2>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="content-header-left">
|
<div class="content-header-left">
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
@blur="confirmHeaderRename"
|
@blur="confirmHeaderRename"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h2 v-else class="content-title">{{ currentList?.name || '请选择清单' }}</h2>
|
<h2 v-else class="content-title">{{ currentList?.name || '请选择计划' }}</h2>
|
||||||
<a-dropdown v-if="currentList && canShowHeaderMore" :trigger="['click']">
|
<a-dropdown v-if="currentList && canShowHeaderMore" :trigger="['click']">
|
||||||
<a-button type="text" size="small" class="header-more-btn">
|
<a-button type="text" size="small" class="header-more-btn">
|
||||||
<Icon icon="ant-design:ellipsis-outlined" />
|
<Icon icon="ant-design:ellipsis-outlined" />
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<span v-if="currentList.secretText" class="header-secret-badge" :style="{ color: SECRET_LEVEL_COLOR[currentList.secretLevel || 1] || '#8c8c8c', borderColor: SECRET_LEVEL_COLOR[currentList.secretLevel || 1] || '#8c8c8c' }">
|
<span v-if="currentList.secretText" class="header-secret-badge" :style="{ color: SECRET_LEVEL_COLOR[currentList.secretLevel || 1] || '#8c8c8c', borderColor: SECRET_LEVEL_COLOR[currentList.secretLevel || 1] || '#8c8c8c' }">
|
||||||
{{ currentList.secretText }}
|
{{ currentList.secretText }}
|
||||||
</span>
|
</span>
|
||||||
<div class="collaborator-bar" @click="emit('open-collaborator')">
|
<div class="collaborator-bar" :class="{ disabled: !_isOwner }" @click="_isOwner && emit('open-collaborator')">
|
||||||
<AvatarDisplay
|
<AvatarDisplay
|
||||||
v-if="collaboratorDisplayNames"
|
v-if="collaboratorDisplayNames"
|
||||||
:names="collaboratorDisplayNames"
|
:names="collaboratorDisplayNames"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu @click="onStatusFilterChange">
|
<a-menu @click="onStatusFilterChange">
|
||||||
<a-menu-item key="all" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'all' }">全部清单</a-menu-item>
|
<a-menu-item key="all" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'all' }">全部</a-menu-item>
|
||||||
<a-menu-item key="completed" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'completed' }">已完成</a-menu-item>
|
<a-menu-item key="completed" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'completed' }">已完成</a-menu-item>
|
||||||
<a-menu-item key="uncompleted" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'uncompleted' }">未完成</a-menu-item>
|
<a-menu-item key="uncompleted" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'uncompleted' }">未完成</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@@ -269,13 +269,13 @@
|
|||||||
</template>
|
</template>
|
||||||
<span v-else class="field-placeholder">-</span>
|
<span v-else class="field-placeholder">-</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group-col list-group-col-collaborators">
|
<div class="list-group-col list-group-col-collaborators clickable" @click.stop="emit('open-collaborator-for-list', list.id)">
|
||||||
<template v-if="getAllCollaborators(list)">
|
<template v-if="getAllCollaborators(list)">
|
||||||
<a-tooltip :title="getAllCollaborators(list)" placement="top"
|
<a-tooltip title="点击管理协作者" placement="top">
|
||||||
><AvatarDisplay :names="getAllCollaborators(list)" :size="24" :show-name="false"
|
<AvatarDisplay :names="getAllCollaborators(list)" :size="24" :show-name="getCollaboratorCount(list) === 1" />
|
||||||
/></a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="field-placeholder">-</span>
|
<Icon v-else icon="tasklist-add-user|svg" class="collab-add-icon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group-col list-group-col-secret">
|
<div class="list-group-col list-group-col-secret">
|
||||||
<span
|
<span
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="allTaskLists.length === 0" class="empty-state">
|
<div v-if="allTaskLists.length === 0" class="empty-state">
|
||||||
<Icon icon="ant-design:inbox-outlined" class="empty-icon" />
|
<Icon icon="ant-design:inbox-outlined" class="empty-icon" />
|
||||||
<p>暂无任务清单</p>
|
<p>暂无可见计划</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -303,7 +303,7 @@
|
|||||||
<div class="table-col table-col-drag-handle"></div>
|
<div class="table-col table-col-drag-handle"></div>
|
||||||
<div class="table-col table-col-title">
|
<div class="table-col table-col-title">
|
||||||
<Icon icon="tasklist-title|svg" class="header-icon" />
|
<Icon icon="tasklist-title|svg" class="header-icon" />
|
||||||
<span class="header-label">任务标题</span>
|
<span class="header-label">事项标题</span>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="field in visibleFieldConfigs" :key="field.key">
|
<template v-for="field in visibleFieldConfigs" :key="field.key">
|
||||||
<div class="table-col header-col" :style="{ width: fieldWidthMap[field.key] || '100px' }">
|
<div class="table-col header-col" :style="{ width: fieldWidthMap[field.key] || '100px' }">
|
||||||
@@ -387,7 +387,7 @@
|
|||||||
<span
|
<span
|
||||||
v-if="canEdit && groupDimension === 'custom'"
|
v-if="canEdit && groupDimension === 'custom'"
|
||||||
class="group-action-btn group-action-add"
|
class="group-action-btn group-action-add"
|
||||||
title="新建任务"
|
title="新建事项"
|
||||||
@click.stop="showNewTaskInput = group.id"
|
@click.stop="showNewTaskInput = group.id"
|
||||||
>
|
>
|
||||||
<Icon icon="ant-design:plus-outlined" />
|
<Icon icon="ant-design:plus-outlined" />
|
||||||
@@ -744,7 +744,7 @@
|
|||||||
ref="newTaskInputRef"
|
ref="newTaskInputRef"
|
||||||
v-model:value="newTaskTitle"
|
v-model:value="newTaskTitle"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="输入任务标题,按回车确认..."
|
placeholder="输入事项标题,按回车确认..."
|
||||||
autofocus
|
autofocus
|
||||||
@press-enter="submitNewTask(group.id)"
|
@press-enter="submitNewTask(group.id)"
|
||||||
@blur="submitNewTask(group.id)"
|
@blur="submitNewTask(group.id)"
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
<span class="expand-arrow-placeholder"></span>
|
<span class="expand-arrow-placeholder"></span>
|
||||||
</div>
|
</div>
|
||||||
<Icon icon="ant-design:plus-outlined" class="new-task-plus" />
|
<Icon icon="ant-design:plus-outlined" class="new-task-plus" />
|
||||||
<span class="new-task-text">新建任务</span>
|
<span class="new-task-text">新建事项</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -793,7 +793,7 @@
|
|||||||
|
|
||||||
<div v-else class="empty-state">
|
<div v-else class="empty-state">
|
||||||
<Icon icon="ant-design:inbox-outlined" class="empty-icon" />
|
<Icon icon="ant-design:inbox-outlined" class="empty-icon" />
|
||||||
<p>请从左侧选择一个任务清单</p>
|
<p>请从左侧选择一个计划</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -864,6 +864,7 @@
|
|||||||
'update:list-group-tab': [value: string];
|
'update:list-group-tab': [value: string];
|
||||||
'list-select': [listId: string];
|
'list-select': [listId: string];
|
||||||
'open-collaborator': [];
|
'open-collaborator': [];
|
||||||
|
'open-collaborator-for-list': [listId: string];
|
||||||
'add-to-favorites': [taskListId: string];
|
'add-to-favorites': [taskListId: string];
|
||||||
'remove-from-favorites': [favoriteId: string];
|
'remove-from-favorites': [favoriteId: string];
|
||||||
'move-task': [taskId: string, targetPid: string, targetSortOrder: number];
|
'move-task': [taskId: string, targetPid: string, targetSortOrder: number];
|
||||||
@@ -920,7 +921,7 @@
|
|||||||
|
|
||||||
function showDragBlockedMessage() {
|
function showDragBlockedMessage() {
|
||||||
import('ant-design-vue').then(({ message }) => {
|
import('ant-design-vue').then(({ message }) => {
|
||||||
message.warning('子任务只能在当前父任务下移动');
|
message.warning('子事项只能在当前父事项下移动');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -981,6 +982,12 @@
|
|||||||
return names.join(',');
|
return names.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCollaboratorCount(list: TaskList): number {
|
||||||
|
const names = getAllCollaborators(list);
|
||||||
|
if (!names) return 0;
|
||||||
|
return names.split(/[,,、]/).filter(Boolean).length;
|
||||||
|
}
|
||||||
|
|
||||||
function getFormattedCreateTime(list: TaskList): string {
|
function getFormattedCreateTime(list: TaskList): string {
|
||||||
if (list.createTimeStr) {
|
if (list.createTimeStr) {
|
||||||
return list.createTimeStr;
|
return list.createTimeStr;
|
||||||
@@ -1137,6 +1144,10 @@
|
|||||||
const canEdit = computed(() => props.currentList?.myPermission === '1' || props.currentList?.myPermission === '2');
|
const canEdit = computed(() => props.currentList?.myPermission === '1' || props.currentList?.myPermission === '2');
|
||||||
const isReadOnly = computed(() => props.currentList?.myPermission === '3');
|
const isReadOnly = computed(() => props.currentList?.myPermission === '3');
|
||||||
|
|
||||||
|
function isListOwner(list: TaskList): boolean {
|
||||||
|
return list.myPermission === '1';
|
||||||
|
}
|
||||||
|
|
||||||
const canShowHeaderMore = computed(() => {
|
const canShowHeaderMore = computed(() => {
|
||||||
if (!props.currentList) return false;
|
if (!props.currentList) return false;
|
||||||
const perm = props.currentList.myPermission;
|
const perm = props.currentList.myPermission;
|
||||||
@@ -1162,7 +1173,7 @@
|
|||||||
createConfirm({
|
createConfirm({
|
||||||
iconType: 'warning',
|
iconType: 'warning',
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: '删除任务清单后,其下所有任务也将被删除,确定要删除吗?',
|
content: '删除计划后,其下所有事项也将被删除,确定要删除吗?',
|
||||||
okText: '确认',
|
okText: '确认',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
@@ -1238,7 +1249,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const statusFilterLabel = computed(() => {
|
const statusFilterLabel = computed(() => {
|
||||||
const map: Record<StatusFilter, string> = { all: '全部清单', completed: '已完成', uncompleted: '未完成' };
|
const map: Record<StatusFilter, string> = { all: '全部', completed: '已完成', uncompleted: '未完成' };
|
||||||
return map[props.statusFilter];
|
return map[props.statusFilter];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1780,7 +1791,7 @@
|
|||||||
createConfirm({
|
createConfirm({
|
||||||
iconType: 'warning',
|
iconType: 'warning',
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: '删除分组后,分组内的任务将合并到默认分组,确定要删除吗?',
|
content: '删除分组后,分组内的事项将合并到默认分组,确定要删除吗?',
|
||||||
okText: '确认',
|
okText: '确认',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
@@ -1889,6 +1900,16 @@
|
|||||||
border-color: #d0d0d0;
|
border-color: #d0d0d0;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #e8e8e8;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.collab-btn {
|
.collab-btn {
|
||||||
@@ -2676,6 +2697,29 @@
|
|||||||
.list-group-col-collaborators {
|
.list-group-col-collaborators {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f5ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.collab-add-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #bfbfbf;
|
||||||
|
transition: color 0.15s;
|
||||||
|
|
||||||
|
.clickable:hover & {
|
||||||
|
color: #3370ff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group-col-secret {
|
.list-group-col-secret {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<div class="detail-toolbar">
|
<div class="detail-toolbar">
|
||||||
<a-button size="small" :type="taskData.completed ? 'primary' : 'default'" :disabled="isFullReadOnly" @click="toggleComplete">
|
<a-button size="small" :type="taskData.completed ? 'primary' : 'default'" :disabled="isFullReadOnly" @click="toggleComplete">
|
||||||
<Icon icon="ant-design:check-outlined" class="mr-1" />
|
<Icon icon="ant-design:check-outlined" class="mr-1" />
|
||||||
{{ taskData.completed ? '已完成' : '完成任务' }}
|
{{ taskData.completed ? '已完成' : '完成事项' }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<div class="toolbar-spacer"></div>
|
<div class="toolbar-spacer"></div>
|
||||||
<div class="toolbar-actions">
|
<div class="toolbar-actions">
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
<div v-if="parentTask && parentTask.completed" class="detail-parent-completed-notice">
|
<div v-if="parentTask && parentTask.completed" class="detail-parent-completed-notice">
|
||||||
<Icon icon="ant-design:info-circle-outlined" />
|
<Icon icon="ant-design:info-circle-outlined" />
|
||||||
<span>该任务的上级任务已被完成</span>
|
<span>该事项的上级事项已被完成</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-parent-nav" v-if="taskPath.length > 1">
|
<div class="detail-parent-nav" v-if="taskPath.length > 1">
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
size="large"
|
size="large"
|
||||||
:bordered="true"
|
:bordered="true"
|
||||||
class="detail-title-input"
|
class="detail-title-input"
|
||||||
placeholder="请输入任务标题"
|
placeholder="请输入事项标题"
|
||||||
@blur="onTitleBlur"
|
@blur="onTitleBlur"
|
||||||
@press-enter="editingTitle = false"
|
@press-enter="editingTitle = false"
|
||||||
/>
|
/>
|
||||||
@@ -254,12 +254,12 @@
|
|||||||
<span class="feishu-subtask-icon">
|
<span class="feishu-subtask-icon">
|
||||||
<Icon icon="tasklist-subtask|svg" />
|
<Icon icon="tasklist-subtask|svg" />
|
||||||
</span>
|
</span>
|
||||||
<span class="feishu-subtask-title">子任务</span>
|
<span class="feishu-subtask-title">子事项</span>
|
||||||
<span v-if="subtaskProgress.text" class="feishu-subtask-count">{{ subtaskProgress.text }}</span>
|
<span v-if="subtaskProgress.text" class="feishu-subtask-count">{{ subtaskProgress.text }}</span>
|
||||||
<div v-if="subtaskProgress.text" class="feishu-subtask-progress">
|
<div v-if="subtaskProgress.text" class="feishu-subtask-progress">
|
||||||
<div class="feishu-subtask-progress-bar" :style="{ width: subtaskProgress.percent + '%' }"></div>
|
<div class="feishu-subtask-progress-bar" :style="{ width: subtaskProgress.percent + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="!canAddSubTask" class="feishu-subtask-limit">当前最多创建{{ MAX_TASK_DEPTH }}层任务</span>
|
<span v-if="!canAddSubTask" class="feishu-subtask-limit">当前最多创建{{ MAX_TASK_DEPTH }}层事项</span>
|
||||||
<Icon v-if="canAddSubTask" :icon="subtaskExpanded ? 'ant-design:down-outlined' : 'ant-design:right-outlined'" class="feishu-subtask-arrow" />
|
<Icon v-if="canAddSubTask" :icon="subtaskExpanded ? 'ant-design:down-outlined' : 'ant-design:right-outlined'" class="feishu-subtask-arrow" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@
|
|||||||
<a-input
|
<a-input
|
||||||
v-model:value="newSubTaskName"
|
v-model:value="newSubTaskName"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="输入子任务标题,按回车确认..."
|
placeholder="输入子事项标题,按回车确认..."
|
||||||
autofocus
|
autofocus
|
||||||
class="feishu-subtask-input"
|
class="feishu-subtask-input"
|
||||||
@press-enter="handleSubmitSubTask"
|
@press-enter="handleSubmitSubTask"
|
||||||
@@ -334,7 +334,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="canAddSubTask && !isFullReadOnly" class="feishu-subtask-add" @click="showNewSubTaskInput = true">
|
<div v-else-if="canAddSubTask && !isFullReadOnly" class="feishu-subtask-add" @click="showNewSubTaskInput = true">
|
||||||
<Icon icon="ant-design:plus-outlined" class="feishu-subtask-add-icon" />
|
<Icon icon="ant-design:plus-outlined" class="feishu-subtask-add-icon" />
|
||||||
<span>添加子任务</span>
|
<span>添加子事项</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -628,7 +628,7 @@
|
|||||||
createConfirm({
|
createConfirm({
|
||||||
iconType: 'warning',
|
iconType: 'warning',
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: '删除后无法恢复,确定要删除这个任务吗?',
|
content: '删除后无法恢复,确定要删除这个事项吗?',
|
||||||
okText: '确认',
|
okText: '确认',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu @click="onStatusFilterChange">
|
<a-menu @click="onStatusFilterChange">
|
||||||
<a-menu-item key="all" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'all' }">全部任务</a-menu-item>
|
<a-menu-item key="all" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'all' }">全部事项</a-menu-item>
|
||||||
<a-menu-item key="completed" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'completed' }">已完成</a-menu-item>
|
<a-menu-item key="completed" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'completed' }">已完成</a-menu-item>
|
||||||
<a-menu-item key="uncompleted" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'uncompleted' }">未完成</a-menu-item>
|
<a-menu-item key="uncompleted" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'uncompleted' }">未完成</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<div class="table-col table-col-drag-handle"></div>
|
<div class="table-col table-col-drag-handle"></div>
|
||||||
<div class="table-col table-col-title">
|
<div class="table-col table-col-title">
|
||||||
<Icon icon="tasklist-title|svg" class="header-icon" />
|
<Icon icon="tasklist-title|svg" class="header-icon" />
|
||||||
<span class="header-label">任务标题</span>
|
<span class="header-label">事项标题</span>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="field in visibleFieldConfigs" :key="field.key">
|
<template v-for="field in visibleFieldConfigs" :key="field.key">
|
||||||
<div class="table-col header-col" :style="{ width: fieldWidthMap[field.key] || '100px' }">
|
<div class="table-col header-col" :style="{ width: fieldWidthMap[field.key] || '100px' }">
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
|
|
||||||
<div v-if="flattenedTasks.length === 0" class="empty-state">
|
<div v-if="flattenedTasks.length === 0" class="empty-state">
|
||||||
<Icon icon="ant-design:inbox-outlined" class="empty-icon" />
|
<Icon icon="ant-design:inbox-outlined" class="empty-icon" />
|
||||||
<p>暂无任务</p>
|
<p>暂无事项</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -436,7 +436,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const statusFilterLabel = computed(() => {
|
const statusFilterLabel = computed(() => {
|
||||||
const map: Record<StatusFilter, string> = { all: '全部任务', completed: '已完成', uncompleted: '未完成' };
|
const map: Record<StatusFilter, string> = { all: '全部事项', completed: '已完成', uncompleted: '未完成' };
|
||||||
return map[props.statusFilter];
|
return map[props.statusFilter];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<BasicForm @register="registerForm" ref="formRef" />
|
<BasicForm @register="registerForm" ref="formRef" />
|
||||||
<!-- 子表单区域 -->
|
<!-- 子表单区域 -->
|
||||||
<a-tabs v-model:activeKey="activeKey" animated @change="handleChangeTabs">
|
<a-tabs v-model:activeKey="activeKey" animated @change="handleChangeTabs">
|
||||||
<a-tab-pane tab="工作任务清单详情" key="taskListDetial" :forceRender="true">
|
<a-tab-pane tab="计划详情" key="taskListDetial" :forceRender="true">
|
||||||
<JVxeTable
|
<JVxeTable
|
||||||
keep-source
|
keep-source
|
||||||
resizable
|
resizable
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<BasicForm @register="registerForm" ref="formRef" name="TaskListForm" />
|
<BasicForm @register="registerForm" ref="formRef" name="TaskListForm" />
|
||||||
<!-- 子表单区域 -->
|
<!-- 子表单区域 -->
|
||||||
<a-tabs v-model:activeKey="activeKey" animated @change="handleChangeTabs">
|
<a-tabs v-model:activeKey="activeKey" animated @change="handleChangeTabs">
|
||||||
<a-tab-pane tab="工作任务清单详情" key="taskListDetial" :forceRender="true">
|
<a-tab-pane tab="计划详情" key="taskListDetial" :forceRender="true">
|
||||||
<JVxeTable
|
<JVxeTable
|
||||||
keep-source
|
keep-source
|
||||||
resizable
|
resizable
|
||||||
|
|||||||
@@ -1,33 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="task-sidebar">
|
<div class="task-sidebar">
|
||||||
<div class="sidebar-header">
|
|
||||||
<span class="sidebar-title">
|
|
||||||
<Icon icon="ant-design:menu-outlined" class="mr-1" />
|
|
||||||
任务
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sidebar-nav">
|
|
||||||
<div
|
|
||||||
v-for="item in NAV_ITEMS"
|
|
||||||
:key="item.id"
|
|
||||||
class="sidebar-item"
|
|
||||||
:class="{ active: currentView === item.id }"
|
|
||||||
@click="emit('view-select', item.id)"
|
|
||||||
>
|
|
||||||
<Icon :icon="item.icon" class="sidebar-item-icon" />
|
|
||||||
<span>{{ item.label }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sidebar-divider"></div>
|
|
||||||
|
|
||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
<div class="sidebar-quick-header" @click="quickAccessExpanded = !quickAccessExpanded">
|
<div class="sidebar-quick-header" @click="planExpanded = !planExpanded">
|
||||||
<Icon icon="tasklist-arrow-down|svg" class="quick-access-arrow" :class="{ expanded: quickAccessExpanded }" />
|
<Icon icon="tasklist-arrow-down|svg" class="quick-access-arrow" :class="{ expanded: planExpanded }" />
|
||||||
<span class="sidebar-quick-title">快速访问</span>
|
<span class="sidebar-quick-title">计划总览</span>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="quickAccessExpanded">
|
<template v-if="planExpanded">
|
||||||
<div
|
<div
|
||||||
v-for="item in QUICK_ACCESS_ITEMS"
|
v-for="item in QUICK_ACCESS_ITEMS"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@@ -43,17 +21,38 @@
|
|||||||
|
|
||||||
<div class="sidebar-divider"></div>
|
<div class="sidebar-divider"></div>
|
||||||
|
|
||||||
|
<div class="sidebar-section">
|
||||||
|
<div class="sidebar-quick-header" @click="taskExpanded = !taskExpanded">
|
||||||
|
<Icon icon="tasklist-arrow-down|svg" class="quick-access-arrow" :class="{ expanded: taskExpanded }" />
|
||||||
|
<span class="sidebar-quick-title">事项总览</span>
|
||||||
|
</div>
|
||||||
|
<template v-if="taskExpanded">
|
||||||
|
<div
|
||||||
|
v-for="item in NAV_ITEMS"
|
||||||
|
:key="item.id"
|
||||||
|
class="sidebar-item sidebar-quick-access-sub"
|
||||||
|
:class="{ active: currentView === item.id }"
|
||||||
|
@click="emit('view-select', item.id)"
|
||||||
|
>
|
||||||
|
<Icon :icon="item.icon" class="sidebar-item-icon" />
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-divider"></div>
|
||||||
|
|
||||||
<div class="sidebar-section sidebar-lists-section">
|
<div class="sidebar-section sidebar-lists-section">
|
||||||
<div class="sidebar-favorites-header">
|
<div class="sidebar-favorites-header">
|
||||||
<Icon icon="tasklist-task-list|svg" class="sidebar-favorites-icon" />
|
<Icon icon="tasklist-task-list|svg" class="sidebar-favorites-icon" />
|
||||||
<span class="sidebar-favorites-name">收藏清单</span>
|
<span class="sidebar-favorites-name">常用计划</span>
|
||||||
<a-dropdown :trigger="['click']">
|
<a-dropdown :trigger="['click']">
|
||||||
<Icon icon="ant-design:plus-outlined" class="sidebar-group-add" @click.stop />
|
<Icon icon="ant-design:plus-outlined" class="sidebar-group-add" @click.stop />
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu @click="onAddMenuClick">
|
<a-menu @click="onAddMenuClick">
|
||||||
<a-menu-item key="new-list">
|
<a-menu-item key="new-list">
|
||||||
<Icon icon="ant-design:file-add-outlined" class="mr-1" />
|
<Icon icon="ant-design:file-add-outlined" class="mr-1" />
|
||||||
新建清单
|
新建计划
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="new-group">
|
<a-menu-item key="new-group">
|
||||||
<Icon icon="ant-design:folder-add-outlined" class="mr-1" />
|
<Icon icon="ant-design:folder-add-outlined" class="mr-1" />
|
||||||
@@ -190,7 +189,7 @@
|
|||||||
v-if="getLocalGroupTaskLists(group).length === 0"
|
v-if="getLocalGroupTaskLists(group).length === 0"
|
||||||
class="sidebar-empty-hint"
|
class="sidebar-empty-hint"
|
||||||
>
|
>
|
||||||
可拖拽清单加入该分组
|
可拖拽计划加入该分组
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -258,7 +257,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const quickAccessExpanded = ref(true);
|
const planExpanded = ref(true);
|
||||||
|
const taskExpanded = ref(true);
|
||||||
|
|
||||||
const ungroupedTaskLists = computed(() => {
|
const ungroupedTaskLists = computed(() => {
|
||||||
const group = props.taskListGroups.find((g) => g.id === '__ungrouped__');
|
const group = props.taskListGroups.find((g) => g.id === '__ungrouped__');
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
createConfirm({
|
createConfirm({
|
||||||
iconType: 'warning',
|
iconType: 'warning',
|
||||||
title: '确认移除',
|
title: '确认移除',
|
||||||
content: '确定从收藏清单中移除吗?',
|
content: '确定从常用计划移除吗?',
|
||||||
okText: '确认',
|
okText: '确认',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
const { ok, invalidNames } = await checkUserSecLevel(addForm.userIds, secretLevel);
|
const { ok, invalidNames } = await checkUserSecLevel(addForm.userIds, secretLevel);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
const map: Record<number, string> = { 1: '非密', 2: '内部', 3: '秘密', 4: '机密' };
|
const map: Record<number, string> = { 1: '非密', 2: '内部', 3: '秘密', 4: '机密' };
|
||||||
message.warning(`以下人员的密级不满足当前清单(${map[secretLevel] || '非密'})要求:${invalidNames.join('、')}`);
|
message.warning(`以下人员的密级不满足当前计划(${map[secretLevel] || '非密'})要求:${invalidNames.join('、')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
@update:visible-fields="(v) => (visibleFields = v)"
|
@update:visible-fields="(v) => (visibleFields = v)"
|
||||||
@list-select="onListSelect"
|
@list-select="onListSelect"
|
||||||
@open-collaborator="onOpenCollaborator"
|
@open-collaborator="onOpenCollaborator"
|
||||||
|
@open-collaborator-for-list="onOpenCollaboratorForList"
|
||||||
@add-to-favorites="onAddToFavorites"
|
@add-to-favorites="onAddToFavorites"
|
||||||
@remove-from-favorites="onRemoveFromFavorites"
|
@remove-from-favorites="onRemoveFromFavorites"
|
||||||
@move-task="onMoveTask"
|
@move-task="onMoveTask"
|
||||||
@@ -90,7 +91,7 @@
|
|||||||
@unfollow-task="onUnfollowTask"
|
@unfollow-task="onUnfollowTask"
|
||||||
@move-task="onMoveTask"
|
@move-task="onMoveTask"
|
||||||
/>
|
/>
|
||||||
<CollaboratorModal ref="collaboratorModalRef" :task-list-id="currentListId" :is-owner="isCurrentOwner" :secret-level="currentList?.secretLevel || 1" :secret-text="currentList?.secretText || '非密'" @changed="onCollaboratorsChanged" />
|
<CollaboratorModal ref="collaboratorModalRef" :task-list-id="collaboratorTargetListId || currentListId" :is-owner="collaboratorTargetListId ? isOwnerOfTargetList : isCurrentOwner" :secret-level="collaboratorTargetList ? collaboratorTargetList.secretLevel || 1 : currentList?.secretLevel || 1" :secret-text="collaboratorTargetList ? collaboratorTargetList.secretText || '非密' : currentList?.secretText || '非密'" @changed="onCollaboratorsChanged" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -169,6 +170,7 @@
|
|||||||
const [registerDetailDrawer, { openDrawer: openDetailDrawer }] = useDrawer();
|
const [registerDetailDrawer, { openDrawer: openDetailDrawer }] = useDrawer();
|
||||||
|
|
||||||
const collaboratorModalRef = ref<InstanceType<typeof CollaboratorModal> | null>(null);
|
const collaboratorModalRef = ref<InstanceType<typeof CollaboratorModal> | null>(null);
|
||||||
|
const collaboratorTargetListId = ref('');
|
||||||
const defaultGroupRealIdMap = ref<Record<string, string>>({});
|
const defaultGroupRealIdMap = ref<Record<string, string>>({});
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@@ -277,7 +279,7 @@
|
|||||||
console.error('[TaskList] 获取用户密级失败', e);
|
console.error('[TaskList] 获取用户密级失败', e);
|
||||||
}
|
}
|
||||||
loadFavorites();
|
loadFavorites();
|
||||||
onViewSelect('my-tasks');
|
onQuickAccessTabClick('all');
|
||||||
});
|
});
|
||||||
|
|
||||||
function findListInGroups(groups: TaskListGroup[], listId: string): TaskList | null {
|
function findListInGroups(groups: TaskListGroup[], listId: string): TaskList | null {
|
||||||
@@ -297,13 +299,14 @@
|
|||||||
const isCurrentOwner = computed(() => currentList.value?.myPermission === '1');
|
const isCurrentOwner = computed(() => currentList.value?.myPermission === '1');
|
||||||
|
|
||||||
async function loadPermissionForList(listId: string) {
|
async function loadPermissionForList(listId: string) {
|
||||||
|
const silentOpts = { successMessageMode: 'none' as const };
|
||||||
try {
|
try {
|
||||||
const perm = await getMyPermission({ taskListId: listId });
|
const perm = await getMyPermission({ taskListId: listId }, silentOpts);
|
||||||
const list = findListById(listId);
|
const list = findListById(listId);
|
||||||
if (list) {
|
if (list) {
|
||||||
list.myPermission = perm || '';
|
list.myPermission = perm || '';
|
||||||
}
|
}
|
||||||
const collabs: any[] = await getCollaborators({ taskListId: listId });
|
const collabs: any[] = await getCollaborators({ taskListId: listId }, silentOpts);
|
||||||
const names = collabs.map((c: any) => c.username).filter(Boolean);
|
const names = collabs.map((c: any) => c.username).filter(Boolean);
|
||||||
if (list) {
|
if (list) {
|
||||||
list.collaboratorNames = names.join(',');
|
list.collaboratorNames = names.join(',');
|
||||||
@@ -446,7 +449,7 @@
|
|||||||
|
|
||||||
if (view === 'my-tasks') {
|
if (view === 'my-tasks') {
|
||||||
try {
|
try {
|
||||||
flatViewTitle.value = '我负责的任务';
|
flatViewTitle.value = '我的事项';
|
||||||
flatViewEditable.value = true;
|
flatViewEditable.value = true;
|
||||||
flatTaskEditMap.value = new Map();
|
flatTaskEditMap.value = new Map();
|
||||||
const data: any[] = await myResponsibleTasks();
|
const data: any[] = await myResponsibleTasks();
|
||||||
@@ -456,7 +459,7 @@
|
|||||||
}
|
}
|
||||||
} else if (view === 'followed') {
|
} else if (view === 'followed') {
|
||||||
try {
|
try {
|
||||||
flatViewTitle.value = '我关注的任务';
|
flatViewTitle.value = '关注事项';
|
||||||
flatViewEditable.value = false;
|
flatViewEditable.value = false;
|
||||||
const data: any[] = await myFollowedTasks();
|
const data: any[] = await myFollowedTasks();
|
||||||
const items = (data || []).map(buildFlatTaskItem);
|
const items = (data || []).map(buildFlatTaskItem);
|
||||||
@@ -485,12 +488,71 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onOpenCollaborator() {
|
function onOpenCollaborator() {
|
||||||
|
collaboratorTargetListId.value = '';
|
||||||
collaboratorModalRef.value?.open();
|
collaboratorModalRef.value?.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onCollaboratorsChanged() {
|
const collaboratorTargetList = computed(() => {
|
||||||
if (currentListId.value) {
|
if (!collaboratorTargetListId.value) return null;
|
||||||
await loadPermissionForList(currentListId.value);
|
for (const group of quickAccessGroups.value) {
|
||||||
|
for (const list of group.taskLists) {
|
||||||
|
if (list.id === collaboratorTargetListId.value) return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isOwnerOfTargetList = computed(() => {
|
||||||
|
return collaboratorTargetList.value?.myPermission === '1';
|
||||||
|
});
|
||||||
|
|
||||||
|
function onOpenCollaboratorForList(listId: string) {
|
||||||
|
const list = findListInQuickAccess(listId);
|
||||||
|
if (list && list.myPermission !== '1') {
|
||||||
|
message.warning('仅所有者可管理协作者');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
collaboratorTargetListId.value = listId;
|
||||||
|
collaboratorModalRef.value?.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function findListInQuickAccess(listId: string): TaskList | null {
|
||||||
|
for (const group of quickAccessGroups.value) {
|
||||||
|
for (const list of group.taskLists) {
|
||||||
|
if (list.id === listId) return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onCollaboratorsChanged(collaboratorNames?: string) {
|
||||||
|
const targetId = collaboratorTargetListId.value || currentListId.value;
|
||||||
|
if (targetId && collaboratorNames !== undefined) {
|
||||||
|
updateListCollaboratorNames(targetId, collaboratorNames);
|
||||||
|
}
|
||||||
|
if (targetId) {
|
||||||
|
await loadPermissionForList(targetId);
|
||||||
|
}
|
||||||
|
collaboratorTargetListId.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateListCollaboratorNames(listId: string, names: string) {
|
||||||
|
for (const group of quickAccessGroups.value) {
|
||||||
|
const idx = group.taskLists.findIndex((l) => l.id === listId);
|
||||||
|
if (idx >= 0) {
|
||||||
|
group.taskLists[idx] = { ...group.taskLists[idx], collaboratorNames: names };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentList.value && currentList.value.id === listId) {
|
||||||
|
currentList.value = { ...currentList.value, collaboratorNames: names };
|
||||||
|
}
|
||||||
|
for (const group of taskListGroups.value) {
|
||||||
|
const idx = group.taskLists.findIndex((l) => l.id === listId);
|
||||||
|
if (idx >= 0) {
|
||||||
|
group.taskLists[idx] = { ...group.taskLists[idx], collaboratorNames: names };
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,6 +643,7 @@
|
|||||||
createTimeStr: item.createTimeStr || '',
|
createTimeStr: item.createTimeStr || '',
|
||||||
secretLevel: (item as any).secretLevel,
|
secretLevel: (item as any).secretLevel,
|
||||||
secretText: (item as any).secretText,
|
secretText: (item as any).secretText,
|
||||||
|
myPermission: item.myPermission,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
@@ -630,8 +693,19 @@
|
|||||||
async function onCreateGroup(name: string) {
|
async function onCreateGroup(name: string) {
|
||||||
const groupName = name?.trim() || '新建分组';
|
const groupName = name?.trim() || '新建分组';
|
||||||
try {
|
try {
|
||||||
await addTaskListGroup({ tasklistName: groupName });
|
const newId = await addTaskListGroup({ tasklistName: groupName });
|
||||||
await loadFavorites();
|
const maxSort = taskListGroups.value.reduce((max, g) => Math.max(max, g.sortOrder ?? 0), 0);
|
||||||
|
const newGroup: TaskListGroup = {
|
||||||
|
id: newId,
|
||||||
|
name: groupName,
|
||||||
|
pid: '0',
|
||||||
|
hasChild: '0',
|
||||||
|
type: 0,
|
||||||
|
sortOrder: maxSort + 1,
|
||||||
|
delFlag: 0,
|
||||||
|
taskLists: [],
|
||||||
|
};
|
||||||
|
taskListGroups.value.push(newGroup);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[TaskList] 创建分组失败', e);
|
console.error('[TaskList] 创建分组失败', e);
|
||||||
}
|
}
|
||||||
@@ -681,8 +755,6 @@
|
|||||||
await moveTaskList({ favoriteId, targetGroupId: targetGroupId || '', sortOrder });
|
await moveTaskList({ favoriteId, targetGroupId: targetGroupId || '', sortOrder });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[TaskList] 移动清单失败', e);
|
console.error('[TaskList] 移动清单失败', e);
|
||||||
} finally {
|
|
||||||
await loadFavorites();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,8 +763,6 @@
|
|||||||
await moveGroup({ groupId, sortOrder });
|
await moveGroup({ groupId, sortOrder });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[TaskList] 移动分组失败', e);
|
console.error('[TaskList] 移动分组失败', e);
|
||||||
} finally {
|
|
||||||
await loadFavorites();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,7 +920,7 @@
|
|||||||
const secretLevel = currentList.value?.secretLevel || 1;
|
const secretLevel = currentList.value?.secretLevel || 1;
|
||||||
const { ok, invalidNames } = await checkUserSecLevel(value, secretLevel);
|
const { ok, invalidNames } = await checkUserSecLevel(value, secretLevel);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
message.warning(`以下人员的密级不满足当前清单(${currentList.value?.secretText || '非密'})要求:${invalidNames.join('、')}`);
|
message.warning(`以下人员的密级不满足当前计划(${currentList.value?.secretText || '非密'})要求:${invalidNames.join('、')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -876,7 +946,7 @@
|
|||||||
const secretLevel = currentList.value?.secretLevel || 1;
|
const secretLevel = currentList.value?.secretLevel || 1;
|
||||||
const { ok, invalidNames } = await checkUserSecLevel(fields[f], secretLevel);
|
const { ok, invalidNames } = await checkUserSecLevel(fields[f], secretLevel);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
message.warning(`以下人员的密级不满足当前清单(${currentList.value?.secretText || '非密'})要求:${invalidNames.join('、')}`);
|
message.warning(`以下人员的密级不满足当前计划(${currentList.value?.secretText || '非密'})要求:${invalidNames.join('、')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -908,7 +978,7 @@
|
|||||||
if (value) {
|
if (value) {
|
||||||
const { ok, invalidNames } = await checkUserSecLevel(value, secretLevel);
|
const { ok, invalidNames } = await checkUserSecLevel(value, secretLevel);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
message.warning(`以下人员的密级不满足当前清单(${secretText})要求:${invalidNames.join('、')}`);
|
message.warning(`以下人员的密级不满足当前计划(${secretText})要求:${invalidNames.join('、')}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ export interface NavItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const NAV_ITEMS: NavItem[] = [
|
export const NAV_ITEMS: NavItem[] = [
|
||||||
{ id: 'my-tasks', label: '我负责的', icon: 'ant-design:user-outlined' },
|
{ id: 'my-tasks', label: '我的事项', icon: 'ant-design:user-outlined' },
|
||||||
{ id: 'followed', label: '我关注的', icon: 'ant-design:star-outlined' },
|
{ id: 'followed', label: '关注事项', icon: 'ant-design:star-outlined' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const QUICK_ACCESS_ITEMS: NavItem[] = [
|
export const QUICK_ACCESS_ITEMS: NavItem[] = [
|
||||||
{ id: 'all-tasks', label: '全部清单', icon: 'ant-design:unordered-list-outlined' },
|
{ id: 'all-tasks', label: '所有计划', icon: 'ant-design:appstore-outlined' },
|
||||||
{ id: 'created', label: '我所有的', icon: 'ant-design:edit-outlined' },
|
{ id: 'created', label: '我创建的', icon: 'ant-design:folder-outlined' },
|
||||||
{ id: 'assigned', label: '我协作的', icon: 'ant-design:send-outlined' },
|
{ id: 'assigned', label: '我参与的', icon: 'ant-design:team-outlined' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const QUICK_ACCESS_TAB_MAP: Record<string, string> = {
|
export const QUICK_ACCESS_TAB_MAP: Record<string, string> = {
|
||||||
@@ -143,12 +143,12 @@ export interface FieldConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const FIELD_CONFIG: FieldConfig[] = [
|
export const FIELD_CONFIG: FieldConfig[] = [
|
||||||
{ key: 'taskDesc', label: '任务描述', defaultVisible: false },
|
{ key: 'taskDesc', label: '事项描述', defaultVisible: false },
|
||||||
{ key: 'assigneeName', label: '负责人', defaultVisible: true },
|
{ key: 'assigneeName', label: '负责人', defaultVisible: true },
|
||||||
{ key: 'participantName', label: '参与人', defaultVisible: true },
|
{ key: 'participantName', label: '参与人', defaultVisible: true },
|
||||||
{ key: 'startTime', label: '开始时间', defaultVisible: true },
|
{ key: 'startTime', label: '开始时间', defaultVisible: true },
|
||||||
{ key: 'endTime', label: '截止时间', defaultVisible: true },
|
{ key: 'endTime', label: '截止时间', defaultVisible: true },
|
||||||
{ key: 'subTaskProgress', label: '子任务进度', defaultVisible: true },
|
{ key: 'subTaskProgress', label: '子事项进度', defaultVisible: true },
|
||||||
{ key: 'createBy', label: '创建人', defaultVisible: true },
|
{ key: 'createBy', label: '创建人', defaultVisible: true },
|
||||||
{ key: 'assignName', label: '分配人', defaultVisible: false },
|
{ key: 'assignName', label: '分配人', defaultVisible: false },
|
||||||
{ key: 'followersName', label: '关注人', defaultVisible: false },
|
{ key: 'followersName', label: '关注人', defaultVisible: false },
|
||||||
|
|||||||
Reference in New Issue
Block a user