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.

780 lines
29 KiB

2 weeks ago
2 weeks ago
2 weeks 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.modelNo" clearable placeholder="请输入物料号" style="width: 160px"></el-input>
  6. </el-form-item>
  7. <el-form-item label="状态">
  8. <el-select v-model="searchData.status" clearable placeholder="全部" style="width: 120px">
  9. <el-option label="已排产" value="已排产"></el-option>
  10. <el-option label="进行中" value="进行中"></el-option>
  11. <el-option label="已完成" value="已完成"></el-option>
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item label="计划完工日期">
  15. <el-date-picker v-model="searchData.planStartDate" type="date" value-format="yyyy-MM-dd" placeholder="开始" style="width: 130px"></el-date-picker>
  16. </el-form-item>
  17. <el-form-item label="至">
  18. <el-date-picker v-model="searchData.planEndDate" type="date" value-format="yyyy-MM-dd" placeholder="结束" style="width: 130px"></el-date-picker>
  19. </el-form-item>
  20. <el-form-item label=" " style="margin-top: -11px">
  21. <el-button @click="searchTable('Y')" plain class="search-btn">查询</el-button>
  22. <el-button @click="resetQuery()" plain class="reset-btn">重置</el-button>
  23. <el-button @click="openEditDialog()" plain class="add-btn">新增任务单</el-button>
  24. </el-form-item>
  25. </el-form>
  26. <el-table
  27. ref="orderTable"
  28. class="data-table"
  29. :data="dataList"
  30. :height="tableHeight"
  31. border
  32. highlight-current-row
  33. v-loading="dataListLoading"
  34. style="width: 100%"
  35. @current-change="onCurrentRowChange">
  36. <el-table-column type="index" label="#" width="50" align="center"></el-table-column>
  37. <el-table-column prop="orderNo" label="任务单号" width="180" align="center"></el-table-column>
  38. <el-table-column prop="modelNo" label="物料号" width="160" align="center"></el-table-column>
  39. <el-table-column prop="taskQty" label="计划数量" width="100" align="center"></el-table-column>
  40. <el-table-column prop="reportQty" label="累计报工数量" width="120" align="center"></el-table-column>
  41. <el-table-column prop="currentNode" label="当前节点" width="120" align="center"></el-table-column>
  42. <el-table-column prop="assigneeSummary" label="节点负责人" min-width="160" show-overflow-tooltip></el-table-column>
  43. <el-table-column label="节点进度" width="100" align="center">
  44. <template slot-scope="scope">
  45. <el-tag size="small" type="info">{{ scope.row.nodeDoneCount }}/{{ scope.row.nodeTotalCount }}</el-tag>
  46. </template>
  47. </el-table-column>
  48. <el-table-column prop="status" label="状态" width="90" align="center">
  49. <template slot-scope="scope">
  50. <el-tag :type="getStatusType(scope.row.status)" size="small">{{ scope.row.status }}</el-tag>
  51. </template>
  52. </el-table-column>
  53. <el-table-column prop="planFinishDate" label="计划完工日期" width="120" align="center"></el-table-column>
  54. <el-table-column prop="finishDate" label="完工日期" width="120" align="center">
  55. <template slot-scope="scope">{{ scope.row.finishDate || '-' }}</template>
  56. </el-table-column>
  57. <el-table-column label="操作" width="300" align="center">
  58. <template slot-scope="scope">
  59. <a type="text" @click="openEditDialog(scope.row)" v-if="scope.row.status === '已排产'">修改</a>
  60. <a type="text" @click="openAssignDialog(scope.row)" v-if="scope.row.status !== '已完成' && scope.row.currentNode!=='全部完成'">分配人员</a>
  61. <a type="text" @click="finishTask(scope.row)" v-if="scope.row.status !== '已完成'">完工</a>
  62. <a type="text" style="color:#F56C6C" @click="deleteTask(scope.row)" v-if="scope.row.status !== '已完成'">删除</a>
  63. </template>
  64. </el-table-column>
  65. </el-table>
  66. <el-pagination
  67. @size-change="sizeChangeHandle"
  68. @current-change="currentChangeHandle"
  69. :current-page="pageIndex"
  70. :page-sizes="[10, 20, 50, 100]"
  71. :page-size="pageSize"
  72. :total="totalPage"
  73. layout="total, sizes, prev, pager, next, jumper"
  74. style="margin-top: 20px; text-align: right">
  75. </el-pagination>
  76. <div class="detail-tabs-wrap">
  77. <el-tabs v-model="detailTabName" type="border-card">
  78. <el-tab-pane label="节点状态和日志" name="statusLogs">
  79. <div v-if="selectedOrder.orderNo" class="two-column-layout">
  80. <div class="stages-column">
  81. <div class="column-header">
  82. <i class="el-icon-s-order"></i>
  83. <span>节点流程</span>
  84. <span class="progress-badge">{{ selectedOrderProgressPercent }}%</span>
  85. </div>
  86. <div class="stages-list">
  87. <div
  88. v-for="(stage, index) in selectedOrderNodeList"
  89. :key="stage.nodeCode || index"
  90. class="stage-item"
  91. :class="'stage-' + getStageClass(stage.status)">
  92. <div class="stage-icon">
  93. <i :class="getStageIcon(stage.status)"></i>
  94. </div>
  95. <div class="stage-content">
  96. <div class="stage-name">{{ stage.nodeName }}</div>
  97. <div class="stage-meta">
  98. <el-tag :type="getStageTagType(stage.status)" size="mini" effect="plain">{{ stage.status || '未开始' }}</el-tag>
  99. <span class="stage-owner">负责人{{ stage.assigneeUserName || '-' }}</span>
  100. </div>
  101. </div>
  102. </div>
  103. </div>
  104. </div>
  105. <div class="logs-column">
  106. <div class="column-header">
  107. <i class="el-icon-tickets"></i>
  108. <span>操作日志</span>
  109. <span class="logs-count">{{ selectedOrderLogList.length }}</span>
  110. </div>
  111. <div class="logs-table-wrapper">
  112. <el-table :data="selectedOrderLogList" border size="small" class="detail-table" v-loading="detailLogLoading" height="295px">
  113. <el-table-column prop="logTime" label="时间" width="160" align="center"></el-table-column>
  114. <el-table-column prop="action" label="操作" width="95" align="center"></el-table-column>
  115. <el-table-column prop="nodeName" label="节点" width="130" align="center">
  116. <template slot-scope="scope">{{ scope.row.nodeName || scope.row.nodeCode || '-' }}</template>
  117. </el-table-column>
  118. <el-table-column prop="operatorName" label="操作人" width="100" align="center"></el-table-column>
  119. <el-table-column prop="comment" label="备注" min-width="220" show-overflow-tooltip>
  120. <template slot-scope="scope">{{ scope.row.comment || '-' }}</template>
  121. </el-table-column>
  122. </el-table>
  123. </div>
  124. </div>
  125. </div>
  126. <el-empty v-else description="请先选择一条任务记录"></el-empty>
  127. </el-tab-pane>
  128. </el-tabs>
  129. </div>
  130. <el-dialog :title="saveHeaderData.orderNo ? '修改机加工生产任务单' : '新增机加工生产任务单'" :visible.sync="setUp.reviewFlag" width="550px" :close-on-click-modal="false" v-drag>
  131. <el-form
  132. ref="editForm"
  133. :model="saveHeaderData"
  134. label-position="top"
  135. class="edit-form">
  136. <el-row :gutter="20">
  137. <el-col :span="12"><el-form-item label="物料号" required><el-input v-model="saveHeaderData.modelNo"></el-input></el-form-item></el-col>
  138. <el-col :span="12"><el-form-item label="计划数量" required><el-input v-model="saveHeaderData.taskQty" :min="1" :max="999999" style="width: 100%"></el-input></el-form-item></el-col>
  139. </el-row>
  140. <el-row :gutter="20">
  141. <el-col :span="12"><el-form-item label="计划完工日期" required><el-date-picker v-model="saveHeaderData.planFinishDate" type="date" value-format="yyyy-MM-dd" style="width: 100%"></el-date-picker></el-form-item></el-col>
  142. <el-col :span="12"><el-form-item label="状态"><el-select disabled 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>
  143. </el-row>
  144. <!-- <el-row :gutter="20">
  145. <el-col :span="12">
  146. <el-form-item>
  147. <template slot="label">
  148. 人员分配策略
  149. <el-tooltip effect="dark" placement="top">
  150. <div slot="content">
  151. 默认分配创建订单后系统会按节点角色自动分配该角色下全部人员<br>
  152. 手动分配创建订单后不自动分配需要在分配人员里手工选择负责人
  153. </div>
  154. <i class="el-icon-question" style="margin-left:6px;color:#909399;cursor:pointer;"></i>
  155. </el-tooltip>
  156. </template>
  157. <el-radio-group v-model="saveHeaderData.autoAssignAllUsers">
  158. <el-radio :label="true">默认分配</el-radio>
  159. <el-radio :label="false">手动分配</el-radio>
  160. </el-radio-group>
  161. </el-form-item>
  162. </el-col>
  163. <el-col :span="12">
  164. <el-form-item label="节点报工模式">
  165. <el-radio-group v-model="saveHeaderData.nodeReportMode">
  166. <el-radio label="PARALLEL">并行</el-radio>
  167. <el-radio label="SEQUENTIAL">串行</el-radio>
  168. </el-radio-group>
  169. </el-form-item>
  170. </el-col>
  171. </el-row>-->
  172. </el-form>
  173. <el-footer style="height: 40px; margin-top: 50px; text-align: center">
  174. <el-button plain class="reset-btn" @click="setUp.reviewFlag = false">取消</el-button>
  175. <el-button plain class="add-btn" :loading="setUp.saveButton" @click="saveTask">保存</el-button>
  176. </el-footer>
  177. </el-dialog>
  178. <el-dialog title="机加工生产报工" :visible.sync="setUp.reportFlag" width="460px" :close-on-click-modal="false" v-drag>
  179. <el-form :model="reportData" label-width="110px" label-position="top">
  180. <el-form-item label="任务单号"><el-input v-model="reportData.taskNo" disabled></el-input></el-form-item>
  181. <el-form-item label="本次报工数量"><el-input v-model="reportData.reportQty" :min="1" :max="999999" style="width: 100%"></el-input></el-form-item>
  182. <el-form-item label="报工备注"><el-input v-model="reportData.remark" type="textarea" :rows="2"></el-input></el-form-item>
  183. </el-form>
  184. <el-footer style="height: 40px; margin-top: 50px; text-align: center">
  185. <el-button plain class="reset-btn" @click="setUp.reportFlag = false">取消</el-button>
  186. <el-button plain class="search-btn" :loading="setUp.reportButton" @click="submitNodeReport">提交报工</el-button>
  187. </el-footer>
  188. </el-dialog>
  189. <el-dialog title="节点负责人分配" :visible.sync="setUp.assignFlag" width="550px" :close-on-click-modal="false" v-drag>
  190. <el-table :data="assignNodeList" border class="data-table">
  191. <el-table-column prop="nodeName" label="节点" width="150"></el-table-column>
  192. <el-table-column label="负责人" width="370">
  193. <template slot-scope="scope">
  194. <el-select v-model="scope.row.assigneeUserIdList" filterable clearable multiple placeholder="请选择负责人" style="width: 100%">
  195. <el-option v-for="user in scope.row.userOptions" :key="user.userId" :label="user.displayName || user.username" :value="user.userId"></el-option>
  196. </el-select>
  197. </template>
  198. </el-table-column>
  199. </el-table>
  200. <el-footer style="height: 40px; margin-top: 20px; text-align: center">
  201. <el-button plain class="reset-btn" @click="setUp.assignFlag = false">取消</el-button>
  202. <el-button plain class="add-btn" :loading="setUp.assignButton" @click="saveNodeAssigneeAction">保存分配</el-button>
  203. </el-footer>
  204. </el-dialog>
  205. </div>
  206. </template>
  207. <script>
  208. import { deleteMachiningTask, finishMachiningTask, getMachiningTaskList, getNodeAssigneeList, getNodeAssigneeUsers, getReportLogList, reportMachiningTaskNode, saveMachiningTask, saveNodeAssignee } from '@/api/longchuang/productionPlan'
  209. export default {
  210. name: 'ProductionPlanMachiningTask',
  211. data() {
  212. return {
  213. searchData: { modelNo: '', status: '', planStartDate: '', planEndDate: '', page: 1, limit: 20 },
  214. saveHeaderData: {},
  215. reportData: { orderNo: '', taskNo: '', nodeCode: 'machiningProduction', reportQty: '', remark: '' },
  216. setUp: { reviewFlag: false, reportFlag: false, assignFlag: false, saveButton: false, reportButton: false, assignButton: false },
  217. dataList: [],
  218. currentAssignOrder: { orderNo: '', orderType: 'MACHINING' },
  219. assignNodeList: [],
  220. selectedOrder: {},
  221. detailTabName: 'statusLogs',
  222. selectedOrderLogList: [],
  223. detailLogLoading: false,
  224. pageIndex: 1,
  225. pageSize: 20,
  226. totalPage: 0,
  227. dataListLoading: false,
  228. tableHeight: (window.innerHeight - 320) / 2
  229. }
  230. },
  231. activated() {
  232. this.searchTable()
  233. },
  234. methods: {
  235. searchTable(flag) {
  236. if (flag === 'Y') this.pageIndex = 1
  237. this.searchData.page = this.pageIndex
  238. this.searchData.limit = this.pageSize
  239. this.dataListLoading = true
  240. getMachiningTaskList(this.searchData).then(({ data }) => {
  241. this.dataListLoading = false
  242. if (data && data.code === 0) {
  243. this.dataList = (data.page.list || []).map(this.normalizeRow)
  244. this.totalPage = data.page.totalCount || 0
  245. this.syncSelectedOrder()
  246. } else this.loadMockData()
  247. }).catch(() => {
  248. this.dataListLoading = false
  249. this.loadMockData()
  250. })
  251. },
  252. normalizeRow(row) {
  253. const list = row.nodeList || []
  254. const done = list.filter(item => item.status === '已完成').length
  255. const currentNode = (list.find(item => item.status !== '已完成') || {}).nodeName || '全部完成'
  256. const assigneeSummary = list.filter(item => item.assigneeUserName).map(item => `${item.nodeName}:${item.assigneeUserName}`).join(';')
  257. return { ...row, autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'PARALLEL', nodeList: list, nodeDoneCount: done, nodeTotalCount: list.length, currentNode: row.currentNode || currentNode, reportQty: row.reportQty || 0, assigneeSummary: assigneeSummary || '-' }
  258. },
  259. loadMockData() {
  260. this.dataList = [
  261. {
  262. orderNo: 'MOCK-MACH-001',
  263. projectNo: 'MAT-120001',
  264. modelNo: 'MAT-120001',
  265. taskQty: 120,
  266. reportQty: 30,
  267. autoAssignAllUsers: true,
  268. nodeReportMode: 'PARALLEL',
  269. planFinishDate: '2026-05-25',
  270. finishDate: '',
  271. status: '进行中',
  272. nodeList: [
  273. { nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '进行中' }
  274. ]
  275. },
  276. {
  277. orderNo: 'MOCK-MACH-002',
  278. projectNo: 'MAT-120188',
  279. modelNo: 'MAT-120188',
  280. taskQty: 240,
  281. reportQty: 0,
  282. autoAssignAllUsers: true,
  283. nodeReportMode: 'PARALLEL',
  284. planFinishDate: '2026-05-27',
  285. finishDate: '',
  286. status: '已排产',
  287. nodeList: [
  288. { nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '未开始' }
  289. ]
  290. }
  291. ].map(this.normalizeRow)
  292. this.totalPage = this.dataList.length
  293. this.syncSelectedOrder()
  294. },
  295. syncSelectedOrder() {
  296. if (!this.dataList.length) {
  297. this.selectedOrder = {}
  298. this.selectedOrderLogList = []
  299. return
  300. }
  301. const selectedOrderNo = this.selectedOrder && this.selectedOrder.orderNo
  302. const matched = selectedOrderNo ? this.dataList.find(item => item.orderNo === selectedOrderNo) : null
  303. const current = matched || this.dataList[0]
  304. this.selectedOrder = current
  305. this.$nextTick(() => {
  306. if (this.$refs.orderTable && current) {
  307. this.$refs.orderTable.setCurrentRow(current)
  308. }
  309. })
  310. this.loadSelectedOrderLogList(current)
  311. },
  312. onCurrentRowChange(row) {
  313. if (!row || !row.orderNo) {
  314. this.selectedOrder = {}
  315. this.selectedOrderLogList = []
  316. return
  317. }
  318. this.selectedOrder = row
  319. this.loadSelectedOrderLogList(row)
  320. },
  321. loadSelectedOrderLogList(row) {
  322. if (!row || !row.orderNo) {
  323. this.selectedOrderLogList = []
  324. return
  325. }
  326. this.detailLogLoading = true
  327. getReportLogList({ orderNo: row.orderNo, orderType: 'MACHINING' }).then(({ data }) => {
  328. this.detailLogLoading = false
  329. this.selectedOrderLogList = data && data.code === 0 ? (data.rows || []) : []
  330. }).catch(() => {
  331. this.detailLogLoading = false
  332. this.selectedOrderLogList = []
  333. })
  334. },
  335. resetQuery() {
  336. this.searchData = { modelNo: '', status: '', planStartDate: '', planEndDate: '', page: 1, limit: 20 }
  337. this.searchTable('Y')
  338. },
  339. openEditDialog(row) {
  340. this.saveHeaderData = row
  341. ? { ...row, autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'PARALLEL' }
  342. : { orderNo: '', modelNo: '', taskQty: 1, reportQty: 0, planFinishDate: '', status: '已排产', autoAssignAllUsers: true, nodeReportMode: 'PARALLEL', nodeList: [] }
  343. this.setUp.reviewFlag = true
  344. },
  345. saveTask() {
  346. if (!this.saveHeaderData.modelNo) return this.$message.warning('请先填写物料号')
  347. if (!this.saveHeaderData.taskQty || Number(this.saveHeaderData.taskQty) <= 0) return this.$message.warning('请输入有效的计划数量')
  348. if (!this.saveHeaderData.planFinishDate) return this.$message.warning('请选择计划完工日期')
  349. const payload = {
  350. ...this.saveHeaderData,
  351. projectNo: this.saveHeaderData.modelNo,
  352. nodeList: this.saveHeaderData.nodeList && this.saveHeaderData.nodeList.length
  353. ? this.saveHeaderData.nodeList
  354. : [{ nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '未开始' }]
  355. }
  356. this.setUp.saveButton = true
  357. saveMachiningTask(payload).then(({ data }) => {
  358. this.setUp.saveButton = false
  359. if (data && data.code === 0) {
  360. this.$message.success(data.msg || '保存成功')
  361. this.setUp.reviewFlag = false
  362. this.searchTable()
  363. } else this.$message.error(data.msg || '保存失败')
  364. }).catch(() => {
  365. this.setUp.saveButton = false
  366. const orderNo = this.saveHeaderData.orderNo || String(Date.now())
  367. const index = this.dataList.findIndex(item => item.orderNo === orderNo)
  368. const saveData = this.normalizeRow({
  369. ...payload,
  370. orderNo: orderNo
  371. })
  372. if (index > -1) this.$set(this.dataList, index, saveData)
  373. else this.dataList.unshift(saveData)
  374. this.totalPage = this.dataList.length
  375. this.setUp.reviewFlag = false
  376. this.$message.success('后端未完成,已在前端演示保存')
  377. })
  378. },
  379. openReportDialog(row) {
  380. const defaultNode = (row.nodeList || []).find(item => item.status !== '已完成') || {}
  381. this.reportData = {
  382. orderNo: row.orderNo,
  383. taskNo: row.orderNo,
  384. nodeCode: defaultNode.nodeCode || 'machiningProduction',
  385. reportQty: '',
  386. remark: ''
  387. }
  388. this.setUp.reportFlag = true
  389. },
  390. openAssignDialog(row) {
  391. if (!row.orderNo) return this.$message.warning('请先保存任务单后再分配人员')
  392. this.currentAssignOrder = { orderNo: row.orderNo, orderType: 'MACHINING' }
  393. getNodeAssigneeList({ orderNo: row.orderNo, orderType: 'MACHINING' }).then(({ data }) => {
  394. const assignRows = data && data.code === 0 ? (data.rows || []) : []
  395. this.assignNodeList = assignRows.map(item => ({ ...item, assigneeUserIdList: item.assigneeUserIdList || [], userOptions: [] }))
  396. const requests = this.assignNodeList.map(item =>
  397. getNodeAssigneeUsers({ orderType: 'MACHINING', nodeCode: item.nodeCode }).then(({ data: userData }) => {
  398. item.userOptions = userData && userData.code === 0 ? (userData.rows || []) : []
  399. }).catch(() => { item.userOptions = [] })
  400. )
  401. Promise.all(requests).finally(() => { this.setUp.assignFlag = true })
  402. }).catch(() => {
  403. this.$message.error('加载节点分配信息失败')
  404. })
  405. },
  406. saveNodeAssigneeAction() {
  407. this.setUp.assignButton = true
  408. const assigneeList = this.assignNodeList.map(item => ({
  409. nodeCode: item.nodeCode,
  410. nodeName: item.nodeName,
  411. assigneeUserIdList: item.assigneeUserIdList || []
  412. }))
  413. saveNodeAssignee({
  414. orderNo: this.currentAssignOrder.orderNo,
  415. orderType: this.currentAssignOrder.orderType,
  416. assigneeList: assigneeList
  417. }).then(({ data }) => {
  418. this.setUp.assignButton = false
  419. if (data && data.code === 0) {
  420. this.$message.success(data.msg || '分配成功')
  421. this.setUp.assignFlag = false
  422. this.searchTable()
  423. } else this.$message.error(data.msg || '分配失败')
  424. }).catch(() => {
  425. this.setUp.assignButton = false
  426. this.$message.error('分配失败')
  427. })
  428. },
  429. submitNodeReport() {
  430. if (!this.reportData.reportQty || Number(this.reportData.reportQty) <= 0) return this.$message.warning('请输入有效报工数量')
  431. if (!this.reportData.nodeCode) this.reportData.nodeCode = 'machiningProduction'
  432. this.setUp.reportButton = true
  433. reportMachiningTaskNode(this.reportData).then(({ data }) => {
  434. this.setUp.reportButton = false
  435. if (data && data.code === 0) {
  436. this.$message.success(data.msg || '报工成功')
  437. this.setUp.reportFlag = false
  438. this.searchTable()
  439. } else this.$message.error(data.msg || '报工失败')
  440. }).catch(() => {
  441. this.setUp.reportButton = false
  442. this.simulateNodeReport(this.reportData.orderNo, this.reportData.nodeCode, this.reportData.reportQty)
  443. this.setUp.reportFlag = false
  444. this.$message.success('后端未完成,已在前端演示节点报工')
  445. })
  446. },
  447. simulateNodeReport(orderNo, nodeCode, qty) {
  448. const row = this.dataList.find(item => item.orderNo === orderNo)
  449. if (!row) return
  450. const node = row.nodeList.find(item => item.nodeCode === nodeCode)
  451. if (!node) return
  452. node.status = '已完成'
  453. row.reportQty = Number(row.reportQty || 0) + Number(qty || 0)
  454. row.status = '进行中'
  455. const nextNode = row.nodeList.find(item => item.status !== '已完成')
  456. row.currentNode = nextNode ? nextNode.nodeName : '全部完成'
  457. row.nodeDoneCount = row.nodeList.filter(item => item.status === '已完成').length
  458. row.finishDate = ''
  459. },
  460. finishTask(row) {
  461. finishMachiningTask({ orderNo: row.orderNo }).then(({ data }) => {
  462. if (data && data.code === 0) {
  463. this.$message.success(data.msg || '完工成功')
  464. this.searchTable()
  465. } else this.$message.error(data.msg || '完工失败')
  466. }).catch(() => {
  467. row.status = '已完成'
  468. row.finishDate = this.dayjs().format('YYYY-MM-DD')
  469. row.nodeList = row.nodeList.map(item => ({ ...item, status: '已完成' }))
  470. row.currentNode = '全部完成'
  471. row.nodeDoneCount = row.nodeTotalCount
  472. this.$message.success('后端未完成,已在前端演示完工')
  473. })
  474. },
  475. deleteTask(row) {
  476. this.$confirm('确定删除该任务单吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
  477. deleteMachiningTask({ orderNo: row.orderNo }).then(({ data }) => {
  478. if (data && data.code === 0) {
  479. this.$message.success(data.msg || '删除成功')
  480. this.searchTable()
  481. } else this.$message.error(data.msg || '删除失败')
  482. }).catch(() => {
  483. this.dataList = this.dataList.filter(item => item.orderNo !== row.orderNo)
  484. this.totalPage = this.dataList.length
  485. this.$message.success('后端未完成,已在前端演示删除')
  486. })
  487. }).catch(() => {})
  488. },
  489. getStatusType(status) {
  490. const map = { 已排产: 'info', 进行中: 'warning', 已完成: 'success' }
  491. return map[status] || 'info'
  492. },
  493. getStageClass(status) {
  494. if (status === '已完成') return 'done'
  495. if (status === '进行中') return 'processing'
  496. return 'pending'
  497. },
  498. getStageIcon(status) {
  499. if (status === '已完成') return 'el-icon-check'
  500. if (status === '进行中') return 'el-icon-loading'
  501. return 'el-icon-time'
  502. },
  503. getStageTagType(status) {
  504. if (status === '已完成') return 'success'
  505. if (status === '进行中') return 'warning'
  506. return 'info'
  507. },
  508. sizeChangeHandle(val) {
  509. this.pageSize = val
  510. this.pageIndex = 1
  511. this.searchTable()
  512. },
  513. currentChangeHandle(val) {
  514. this.pageIndex = val
  515. this.searchTable()
  516. }
  517. },
  518. computed: {
  519. selectedOrderNodeList() {
  520. return (this.selectedOrder && this.selectedOrder.nodeList) ? this.selectedOrder.nodeList : []
  521. },
  522. selectedOrderProgressPercent() {
  523. const total = this.selectedOrderNodeList.length
  524. if (!total) {
  525. return 0
  526. }
  527. const done = this.selectedOrderNodeList.filter(item => item.status === '已完成').length
  528. return Math.round((done * 100) / total)
  529. }
  530. }
  531. }
  532. </script>
  533. <style scoped>
  534. .data-table {
  535. background-color: #fff;
  536. border-radius: 4px;
  537. }
  538. .data-table >>> .cell {
  539. line-height: 20px;
  540. height: 20px;
  541. }
  542. .data-table >>> .el-table__header-wrapper th,
  543. .data-table >>> .el-table__fixed-header-wrapper th {
  544. background-color: #f5f7fa !important;
  545. color: #333;
  546. font-weight: 600;
  547. border-color: #ebeef5;
  548. padding: 8px 0;
  549. }
  550. .data-table >>> .el-table__header-wrapper .cell,
  551. .data-table >>> .el-table__fixed-header-wrapper .cell,
  552. .data-table >>> .el-table__body-wrapper .cell,
  553. .data-table >>> .el-table__fixed-body-wrapper .cell {
  554. padding: 0 10px;
  555. overflow: hidden;
  556. text-overflow: ellipsis;
  557. white-space: nowrap;
  558. font-size: 13px !important;
  559. }
  560. .data-table >>> .el-table__body tr:hover > td {
  561. background-color: #f5f7fa !important;
  562. }
  563. .data-table >>> .el-table__body tr.current-row > td {
  564. background-color: #ecf5ff !important;
  565. }
  566. .query-form {
  567. background-color: #fff;
  568. padding: 15px 15px 5px 15px;
  569. border-radius: 4px;
  570. margin-bottom: 12px;
  571. }
  572. .query-form >>> .el-form-item__label {
  573. color: #333;
  574. font-size: 13px;
  575. padding-bottom: 5px;
  576. }
  577. .query-form >>> .el-input__inner {
  578. height: 32px;
  579. line-height: 32px;
  580. border-radius: 4px;
  581. font-size: 13px;
  582. }
  583. .query-form >>> .el-button {
  584. height: 32px;
  585. padding: 0 15px;
  586. font-size: 13px;
  587. border-radius: 4px;
  588. }
  589. .search-btn {
  590. background-color: #ecf5ff;
  591. border-color: #b3d8ff;
  592. color: #409eff;
  593. }
  594. .search-btn:hover {
  595. background-color: #409eff;
  596. border-color: #409eff;
  597. color: #fff;
  598. }
  599. .reset-btn {
  600. background-color: #f5f7fa;
  601. border-color: #d3d4d6;
  602. color: #606266;
  603. }
  604. .reset-btn:hover {
  605. background-color: #909399;
  606. border-color: #909399;
  607. color: #fff;
  608. }
  609. .add-btn {
  610. background-color: #f0f9eb;
  611. border-color: #c2e7b0;
  612. color: #67c23a;
  613. }
  614. .add-btn:hover {
  615. background-color: #67c23a;
  616. border-color: #67c23a;
  617. color: #fff;
  618. }
  619. .dialog-footer {
  620. text-align: right;
  621. }
  622. .edit-form {
  623. margin-left: 5px;
  624. margin-top: -5px;
  625. }
  626. .detail-tabs-wrap {
  627. margin-top: 12px;
  628. background-color: #fff;
  629. border-radius: 4px;
  630. }
  631. .detail-table {
  632. width: 100%;
  633. }
  634. .detail-table >>> .el-table__header-wrapper th,
  635. .detail-table >>> .el-table__fixed-header-wrapper th {
  636. background-color: #f5f7fa !important;
  637. color: #333;
  638. font-weight: 600;
  639. border-color: #ebeef5;
  640. }
  641. .two-column-layout {
  642. display: flex;
  643. gap: 12px;
  644. }
  645. .stages-column {
  646. width: 38%;
  647. min-width: 320px;
  648. border: 1px solid #ebeef5;
  649. border-radius: 4px;
  650. background: #fff;
  651. }
  652. .logs-column {
  653. flex: 1;
  654. border: 1px solid #ebeef5;
  655. border-radius: 4px;
  656. background: #fff;
  657. }
  658. .column-header {
  659. height: 24px;
  660. display: flex;
  661. align-items: center;
  662. gap: 6px;
  663. padding: 0 12px;
  664. border-bottom: 1px solid #ebeef5;
  665. font-weight: 600;
  666. color: #303133;
  667. }
  668. .progress-badge,
  669. .logs-count {
  670. margin-left: auto;
  671. color: #409eff;
  672. font-size: 12px;
  673. font-weight: 500;
  674. }
  675. .stages-list {
  676. max-height: 295px;
  677. overflow-y: auto;
  678. padding: 10px;
  679. }
  680. .stage-item {
  681. display: flex;
  682. align-items: flex-start;
  683. gap: 10px;
  684. padding: 3px 8px;
  685. border-radius: 4px;
  686. }
  687. .stage-item + .stage-item {
  688. margin-top: 2px;
  689. }
  690. .stage-item.stage-done {
  691. background: #f0f9eb;
  692. }
  693. .stage-item.stage-processing {
  694. background: #fdf6ec;
  695. }
  696. .stage-item.stage-pending {
  697. background: #f5f7fa;
  698. }
  699. .stage-icon {
  700. width: 22px;
  701. height: 22px;
  702. border-radius: 50%;
  703. display: flex;
  704. align-items: center;
  705. justify-content: center;
  706. font-size: 13px;
  707. background: #fff;
  708. border: 1px solid #dcdfe6;
  709. color: #606266;
  710. }
  711. .stage-content {
  712. flex: 1;
  713. }
  714. .stage-name {
  715. font-size: 13px;
  716. color: #303133;
  717. line-height: 20px;
  718. }
  719. .stage-meta {
  720. margin-top: 6px;
  721. display: flex;
  722. align-items: center;
  723. gap: 8px;
  724. }
  725. .stage-owner {
  726. color: #606266;
  727. font-size: 12px;
  728. }
  729. .logs-table-wrapper {
  730. padding: 8px;
  731. }
  732. .el-icon-check {
  733. color: #67c23a;
  734. font-weight: 1000;
  735. }
  736. </style>