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.

514 lines
12 KiB

  1. <template>
  2. <div>
  3. <div class="pda-container">
  4. <!-- 头部栏 -->
  5. <div class="header-bar">
  6. <div class="header-left" @click="handleBack">
  7. <i class="el-icon-arrow-left"></i>
  8. <span>销售临时</span>
  9. </div>
  10. <div class="header-right" @click="$router.push({ path: '/' })">
  11. 首页
  12. </div>
  13. </div>
  14. <div class="table-body" style="max-height: 500px; overflow-y: auto;">
  15. <div class="main-content form-section">
  16. <!-- 标签扫描输入框 -->
  17. <div class="input-group">
  18. <el-input v-model="labelCode" placeholder="请扫描标签编码" class="form-input" clearable inputmode="none"
  19. autocomplete="off" autocorrect="off" spellcheck="false" @keyup.enter.native="handleLabelScan"
  20. ref="labelInput" />
  21. </div>
  22. <!-- 标签信息显示 (扫描后显示) -->
  23. <div v-if="labelInfo" class="info-section">
  24. <div class="info-title">
  25. <i class="el-icon-document">标签信息</i>
  26. </div>
  27. <div class="info-row">
  28. <span class="info-label">标签编码:</span>
  29. <span class="info-value">{{ labelInfo.unitId || '-' }}</span>
  30. </div>
  31. <div class="info-row">
  32. <span class="info-label">物料编码:</span>
  33. <span class="info-value">{{ labelInfo.partNo || '-' }}</span>
  34. </div>
  35. <div class="info-row">
  36. <span class="info-label">入库数量:</span>
  37. <el-input v-model="editableQty" class="qty-input" type="number" :min="0" placeholder="请输入数量" clearable />
  38. </div>
  39. <div class="info-row">
  40. <span class="info-label">批次号:</span>
  41. <span class="info-value">{{ labelInfo.batchNo || '-' }}</span>
  42. </div>
  43. <div class="info-row">
  44. <span class="info-label">仓库:</span>
  45. <span class="info-value">{{ labelInfo.warehouseId || '-' }}</span>
  46. </div>
  47. <div class="info-row">
  48. <span class="info-label">库位:</span>
  49. <span class="info-value">{{ labelInfo.locationId || '-' }}</span>
  50. </div>
  51. <div class="info-row">
  52. <span class="info-label">WDR:</span>
  53. <span class="info-value">{{ labelInfo.wdr || '-' }}</span>
  54. </div>
  55. <div class="info-row">
  56. <span class="info-label">创建时间:</span>
  57. <span class="info-value">{{ formatDate(labelInfo.createdDate) }}</span>
  58. </div>
  59. </div>
  60. <!-- 底部操作按钮 -->
  61. <div v-if="labelInfo" class="bottom-actions">
  62. <button class="action-btn primary" @click="handlePrint" :disabled="printLoading || createloading">
  63. 创建打印
  64. </button>
  65. <button class="action-btn secondary" @click="clearData">
  66. 清空
  67. </button>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. </div>
  73. </template>
  74. <script>
  75. import {
  76. queryLabelInfo,
  77. createNewHu,
  78. } from '@/api/customerIssue/customer-issue.js'
  79. import { printLabelCommon } from '@/api/production/production-inbound.js'
  80. export default {
  81. name: 'SalesTemporary',
  82. data() {
  83. return {
  84. site: localStorage.getItem('site'),
  85. labelCode: '',
  86. labelInfo: null,
  87. editableQty: '',
  88. loading: false,
  89. printLoading: false,
  90. createloading: false,
  91. }
  92. },
  93. methods: {
  94. /**
  95. * 返回上一页
  96. */
  97. handleBack() {
  98. this.$router.back()
  99. },
  100. /**
  101. * 处理标签扫描
  102. */
  103. handleLabelScan() {
  104. if (!this.labelCode.trim()) {
  105. this.$message.error('请扫描有效的标签编码')
  106. return
  107. }
  108. this.loading = true
  109. queryLabelInfo({
  110. site: this.site,
  111. labelCode: this.labelCode.trim(),
  112. })
  113. .then(({ data }) => {
  114. this.loading = false
  115. if (data && data.code === 0) {
  116. this.labelInfo = data.data
  117. this.editableQty = data.data.qty || '0'
  118. this.$message.success('查询成功')
  119. } else {
  120. this.$message.error(data.msg || '标签不存在')
  121. this.labelCode = ''
  122. this.labelInfo = null
  123. this.focusLabelInput()
  124. }
  125. })
  126. .catch((error) => {
  127. this.loading = false
  128. console.error('查询标签失败:', error)
  129. this.$message.error('查询异常')
  130. this.labelCode = ''
  131. this.labelInfo = null
  132. this.focusLabelInput()
  133. })
  134. },
  135. /**
  136. * 清空数据
  137. */
  138. clearData() {
  139. this.labelCode = ''
  140. this.labelInfo = null
  141. this.editableQty = ''
  142. this.focusLabelInput()
  143. },
  144. /**
  145. * 聚焦标签输入框
  146. */
  147. focusLabelInput() {
  148. this.$nextTick(() => {
  149. if (this.$refs.labelInput) {
  150. this.$refs.labelInput.focus()
  151. }
  152. })
  153. },
  154. /**
  155. * 格式化日期
  156. */
  157. formatDate(date) {
  158. if (!date) return '-'
  159. const d = new Date(date)
  160. const year = d.getFullYear()
  161. const month = String(d.getMonth() + 1).padStart(2, '0')
  162. const day = String(d.getDate()).padStart(2, '0')
  163. const hours = String(d.getHours()).padStart(2, '0')
  164. const minutes = String(d.getMinutes()).padStart(2, '0')
  165. return `${year}-${month}-${day} ${hours}:${minutes}`
  166. },
  167. /**
  168. * 获取状态标签类型
  169. */
  170. getStatusType(inStockFlag) {
  171. return inStockFlag === 'Y' ? 'success' : 'danger'
  172. },
  173. /**
  174. * 处理打印
  175. */
  176. handlePrint() {
  177. if (!this.labelInfo || !this.labelInfo.unitId) {
  178. this.$message.error('没有可打印的标签信息')
  179. return
  180. }
  181. if(!this.editableQty){
  182. this.$message.error('请输入数量')
  183. return
  184. }
  185. this.labelInfo.qty = this.editableQty || '0'
  186. let selectedMaterials = [
  187. {
  188. ...this.labelInfo,
  189. },
  190. ]
  191. console.log('准备创建新HU并打印,标签信息:', selectedMaterials)
  192. let params = {
  193. site: localStorage.getItem('site'),
  194. workOrderNo: this.labelInfo.workOrderNo || '',
  195. WarehouseId: this.labelInfo.warehouseId || '',
  196. selectedMaterials: selectedMaterials,
  197. }
  198. this.createloading = true
  199. createNewHu(params).then(({ data }) => {
  200. if (data && data.code == 0) {
  201. if(data.unitIds.length == 0){
  202. this.$message.error('创建拆分新HU失败: 未返回有效的HU信息')
  203. return
  204. }
  205. let printLabelType
  206. if (
  207. this.labelInfo.partNo &&
  208. this.labelInfo.partNo.startsWith('80')
  209. ) {
  210. printLabelType = '库存成品标签'
  211. } else {
  212. printLabelType = 'BIL标签'
  213. }
  214. // 调用打印方法,传入unitId数组和标签类型
  215. this.printViaServer(data.unitIds, printLabelType)
  216. }else {
  217. this.$message.error(data.msg || '创建拆分新HU失败')
  218. }
  219. })
  220. .catch((error) => {
  221. this.$message.error('创建拆分新HU失败:', error.msg)
  222. }).finally(() => {
  223. this.createloading = false
  224. })
  225. },
  226. /**
  227. * 通过服务器打印
  228. * @param {Array} unitIds - HU unitId列表
  229. * @param {String} printLabelType - 标签类型
  230. */
  231. async printViaServer(unitIds, printLabelType) {
  232. if (!unitIds || unitIds.length === 0) {
  233. console.warn('没有可打印的标签')
  234. return
  235. }
  236. this.printLoading = true
  237. try {
  238. const printRequest = {
  239. userId: localStorage.getItem('userName'),
  240. username: localStorage.getItem('userName'),
  241. site: localStorage.getItem('site'),
  242. unitIds: unitIds,
  243. labelType: printLabelType,
  244. }
  245. console.log('打印请求:', printRequest)
  246. const { data } = await printLabelCommon(printRequest)
  247. if (data.code === 200 || data.code === 0) {
  248. this.$message.success(`打印任务已发送!`)
  249. this.clearData()
  250. } else {
  251. this.$message.error(data.msg || '打印失败')
  252. }
  253. } catch (error) {
  254. console.error('服务器打印失败:', error)
  255. this.$message.error(`打印失败: ${error.message || error}`)
  256. } finally {
  257. this.printLoading = false
  258. }
  259. },
  260. },
  261. mounted() {
  262. // 页面加载后自动聚焦标签输入框
  263. this.focusLabelInput()
  264. },
  265. }
  266. </script>
  267. <style scoped>
  268. /* PDA容器样式 */
  269. .pda-container {
  270. width: 100vw;
  271. height: 120vh;
  272. display: flex;
  273. flex-direction: column;
  274. background: #f5f5f5;
  275. font-family: 'Arial', sans-serif;
  276. }
  277. /* 头部栏样式 */
  278. .header-bar {
  279. display: flex;
  280. justify-content: space-between;
  281. align-items: center;
  282. padding: 8px 16px;
  283. background: #17b3a3;
  284. color: white;
  285. height: 40px;
  286. min-height: 40px;
  287. max-height: 40px;
  288. }
  289. .header-left {
  290. display: flex;
  291. align-items: center;
  292. cursor: pointer;
  293. }
  294. .header-left i {
  295. margin-right: 8px;
  296. font-size: 18px;
  297. }
  298. .header-left span {
  299. font-size: 16px;
  300. font-weight: 500;
  301. }
  302. .header-right {
  303. cursor: pointer;
  304. font-size: 14px;
  305. padding: 4px 8px;
  306. border-radius: 4px;
  307. }
  308. /* 主要内容区 */
  309. .table-body {
  310. flex: 1;
  311. overflow-y: auto;
  312. }
  313. .main-content {
  314. padding: 16px;
  315. }
  316. /* 输入组样式 */
  317. .input-label {
  318. display: block;
  319. margin-bottom: 8px;
  320. font-size: 14px;
  321. font-weight: 500;
  322. color: #333;
  323. }
  324. .form-input {
  325. width: 100%;
  326. height: 44px;
  327. }
  328. /* 信息展示区 */
  329. .info-section {
  330. background: white;
  331. border-radius: 8px;
  332. padding: 16px;
  333. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  334. }
  335. .info-title {
  336. display: flex;
  337. align-items: center;
  338. justify-content: space-between;
  339. font-size: 16px;
  340. font-weight: bold;
  341. color: #17b3a3;
  342. margin-bottom: 6px;
  343. padding-bottom: 5px;
  344. border-bottom: 2px solid #17b3a3;
  345. }
  346. .info-title i {
  347. margin-right: 8px;
  348. font-size: 18px;
  349. }
  350. /* 数量输入框 */
  351. .qty-input {
  352. flex: 1;
  353. max-width: 200px;
  354. margin-left: auto;
  355. }
  356. .qty-input >>> .el-input__inner {
  357. text-align: center;
  358. font-size: 14px;
  359. font-weight: 500;
  360. color: #333;
  361. height: 32px;
  362. line-height: 32px;
  363. }
  364. .info-row {
  365. display: flex;
  366. justify-content: space-between;
  367. align-items: flex-start;
  368. padding: 10px 0;
  369. border-bottom: 1px solid #f0f0f0;
  370. }
  371. .info-row:last-child {
  372. border-bottom: none;
  373. }
  374. .info-label {
  375. font-size: 14px;
  376. color: #666;
  377. min-width: 90px;
  378. flex-shrink: 0;
  379. }
  380. .info-value {
  381. font-size: 14px;
  382. font-weight: 500;
  383. color: #333;
  384. flex: 1;
  385. text-align: right;
  386. word-break: break-all;
  387. }
  388. /* 底部按钮区 */
  389. .bottom-actions {
  390. display: flex;
  391. gap: 12px;
  392. padding-top: 16px;
  393. }
  394. .action-btn {
  395. flex: 1;
  396. padding: 12px 24px;
  397. border: none;
  398. border-radius: 6px;
  399. font-size: 16px;
  400. font-weight: 500;
  401. cursor: pointer;
  402. transition: all 0.2s;
  403. min-height: 44px;
  404. }
  405. .action-btn.primary {
  406. background: #17b3a3;
  407. color: white;
  408. }
  409. .action-btn.primary:hover:not(:disabled) {
  410. background: #15a394;
  411. }
  412. .action-btn.primary:disabled {
  413. background: #ccc;
  414. cursor: not-allowed;
  415. opacity: 0.6;
  416. }
  417. .action-btn.secondary {
  418. background: #f5f5f5;
  419. color: #333;
  420. border: 1px solid #ddd;
  421. }
  422. .action-btn.secondary:hover {
  423. background: #e8e8e8;
  424. }
  425. /* 响应式设计 */
  426. @media screen and (max-width: 480px) {
  427. .header-bar {
  428. padding: 8px 12px;
  429. }
  430. .main-content {
  431. padding: 12px;
  432. }
  433. .info-section {
  434. padding: 2px;
  435. }
  436. .info-label {
  437. font-size: 13px;
  438. min-width: 80px;
  439. }
  440. .info-value {
  441. font-size: 13px;
  442. }
  443. .action-btn {
  444. padding: 10px 16px;
  445. font-size: 14px;
  446. }
  447. }
  448. </style>