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.

738 lines
28 KiB

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