1052 lines
32 KiB
Vue
1052 lines
32 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<el-form :model="queryParams" ref="queryFormProject" size="small" :inline="true" label-width="90px">
|
||
<el-form-item label="项目名称" prop="projectName">
|
||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="项目编号" prop="projectNum">
|
||
<el-input v-model="queryParams.projectNum" placeholder="请输入项目编号" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="项目代号" prop="projectCode">
|
||
<el-input v-model="queryParams.projectCode" placeholder="请输入项目代号" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="发起人姓名" prop="createUserNickName">
|
||
<el-input v-model="queryParams.createUserNickName" placeholder="请输入发起人姓名" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="执行人姓名" prop="createUserNickName">
|
||
<el-input v-model="queryParams.workerNickName" placeholder="请输入执行人姓名" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="日期范围">
|
||
<el-date-picker v-model="queryParams.searchTime" type="daterange" value-format="yyyy-MM-dd"
|
||
start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增任务
|
||
</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete">删除
|
||
</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-alert type="info">
|
||
<span @click="gotoPurchase" style="cursor: pointer;">如要发放采购相关需求请移步采购需求管理(点击此处快速跳转)</span>
|
||
</el-alert>
|
||
|
||
<el-row :gutter="10" v-loading="loading">
|
||
<el-col class="task-col" v-for="item in taskList" :key="item.taskId">
|
||
<el-card class="task-card" shadow="hover" @click.native="handleLookTask(item)">
|
||
<!-- 卡片头部 -->
|
||
<template slot="header">
|
||
<div class="card-header">
|
||
<!-- 第一行:任务标题和优先级 -->
|
||
<div class="header-row">
|
||
<div class="task-title">{{ item.taskTitle }}</div>
|
||
<div class="task-priority">
|
||
<el-tag v-if="item.taskGrade === '2'" type="danger">紧急</el-tag>
|
||
<el-tag v-else-if="item.taskGrade === '1'" type="warning">中等</el-tag>
|
||
<el-tag v-else-if="item.taskGrade === '0'" type="info">一般</el-tag>
|
||
</div>
|
||
</div>
|
||
<!-- 第二行:任务状态和任务时间 -->
|
||
<div class="header-row">
|
||
<div class="task-status">
|
||
<el-tag v-if="item.state === 2" type="success">执行完成</el-tag>
|
||
<el-tag v-else-if="item.state === 1" type="warning">待验收</el-tag>
|
||
<el-tag v-else-if="item.overDays > 0" type="danger">逾期{{ Math.abs(item.overDays) }}天</el-tag>
|
||
<el-tag v-else type="info">剩余{{ Math.abs(item.overDays) }}天</el-tag>
|
||
</div>
|
||
<div class="task-time">
|
||
<el-tag type="info" size="small">{{ parseTime(item.beginTime, '{y}-{m}-{d}') }} 至 {{
|
||
parseTime(item.finishTime, '{y}-{m}-{d}') }}</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 卡片内容 -->
|
||
<div class="card-content">
|
||
<!-- 项目信息 -->
|
||
<div class="info-item">
|
||
<span class="info-label">项目名称:</span>
|
||
<el-tag v-if="item.projectName === null" type="info">未关联项目</el-tag>
|
||
<span v-else>{{ item.projectName }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">项目代号:</span>
|
||
<el-tag v-if="item.projectCode == null" type="info">无</el-tag>
|
||
<el-tag v-else type="primary">{{ item.projectCode }}</el-tag>
|
||
</div>
|
||
|
||
<!-- 任务信息 -->
|
||
<div class="info-item">
|
||
<span class="info-label">发起人:</span>
|
||
<span>{{ item.createUserNickName }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">执行人:</span>
|
||
<div v-if="item.workerId === null">
|
||
<el-button type="text" size="mini" @click="visible = true">点击发放任务</el-button>
|
||
</div>
|
||
<span v-else>{{ item.workerNickName }}</span>
|
||
</div>
|
||
<div class="info-item attachment-row" @click.stop>
|
||
<span class="info-label">附件:</span>
|
||
<file-preview v-if="item.accessory" :value="item.accessory" class="card-attachment" />
|
||
<el-tag v-else type="info" size="mini">无附件</el-tag>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 卡片底部操作按钮 -->
|
||
<div class="card-footer">
|
||
<el-button size="mini" type="text" v-if="item.taskRank === 0" icon="el-icon-copy-document"
|
||
@click.stop="handleRank(item, 1)">置顶</el-button>
|
||
<el-button size="mini" type="text" v-if="item.taskRank === 1" icon="el-icon-copy-document"
|
||
@click.stop="handleRank(item, 0)">取消置顶</el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-copy-document"
|
||
@click.stop="handleUpdate(item)">修改</el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)">删除</el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-s-order" @click.stop="handleOpLog(item)">操作历史</el-button>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||
@pagination="getList" />
|
||
|
||
<!-- 添加或修改任务管理对话框 -->
|
||
<el-dialog :title="title" :visible.sync="open" width="70%" append-to-body>
|
||
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
|
||
<el-row>
|
||
<el-row>
|
||
<el-col :span="16">
|
||
<el-form-item label="任务主题" prop="taskTitle">
|
||
<el-input v-model="form.taskTitle" placeholder="请输入任务主题" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="工作类型" prop="taskType">
|
||
<el-select v-model="form.taskType" placeholder="请选择工作类型">
|
||
<el-option v-for="dict in dict.type.sys_work_type" :key="dict.value" :label="dict.label"
|
||
:value="dict.value"></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-col :span="16">
|
||
<el-form-item label="任务时间">
|
||
<el-col :span="11">
|
||
<el-form-item prop="beginTime">
|
||
<el-date-picker clearable v-model="form.beginTime" type="datetime" value-format="yyyy-MM-dd hh:mm:ss"
|
||
placeholder="请选择开始时间">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="2" v-if="form.status === 0">至</el-col>
|
||
<el-col :span="11">
|
||
<el-form-item prop="finishTime" v-if="form.status === 0">
|
||
<el-date-picker clearable v-model="form.finishTime" type="datetime" value-format="yyyy-MM-dd hh:mm:ss"
|
||
placeholder="请选择结束时间">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="优先级" prop="taskGrade">
|
||
<el-radio-group v-model="form.taskGrade">
|
||
<el-radio v-for="dict in dict.type.sys_sort_grade" :key="dict.value" :label="dict.value">{{ dict.label
|
||
}}
|
||
</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="关联项目" prop="projectId">
|
||
<project-select v-model="form.projectId" style="width: 100%;" adjustable />
|
||
</el-form-item>
|
||
</el-col>
|
||
|
||
<el-col :span="12" v-loading="trackLoading">
|
||
<el-form-item label="项目进度" prop="trackId">
|
||
<el-cascader v-if="trackOptions" v-model="form.trackId" :options="trackOptions" :props="cascaderProps"
|
||
style="width: 100%;" placeholder="请选择" clearable></el-cascader>
|
||
<div v-else>该项目未设置项目进度或未选择项目</div>
|
||
<!-- <track-select v-model="form.trackId" style="width: 100%;" /> -->
|
||
</el-form-item>
|
||
</el-col>
|
||
|
||
<el-col :span="14">
|
||
<el-form-item label="执行人" prop="workerIds">
|
||
<div style="display: flex; align-items: center;">
|
||
<el-tooltip content="请选择执行人, 多选执行人后第一个指定人会作为负责人,其他被选中的人员作为协助者" placement="top">
|
||
<el-icon class="el-icon-question"></el-icon>
|
||
</el-tooltip>
|
||
<user-select v-model="form.workerIds" :disabled="!!form.taskId" />
|
||
</div>
|
||
</el-form-item>
|
||
</el-col>
|
||
|
||
|
||
<el-col :span="24">
|
||
<el-form-item label="详细描述">
|
||
<editor v-model="form.content" :min-height="192" />
|
||
</el-form-item>
|
||
</el-col>
|
||
|
||
<el-col :span="24">
|
||
<el-form-item label="附件" prop="accessory">
|
||
<file-upload v-model="form.accessory" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||
<el-button @click="cancel">取 消</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 操作历史抽屉 -->
|
||
<operation-log-drawer
|
||
:visible.sync="opLogDrawer"
|
||
:target-type="3"
|
||
:target-id="opLogTargetId"
|
||
:title="opLogTitle"
|
||
/>
|
||
|
||
<!--查看弹出层-->
|
||
<el-dialog title="任务详情" :visible.sync="openLook" width="70%" center>
|
||
<div class="task-detail-container">
|
||
<el-descriptions class="task-descriptions" :column="2" border>
|
||
<!-- 基本信息 -->
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-user"></i>
|
||
任务主题
|
||
</template>
|
||
<span class="task-title">{{ form.taskTitle }}</span>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
工作类型
|
||
</template>
|
||
<dict-tag :options="dict.type.sys_work_type" :value="form.taskType" />
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
优先级
|
||
</template>
|
||
<dict-tag :options="dict.type.sys_sort_grade" :value="form.taskGrade" />
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
任务状态
|
||
</template>
|
||
<el-tag v-if="form.state === 2" type="success">执行完成</el-tag>
|
||
<el-tag v-else-if="form.state === 1" type="warning">待验收</el-tag>
|
||
<el-tag v-else-if="form.overDays > 0" type="danger">逾期{{ Math.abs(form.overDays) }}天</el-tag>
|
||
<el-tag v-else type="info">剩余{{ Math.abs(form.overDays) }}天</el-tag>
|
||
</el-descriptions-item>
|
||
|
||
<!-- 项目信息 -->
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
项目名称
|
||
</template>
|
||
<span>{{ form.projectName || '未关联项目' }}</span>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
项目代号
|
||
</template>
|
||
<el-tag v-if="form.projectCode" type="primary">{{ form.projectCode }}</el-tag>
|
||
<el-tag v-else type="info">无</el-tag>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
项目进度
|
||
</template>
|
||
<span>{{ form.secondLevelNode ? form.tabNode + ' / ' + form.firstLevelNode + ' / ' + form.secondLevelNode :
|
||
'-'
|
||
}}</span>
|
||
</el-descriptions-item>
|
||
|
||
<!-- 人员信息 -->
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
发起人
|
||
</template>
|
||
<span>{{ form.createUserNickName }}</span>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
执行人
|
||
</template>
|
||
<span>{{ form.workerNickName || '未分配' }}</span>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
协同人员
|
||
</template>
|
||
<span>{{ form.collaborator || '无' }}</span>
|
||
</el-descriptions-item>
|
||
|
||
<!-- 时间信息 -->
|
||
<el-descriptions-item :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
任务时间
|
||
</template>
|
||
<span>{{ parseTime(form.beginTime, '{y}-{m}-{d}') }} 至 {{ parseTime(form.finishTime, '{y}-{m}-{d}')
|
||
}}</span>
|
||
</el-descriptions-item>
|
||
|
||
<!-- 详细内容 -->
|
||
<el-descriptions-item span="2" :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
工作内容
|
||
</template>
|
||
<div class="content-box">
|
||
<div v-if="form.content" v-html="form.content"></div>
|
||
<div v-else class="empty-content">暂无工作内容</div>
|
||
</div>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item span="2" :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
备注
|
||
</template>
|
||
<div class="content-box">
|
||
<div v-if="form.remark" v-html="form.remark"></div>
|
||
<div v-else class="empty-content">暂无备注</div>
|
||
</div>
|
||
</el-descriptions-item>
|
||
|
||
<el-descriptions-item span="2" :labelStyle="lableBg">
|
||
<template slot="label">
|
||
<i class="el-icon-s-unfold"></i>
|
||
附件
|
||
</template>
|
||
<div class="attachment-box">
|
||
<file-preview v-if="form.accessory" v-model="form.accessory" />
|
||
<div v-else class="no-attachment">暂无附件...</div>
|
||
</div>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listProjectSchedule } from "@/api/oa/projectSchedule";
|
||
import { listPage } from "@/api/oa/projectScheduleStep";
|
||
import { addTask, delTask, getTask, listTask, updateTask } from "@/api/oa/task";
|
||
import { deptTreeSelect, selectUser } from "@/api/system/user";
|
||
import UserSelect from "@/components/UserSelect";
|
||
import ProjectSelect from "@/components/fad-service/ProjectSelect";
|
||
import FilePreview from "@/components/FilePreview";
|
||
import OperationLogDrawer from "@/views/oa/project/operationLog/OperationLogDrawer.vue";
|
||
|
||
|
||
export default {
|
||
name: "Task",
|
||
components: {
|
||
UserSelect,
|
||
ProjectSelect,
|
||
FilePreview,
|
||
OperationLogDrawer
|
||
},
|
||
dicts: ['sys_project_type', 'sys_project_status', 'sys_work_type', 'sys_sort_grade'],
|
||
data () {
|
||
return {
|
||
//项目id
|
||
projectId: '',
|
||
projectName: '',
|
||
// 项目总条数
|
||
total: 0,
|
||
taskTotal: 0,
|
||
deptName: null,
|
||
// 遮罩层
|
||
loading: true,
|
||
//搜索日期范围
|
||
searchTime: [],
|
||
//任务弹出层标题
|
||
titleDialog: '',
|
||
//任务管理弹出层
|
||
taskDialog: false,
|
||
visible: false,
|
||
// 任务管理表格数据
|
||
taskList: [],
|
||
deptProps: {
|
||
children: "children",
|
||
label: "label"
|
||
},
|
||
//任务表单
|
||
form: {},
|
||
// 按钮loading
|
||
buttonLoading: false,
|
||
// 任务修改弹出层
|
||
open: false,
|
||
//查看弹出层
|
||
openLook: false,
|
||
//附件
|
||
fileList: [],
|
||
//详情lable背景
|
||
lableBg: "background: #f0f9eb; width:150px; text-align: center;",
|
||
//任务弹出层标题
|
||
title: '',
|
||
// 查询参数
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 20,
|
||
searchTime: [],
|
||
},
|
||
userList: [],
|
||
// 非多个禁用
|
||
multiple: true,
|
||
userMultipleSelection: [],
|
||
deptOptions: [],
|
||
userParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
},
|
||
userLoading: false,
|
||
copyUser: [],
|
||
rules: {
|
||
workerIds: [
|
||
{ required: true, message: "任务执行者不能为空", trigger: "blur" },
|
||
],
|
||
},
|
||
trackOptions: null,
|
||
cascaderProps: {
|
||
checkStrictly: false, // 严格检查模式(父子节点不关联)
|
||
label: 'label', // 显示的文本字段
|
||
value: 'value', // 实际的值字段
|
||
children: 'children', // 子节点字段
|
||
// 判断是否禁用(前两级禁用,第三级启用)
|
||
disabled: (data) => {
|
||
// 第三级没有children,以此判断层级
|
||
return !!data.children;
|
||
}
|
||
},
|
||
trackLoading: false,
|
||
// 操作历史抽屉
|
||
opLogDrawer: false,
|
||
opLogTargetId: null,
|
||
opLogTitle: ''
|
||
};
|
||
|
||
},
|
||
created () {
|
||
this.getList();
|
||
this.getTreeSelect();
|
||
this.getUserList();
|
||
},
|
||
watch: {
|
||
'form.projectId': {
|
||
handler (val) {
|
||
this.form.trackId = undefined;
|
||
if (val) {
|
||
this.trackLoading = true;
|
||
listProjectSchedule({ projectId: val })
|
||
.then(res => {
|
||
if (res.rows && res.rows.length > 0) {
|
||
// 有数据,返回scheduleId继续执行
|
||
return res.rows[0].scheduleId;
|
||
} else {
|
||
// 无数据,设置trackOptions并返回拒绝状态的Promise,阻止后续执行
|
||
this.trackOptions = null;
|
||
this.trackLoading = false; // 这里也要停止加载状态
|
||
return Promise.reject('无项目计划数据'); // 关键:拒绝后续执行
|
||
}
|
||
})
|
||
.then(scheduleId => {
|
||
if (scheduleId) {
|
||
// 有scheduleId,继续调用listPage
|
||
return listPage({ scheduleId: scheduleId, pageSize: 9999, pageNum: 1 });
|
||
} else {
|
||
// 无scheduleId,返回拒绝状态的Promise
|
||
this.trackLoading = false;
|
||
return Promise.reject('无有效的scheduleId');
|
||
}
|
||
})
|
||
.then(res => {
|
||
// 只有前面都成功,才会执行到这里
|
||
this.trackOptions = this.convertToCascader(res.rows);
|
||
this.trackLoading = false;
|
||
})
|
||
.catch(err => {
|
||
// 捕获前面所有的拒绝状态,避免控制台报错(可选,根据需要处理错误)
|
||
console.log('流程终止:', err);
|
||
// 确保加载状态关闭(兜底)
|
||
this.trackLoading = false;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
|
||
// 转换数据为级联选择器格式
|
||
convertToCascader (rows) {
|
||
if (!rows || !rows.length) return [];
|
||
|
||
// 第一级分组:按tabNode
|
||
const firstLevelMap = new Map();
|
||
rows.forEach(item => {
|
||
const tabNode = item.tabNode;
|
||
if (!firstLevelMap.has(tabNode)) {
|
||
firstLevelMap.set(tabNode, {
|
||
label: tabNode,
|
||
value: tabNode, // 第一级值可用tabNode(非必须,因不可选)
|
||
children: []
|
||
});
|
||
}
|
||
|
||
// 第二级分组:按firstLevelNode
|
||
const firstLevelItem = firstLevelMap.get(tabNode);
|
||
const secondLevelMap = new Map();
|
||
firstLevelItem.children.forEach(secondItem => {
|
||
secondLevelMap.set(secondItem.label, secondItem);
|
||
});
|
||
|
||
const firstLevelNode = item.firstLevelNode;
|
||
if (!secondLevelMap.has(firstLevelNode)) {
|
||
const secondLevelItem = {
|
||
label: firstLevelNode,
|
||
value: firstLevelNode, // 第二级值可用firstLevelNode(非必须,因不可选)
|
||
children: []
|
||
};
|
||
firstLevelItem.children.push(secondLevelItem);
|
||
secondLevelMap.set(firstLevelNode, secondLevelItem);
|
||
}
|
||
|
||
// 第三级:secondLevelNode(可选项)
|
||
const secondLevelItem = secondLevelMap.get(firstLevelNode);
|
||
secondLevelItem.children.push({
|
||
label: item.secondLevelNode,
|
||
value: item.trackId, // 第三级值为trackId
|
||
children: null // 标记为第三级(无children)
|
||
});
|
||
});
|
||
|
||
// 转换为数组格式
|
||
return Array.from(firstLevelMap.values());
|
||
},
|
||
gotoPurchase () {
|
||
this.$router.push({ path: '/hint/requirement' });
|
||
},
|
||
// 单选矿变化
|
||
handChange () {
|
||
if (this.form.status === 1) {
|
||
this.form.timeGap = 3
|
||
} else {
|
||
this.form.timeGap = null;
|
||
}
|
||
},
|
||
|
||
handleClose (tag) {
|
||
let userObj = this.userMultipleSelection.find(item => item.userId === tag.id);
|
||
this.userMultipleSelection.splice(this.userMultipleSelection.indexOf(userObj), 1);
|
||
this.copyUser = this.userMultipleSelection;
|
||
// 设置抄送人ID
|
||
if (this.copyUser && this.copyUser.length > 0) {
|
||
const val = this.copyUser.map(item => item.id);
|
||
this.form.workerIds = val instanceof Array ? val.join(',') : val;
|
||
} else {
|
||
this.form.workerIds = '';
|
||
}
|
||
},
|
||
|
||
submitUserData () {
|
||
this.visible = false;
|
||
if (!this.userMultipleSelection || this.userMultipleSelection.length <= 0) {
|
||
this.$modal.msgError("请选择用户");
|
||
return false;
|
||
}
|
||
let userIds = this.userMultipleSelection.map(k => k.userId);
|
||
this.copyUser = this.userMultipleSelection;
|
||
this.form.workerIds = userIds instanceof Array ? userIds.join(',') : userIds;
|
||
this.form.workerId = userIds instanceof Array ? userIds.join(',') : userIds;
|
||
},
|
||
|
||
changeCurrentUser (val) {
|
||
this.currentUserId = val.userId
|
||
},
|
||
|
||
handleSelectionChange (selection) {
|
||
this.userMultipleSelection = selection
|
||
},
|
||
|
||
// 节点单击事件
|
||
handleNodeClick (data) {
|
||
this.userParams.deptId = data.id;
|
||
this.getUserList();
|
||
},
|
||
filterNode (value, data) {
|
||
if (!value) return true;
|
||
return data.label.indexOf(value) !== -1;
|
||
},
|
||
/** 查询部门下拉树结构 */
|
||
getTreeSelect () {
|
||
deptTreeSelect().then(response => {
|
||
this.deptOptions = response.data;
|
||
});
|
||
},
|
||
getUserList () {
|
||
this.userLoading = true
|
||
selectUser(this.userParams).then(response => {
|
||
this.userList = response.rows;
|
||
this.userLoading = false;
|
||
});
|
||
},
|
||
/** 查询项目管理列表 */
|
||
getList () {
|
||
|
||
this.loading = true;
|
||
const payload = {
|
||
...this.queryParams,
|
||
taskId:this.$route.query.taskId!=null?this.$route.query.taskId:null,
|
||
beginTime: this.queryParams.searchTime[0] ? this.queryParams.searchTime[0] + ' 00:00:00' : undefined,
|
||
finishTime: this.queryParams.searchTime[1] ? this.queryParams.searchTime[1] + ' 23:59:59' : undefined
|
||
}
|
||
|
||
listTask(payload).then(response => {
|
||
this.taskList = response.rows;
|
||
this.total = response.total;
|
||
this.loading = false;
|
||
});
|
||
|
||
},
|
||
|
||
/** 任务搜索按钮操作 */
|
||
handleQuery () {
|
||
this.queryParams.pageNum = 1;
|
||
this.getList();
|
||
},
|
||
|
||
/**查看任务**/
|
||
handleLookTask (row) {
|
||
this.loading = true;
|
||
this.reset();
|
||
const taskId = row.taskId || this.ids
|
||
getTask(taskId).then(response => {
|
||
this.loading = false;
|
||
this.form = response.data;
|
||
// this.getFile(response.data.accessory)
|
||
this.openLook = true;
|
||
this.title = "查看任务";
|
||
});
|
||
},
|
||
|
||
/** 处理置顶 */
|
||
handleRank (row, rank) {
|
||
this.loading = true;
|
||
row.taskRank = rank;
|
||
updateTask(row).then(response => {
|
||
this.$modal.msgSuccess("操作成功")
|
||
this.getList();
|
||
})
|
||
},
|
||
/** 修改按钮操作 */
|
||
handleUpdate (row) {
|
||
this.loading = true;
|
||
this.reset();
|
||
const taskId = row.taskId || this.ids
|
||
getTask(taskId).then(response => {
|
||
this.loading = false;
|
||
this.form = response.data;
|
||
this.open = true;
|
||
this.title = "修改任务";
|
||
});
|
||
},
|
||
|
||
handleAdd () {
|
||
this.reset();
|
||
this.open = true;
|
||
this.title = "新增任务";
|
||
|
||
},
|
||
|
||
// 取消按钮
|
||
cancel () {
|
||
this.open = false;
|
||
this.reset();
|
||
},
|
||
submitForm () {
|
||
const payload = {
|
||
...this.form,
|
||
trackId: this.form.trackId ? this.form.trackId[2] : undefined,
|
||
}
|
||
if (!this.form.taskId) {
|
||
const workers = this.form.workerIds.split(',')
|
||
payload.workerIds = workers[0]
|
||
payload.collaborator = workers.slice(1).length > 0 ? workers.slice(1).join(',') : ''
|
||
}
|
||
if (this.form.taskId) {
|
||
updateTask(payload).then(response => {
|
||
this.open = false;
|
||
this.getList();
|
||
this.$modal.msgSuccess("修改成功")
|
||
})
|
||
} else {
|
||
this.$refs["form"].validate(valid => {
|
||
if (valid) {
|
||
addTask(payload).then(response => {
|
||
this.open = false;
|
||
this.getList();
|
||
this.$modal.msgSuccess("添加成功")
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 表单重置
|
||
reset () {
|
||
this.form = {
|
||
status: 0
|
||
};
|
||
this.resetForm("form");
|
||
this.fileList = [];
|
||
},
|
||
handleOpLog (item) {
|
||
this.opLogTargetId = item.taskId
|
||
this.opLogTitle = '操作历史 — ' + (item.taskTitle || '任务')
|
||
this.opLogDrawer = true
|
||
},
|
||
|
||
/** 删除按钮操作 */
|
||
handleDelete (row) {
|
||
const taskIds = row.taskId || this.ids;
|
||
this.$modal.confirm('是否确认删除任务管理编号为"' + taskIds + '"的数据项?').then(() => {
|
||
this.loading = true;
|
||
return delTask(taskIds);
|
||
}).then(() => {
|
||
this.loading = false;
|
||
this.getList();
|
||
this.$modal.msgSuccess("删除成功");
|
||
}).catch(() => {
|
||
}).finally(() => {
|
||
this.loading = false;
|
||
});
|
||
},
|
||
|
||
},
|
||
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.task-col {
|
||
flex: 0 0 20%;
|
||
max-width: 20%;
|
||
}
|
||
|
||
.task-card {
|
||
margin-bottom: 12px;
|
||
height: 300px;
|
||
/* 固定卡片高度 */
|
||
display: flex;
|
||
flex-direction: column;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.task-card >>> .el-card__header {
|
||
padding: 10px 12px;
|
||
}
|
||
|
||
.task-card >>> .el-card__body {
|
||
padding: 10px 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex: 1;
|
||
min-height: 0;
|
||
}
|
||
|
||
.task-card .task-title {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.task-card .info-label {
|
||
font-size: 12px;
|
||
min-width: 56px;
|
||
}
|
||
|
||
.task-card .info-item {
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.task-card .card-footer .el-button {
|
||
margin-right: 4px;
|
||
padding: 4px 2px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
@media (max-width: 1366px) {
|
||
.task-col {
|
||
flex: 0 0 25%;
|
||
max-width: 25%;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.task-col {
|
||
flex: 0 0 33.3333%;
|
||
max-width: 33.3333%;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.task-col {
|
||
flex: 0 0 50%;
|
||
max-width: 50%;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.task-col {
|
||
flex: 0 0 100%;
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
|
||
.attachment-row {
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.card-attachment {
|
||
flex: 1;
|
||
min-width: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
.card-attachment >>> .el-upload-list__item {
|
||
margin: 0;
|
||
border: none;
|
||
padding: 0;
|
||
background: transparent;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.card-attachment >>> .el-upload-list__item:hover {
|
||
background: transparent;
|
||
}
|
||
|
||
.card-attachment >>> .ele-upload-list__item-content-action {
|
||
display: none;
|
||
}
|
||
|
||
.card-attachment >>> .el-upload-list__item:hover .ele-upload-list__item-content-action {
|
||
display: flex;
|
||
gap: 4px;
|
||
}
|
||
|
||
.card-attachment >>> .el-icon-document {
|
||
color: #409eff;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
max-width: 100%;
|
||
}
|
||
|
||
.card-header {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.header-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 100%;
|
||
}
|
||
|
||
.task-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
flex: 1;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.task-priority {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.task-status {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.task-time {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.card-content {
|
||
padding: 10px 0;
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
min-height: 0;
|
||
}
|
||
|
||
.info-item {
|
||
margin-bottom: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: nowrap;
|
||
/* 禁止换行 */
|
||
}
|
||
|
||
.info-label {
|
||
color: #606266;
|
||
font-size: 14px;
|
||
margin-right: 5px;
|
||
min-width: 80px;
|
||
white-space: nowrap;
|
||
/* 禁止换行 */
|
||
}
|
||
|
||
.info-item span:not(.info-label) {
|
||
white-space: nowrap;
|
||
/* 禁止项目名称等内容换行 */
|
||
overflow: hidden;
|
||
/* 超出部分隐藏 */
|
||
text-overflow: ellipsis;
|
||
/* 显示省略号 */
|
||
flex: 1;
|
||
/* 占据剩余空间 */
|
||
}
|
||
|
||
.card-footer {
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
/* 按钮左对齐 */
|
||
align-items: center;
|
||
border-top: 1px solid #ebeef5;
|
||
padding-top: 10px;
|
||
margin-top: 10px;
|
||
width: 100%;
|
||
}
|
||
|
||
.card-footer .el-button {
|
||
margin-right: 10px;
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
@media (max-width: 768px) {
|
||
.task-card {
|
||
height: auto;
|
||
}
|
||
|
||
.header-row {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 5px;
|
||
}
|
||
|
||
.info-item {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.info-label {
|
||
margin-bottom: 5px;
|
||
}
|
||
}
|
||
|
||
/* 任务详情弹窗样式 */
|
||
.task-detail-container {
|
||
padding: 10px;
|
||
}
|
||
|
||
.task-descriptions {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.task-descriptions .el-descriptions__label {
|
||
font-weight: bold;
|
||
background-color: #f0f9eb;
|
||
text-align: center;
|
||
width: 120px;
|
||
}
|
||
|
||
.task-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.content-box {
|
||
padding: 10px;
|
||
background-color: #fafafa;
|
||
border-radius: 4px;
|
||
min-height: 100px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.attachment-box {
|
||
padding: 10px;
|
||
background-color: #fafafa;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.attachment-item {
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.attachment-item i {
|
||
margin-right: 5px;
|
||
color: #409eff;
|
||
}
|
||
|
||
.no-attachment {
|
||
color: #909399;
|
||
text-align: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
.dialog-footer {
|
||
text-align: center;
|
||
}
|
||
</style>
|