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.

856 lines
23 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
3 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
  1. <template>
  2. <div class="report-container">
  3. <div class="page-header">
  4. <div class="header-left">
  5. <h2 class="page-title">
  6. <i class="el-icon-s-order"></i> 生产报工
  7. </h2>
  8. <p class="page-subtitle">家用电梯 / 线缆COP / 改造项目节点报工管理</p>
  9. </div>
  10. <div class="header-right">
  11. <div class="stat-cards">
  12. <div class="stat-card stat-total">
  13. <div class="stat-icon"><i class="el-icon-document"></i></div>
  14. <div class="stat-content">
  15. <div class="stat-value">{{ totalOrders }}</div>
  16. <div class="stat-label">订单总数</div>
  17. </div>
  18. </div>
  19. <div class="stat-card stat-pending">
  20. <div class="stat-icon"><i class="el-icon-time"></i></div>
  21. <div class="stat-content">
  22. <div class="stat-value">{{ pendingNodeCount }}</div>
  23. <div class="stat-label">待报工节点</div>
  24. </div>
  25. </div>
  26. </div>
  27. </div>
  28. </div>
  29. <div class="search-section">
  30. <el-collapse class="no-arrow" v-model="searchExpanded">
  31. <el-collapse-item name="1">
  32. <template slot="title">
  33. <i class="el-icon-search"></i>
  34. <span class="search-title">筛选条件</span>
  35. </template>
  36. <el-form :inline="true" label-position="top" class="search-form">
  37. <el-form-item label="项目号">
  38. <el-input v-model="searchData.projectNo" placeholder="支持模糊查询" clearable style="width: 150px"></el-input>
  39. </el-form-item>
  40. <el-form-item label="订单类型">
  41. <el-select v-model="searchData.orderType" placeholder="请选择" clearable style="width: 150px">
  42. <el-option label="全部" value=""></el-option>
  43. <el-option label="家用电梯" value="HOME_LIFT"></el-option>
  44. <el-option label="线缆COP" value="CABLE_COP"></el-option>
  45. <el-option label="改造项目" value="RENOVATION"></el-option>
  46. </el-select>
  47. </el-form-item>
  48. <el-form-item label="状态">
  49. <el-select v-model="searchData.status" placeholder="请选择" clearable style="width: 120px">
  50. <el-option label="全部" value=""></el-option>
  51. <el-option label="已排产" value="已排产"></el-option>
  52. <el-option label="进行中" value="进行中"></el-option>
  53. </el-select>
  54. </el-form-item>
  55. <el-form-item label="计划日期">
  56. <el-date-picker
  57. v-model="searchData.planStartDate"
  58. type="date"
  59. value-format="yyyy-MM-dd"
  60. placeholder="开始日期"
  61. style="width: 140px">
  62. </el-date-picker>
  63. </el-form-item>
  64. <el-form-item label="至">
  65. <el-date-picker
  66. v-model="searchData.planEndDate"
  67. type="date"
  68. value-format="yyyy-MM-dd"
  69. placeholder="结束日期"
  70. style="width: 140px">
  71. </el-date-picker>
  72. </el-form-item>
  73. <el-form-item label=" " class="search-btn-group">
  74. <el-button @click="getDataList('Y')" type="primary" icon="el-icon-search">查询</el-button>
  75. <el-button @click="resetQuery()" type="default" icon="el-icon-refresh-left">重置</el-button>
  76. </el-form-item>
  77. </el-form>
  78. </el-collapse-item>
  79. </el-collapse>
  80. </div>
  81. <div class="cards-container">
  82. <div class="cards-grid">
  83. <div
  84. v-for="item in dataList"
  85. :key="item.orderNo"
  86. class="report-card"
  87. :class="getCardClass(item)">
  88. <div class="card-header">
  89. <div class="card-title-area">
  90. <el-tag :type="getTypeTag(item.orderType)" effect="dark" class="type-tag">{{ item.orderTypeName }}</el-tag>
  91. <span class="project-no">{{ item.projectNo }}</span>
  92. </div>
  93. <el-tag :type="getStatusType(item.status)" size="small">{{ item.status }}</el-tag>
  94. </div>
  95. <div class="card-body">
  96. <h3 class="item-title">
  97. <i class="el-icon-document"></i>
  98. {{ item.productName }}
  99. </h3>
  100. <div class="card-details">
  101. <div class="detail-row">
  102. <span class="detail-label"><i class="el-icon-goods"></i> 型号</span>
  103. <span class="detail-value">{{ item.modelNo }}</span>
  104. </div>
  105. <div class="detail-row">
  106. <span class="detail-label"><i class="el-icon-date"></i> 计划完工</span>
  107. <span class="detail-value">{{ item.planDate }}</span>
  108. </div>
  109. <div class="detail-row">
  110. <span class="detail-label"><i class="el-icon-user"></i> 责任人</span>
  111. <span class="detail-value">{{ item.owner }}</span>
  112. </div>
  113. <div class="detail-row">
  114. <span class="detail-label"><i class="el-icon-finished"></i> 节点进度</span>
  115. <span class="detail-value">{{ item.nodeDoneCount }}/{{ item.nodeTotalCount }}</span>
  116. </div>
  117. </div>
  118. <div class="node-section">
  119. <div class="node-title">
  120. <i class="el-icon-position"></i> 节点报工
  121. </div>
  122. <div v-for="node in item.visiblePendingNodeList" :key="node.nodeCode" class="node-row">
  123. <span class="node-name">{{ node.nodeName }}</span>
  124. <div class="node-right">
  125. <!-- <el-tag :type="getNodeStatusType(node.status)" size="mini">{{ node.status }}</el-tag>-->
  126. <el-button
  127. size="mini"
  128. type="primary"
  129. plain
  130. :disabled="node.status === '已完成' || !canReportNode(item, node)"
  131. @click="directReportNode(item, node)">
  132. 报工
  133. </el-button>
  134. </div>
  135. </div>
  136. </div>
  137. </div>
  138. <!-- <div class="card-footer">
  139. <div class="current-step"><i class="el-icon-caret-right"></i> 当前工序{{ item.currentNode }}</div>
  140. <el-button type="text" @click="openHistoryDialog(item)">报工记录</el-button>
  141. </div>-->
  142. </div>
  143. </div>
  144. <div v-if="dataList.length === 0" class="empty-state">
  145. <i class="el-icon-document-checked empty-icon"></i>
  146. <p class="empty-text">暂无报工任务</p>
  147. <p class="empty-subtext">请调整筛选条件后重试</p>
  148. </div>
  149. </div>
  150. <div class="pagination-wrapper" v-if="totalPage > 0">
  151. <el-pagination
  152. @size-change="sizeChangeHandle"
  153. @current-change="currentChangeHandle"
  154. :current-page="pageIndex"
  155. :page-sizes="[8, 12, 24]"
  156. :page-size="pageSize"
  157. :total="totalPage"
  158. layout="total, sizes, prev, pager, next, jumper"
  159. background>
  160. </el-pagination>
  161. </div>
  162. <el-dialog
  163. title="节点报工"
  164. :visible.sync="reportDialogVisible"
  165. width="520px"
  166. :close-on-click-modal="false"
  167. v-drag>
  168. <el-form :model="reportData" label-position="top">
  169. <el-row :gutter="12">
  170. <el-col :span="12">
  171. <el-form-item label="项目号">
  172. <el-input v-model="reportData.projectNo" disabled></el-input>
  173. </el-form-item>
  174. </el-col>
  175. <el-col :span="12">
  176. <el-form-item label="报工节点">
  177. <el-input v-model="reportData.nodeName" disabled></el-input>
  178. </el-form-item>
  179. </el-col>
  180. </el-row>
  181. <el-row :gutter="12">
  182. <el-col :span="12">
  183. <el-form-item label="报工数量" required>
  184. <el-input v-model="reportData.reportQty" style="width: 100%"></el-input>
  185. </el-form-item>
  186. </el-col>
  187. <el-col :span="12">
  188. <el-form-item label="报工人员">
  189. <el-input v-model="reportData.reportBy" disabled></el-input>
  190. </el-form-item>
  191. </el-col>
  192. </el-row>
  193. <el-form-item label="报工备注">
  194. <el-input v-model="reportData.remark" type="textarea" :rows="3" placeholder="请输入报工说明"></el-input>
  195. </el-form-item>
  196. </el-form>
  197. <div slot="footer" class="dialog-footer">
  198. <el-button @click="reportDialogVisible = false">取消</el-button>
  199. <el-button type="primary" :loading="reportLoading" @click="submitNodeReport">
  200. {{ reportLoading ? '提交中...' : '确认报工' }}
  201. </el-button>
  202. </div>
  203. </el-dialog>
  204. <el-dialog
  205. :title="`报工记录 - ${historyOrder.projectNo || ''}`"
  206. :visible.sync="historyDialogVisible"
  207. width="760px"
  208. :close-on-click-modal="false"
  209. v-drag>
  210. <el-table
  211. class="history-table"
  212. :data="historyOrder.visibleReportHistory || []"
  213. border
  214. stripe
  215. max-height="420px"
  216. style="width: 100%">
  217. <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
  218. <el-table-column prop="nodeName" label="报工节点" min-width="140" align="left" header-align="center"></el-table-column>
  219. <el-table-column prop="reportQty" label="数量" width="90" align="center" header-align="center"></el-table-column>
  220. <el-table-column prop="reportBy" label="报工人" width="110" align="center" header-align="center"></el-table-column>
  221. <el-table-column prop="reportTime" label="报工时间" width="170" align="center" header-align="center"></el-table-column>
  222. <el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip align="left" header-align="center"></el-table-column>
  223. </el-table>
  224. <div v-if="(historyOrder.visibleReportHistory || []).length === 0" class="empty-attachment">
  225. <i class="el-icon-folder-opened"></i>
  226. <p>暂无报工记录</p>
  227. </div>
  228. <div slot="footer" class="dialog-footer">
  229. <el-button type="primary" @click="historyDialogVisible = false">关闭</el-button>
  230. </div>
  231. </el-dialog>
  232. </div>
  233. </template>
  234. <script>
  235. import { getWorkReportOrderList, reportCableCopTaskNode, reportHomeLiftOrderNode, reportRenovationOrderNode } from '@/api/longchuang/productionPlan'
  236. export default {
  237. name: 'ProductionWorkReport',
  238. data() {
  239. return {
  240. searchExpanded: ['0'],
  241. searchData: {
  242. projectNo: '',
  243. orderType: '',
  244. status: '',
  245. planStartDate: '',
  246. planEndDate: '',
  247. page: 1,
  248. limit: 8
  249. },
  250. dataList: [],
  251. pageIndex: 1,
  252. pageSize: 8,
  253. totalPage: 0,
  254. reportDialogVisible: false,
  255. historyDialogVisible: false,
  256. historyOrder: {},
  257. reportLoading: false,
  258. reportData: {
  259. orderNo: '',
  260. orderType: '',
  261. nodeCode: '',
  262. projectNo: '',
  263. nodeName: '',
  264. reportQty: 1,
  265. reportBy: '',
  266. remark: ''
  267. }
  268. }
  269. },
  270. computed: {
  271. totalOrders() {
  272. return this.totalPage || 0
  273. },
  274. pendingNodeCount() {
  275. return this.dataList.reduce((sum, item) => sum + (item.visibleNodeList || []).filter(node => node.status !== '已完成').length, 0)
  276. }
  277. },
  278. activated() {
  279. this.getDataList('Y')
  280. },
  281. methods: {
  282. getDataList(flag) {
  283. if (flag === 'Y') {
  284. this.pageIndex = 1
  285. }
  286. this.searchData.page = this.pageIndex
  287. this.searchData.limit = this.pageSize
  288. getWorkReportOrderList(this.searchData).then(({ data }) => {
  289. if (data && data.code === 0) {
  290. const page = data.page || {}
  291. const list = page.list || []
  292. this.dataList = list.map(this.normalizeOrderRow)
  293. this.totalPage = page.totalCount || 0
  294. } else {
  295. this.dataList = []
  296. this.totalPage = 0
  297. this.$message.error((data && data.msg) || '查询失败')
  298. }
  299. }).catch(() => {
  300. this.dataList = []
  301. this.totalPage = 0
  302. this.$message.error('查询失败')
  303. })
  304. },
  305. normalizeOrderRow(row) {
  306. const nodeList = row.nodeList || []
  307. const fallbackNodeDoneCount = nodeList.filter(item => item.status === '已完成').length
  308. const fallbackCurrentNodeObj = nodeList.find(item => item.status !== '已完成') || {}
  309. const nodeDoneCount = typeof row.nodeDoneCount === 'number' ? row.nodeDoneCount : fallbackNodeDoneCount
  310. const nodeTotalCount = typeof row.nodeTotalCount === 'number' ? row.nodeTotalCount : nodeList.length
  311. const currentNode = row.currentNode || fallbackCurrentNodeObj.nodeName || '全部完成'
  312. return {
  313. ...row,
  314. nodeReportMode: row.nodeReportMode || 'PARALLEL',
  315. currentNodeCode: row.currentNodeCode || fallbackCurrentNodeObj.nodeCode || '',
  316. orderTypeName: this.getOrderTypeName(row.orderType),
  317. productName: row.taskNo || row.projectNo || '-',
  318. planDate: row.planFinishDate || row.planDeliveryDate || '-',
  319. owner: this.$store.state.user.userDisplay || this.$store.state.user.name || '-',
  320. nodeDoneCount: nodeDoneCount,
  321. nodeTotalCount: nodeTotalCount,
  322. currentNode: currentNode,
  323. visibleNodeList: nodeList,
  324. visiblePendingNodeList: nodeList.filter(item => item.status !== '已完成')
  325. }
  326. },
  327. getOrderTypeName(orderType) {
  328. const map = {
  329. HOME_LIFT: '家用电梯',
  330. CABLE_COP: '线缆COP',
  331. RENOVATION: 'VL2.5升级'
  332. }
  333. return map[orderType] || orderType || '-'
  334. },
  335. resetQuery() {
  336. this.searchData = {
  337. projectNo: '',
  338. orderType: '',
  339. status: '',
  340. planStartDate: '',
  341. planEndDate: '',
  342. page: 1,
  343. limit: this.pageSize
  344. }
  345. this.getDataList('Y')
  346. },
  347. getTypeTag(type) {
  348. const tagMap = {
  349. HOME_LIFT: 'primary',
  350. CABLE_COP: 'warning',
  351. RENOVATION: 'success'
  352. }
  353. return tagMap[type] || 'info'
  354. },
  355. getStatusType(status) {
  356. const statusMap = {
  357. 已排产: 'info',
  358. 进行中: 'warning',
  359. 已完成: 'success'
  360. }
  361. return statusMap[status] || 'info'
  362. },
  363. getNodeStatusType(status) {
  364. const statusMap = {
  365. 未开始: 'info',
  366. 进行中: 'warning',
  367. 已完成: 'success'
  368. }
  369. return statusMap[status] || 'info'
  370. },
  371. getCardClass(item) {
  372. if (item.status === '已完成') {
  373. return 'card-done'
  374. }
  375. if (item.status === '进行中') {
  376. return 'card-progress'
  377. }
  378. return 'card-plan'
  379. },
  380. canReportNode(order, node) {
  381. if (!order || !node) {
  382. return false
  383. }
  384. if ((order.nodeReportMode || 'PARALLEL') !== 'SEQUENTIAL') {
  385. return true
  386. }
  387. return !!order.currentNodeCode && order.currentNodeCode === node.nodeCode
  388. },
  389. directReportNode(order, node) {
  390. this.reportData = {
  391. orderNo: order.orderNo,
  392. orderType: order.orderType,
  393. nodeCode: node.nodeCode,
  394. projectNo: order.projectNo,
  395. nodeName: node.nodeName,
  396. reportQty: 1,
  397. reportBy: this.$store.state.user.userDisplay || this.$store.state.user.name || '当前用户',
  398. remark: ''
  399. }
  400. this.submitNodeReport()
  401. },
  402. openReportDialog(order, node) {
  403. this.reportData = {
  404. orderNo: order.orderNo,
  405. orderType: order.orderType,
  406. nodeCode: node.nodeCode,
  407. projectNo: order.projectNo,
  408. nodeName: node.nodeName,
  409. reportQty: 1,
  410. reportBy: this.$store.state.user.userDisplay || this.$store.state.user.name || '当前用户',
  411. remark: ''
  412. }
  413. this.reportDialogVisible = true
  414. },
  415. submitNodeReport() {
  416. if (!this.reportData.nodeCode) {
  417. this.$message.warning('请选择报工节点')
  418. return
  419. }
  420. const apiMap = {
  421. HOME_LIFT: reportHomeLiftOrderNode,
  422. CABLE_COP: reportCableCopTaskNode,
  423. RENOVATION: reportRenovationOrderNode
  424. }
  425. const apiFn = apiMap[this.reportData.orderType]
  426. if (!apiFn) {
  427. this.$message.error('订单类型不支持报工')
  428. return
  429. }
  430. this.reportLoading = true
  431. apiFn({
  432. orderNo: this.reportData.orderNo,
  433. nodeCode: this.reportData.nodeCode,
  434. reportQty: this.reportData.reportQty,
  435. remark: this.reportData.remark
  436. }).then(({ data }) => {
  437. this.reportLoading = false
  438. if (data && data.code === 0) {
  439. this.$message.success(data.msg || '报工成功')
  440. this.reportDialogVisible = false
  441. this.getDataList()
  442. } else {
  443. this.$message.error((data && data.msg) || '报工失败')
  444. }
  445. }).catch(() => {
  446. this.reportLoading = false
  447. this.$message.error('报工失败')
  448. })
  449. },
  450. openHistoryDialog(item) {
  451. this.historyOrder = item
  452. this.historyDialogVisible = true
  453. },
  454. sizeChangeHandle(val) {
  455. this.pageSize = val
  456. this.pageIndex = 1
  457. this.getDataList()
  458. },
  459. currentChangeHandle(val) {
  460. this.pageIndex = val
  461. this.getDataList()
  462. }
  463. }
  464. }
  465. </script>
  466. <style scoped>
  467. .report-container {
  468. padding: 15px;
  469. background: #f5f7fa;
  470. min-height: calc(100vh - 80px);
  471. }
  472. .page-header {
  473. display: flex;
  474. justify-content: space-between;
  475. align-items: center;
  476. margin-bottom: 8px;
  477. padding: 5px 20px;
  478. background: #FFFFFF;
  479. border-radius: 4px;
  480. border: 1px solid #EBEEF5;
  481. }
  482. .page-title {
  483. margin: 0;
  484. font-size: 20px;
  485. font-weight: 600;
  486. color: #303133;
  487. display: flex;
  488. align-items: center;
  489. gap: 10px;
  490. }
  491. .page-title i {
  492. font-size: 22px;
  493. color: #409EFF;
  494. }
  495. .page-subtitle {
  496. margin: 6px 0 0 0;
  497. font-size: 13px;
  498. color: #909399;
  499. }
  500. .stat-cards {
  501. display: flex;
  502. gap: 12px;
  503. }
  504. .stat-card {
  505. display: flex;
  506. align-items: center;
  507. gap: 12px;
  508. padding: 12px 20px;
  509. background: #FFFFFF;
  510. border-radius: 4px;
  511. border: 1px solid #EBEEF5;
  512. min-width: 140px;
  513. }
  514. .stat-icon {
  515. width: 40px;
  516. height: 40px;
  517. display: flex;
  518. align-items: center;
  519. justify-content: center;
  520. border-radius: 4px;
  521. font-size: 20px;
  522. color: white;
  523. }
  524. .stat-total .stat-icon {
  525. background: #409EFF;
  526. }
  527. .stat-pending .stat-icon {
  528. background: #E6A23C;
  529. }
  530. .stat-done .stat-icon {
  531. background: #67C23A;
  532. }
  533. .stat-value {
  534. font-size: 24px;
  535. font-weight: 600;
  536. color: #303133;
  537. line-height: 1;
  538. }
  539. .stat-label {
  540. font-size: 12px;
  541. color: #909399;
  542. margin-top: 4px;
  543. }
  544. .search-section {
  545. margin-bottom: 8px;
  546. background: #FFFFFF;
  547. border-radius: 4px;
  548. border: 1px solid #EBEEF5;
  549. overflow: hidden;
  550. }
  551. .search-title {
  552. margin-left: 8px;
  553. font-weight: 500;
  554. }
  555. .search-section >>> .el-collapse-item__header {
  556. padding: 0 15px;
  557. height: 35px;
  558. line-height: 35px;
  559. background: white;
  560. border-bottom: 1px solid #ebeef5;
  561. font-size: 13px;
  562. color: #303133;
  563. font-weight: 500;
  564. }
  565. .search-section >>> .el-collapse-item__content {
  566. padding: 15px;
  567. background: #FFFFFF;
  568. }
  569. .search-form >>> .el-form-item {
  570. margin-bottom: 10px;
  571. }
  572. .cards-container {
  573. min-height: 360px;
  574. }
  575. .cards-grid {
  576. display: grid;
  577. grid-template-columns: repeat(auto-fill, minmax(390px, 1fr));
  578. gap: 15px;
  579. }
  580. .report-card {
  581. background: white;
  582. border-radius: 4px;
  583. padding: 8px 16px;
  584. border: 1px solid #EBEEF5;
  585. transition: all 0.3s ease;
  586. position: relative;
  587. }
  588. .report-card::before {
  589. content: '';
  590. position: absolute;
  591. left: 0;
  592. top: 0;
  593. width: 2px;
  594. height: 100%;
  595. }
  596. .report-card.card-plan::before {
  597. background: #909399;
  598. }
  599. .report-card.card-progress::before {
  600. background: #E6A23C;
  601. }
  602. .report-card.card-done::before {
  603. background: #67C23A;
  604. }
  605. .report-card:hover {
  606. border-color: #409EFF;
  607. box-shadow: 0 4px 12px rgba(64, 158, 255, 0.12);
  608. }
  609. .card-header {
  610. display: flex;
  611. justify-content: space-between;
  612. align-items: center;
  613. margin-bottom: 4px;
  614. padding-bottom: 6px;
  615. border-bottom: 1px solid #EBEEF5;
  616. }
  617. .card-title-area {
  618. display: flex;
  619. align-items: center;
  620. gap: 10px;
  621. }
  622. .type-tag {
  623. font-size: 12px;
  624. }
  625. .project-no {
  626. font-size: 13px;
  627. color: #606266;
  628. font-family: 'Courier New', monospace;
  629. font-weight: 500;
  630. }
  631. .item-title {
  632. font-size: 15px;
  633. font-weight: 600;
  634. color: #303133;
  635. margin: 0 0 6px 0;
  636. display: flex;
  637. align-items: center;
  638. gap: 6px;
  639. }
  640. .item-title i {
  641. color: #409EFF;
  642. }
  643. .card-details {
  644. display: flex;
  645. flex-direction: column;
  646. gap: 4px;
  647. }
  648. .detail-row {
  649. display: flex;
  650. justify-content: space-between;
  651. align-items: center;
  652. font-size: 13px;
  653. padding: 6px 10px;
  654. background: #F5F7FA;
  655. border-radius: 3px;
  656. }
  657. .detail-label {
  658. color: #909399;
  659. display: flex;
  660. align-items: center;
  661. gap: 5px;
  662. font-weight: 500;
  663. }
  664. .detail-label i {
  665. color: #409EFF;
  666. font-size: 14px;
  667. }
  668. .detail-value {
  669. color: #606266;
  670. font-weight: 500;
  671. }
  672. .node-section {
  673. margin-top: 4px;
  674. border-top: 1px dashed #EBEEF5;
  675. padding-top: 10px;
  676. }
  677. .node-title {
  678. font-size: 12px;
  679. color: #909399;
  680. margin-bottom: 2px;
  681. }
  682. .node-row {
  683. display: flex;
  684. justify-content: space-between;
  685. align-items: center;
  686. padding: 4px 8px;
  687. border-radius: 4px;
  688. }
  689. .node-row:hover {
  690. background: #F5F7FA;
  691. }
  692. .node-name {
  693. color: #303133;
  694. font-size: 13px;
  695. }
  696. .node-right {
  697. display: flex;
  698. align-items: center;
  699. gap: 6px;
  700. }
  701. .card-footer {
  702. display: flex;
  703. justify-content: space-between;
  704. align-items: center;
  705. padding-top: 10px;
  706. margin-top: 8px;
  707. border-top: 1px solid #EBEEF5;
  708. }
  709. .current-step {
  710. font-size: 12px;
  711. color: #909399;
  712. }
  713. .pagination-wrapper {
  714. display: flex;
  715. justify-content: center;
  716. padding: 15px;
  717. margin-top: 15px;
  718. background: #FFFFFF;
  719. border-radius: 4px;
  720. border: 1px solid #EBEEF5;
  721. }
  722. .empty-state {
  723. text-align: center;
  724. padding: 80px 20px;
  725. background: white;
  726. border-radius: 4px;
  727. border: 1px solid #EBEEF5;
  728. }
  729. .empty-icon {
  730. font-size: 64px;
  731. color: #c0c4cc;
  732. margin-bottom: 12px;
  733. }
  734. .empty-text {
  735. font-size: 15px;
  736. color: #606266;
  737. margin: 0 0 6px 0;
  738. font-weight: 500;
  739. }
  740. .empty-subtext {
  741. font-size: 13px;
  742. color: #909399;
  743. margin: 0;
  744. }
  745. .empty-attachment {
  746. text-align: center;
  747. padding: 40px 20px;
  748. color: #909399;
  749. }
  750. .empty-attachment i {
  751. font-size: 42px;
  752. }
  753. .history-table >>> .el-table__header-wrapper th,
  754. .history-table >>> .el-table__header-wrapper .el-table__cell {
  755. background-color: #F5F7FA !important;
  756. color: #606266 !important;
  757. font-size: 12px;
  758. font-weight: 600;
  759. }
  760. /deep/ .no-arrow .el-collapse-item__header .el-collapse-item__arrow {
  761. display: none !important;
  762. }
  763. @media screen and (max-width: 1200px) {
  764. .page-header {
  765. flex-direction: column;
  766. align-items: flex-start;
  767. gap: 12px;
  768. }
  769. .cards-grid {
  770. grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  771. }
  772. }
  773. @media screen and (max-width: 768px) {
  774. .stat-cards {
  775. flex-direction: column;
  776. width: 100%;
  777. }
  778. .stat-card {
  779. width: 100%;
  780. }
  781. .cards-grid {
  782. grid-template-columns: 1fr;
  783. }
  784. }
  785. </style>