You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

470 lines
21 KiB

5 days ago
5 days ago
  1. <template>
  2. <div class="mod-config">
  3. <el-form :inline="true" label-position="top" class="query-form">
  4. <el-form-item label="物料号">
  5. <el-input v-model="searchData.projectNo" clearable placeholder="请输入物料号" style="width: 140px"></el-input>
  6. </el-form-item>
  7. <el-form-item label="数量">
  8. <el-input v-model="searchData.taskQty" clearable placeholder="请输入数量" style="width: 140px"></el-input>
  9. </el-form-item>
  10. <el-form-item label="状态">
  11. <el-select v-model="searchData.status" clearable placeholder="全部" style="width: 120px">
  12. <el-option label="已排产" value="已排产"></el-option>
  13. <el-option label="进行中" value="进行中"></el-option>
  14. <el-option label="已完成" value="已完成"></el-option>
  15. </el-select>
  16. </el-form-item>
  17. <el-form-item label="计划发货日期">
  18. <el-date-picker v-model="searchData.deliveryStartDate" type="date" value-format="yyyy-MM-dd" placeholder="开始" style="width: 130px"></el-date-picker>
  19. </el-form-item>
  20. <el-form-item label="至">
  21. <el-date-picker v-model="searchData.deliveryEndDate" type="date" value-format="yyyy-MM-dd" placeholder="结束" style="width: 130px"></el-date-picker>
  22. </el-form-item>
  23. <el-form-item label=" " style="margin-top: -11px">
  24. <el-button @click="getDataList('Y')" plain class="search-btn">查询</el-button>
  25. <el-button @click="resetQuery()" plain class="reset-btn">重置</el-button>
  26. <el-button @click="openEditDialog()" plain class="add-btn">新增任务单</el-button>
  27. </el-form-item>
  28. </el-form>
  29. <el-table class="data-table" :data="dataList" :height="tableHeight" border highlight-current-row v-loading="dataListLoading" style="width: 100%">
  30. <el-table-column type="index" label="#" width="50" align="center"></el-table-column>
  31. <el-table-column prop="projectNo" label="物料号" width="120" align="center"></el-table-column>
  32. <el-table-column prop="taskQty" label="数量" width="130" align="center"></el-table-column>
  33. <el-table-column prop="specialRequirement" label="特殊要求" min-width="180" show-overflow-tooltip></el-table-column>
  34. <el-table-column prop="planDeliveryDate" label="计划发货日期" width="120" align="center"></el-table-column>
  35. <el-table-column prop="currentNode" label="当前节点" width="120" align="center"></el-table-column>
  36. <el-table-column prop="assigneeSummary" label="节点负责人" min-width="160" show-overflow-tooltip></el-table-column>
  37. <el-table-column label="节点进度" width="100" align="center">
  38. <template slot-scope="scope">
  39. <el-tag size="small" type="info">{{ scope.row.nodeDoneCount }}/{{ scope.row.nodeTotalCount }}</el-tag>
  40. </template>
  41. </el-table-column>
  42. <el-table-column prop="status" label="状态" width="90" align="center">
  43. <template slot-scope="scope">
  44. <el-tag :type="getStatusType(scope.row.status)" size="small">{{ scope.row.status }}</el-tag>
  45. </template>
  46. </el-table-column>
  47. <el-table-column prop="finishDate" label="完工日期" width="120" align="center">
  48. <template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template>
  49. </el-table-column>
  50. <el-table-column label="操作" width="300" align="center" >
  51. <template slot-scope="scope">
  52. <a type="text" @click="openEditDialog(scope.row)" v-if="scope.row.status !== '已完成'">修改</a>
  53. <a type="text" @click="openAssignDialog(scope.row)" v-if="scope.row.status !== '已完成'">分配人员</a>
  54. <a type="text" @click="finishOrder(scope.row)" v-if="scope.row.status !== '已完成'">完工</a>
  55. <a type="text" style="color:#F56C6C" @click="deleteOrder(scope.row)" v-if="scope.row.status !== '已完成'">删除</a>
  56. </template>
  57. </el-table-column>
  58. </el-table>
  59. <el-pagination
  60. @size-change="sizeChangeHandle"
  61. @current-change="currentChangeHandle"
  62. :current-page="pageIndex"
  63. :page-sizes="[10, 20, 50, 100]"
  64. :page-size="pageSize"
  65. :total="totalPage"
  66. layout="total, sizes, prev, pager, next, jumper"
  67. style="margin-top: 20px; text-align: right">
  68. </el-pagination>
  69. <el-dialog :title="saveHeaderData.orderNo ? '修改任务订单' : '新增任务订单'" :visible.sync="setUp.reviewFlag" width="550px" :close-on-click-modal="false" v-drag>
  70. <el-form
  71. ref="editForm"
  72. :model="saveHeaderData"
  73. label-position="top"
  74. class="edit-form">
  75. <el-row :gutter="20">
  76. <el-col :span="12"><el-form-item label="物料号" required><el-input v-model="saveHeaderData.projectNo"></el-input></el-form-item></el-col>
  77. <el-col :span="12"><el-form-item label="数量" required><el-input v-model="saveHeaderData.taskQty"></el-input></el-form-item></el-col>
  78. </el-row>
  79. <el-row :gutter="20">
  80. <el-col :span="12"><el-form-item label="计划发货日期"><el-date-picker v-model="saveHeaderData.planDeliveryDate" type="date" value-format="yyyy-MM-dd" style="width: 100%"></el-date-picker></el-form-item></el-col>
  81. <el-col :span="12"><el-form-item label="状态"><el-select v-model="saveHeaderData.status" style="width: 100%"><el-option label="已排产" value="已排产"></el-option><el-option label="进行中" value="进行中"></el-option><el-option label="已完成" value="已完成"></el-option></el-select></el-form-item></el-col>
  82. </el-row>
  83. <el-row :gutter="20">
  84. <el-col :span="12">
  85. <el-form-item>
  86. <template slot="label">
  87. 人员分配策略
  88. <el-tooltip effect="dark" placement="top">
  89. <div slot="content">
  90. 默认分配创建订单后系统会按节点角色自动分配该角色下全部人员<br>
  91. 手动分配创建订单后不自动分配需要在分配人员里手工选择负责人
  92. </div>
  93. <i class="el-icon-question" style="margin-left:6px;color:#909399;cursor:pointer;"></i>
  94. </el-tooltip>
  95. </template>
  96. <el-radio-group v-model="saveHeaderData.autoAssignAllUsers">
  97. <el-radio :label="true">默认分配</el-radio>
  98. <el-radio :label="false">手动分配</el-radio>
  99. </el-radio-group>
  100. </el-form-item>
  101. </el-col>
  102. <el-col :span="12">
  103. <el-form-item label="节点报工模式">
  104. <el-radio-group v-model="saveHeaderData.nodeReportMode">
  105. <el-radio label="PARALLEL">并行</el-radio>
  106. <el-radio label="SEQUENTIAL">按顺序</el-radio>
  107. </el-radio-group>
  108. </el-form-item>
  109. </el-col>
  110. </el-row>
  111. <el-form-item label="特殊要求"><el-input v-model="saveHeaderData.specialRequirement" type="textarea" :rows="3"></el-input></el-form-item>
  112. </el-form>
  113. <el-footer style="height: 40px; margin-top: 50px; text-align: center">
  114. <el-button plain class="reset-btn" @click="setUp.reviewFlag = false">取消</el-button>
  115. <el-button plain class="add-btn" :loading="setUp.saveButton" @click="saveOrder">保存</el-button>
  116. </el-footer>
  117. </el-dialog>
  118. <el-dialog title="车间节点报工" :visible.sync="setUp.reportFlag" width="460px" :close-on-click-modal="false" v-drag>
  119. <el-form :model="reportData" label-width="110px" label-position="top">
  120. <el-form-item label="项目号"><el-input v-model="reportData.projectNo" disabled></el-input></el-form-item>
  121. <el-form-item label="报工节点">
  122. <el-select v-model="reportData.nodeCode" placeholder="请选择节点" style="width: 100%">
  123. <el-option v-for="item in reportNodeOptions" :key="item.nodeCode" :label="item.nodeName" :value="item.nodeCode"></el-option>
  124. </el-select>
  125. </el-form-item>
  126. <el-form-item label="报工备注"><el-input v-model="reportData.remark" type="textarea" :rows="2"></el-input></el-form-item>
  127. </el-form>
  128. <el-footer style="height: 40px; margin-top: 50px; text-align: center">
  129. <el-button plain class="reset-btn" @click="setUp.reportFlag = false">取消</el-button>
  130. <el-button plain class="search-btn" :loading="setUp.reportButton" @click="submitNodeReport">提交报工</el-button>
  131. </el-footer>
  132. </el-dialog>
  133. <el-dialog title="节点负责人分配" :visible.sync="setUp.assignFlag" width="700px" :close-on-click-modal="false" v-drag>
  134. <el-table :data="assignNodeList" border>
  135. <el-table-column prop="nodeName" label="节点" width="180"></el-table-column>
  136. <el-table-column label="负责人">
  137. <template slot-scope="scope">
  138. <el-select v-model="scope.row.assigneeUserIdList" filterable clearable multiple collapse-tags placeholder="请选择负责人" style="width: 100%">
  139. <el-option v-for="user in scope.row.userOptions" :key="user.userId" :label="user.displayName || user.username" :value="user.userId"></el-option>
  140. </el-select>
  141. </template>
  142. </el-table-column>
  143. </el-table>
  144. <el-footer style="height: 40px; margin-top: 20px; text-align: center">
  145. <el-button plain class="reset-btn" @click="setUp.assignFlag = false">取消</el-button>
  146. <el-button plain class="add-btn" :loading="setUp.assignButton" @click="saveNodeAssigneeAction">保存分配</el-button>
  147. </el-footer>
  148. </el-dialog>
  149. </div>
  150. </template>
  151. <script>
  152. import { deleteMachiningOrder, finishMachiningOrder, getNodeAssigneeList, getNodeAssigneeUsers, getMachiningOrderList, reportMachiningOrderNode, saveNodeAssignee, saveMachiningOrder } from '@/api/longchuang/productionPlan'
  153. export default {
  154. name: 'ProductionPlanMachiningOrder',
  155. data() {
  156. return {
  157. searchData: { projectNo: '', taskQty: '', status: '', deliveryStartDate: '', deliveryEndDate: '',orderType:'MACHINING', page: 1, limit: 20 },
  158. saveHeaderData: {},
  159. reportData: { orderNo: '', projectNo: '', nodeCode: '', remark: '' },
  160. reportNodeOptions: [],
  161. setUp: { reviewFlag: false, reportFlag: false, assignFlag: false, saveButton: false, reportButton: false, assignButton: false },
  162. dataList: [],
  163. currentAssignOrder: { orderNo: '', orderType: 'MACHINING' },
  164. assignNodeList: [],
  165. pageIndex: 1,
  166. pageSize: 20,
  167. totalPage: 0,
  168. dataListLoading: false,
  169. tableHeight: 520
  170. }
  171. },
  172. activated() {
  173. this.getDataList()
  174. },
  175. methods: {
  176. getDataList(flag) {
  177. if (flag === 'Y') this.pageIndex = 1
  178. this.searchData.page = this.pageIndex
  179. this.searchData.limit = this.pageSize
  180. this.dataListLoading = true
  181. getMachiningOrderList(this.searchData).then(({data}) => {
  182. this.dataListLoading = false
  183. if (data && data.code === 0) {
  184. this.dataList = (data.page.list || []).map(this.normalizeRow)
  185. this.totalPage = data.page.totalCount || 0
  186. } else this.loadMockData()
  187. }).catch(() => {
  188. this.dataListLoading = false
  189. this.loadMockData()
  190. })
  191. },
  192. normalizeRow(row) {
  193. const list = row.nodeList || []
  194. const done = list.filter(item => item.status === '已完成').length
  195. const currentNode = (list.find(item => item.status !== '已完成') || {}).nodeName || '全部完成'
  196. const assigneeSummary = list.filter(item => item.assigneeUserName).map(item => `${item.nodeName}:${item.assigneeUserName}`).join(';')
  197. return { ...row, taskQty: row.taskQty != null && row.taskQty !== '' ? row.taskQty : row.modelNo || '', modelNo: row.modelNo != null && row.modelNo !== '' ? row.modelNo : row.taskQty || '', autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'PARALLEL', nodeList: list, nodeDoneCount: done, nodeTotalCount: list.length, currentNode: row.currentNode || currentNode, assigneeSummary: assigneeSummary || '-' }
  198. },
  199. resetQuery() {
  200. this.searchData = { projectNo: '', taskQty: '', status: '', deliveryStartDate: '', deliveryEndDate: '', page: 1, limit: 20 }
  201. this.getDataList('Y')
  202. },
  203. openEditDialog(row) {
  204. this.saveHeaderData = row ? { ...row, taskQty: row.taskQty != null && row.taskQty !== '' ? row.taskQty : row.modelNo || '', modelNo: row.modelNo != null && row.modelNo !== '' ? row.modelNo : row.taskQty || '', autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'PARALLEL' } : { orderNo: '', projectNo: '', taskQty: '', modelNo: '', floorCount: 1, specialRequirement: '', planDeliveryDate: '', status: '已排产', autoAssignAllUsers: true, nodeReportMode: 'PARALLEL', nodeList: [] }
  205. this.setUp.reviewFlag = true
  206. },
  207. saveOrder() {
  208. if (!this.saveHeaderData.projectNo || !this.saveHeaderData.taskQty) return this.$message.warning('请先填写物料号和数量')
  209. this.setUp.saveButton = true
  210. const payload = { ...this.saveHeaderData, taskQty: this.saveHeaderData.taskQty }
  211. saveMachiningOrder(payload).then(({data}) => {
  212. this.setUp.saveButton = false
  213. if (data && data.code === 0) {
  214. this.$message.success(data.msg || '保存成功')
  215. this.setUp.reviewFlag = false
  216. this.getDataList()
  217. } else this.$message.error(data.msg || '保存失败')
  218. }).catch(() => {
  219. this.setUp.saveButton = false
  220. const orderNo = this.saveHeaderData.orderNo || String(Date.now())
  221. const index = this.dataList.findIndex(item => item.orderNo === orderNo)
  222. const saveData = this.normalizeRow({
  223. ...this.saveHeaderData,
  224. orderNo: orderNo,
  225. nodeList: this.saveHeaderData.nodeList && this.saveHeaderData.nodeList.length ? this.saveHeaderData.nodeList : [
  226. { nodeCode: 'assy', nodeName: '生产', status: '未开始' },
  227. { nodeCode: 'inspect', nodeName: '检验', status: '未开始' },
  228. ]
  229. })
  230. if (index > -1) this.$set(this.dataList, index, saveData)
  231. else this.dataList.unshift(saveData)
  232. this.totalPage = this.dataList.length
  233. this.setUp.reviewFlag = false
  234. this.$message.success('后端未完成,已在前端演示保存')
  235. })
  236. },
  237. openReportDialog(row) {
  238. this.reportNodeOptions = (row.nodeList || []).filter(item => item.status !== '已完成')
  239. this.reportData = { orderNo: row.orderNo, projectNo: row.projectNo, nodeCode: '', remark: '' }
  240. this.setUp.reportFlag = true
  241. },
  242. openAssignDialog(row) {
  243. if (!row.orderNo) return this.$message.warning('请先保存订单后再分配人员')
  244. this.currentAssignOrder = { orderNo: row.orderNo, orderType: 'MACHINING' }
  245. getNodeAssigneeList({ orderNo: row.orderNo, orderType: 'MACHINING' }).then(({ data }) => {
  246. const assignRows = data && data.code === 0 ? (data.rows || []) : []
  247. this.assignNodeList = assignRows.map(item => ({ ...item, assigneeUserIdList: item.assigneeUserIdList || [], userOptions: [] }))
  248. const requests = this.assignNodeList.map(item =>
  249. getNodeAssigneeUsers({ orderType: 'MACHINING', nodeCode: item.nodeCode }).then(({ data: userData }) => {
  250. item.userOptions = userData && userData.code === 0 ? (userData.rows || []) : []
  251. }).catch(() => { item.userOptions = [] })
  252. )
  253. Promise.all(requests).finally(() => { this.setUp.assignFlag = true })
  254. }).catch(() => {
  255. this.$message.error('加载节点分配信息失败')
  256. })
  257. },
  258. saveNodeAssigneeAction() {
  259. this.setUp.assignButton = true
  260. const assigneeList = this.assignNodeList.map(item => ({
  261. nodeCode: item.nodeCode,
  262. nodeName: item.nodeName,
  263. assigneeUserIdList: item.assigneeUserIdList || []
  264. }))
  265. saveNodeAssignee({
  266. orderNo: this.currentAssignOrder.orderNo,
  267. orderType: this.currentAssignOrder.orderType,
  268. assigneeList: assigneeList
  269. }).then(({ data }) => {
  270. this.setUp.assignButton = false
  271. if (data && data.code === 0) {
  272. this.$message.success(data.msg || '分配成功')
  273. this.setUp.assignFlag = false
  274. this.getDataList()
  275. } else this.$message.error(data.msg || '分配失败')
  276. }).catch(() => {
  277. this.setUp.assignButton = false
  278. this.$message.error('分配失败')
  279. })
  280. },
  281. submitNodeReport() {
  282. if (!this.reportData.nodeCode) return this.$message.warning('请选择报工节点')
  283. this.setUp.reportButton = true
  284. reportMachiningOrderNode(this.reportData).then(({data}) => {
  285. this.setUp.reportButton = false
  286. if (data && data.code === 0) {
  287. this.$message.success(data.msg || '报工成功')
  288. this.setUp.reportFlag = false
  289. this.getDataList()
  290. } else this.$message.error(data.msg || '报工失败')
  291. }).catch(() => {
  292. this.setUp.reportButton = false
  293. this.simulateNodeReport(this.reportData.orderNo, this.reportData.nodeCode)
  294. this.setUp.reportFlag = false
  295. this.$message.success('后端未完成,已在前端演示节点报工')
  296. })
  297. },
  298. simulateNodeReport(orderNo, nodeCode) {
  299. const row = this.dataList.find(item => item.orderNo === orderNo)
  300. if (!row) return
  301. const node = row.nodeList.find(item => item.nodeCode === nodeCode)
  302. if (!node) return
  303. node.status = '已完成'
  304. row.status = '进行中'
  305. const nextNode = row.nodeList.find(item => item.status !== '已完成')
  306. row.currentNode = nextNode ? nextNode.nodeName : '全部完成'
  307. row.nodeDoneCount = row.nodeList.filter(item => item.status === '已完成').length
  308. if (!nextNode) {
  309. row.status = '已完成'
  310. row.finishDate = this.dayjs().format('YYYY-MM-DD')
  311. }
  312. },
  313. finishOrder(row) {
  314. finishMachiningOrder({ orderNo: row.orderNo }).then(({data}) => {
  315. if (data && data.code === 0) {
  316. this.$message.success(data.msg || '完工成功')
  317. this.getDataList()
  318. } else this.$message.error(data.msg || '完工失败')
  319. }).catch(() => {
  320. row.status = '已完成'
  321. row.finishDate = this.dayjs().format('YYYY-MM-DD')
  322. row.nodeList = row.nodeList.map(item => ({ ...item, status: '已完成' }))
  323. row.currentNode = '全部完成'
  324. row.nodeDoneCount = row.nodeTotalCount
  325. this.$message.success('后端未完成,已在前端演示完工')
  326. })
  327. },
  328. deleteOrder(row) {
  329. this.$confirm('确定删除该加工订单吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
  330. deleteMachiningOrder({ orderNo: row.orderNo }).then(({data}) => {
  331. if (data && data.code === 0) {
  332. this.$message.success(data.msg || '删除成功')
  333. this.getDataList()
  334. } else this.$message.error(data.msg || '删除失败')
  335. }).catch(() => {
  336. this.dataList = this.dataList.filter(item => item.orderNo !== row.orderNo)
  337. this.totalPage = this.dataList.length
  338. this.$message.success('后端未完成,已在前端演示删除')
  339. })
  340. }).catch(() => {})
  341. },
  342. getStatusType(status) {
  343. const map = { 已排产: 'info', 进行中: 'warning', 已完成: 'success' }
  344. return map[status] || 'info'
  345. },
  346. sizeChangeHandle(val) {
  347. this.pageSize = val
  348. this.pageIndex = 1
  349. this.getDataList()
  350. },
  351. currentChangeHandle(val) {
  352. this.pageIndex = val
  353. this.getDataList()
  354. }
  355. }
  356. }
  357. </script>
  358. <style scoped>
  359. .data-table {
  360. background-color: #fff;
  361. border-radius: 4px;
  362. }
  363. .data-table >>> .el-table__header-wrapper th,
  364. .data-table >>> .el-table__fixed-header-wrapper th {
  365. background-color: #f5f7fa !important;
  366. color: #333;
  367. font-weight: 600;
  368. border-color: #ebeef5;
  369. padding: 8px 0;
  370. }
  371. .data-table >>> .el-table__header-wrapper .cell,
  372. .data-table >>> .el-table__fixed-header-wrapper .cell,
  373. .data-table >>> .el-table__body-wrapper .cell,
  374. .data-table >>> .el-table__fixed-body-wrapper .cell {
  375. padding: 0 10px;
  376. overflow: hidden;
  377. text-overflow: ellipsis;
  378. white-space: nowrap;
  379. font-size: 13px !important;
  380. }
  381. .data-table >>> .el-table__body tr:hover > td {
  382. background-color: #f5f7fa !important;
  383. }
  384. .data-table >>> .el-table__body tr.current-row > td {
  385. background-color: #ecf5ff !important;
  386. }
  387. .query-form {
  388. background-color: #fff;
  389. padding: 15px 15px 5px 15px;
  390. border-radius: 4px;
  391. margin-bottom: 12px;
  392. }
  393. .query-form >>> .el-form-item__label {
  394. color: #333;
  395. font-size: 13px;
  396. padding-bottom: 5px;
  397. }
  398. .query-form >>> .el-input__inner {
  399. height: 32px;
  400. line-height: 32px;
  401. border-radius: 4px;
  402. font-size: 13px;
  403. }
  404. .query-form >>> .el-button {
  405. height: 32px;
  406. padding: 0 15px;
  407. font-size: 13px;
  408. border-radius: 4px;
  409. }
  410. .search-btn {
  411. background-color: #ecf5ff;
  412. border-color: #b3d8ff;
  413. color: #409eff;
  414. }
  415. .search-btn:hover {
  416. background-color: #409eff;
  417. border-color: #409eff;
  418. color: #fff;
  419. }
  420. .reset-btn {
  421. background-color: #f5f7fa;
  422. border-color: #d3d4d6;
  423. color: #606266;
  424. }
  425. .reset-btn:hover {
  426. background-color: #909399;
  427. border-color: #909399;
  428. color: #fff;
  429. }
  430. .add-btn {
  431. background-color: #f0f9eb;
  432. border-color: #c2e7b0;
  433. color: #67c23a;
  434. }
  435. .add-btn:hover {
  436. background-color: #67c23a;
  437. border-color: #67c23a;
  438. color: #fff;
  439. }
  440. .dialog-footer {
  441. text-align: right;
  442. }
  443. .edit-form {
  444. margin-left: 5px;
  445. margin-top: -5px;
  446. }
  447. </style>