396 lines
14 KiB
Vue
396 lines
14 KiB
Vue
<template>
|
||
<div class="app-container" v-loading="loading">
|
||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||
<el-form-item label="项目名称" prop="projectId">
|
||
<project-select v-model="queryParams.projectId" placeholder="请选择项目" clearable />
|
||
<!-- <el-select v-model="queryParams.projectId" filterable placeholder="请选择">
|
||
<el-option v-for="item in projects" :key="item.projectId" :label="item.projectName" :value="item.projectId">
|
||
</el-option>
|
||
</el-select> -->
|
||
</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="tradeType">
|
||
<el-select v-model="queryParams.tradeType" placeholder="请选择项目类型" clearable>
|
||
<el-option v-for="dict in dict.type.sys_trade_type" :key="dict.value" :label="dict.label"
|
||
:value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="项目代号" prop="projectCode">
|
||
<el-select v-model="queryParams.projectCode" placeholder="请选择代号类型" style="width: 100%" filterable
|
||
@change="handleQuery">
|
||
<el-option v-for="dict in dict.type.sys_project_code" :key="dict.value" :label="dict.label"
|
||
:value="dict.value">
|
||
<span style="float: left">{{ dict.label }}</span>
|
||
<span style="float: right; color: #8492a6; font-size: 13px">{{ dict.value }}</span>
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
|
||
|
||
<el-form-item label="项目周期">
|
||
<el-date-picker v-model="searchTime" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
|
||
:default-time="['00:00:00', '23:59:59']">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="负责人">
|
||
<el-select filterable allow-add v-model="queryParams.steward" @change="handleStatusChange(scope.row)">
|
||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
<!-- <el-form-item label="优质筛选">
|
||
<el-switch v-model="queryParams.prePay" active-text="开" active-value="0.1" inactive-value="0"
|
||
@change="selectPrePay" inactive-text="关">
|
||
</el-switch>
|
||
</el-form-item> -->
|
||
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</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>
|
||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||
</el-row>
|
||
|
||
|
||
<el-row :gutter="20">
|
||
<el-table v-loading="loading" :data="scheduleList" @selection-change="handleSelectionChange">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="代号" prop="projectCode" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag v-if="scope.row.projectCode == null" type="danger">无</el-tag>
|
||
<el-tag v-else>{{ scope.row.projectCode }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="项目名称" prop="projectName" show-overflow-tooltip>
|
||
<template slot-scope="scope">
|
||
<span v-if="scope.row.prePay > 0">⭐</span>
|
||
<span>{{ scope.row.projectName }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="项目编号" prop="projectNum"></el-table-column>
|
||
<el-table-column label="负责人" align="center">
|
||
<template slot-scope="scope">
|
||
<el-select filterable allow-add v-model="scope.row.steward" @change="handleStatusChange(scope.row)">
|
||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||
</el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="状态" align="center">
|
||
<template slot-scope="scope">
|
||
<span v-if="scope.row.isTop" style="color: #ff4d4f;">重点关注</span>
|
||
<span v-else style="">一般项目</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="延期进度数" prop="delayCount" />
|
||
<el-table-column label="未完成进度" prop="unFinishCount" />
|
||
<el-table-column label="完成状态" align="center" prop="sortNum">
|
||
<template slot-scope="scope">
|
||
<el-select size="mini" v-model="scope.row.status" placeholder="请选择完成状态"
|
||
@change="handleStatusChange(scope.row)">
|
||
<el-option label="进行中" :value="1"></el-option>
|
||
<el-option label="已完成" :value="2"></el-option>
|
||
</el-select>
|
||
</template>
|
||
|
||
</el-table-column>
|
||
<el-table-column label="开始时间" align="center" prop="startTime">
|
||
<template slot-scope="scope">
|
||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
|
||
</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-edit" @click="handleDetail(scope.row)">进度详情
|
||
</el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-time" @click="handlePostpone(scope.row)">延期记录
|
||
</el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-check"
|
||
v-if="scope.row.schedulePercentage === 100 && scope.row.status !== 2"
|
||
@click="handleComplete(scope.row)">完成任务
|
||
</el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||
@pagination="getList" />
|
||
</el-row>
|
||
|
||
<el-drawer title="进度详情" :visible.sync="detailDrawer" direction="btt" size="90%" :before-close="closeDetailShow">
|
||
<div style="padding:0 20px">
|
||
<project-schedule-step :scheduleId="scheduleDetail.scheduleId" :master="scheduleDetail.functionary"
|
||
:projectName="scheduleDetail.projectName" :projectStatus="scheduleDetail.projectStatus"
|
||
:isTop="scheduleDetail.isTop" :projectId="scheduleDetail.projectId" />
|
||
</div>
|
||
</el-drawer>
|
||
|
||
<FormDialog v-model="addDialog" :projects="projects" @save="handleSave" />
|
||
|
||
<el-drawer title="延期记录" :visible.sync="postponeDrawer" direction="btt" size="90%">
|
||
<postpone :scheduleId="scheduleDetail.scheduleId" />
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import { listProject } from "@/api/oa/project";
|
||
import { addByProjectId, delProjectSchedule, listProjectSchedule, updateProjectSchedule } from "@/api/oa/projectSchedule";
|
||
import { listUser } from "@/api/system/user";
|
||
import ProjectSelect from "@/components/fad-service/ProjectSelect/index.vue";
|
||
import UserSelect from "@/components/UserSelect/index.vue";
|
||
import FormDialog from "./components/FormDialog.vue";
|
||
import Postpone from "./components/postpone.vue";
|
||
import ProjectScheduleStep from "./components/step.vue";
|
||
|
||
export default {
|
||
name: "Schedule",
|
||
dicts: ['sys_project_status', 'sys_trade_type', 'sys_project_type', 'sys_project_code'],
|
||
components: {
|
||
UserSelect,
|
||
ProjectScheduleStep,
|
||
FormDialog,
|
||
ProjectSelect,
|
||
Postpone
|
||
},
|
||
data () {
|
||
return {
|
||
loading: false,
|
||
detailDrawer: false,
|
||
fileShow: false,
|
||
addDialog: false,
|
||
multiple: true,
|
||
form: {},
|
||
projects: [],
|
||
scheduleList: [],
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
startTime: '',
|
||
endTime: '',
|
||
steward: ''
|
||
},
|
||
total: 0,
|
||
searchTime: [],
|
||
showSearch: true,
|
||
recentProjects: [],
|
||
scheduleDetail: {},
|
||
userList: [],
|
||
postponeDrawer: false
|
||
};
|
||
|
||
},
|
||
mounted () {
|
||
this.currentUser = this.$store.state.user
|
||
this.getList();
|
||
this.getProjectList();
|
||
this.getAllUser();
|
||
const cache = localStorage.getItem('oa_recent_projects');
|
||
if (cache) {
|
||
this.recentProjects = JSON.parse(cache);
|
||
}
|
||
},
|
||
methods: {
|
||
tryOpenDetail (trackId) {
|
||
if (!trackId) return;
|
||
const found = this.scheduleList.find(item => item.scheduleId === trackId || item.projectId === trackId);
|
||
if (found) {
|
||
this.getScheduleDetail(found);
|
||
} else {
|
||
this.$nextTick(() => {
|
||
const found = this.scheduleList.find(item => item.scheduleId === trackId || item.projectId === trackId);
|
||
if (found) {
|
||
this.getScheduleDetail(found);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
closeDetailShow (done) {
|
||
this.getList();
|
||
done()
|
||
},
|
||
getAllUser () {
|
||
listUser({ pageNum: 1, pageSize: 999 }).then(res => {
|
||
this.userList = res.rows
|
||
})
|
||
},
|
||
handleStatusChange (row) {
|
||
updateProjectSchedule(row).then(res => {
|
||
this.getList();
|
||
this.$modal.msgSuccess("操作成功")
|
||
})
|
||
},
|
||
// 绑定进度
|
||
handleAdd () {
|
||
this.addDialog = true;
|
||
},
|
||
selectPrePay () {
|
||
this.handleQuery()
|
||
},
|
||
/** 搜索按钮操作 */
|
||
handleQuery () {
|
||
this.queryParams.pageNum = 1;
|
||
this.getList();
|
||
},
|
||
getDateStr (date) {
|
||
if (!date) {
|
||
return ''
|
||
}
|
||
return this.parseTime(date, '{y}-{m}-{d} {h}:{i}:{s}')
|
||
},
|
||
getList () {
|
||
this.loading = true
|
||
/* 日期搜索条件 */
|
||
if (this.searchTime && this.searchTime.length) {
|
||
this.queryParams.startTime = this.getDateStr(this.searchTime[0])
|
||
this.queryParams.endTime = this.getDateStr(this.searchTime[1])
|
||
}
|
||
this.queryParams.projectId = this.$route.query.projectId?this.$route.query.projectId:null
|
||
this.queryParams.trackId = this.$route.query.trackId?this.$route.query.trackId:null
|
||
listProjectSchedule(this.queryParams).then(res => {
|
||
this.scheduleList = res.rows
|
||
this.total = res.total
|
||
let cache = JSON.parse(localStorage.getItem('oa_recent_projects') || '[]')
|
||
const id2idx = new Map()
|
||
cache.forEach((item, idx) => id2idx.set(item.scheduleId, idx))
|
||
for (const row of res.rows) {
|
||
const hit = id2idx.get(row.scheduleId)
|
||
if (hit !== undefined) {
|
||
cache[hit] = row
|
||
} else {
|
||
cache.unshift(row)
|
||
}
|
||
}
|
||
cache = cache.slice(0, 2)
|
||
/* 2‑5 回写缓存 + 更新响应式数据 */
|
||
localStorage.setItem('oa_recent_projects', JSON.stringify(cache))
|
||
this.recentProjects = cache
|
||
/* 3. 结束 loading */
|
||
this.loading = false
|
||
})
|
||
},
|
||
getProjectList () {
|
||
let params = {
|
||
pageNum: 1,
|
||
pageSize: 999,
|
||
}
|
||
listProject(params).then(res => {
|
||
this.projects = res.rows
|
||
})
|
||
},
|
||
/** 保存处理*/
|
||
handleSave (payload) {
|
||
addByProjectId(payload).then(response => {
|
||
this.getList();
|
||
this.$modal.msgSuccess("绑定成功")
|
||
|
||
})
|
||
},
|
||
handleDetail (row) {
|
||
// 把当前项目放到数组最前面,去重
|
||
const list = [row, ...this.recentProjects.filter(p => p.projectId !== row.projectId)];
|
||
// 只保留前 2 条
|
||
this.recentProjects = list.slice(0, 2);
|
||
// 持久化
|
||
localStorage.setItem('oa_recent_projects', JSON.stringify(this.recentProjects));
|
||
|
||
this.getScheduleDetail(row)
|
||
},
|
||
handlePostpone (row) {
|
||
// 打开延期记录弹窗
|
||
this.postponeDrawer = true
|
||
this.scheduleDetail = row
|
||
},
|
||
getScheduleDetail (row) {
|
||
this.scheduleDetail = row
|
||
this.detailDrawer = true
|
||
},
|
||
|
||
/* ========= 左侧主列表删除(支持单删或批量 ids) ========= */
|
||
handleDelete (row) {
|
||
/* 支持:row.scheduleId 或 this.ids = '1,2,3' */
|
||
const scheduleIds = row.scheduleId || this.ids
|
||
|
||
this.$modal.confirm(`将会删除进度编号为 "${scheduleIds}" 的数据项, 同时会删除所有的子进度且无法找回 !!! 是否继续? `)
|
||
.then(() => {
|
||
this.loading = true
|
||
return delProjectSchedule(scheduleIds)
|
||
})
|
||
.then(() => {
|
||
/* 刷新左侧列表并提示 */
|
||
this.getList()
|
||
this.$modal.msgSuccess('删除成功')
|
||
})
|
||
},
|
||
|
||
// 多选框选中数据
|
||
handleSelectionChange (selection) {
|
||
this.ids = selection.map(item => item.scheduleId)
|
||
this.single = selection.length !== 1
|
||
this.multiple = !selection.length
|
||
},
|
||
|
||
/** 重置按钮操作 */
|
||
resetQuery () {
|
||
this.searchTime = [];
|
||
this.resetForm("queryForm");
|
||
this.handleQuery();
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
<style scoped>
|
||
.uploader {
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.file-item {
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.file-row {
|
||
display: flex;
|
||
font-size: small;
|
||
color: #414141;
|
||
align-items: center;
|
||
}
|
||
|
||
.file-icon {
|
||
width: 40px;
|
||
text-align: center;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.file-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.file-meta {
|
||
color: #909399;
|
||
font-size: 12px;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.file-actions el-button+el-button {
|
||
margin-left: 4px;
|
||
}
|
||
</style>
|