<template>
|
<div class="app-container">
|
<el-card class="box-card">
|
<div slot="header" class="clearfix">
|
<span>任务详情</span>
|
<el-button style="float: right; padding: 3px 0" type="text" @click="goBack">返回</el-button>
|
</div>
|
|
<!-- 基本信息 -->
|
<el-descriptions title="基本信息" :column="2" border>
|
<el-descriptions-item label="任务编号">{{ taskDetail.taskCode }}</el-descriptions-item>
|
<el-descriptions-item label="任务类型">
|
<dict-tag :options="dict.type.sys_task_type" :value="taskDetail.taskType"/>
|
</el-descriptions-item>
|
<el-descriptions-item label="任务状态">
|
<dict-tag :options="dict.type.sys_task_status" :value="taskDetail.taskStatus"/>
|
</el-descriptions-item>
|
<el-descriptions-item label="创建人">{{ taskDetail.creatorName }}</el-descriptions-item>
|
<el-descriptions-item label="执行人">{{ taskDetail.assigneeName }}</el-descriptions-item>
|
<el-descriptions-item label="部门">{{ taskDetail.deptName }}</el-descriptions-item>
|
<el-descriptions-item label="任务描述" :span="2">{{ taskDetail.taskDescription }}</el-descriptions-item>
|
<el-descriptions-item label="出发地址" :span="2">{{ taskDetail.departureAddress }}</el-descriptions-item>
|
<el-descriptions-item label="目的地址" :span="2">{{ taskDetail.destinationAddress }}</el-descriptions-item>
|
<el-descriptions-item label="计划开始时间">{{ parseTime(taskDetail.plannedStartTime) }}</el-descriptions-item>
|
<el-descriptions-item label="计划结束时间">{{ parseTime(taskDetail.plannedEndTime) }}</el-descriptions-item>
|
<el-descriptions-item label="实际开始时间">{{ parseTime(taskDetail.actualStartTime) }}</el-descriptions-item>
|
<el-descriptions-item label="实际结束时间">{{ parseTime(taskDetail.actualEndTime) }}</el-descriptions-item>
|
<el-descriptions-item label="创建时间">{{ parseTime(taskDetail.createTime) }}</el-descriptions-item>
|
<el-descriptions-item label="更新时间">{{ parseTime(taskDetail.updateTime) }}</el-descriptions-item>
|
<el-descriptions-item label="备注" :span="2">{{ taskDetail.remark }}</el-descriptions-item>
|
</el-descriptions>
|
|
<!-- 操作按钮 -->
|
<div style="margin-top: 20px; text-align: center;">
|
<el-button type="primary" @click="handleEdit" v-hasPermi="['task:general:edit']">编辑任务</el-button>
|
<el-button type="success" @click="handleAssign" v-hasPermi="['task:general:assign']">分配任务</el-button>
|
<el-button type="warning" @click="handleStatusChange" v-hasPermi="['task:general:status']">状态变更</el-button>
|
<el-button type="info" @click="handleVehicleAssign" v-hasPermi="['task:general:assign']">分配车辆</el-button>
|
</div>
|
</el-card>
|
|
<!-- 关联车辆 -->
|
<el-card class="box-card" style="margin-top: 20px;">
|
<div slot="header" class="clearfix">
|
<span>关联车辆</span>
|
<el-button style="float: right; padding: 3px 0" type="text" @click="handleVehicleAssign" v-hasPermi="['task:general:assign']">分配车辆</el-button>
|
</div>
|
|
<el-table :data="taskDetail.assignedVehicles" v-loading="vehicleLoading">
|
<el-table-column label="车牌号" align="center" prop="vehicleNo" />
|
<el-table-column label="车辆类型" align="center" prop="vehicleType">
|
<template slot-scope="scope">
|
<dict-tag :options="dict.type.sys_vehicle_type" :value="scope.row.vehicleType"/>
|
</template>
|
</el-table-column>
|
<el-table-column label="车辆品牌" align="center" prop="vehicleBrand" />
|
<el-table-column label="车辆型号" align="center" prop="vehicleModel" />
|
<el-table-column label="分配时间" align="center" prop="assignTime" width="180">
|
<template slot-scope="scope">
|
<span>{{ parseTime(scope.row.assignTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="分配人" align="center" prop="assignBy" />
|
<el-table-column label="状态" align="center" prop="status">
|
<template slot-scope="scope">
|
<dict-tag :options="dict.type.sys_task_vehicle_status" :value="scope.row.status"/>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<template slot-scope="scope">
|
<el-button
|
size="mini"
|
type="text"
|
icon="el-icon-delete"
|
@click="handleUnassignVehicle(scope.row)"
|
v-hasPermi="['task:general:assign']"
|
>取消分配</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
|
<!-- 任务附件 -->
|
<el-card class="box-card" style="margin-top: 20px;">
|
<div slot="header" class="clearfix">
|
<span>任务附件</span>
|
<el-button style="float: right; padding: 3px 0" type="text" @click="handleUpload" v-hasPermi="['task:general:edit']">上传附件</el-button>
|
</div>
|
|
<el-table :data="taskDetail.attachments" v-loading="attachmentLoading">
|
<el-table-column label="文件名" align="center" prop="fileName" />
|
<el-table-column label="文件类型" align="center" prop="fileType" />
|
<el-table-column label="文件大小" align="center" prop="fileSize">
|
<template slot-scope="scope">
|
<span>{{ formatFileSize(scope.row.fileSize) }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="上传时间" align="center" prop="uploadTime" width="180">
|
<template slot-scope="scope">
|
<span>{{ parseTime(scope.row.uploadTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="上传者" align="center" prop="uploadBy" />
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<template slot-scope="scope">
|
<el-button
|
size="mini"
|
type="text"
|
icon="el-icon-download"
|
@click="handleDownload(scope.row)"
|
>下载</el-button>
|
<el-button
|
size="mini"
|
type="text"
|
icon="el-icon-delete"
|
@click="handleDeleteAttachment(scope.row)"
|
v-hasPermi="['task:general:edit']"
|
>删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
|
<!-- 操作日志 -->
|
<el-card class="box-card" style="margin-top: 20px;">
|
<div slot="header" class="clearfix">
|
<span>操作日志</span>
|
</div>
|
|
<el-timeline>
|
<el-timeline-item
|
v-for="log in taskDetail.operationLogs"
|
:key="log.logId"
|
:timestamp="parseTime(log.operationTime)"
|
placement="top"
|
>
|
<el-card>
|
<h4>{{ log.operationDesc }}</h4>
|
<p>操作人:{{ log.operatorName }}</p>
|
<p v-if="log.oldValue">操作前:{{ log.oldValue }}</p>
|
<p v-if="log.newValue">操作后:{{ log.newValue }}</p>
|
</el-card>
|
</el-timeline-item>
|
</el-timeline>
|
</el-card>
|
|
<!-- 编辑任务对话框 -->
|
<el-dialog :title="'编辑任务'" :visible.sync="editOpen" width="600px" append-to-body>
|
<el-form ref="editForm" :model="editForm" :rules="editRules" label-width="80px">
|
<el-form-item label="任务描述" prop="taskDescription">
|
<el-input v-model="editForm.taskDescription" type="textarea" placeholder="请输入任务描述" />
|
</el-form-item>
|
<el-form-item label="出发地址" prop="departureAddress">
|
<el-input v-model="editForm.departureAddress" placeholder="请输入出发地址" />
|
</el-form-item>
|
<el-form-item label="目的地址" prop="destinationAddress">
|
<el-input v-model="editForm.destinationAddress" placeholder="请输入目的地址" />
|
</el-form-item>
|
<el-form-item label="计划开始时间" prop="plannedStartTime">
|
<el-date-picker clearable
|
v-model="editForm.plannedStartTime"
|
type="datetime"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
placeholder="请选择计划开始时间">
|
</el-date-picker>
|
</el-form-item>
|
<el-form-item label="计划结束时间" prop="plannedEndTime">
|
<el-date-picker clearable
|
v-model="editForm.plannedEndTime"
|
type="datetime"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
placeholder="请选择计划结束时间">
|
</el-date-picker>
|
</el-form-item>
|
<el-form-item label="执行人" prop="assigneeId">
|
<el-select v-model="editForm.assigneeId" placeholder="请选择执行人" clearable>
|
<el-option
|
v-for="user in userList"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="user.userId"
|
/>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="备注" prop="remark">
|
<el-input v-model="editForm.remark" type="textarea" placeholder="请输入备注" />
|
</el-form-item>
|
</el-form>
|
<div slot="footer" class="dialog-footer">
|
<el-button type="primary" @click="submitEdit">确 定</el-button>
|
<el-button @click="cancelEdit">取 消</el-button>
|
</div>
|
</el-dialog>
|
|
<!-- 分配任务对话框 -->
|
<el-dialog title="分配任务" :visible.sync="assignOpen" width="500px" append-to-body>
|
<el-form ref="assignForm" :model="assignForm" :rules="assignRules" label-width="80px">
|
<el-form-item label="执行人" prop="assigneeId">
|
<el-select v-model="assignForm.assigneeId" placeholder="请选择执行人" clearable>
|
<el-option
|
v-for="user in userList"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="user.userId"
|
/>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="备注" prop="remark">
|
<el-input v-model="assignForm.remark" type="textarea" placeholder="请输入备注" />
|
</el-form-item>
|
</el-form>
|
<div slot="footer" class="dialog-footer">
|
<el-button type="primary" @click="submitAssign">确 定</el-button>
|
<el-button @click="cancelAssign">取 消</el-button>
|
</div>
|
</el-dialog>
|
|
<!-- 状态变更对话框 -->
|
<el-dialog title="状态变更" :visible.sync="statusOpen" width="500px" append-to-body>
|
<el-form ref="statusForm" :model="statusForm" :rules="statusRules" label-width="80px">
|
<el-form-item label="任务状态" prop="taskStatus">
|
<el-select v-model="statusForm.taskStatus" placeholder="请选择任务状态">
|
<el-option
|
v-for="dict in dict.type.sys_task_status"
|
:key="dict.value"
|
:label="dict.label"
|
:value="dict.value"
|
/>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="备注" prop="remark">
|
<el-input v-model="statusForm.remark" type="textarea" placeholder="请输入备注" />
|
</el-form-item>
|
</el-form>
|
<div slot="footer" class="dialog-footer">
|
<el-button type="primary" @click="submitStatusChange">确 定</el-button>
|
<el-button @click="cancelStatusChange">取 消</el-button>
|
</div>
|
</el-dialog>
|
|
<!-- 分配车辆对话框 -->
|
<el-dialog title="分配车辆" :visible.sync="vehicleAssignOpen" width="600px" append-to-body>
|
<el-form ref="vehicleAssignForm" :model="vehicleAssignForm" :rules="vehicleAssignRules" label-width="80px">
|
<el-form-item label="车辆" prop="vehicleIds">
|
<el-select v-model="vehicleAssignForm.vehicleIds" placeholder="请选择车辆" multiple clearable style="width: 100%">
|
<el-option
|
v-for="vehicle in availableVehicles"
|
:key="vehicle.vehicleId"
|
:label="vehicle.vehicleNo + ' - ' + vehicle.deptName"
|
:value="vehicle.vehicleId"
|
>
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
<div>
|
<div style="font-weight: bold; color: #303133;">{{ vehicle.vehicleNo }}</div>
|
<div style="font-size: 12px; color: #909399;">ID: {{ vehicle.vehicleId }}</div>
|
</div>
|
<div style="text-align: right;">
|
<div style="color: #67C23A; font-size: 13px;">{{ getVehicleTypeName(vehicle.vehicleType) }}</div>
|
<div style="color: #8492a6; font-size: 12px;">{{ vehicle.vehicleBrand }} {{ vehicle.vehicleModel }}</div>
|
</div>
|
</div>
|
</el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="备注" prop="remark">
|
<el-input v-model="vehicleAssignForm.remark" type="textarea" placeholder="请输入备注" />
|
</el-form-item>
|
</el-form>
|
<div slot="footer" class="dialog-footer">
|
<el-button type="primary" @click="submitVehicleAssign">确 定</el-button>
|
<el-button @click="cancelVehicleAssign">取 消</el-button>
|
</div>
|
</el-dialog>
|
|
<!-- 上传附件对话框 -->
|
<el-dialog title="上传附件" :visible.sync="uploadOpen" width="500px" append-to-body>
|
<el-upload
|
class="upload-demo"
|
drag
|
:action="uploadUrl"
|
:headers="uploadHeaders"
|
:data="uploadData"
|
:on-success="handleUploadSuccess"
|
:on-error="handleUploadError"
|
:before-upload="beforeUpload"
|
multiple>
|
<i class="el-icon-upload"></i>
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
<div class="el-upload__tip" slot="tip">只能上传jpg/png/pdf/doc/docx文件,且不超过10MB</div>
|
</el-upload>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask } from "@/api/task";
|
import { listUser } from "@/api/system/user";
|
import { getToken } from "@/utils/auth";
|
|
export default {
|
name: "TaskDetail",
|
dicts: ['sys_task_type', 'sys_task_status', 'sys_vehicle_type', 'sys_task_vehicle_status'],
|
data() {
|
return {
|
// 任务详情
|
taskDetail: {
|
assignedVehicles: [],
|
attachments: [],
|
operationLogs: []
|
},
|
// 是否显示编辑对话框
|
editOpen: false,
|
// 是否显示分配对话框
|
assignOpen: false,
|
// 是否显示状态变更对话框
|
statusOpen: false,
|
// 是否显示车辆分配对话框
|
vehicleAssignOpen: false,
|
// 是否显示上传对话框
|
uploadOpen: false,
|
// 编辑表单
|
editForm: {},
|
// 分配表单
|
assignForm: {},
|
// 状态变更表单
|
statusForm: {},
|
// 车辆分配表单
|
vehicleAssignForm: {},
|
// 用户列表
|
userList: [],
|
// 可用车辆列表
|
availableVehicles: [],
|
// 加载状态
|
vehicleLoading: false,
|
attachmentLoading: false,
|
// 上传相关
|
uploadUrl: process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId,
|
uploadHeaders: {
|
Authorization: "Bearer " + getToken()
|
},
|
uploadData: {},
|
// 表单校验
|
editRules: {
|
taskDescription: [
|
{ required: true, message: "任务描述不能为空", trigger: "blur" }
|
],
|
plannedStartTime: [
|
{ required: true, message: "计划开始时间不能为空", trigger: "blur" }
|
],
|
plannedEndTime: [
|
{ required: true, message: "计划结束时间不能为空", trigger: "blur" }
|
]
|
},
|
assignRules: {
|
assigneeId: [
|
{ required: true, message: "执行人不能为空", trigger: "change" }
|
]
|
},
|
statusRules: {
|
taskStatus: [
|
{ required: true, message: "任务状态不能为空", trigger: "change" }
|
]
|
},
|
vehicleAssignRules: {
|
vehicleIds: [
|
{ required: true, message: "车辆不能为空", trigger: "change" }
|
]
|
}
|
};
|
},
|
created() {
|
this.getTaskDetail();
|
this.getUserList();
|
},
|
methods: {
|
/** 获取任务详情 */
|
getTaskDetail() {
|
getTask(this.$route.params.taskId).then(response => {
|
this.taskDetail = response.data;
|
});
|
},
|
/** 获取用户列表 */
|
getUserList() {
|
listUser().then(response => {
|
this.userList = response.rows;
|
});
|
},
|
/** 获取可用车辆列表 */
|
getAvailableVehicleList() {
|
getAvailableVehicles(this.taskDetail.deptId, this.taskDetail.taskType).then(response => {
|
this.availableVehicles = response.data;
|
});
|
},
|
/** 返回 */
|
goBack() {
|
this.$router.go(-1);
|
},
|
/** 编辑任务 */
|
handleEdit() {
|
this.editForm = {
|
taskId: this.taskDetail.taskId,
|
taskDescription: this.taskDetail.taskDescription,
|
departureAddress: this.taskDetail.departureAddress,
|
destinationAddress: this.taskDetail.destinationAddress,
|
plannedStartTime: this.taskDetail.plannedStartTime,
|
plannedEndTime: this.taskDetail.plannedEndTime,
|
assigneeId: this.taskDetail.assigneeId,
|
remark: this.taskDetail.remark
|
};
|
this.editOpen = true;
|
},
|
/** 分配任务 */
|
handleAssign() {
|
this.assignForm = {
|
taskId: this.taskDetail.taskId,
|
assigneeId: this.taskDetail.assigneeId,
|
remark: null
|
};
|
this.assignOpen = true;
|
},
|
/** 状态变更 */
|
handleStatusChange() {
|
this.statusForm = {
|
taskId: this.taskDetail.taskId,
|
taskStatus: this.taskDetail.taskStatus,
|
remark: null
|
};
|
this.statusOpen = true;
|
},
|
/** 分配车辆 */
|
handleVehicleAssign() {
|
this.getAvailableVehicleList();
|
this.vehicleAssignForm = {
|
taskId: this.taskDetail.taskId,
|
vehicleIds: [],
|
remark: null
|
};
|
this.vehicleAssignOpen = true;
|
},
|
/** 上传附件 */
|
handleUpload() {
|
this.uploadOpen = true;
|
},
|
/** 取消车辆分配 */
|
handleUnassignVehicle(row) {
|
this.$modal.confirm('是否确认取消车辆"' + row.vehicleNo + '"的分配?').then(() => {
|
return unassignVehicleFromTask(this.taskDetail.taskId, row.vehicleId);
|
}).then(() => {
|
this.$modal.msgSuccess("取消分配成功");
|
this.getTaskDetail();
|
}).catch(() => {});
|
},
|
/** 下载附件 */
|
handleDownload(row) {
|
window.open(row.filePath);
|
},
|
/** 删除附件 */
|
handleDeleteAttachment(row) {
|
this.$modal.confirm('是否确认删除附件"' + row.fileName + '"?').then(() => {
|
return deleteAttachment(row.attachmentId);
|
}).then(() => {
|
this.$modal.msgSuccess("删除成功");
|
this.getTaskDetail();
|
}).catch(() => {});
|
},
|
/** 提交编辑 */
|
submitEdit() {
|
this.$refs["editForm"].validate(valid => {
|
if (valid) {
|
updateTask(this.editForm).then(response => {
|
this.$modal.msgSuccess("修改成功");
|
this.editOpen = false;
|
this.getTaskDetail();
|
});
|
}
|
});
|
},
|
/** 提交分配 */
|
submitAssign() {
|
this.$refs["assignForm"].validate(valid => {
|
if (valid) {
|
assignTask(this.assignForm.taskId, this.assignForm).then(response => {
|
this.$modal.msgSuccess("分配成功");
|
this.assignOpen = false;
|
this.getTaskDetail();
|
});
|
}
|
});
|
},
|
/** 提交状态变更 */
|
submitStatusChange() {
|
this.$refs["statusForm"].validate(valid => {
|
if (valid) {
|
changeTaskStatus(this.statusForm.taskId, this.statusForm).then(response => {
|
this.$modal.msgSuccess("状态变更成功");
|
this.statusOpen = false;
|
this.getTaskDetail();
|
});
|
}
|
});
|
},
|
/** 提交车辆分配 */
|
submitVehicleAssign() {
|
this.$refs["vehicleAssignForm"].validate(valid => {
|
if (valid) {
|
assignVehiclesToTask(this.vehicleAssignForm.taskId, this.vehicleAssignForm).then(response => {
|
this.$modal.msgSuccess("分配成功");
|
this.vehicleAssignOpen = false;
|
this.getTaskDetail();
|
});
|
}
|
});
|
},
|
/** 取消编辑 */
|
cancelEdit() {
|
this.editOpen = false;
|
this.editForm = {};
|
},
|
/** 取消分配 */
|
cancelAssign() {
|
this.assignOpen = false;
|
this.assignForm = {};
|
},
|
/** 取消状态变更 */
|
cancelStatusChange() {
|
this.statusOpen = false;
|
this.statusForm = {};
|
},
|
/** 取消车辆分配 */
|
cancelVehicleAssign() {
|
this.vehicleAssignOpen = false;
|
this.vehicleAssignForm = {};
|
},
|
/** 上传前检查 */
|
beforeUpload(file) {
|
const isValidType = ['image/jpeg', 'image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'].includes(file.type);
|
const isLt10M = file.size / 1024 / 1024 < 10;
|
|
if (!isValidType) {
|
this.$message.error('只能上传 JPG/PNG/PDF/DOC/DOCX 格式的文件!');
|
}
|
if (!isLt10M) {
|
this.$message.error('上传文件大小不能超过 10MB!');
|
}
|
return isValidType && isLt10M;
|
},
|
/** 上传成功 */
|
handleUploadSuccess(response, file, fileList) {
|
this.$modal.msgSuccess("上传成功");
|
this.uploadOpen = false;
|
this.getTaskDetail();
|
},
|
/** 上传失败 */
|
handleUploadError(err, file, fileList) {
|
this.$modal.msgError("上传失败");
|
},
|
/** 格式化文件大小 */
|
formatFileSize(size) {
|
if (size < 1024) {
|
return size + ' B';
|
} else if (size < 1024 * 1024) {
|
return (size / 1024).toFixed(2) + ' KB';
|
} else {
|
return (size / 1024 / 1024).toFixed(2) + ' MB';
|
}
|
},
|
/** 获取车辆类型名称 */
|
getVehicleTypeName(vehicleType) {
|
const typeDict = this.dict.type.sys_vehicle_type;
|
if (typeDict && typeDict.length > 0) {
|
const typeItem = typeDict.find(item => item.value === vehicleType);
|
return typeItem ? typeItem.label : vehicleType;
|
}
|
return vehicleType;
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.box-card {
|
margin-bottom: 20px;
|
}
|
</style>
|