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.

405 lines
10 KiB

12 months ago
10 months ago
12 months ago
10 months ago
12 months ago
12 months ago
12 months ago
10 months ago
10 months ago
10 months ago
12 months ago
12 months ago
12 months ago
10 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
10 months ago
10 months ago
12 months ago
  1. <template>
  2. <div class="pda-container">
  3. <!-- 头部栏 -->
  4. <div class="header-bar">
  5. <div class="header-left" @click="handleBack">
  6. <i class="el-icon-arrow-left"></i>
  7. <span>委外直接发料 - 物料</span>
  8. </div>
  9. <div class="header-right" @click="$router.push({ path: '/' })">首页</div>
  10. </div>
  11. <!-- 订单信息展示 -->
  12. <div class="search-container">
  13. <div class="card-title">
  14. <label class="title-label">委外订单号:{{ outsourcingNo }}</label>
  15. </div>
  16. <div class="card-title">
  17. <label class="title-label">物料编码:{{ partNo }}</label>
  18. </div>
  19. <div class="card-title">
  20. <label class="title-label">需求数量:{{ requiredQty }}</label>
  21. </div>
  22. </div>
  23. <div class="content-area">
  24. <div class="work-order-list" v-if="issueList.length > 0">
  25. <div
  26. v-for="(item, index) in issueList"
  27. :key="index"
  28. class="material-card"
  29. @click="goDetail(item)"
  30. >
  31. <div class="card-title">
  32. <span class="title-label">
  33. 需求日期{{ item.dateRequired }} &nbsp;&nbsp; 行号{{ item.lineNo }}
  34. </span>
  35. </div>
  36. <div class="part-desc-row">
  37. <span class="desc-text">料号{{ item.componentPartNo }}</span>
  38. </div>
  39. <div class="part-desc-row">
  40. <span class="desc-text">{{ item.componentPartDescription }}</span>
  41. </div>
  42. <div class="card-details">
  43. <div class="detail-item">
  44. <div class="detail-label">需求数量</div>
  45. <div class="detail-value">{{ item.qtyRequired }}</div>
  46. </div>
  47. <div class="detail-item">
  48. <div class="detail-label">已发数量</div>
  49. <div class="detail-value">{{ item.qtyIssued || 0 }}</div>
  50. </div>
  51. <div class="detail-item">
  52. <div class="detail-label">单位</div>
  53. <div class="detail-value">{{ item.uom || '个' }}</div>
  54. </div>
  55. </div>
  56. </div>
  57. </div>
  58. <div v-if="!loading && issueList.length === 0" class="empty-state">
  59. <i class="el-icon-box"></i>
  60. <p>暂无物料记录</p>
  61. </div>
  62. <div v-if="loading" class="loading-state">
  63. <i class="el-icon-loading"></i>
  64. <p>加载中...</p>
  65. </div>
  66. </div>
  67. </div>
  68. </template>
  69. <script>
  70. import { getOutsourcingMaterials } from '@/api/outsourcing/outsourcing-issue.js'
  71. export default {
  72. data() {
  73. return {
  74. outsourcingNo: '',
  75. partNo: '',
  76. requiredQty: 0,
  77. loading: false,
  78. issueList: [],
  79. lineNo:'',
  80. releaseNo:'',
  81. site: '',
  82. }
  83. },
  84. methods: {
  85. normalizeValue(value) {
  86. return value == null ? '' : String(value).trim()
  87. },
  88. handleBack() {
  89. sessionStorage.setItem('outsourcingDirectIssue_shouldRestore', 'true')
  90. this.$router.back()
  91. },
  92. // 保存页面状态到sessionStorage(用于从详情页返回时恢复)
  93. savePageStateForDetail() {
  94. const state = {
  95. outsourcingNo: this.outsourcingNo,
  96. partNo: this.partNo,
  97. requiredQty: this.requiredQty,
  98. lineNo: this.lineNo,
  99. releaseNo: this.releaseNo,
  100. site: this.site,
  101. issueList: this.issueList,
  102. }
  103. sessionStorage.setItem('outsourcingDirectIssueList_state_fromDetail', JSON.stringify(state))
  104. },
  105. // 从sessionStorage恢复页面状态(仅当从详情页返回时)
  106. restorePageStateFromDetail() {
  107. try {
  108. const shouldRestore = sessionStorage.getItem('outsourcingDirectIssueList_shouldRestore')
  109. const savedState = sessionStorage.getItem('outsourcingDirectIssueList_state_fromDetail')
  110. if (shouldRestore === 'true' && savedState) {
  111. const state = JSON.parse(savedState)
  112. this.outsourcingNo = state.outsourcingNo || ''
  113. this.partNo = state.partNo || ''
  114. this.requiredQty = state.requiredQty || 0
  115. this.lineNo = state.lineNo || ''
  116. this.releaseNo = state.releaseNo || ''
  117. this.site = state.site || ''
  118. this.issueList = state.issueList || []
  119. // 清除标记
  120. sessionStorage.removeItem('outsourcingDirectIssueList_shouldRestore')
  121. sessionStorage.removeItem('outsourcingDirectIssueList_state_fromDetail')
  122. }
  123. } catch (error) {
  124. sessionStorage.removeItem('outsourcingDirectIssueList_shouldRestore')
  125. sessionStorage.removeItem('outsourcingDirectIssueList_state_fromDetail')
  126. }
  127. },
  128. async loadIssueList() {
  129. if (!this.outsourcingNo || !this.partNo) {
  130. return
  131. }
  132. this.loading = true
  133. const params = {
  134. outsourcingNo: this.outsourcingNo,
  135. site: this.site,
  136. partNo: this.partNo,
  137. }
  138. getOutsourcingMaterials(params).then(({data})=>{
  139. this.loading = false
  140. if (data && data.code === 0) {
  141. const materials = data.materials || []
  142. const site = this.normalizeValue(this.site)
  143. const releaseNo = this.normalizeValue(this.releaseNo)
  144. const lineNo = this.normalizeValue(this.lineNo)
  145. this.issueList = materials.filter((item) => {
  146. return (
  147. this.normalizeValue(item.contract) === site &&
  148. this.normalizeValue(item.releaseNo) === releaseNo &&
  149. this.normalizeValue(item.lineNo) === lineNo
  150. )
  151. })
  152. } else {
  153. this.$message.error(data.msg || '获取发料记录失败')
  154. this.issueList = []
  155. }
  156. })
  157. },
  158. goDetail(item) {
  159. // 跳转前保存当前页面状态(用于从详情页返回时恢复)
  160. this.savePageStateForDetail()
  161. this.$router.push({
  162. name: 'outsourcingDirectIssueDetail',
  163. query: {
  164. outsourcingNo: this.outsourcingNo,
  165. partNo: this.partNo,
  166. partDesc: item.partDesc || '',
  167. requiredQty: this.requiredQty,
  168. issuedQty:item.qtyIssued,
  169. site: this.site,
  170. issueRecord: {
  171. orderType: 'outsourcing',
  172. transactionId: item.transactionId,
  173. quantity: item.quantity,
  174. batchNo: item.batchNo,
  175. issuedQty: item.qtyIssued || 0,
  176. accountingId: item.accountingId,
  177. componentPartNo: item.componentPartNo,
  178. componentPartDescription: item.componentPartDescription,
  179. lineNo: this.lineNo,
  180. releaseNo: this.releaseNo,
  181. lineItemNo:item.lineItemNo
  182. }
  183. },
  184. })
  185. },
  186. },
  187. mounted() {
  188. // 如果是从详情页返回,则恢复页面状态
  189. this.restorePageStateFromDetail()
  190. // 如果没有恢复状态,则从路由参数获取
  191. if (!this.outsourcingNo) {
  192. this.outsourcingNo = this.$route.query.outsourcingNo
  193. this.partNo = this.$route.query.partNo
  194. this.site = this.$route.query.site || ''
  195. this.requiredQty = this.$route.query.outsourcingInfo.requiredQty
  196. this.lineNo = this.$route.query.outsourcingInfo.lineNo
  197. this.releaseNo = this.$route.query.outsourcingInfo.releaseNo
  198. }
  199. this.loadIssueList()
  200. },
  201. }
  202. </script>
  203. <style scoped>
  204. .pda-container {
  205. width: 100vw;
  206. height: 100vh;
  207. display: flex;
  208. flex-direction: column;
  209. background: #f5f5f5;
  210. }
  211. .header-bar {
  212. display: flex;
  213. justify-content: space-between;
  214. align-items: center;
  215. padding: 8px 16px;
  216. background: #17B3A3;
  217. color: white;
  218. height: 40px;
  219. min-height: 40px;
  220. }
  221. .header-left {
  222. display: flex;
  223. align-items: center;
  224. cursor: pointer;
  225. font-size: 16px;
  226. font-weight: 500;
  227. }
  228. .header-left i {
  229. margin-right: 8px;
  230. font-size: 18px;
  231. }
  232. .header-right {
  233. cursor: pointer;
  234. font-size: 16px;
  235. font-weight: 500;
  236. }
  237. .search-container {
  238. padding: 12px 16px;
  239. background: white;
  240. display: flex;
  241. flex-direction: column;
  242. gap: 8px;
  243. }
  244. .card-title {
  245. margin-bottom: 8px;
  246. }
  247. .title-label {
  248. font-size: 12px;
  249. color: #666;
  250. display: block;
  251. margin-bottom: 4px;
  252. }
  253. .content-area {
  254. flex: 1;
  255. overflow-y: auto;
  256. }
  257. .work-order-list {
  258. overflow-y: auto;
  259. }
  260. .material-card {
  261. background: white;
  262. border-radius: 8px;
  263. margin-bottom: 12px;
  264. padding: 16px;
  265. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  266. cursor: pointer;
  267. transition: all 0.2s ease;
  268. border: 2px solid transparent;
  269. }
  270. .material-card:hover {
  271. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
  272. transform: translateY(-1px);
  273. }
  274. .material-card:active {
  275. transform: translateY(0);
  276. }
  277. .part-desc-row {
  278. margin-bottom: 12px;
  279. padding: 0 4px;
  280. }
  281. .desc-text {
  282. font-size: 12px;
  283. color: #666;
  284. line-height: 1.3;
  285. word-break: break-all;
  286. }
  287. .card-details {
  288. display: flex;
  289. justify-content: space-between;
  290. align-items: flex-start;
  291. gap: 4px;
  292. }
  293. .detail-item {
  294. flex: 1;
  295. text-align: center;
  296. min-width: 50px;
  297. max-width: 70px;
  298. }
  299. .detail-label {
  300. font-size: 11px;
  301. color: #666;
  302. margin-bottom: 4px;
  303. line-height: 1.2;
  304. margin-left: -12px;
  305. }
  306. .detail-value {
  307. font-size: 13px;
  308. color: #333;
  309. line-height: 1.2;
  310. margin-left: -12px;
  311. }
  312. .empty-state {
  313. display: flex;
  314. flex-direction: column;
  315. align-items: center;
  316. justify-content: center;
  317. padding: 60px 20px;
  318. color: #999;
  319. }
  320. .empty-state i {
  321. font-size: 48px;
  322. margin-bottom: 16px;
  323. }
  324. .empty-state p {
  325. font-size: 14px;
  326. margin: 0;
  327. }
  328. .loading-state {
  329. display: flex;
  330. flex-direction: column;
  331. align-items: center;
  332. justify-content: center;
  333. padding: 60px 20px;
  334. color: #17B3A3;
  335. }
  336. .loading-state i {
  337. font-size: 24px;
  338. margin-bottom: 12px;
  339. animation: spin 1s linear infinite;
  340. }
  341. @keyframes spin {
  342. from { transform: rotate(0deg); }
  343. to { transform: rotate(360deg); }
  344. }
  345. .loading-state p {
  346. font-size: 14px;
  347. margin: 0;
  348. }
  349. @media (max-width: 360px) {
  350. .header-bar {
  351. padding: 8px 12px;
  352. }
  353. .search-container {
  354. padding: 8px 12px;
  355. }
  356. .material-card {
  357. padding: 12px;
  358. }
  359. .card-details {
  360. flex-wrap: wrap;
  361. gap: 6px;
  362. }
  363. .detail-item {
  364. flex: 0 0 48%;
  365. margin-bottom: 6px;
  366. min-width: 50px;
  367. }
  368. }
  369. </style>