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.

486 lines
11 KiB

1 year ago
  1. <template>
  2. <div class="pda-container">
  3. <!-- 头部栏 -->
  4. <div class="header-bar">
  5. <div class="header-left" @click="$router.back()">
  6. <i class="el-icon-arrow-left"></i>
  7. <span>属性变动</span>
  8. </div>
  9. <div class="header-right" @click="$router.push({ path: '/' })">
  10. 首页
  11. </div>
  12. </div>
  13. <!-- 搜索框 -->
  14. <div class="search-container">
  15. <el-input clearable
  16. v-model="scanCode"
  17. placeholder="请扫描标签条码"
  18. prefix-icon="el-icon-search"
  19. @keyup.enter.native="handleScan"
  20. ref="scanInput"
  21. />
  22. </div>
  23. <!-- 标签信息卡片 -->
  24. <div class="label-info-card" >
  25. <div class="info-row">
  26. <span class="info-label">标签条码</span>
  27. <span class="info-value">{{ stockInfo.labelCode || '' }}</span>
  28. </div>
  29. <div class="info-row">
  30. <span class="info-label">标签数量</span>
  31. <span class="info-value">{{ stockInfo.labelQuantity || '' }}</span>
  32. </div>
  33. <div class="info-row">
  34. <span class="info-label">所在仓库</span>
  35. <span class="info-value">{{ stockInfo.warehouseName || '' }}</span>
  36. </div>
  37. <div class="info-row">
  38. <span class="info-label">所在库位</span>
  39. <div class="input-wrapper">
  40. <el-input
  41. v-model="newLocationId"
  42. placeholder="请扫描/输入库位"
  43. class="location-input"
  44. @keyup.enter.native="focusNextInput"
  45. />
  46. </div>
  47. </div>
  48. <div class="info-row">
  49. <span class="info-label">标签状态</span>
  50. <span class="info-value" :class="stockInfo.status === '冻结' ? 'status-frozen' : 'status-normal'">{{ stockInfo.status || '' }}</span>
  51. </div>
  52. <div class="info-row">
  53. <span class="info-label">生产日期</span>
  54. <div class="input-wrapper">
  55. <el-date-picker
  56. v-model="newProductionDate"
  57. type="date"
  58. placeholder="选择生产日期"
  59. format="yyyy-MM-dd"
  60. value-format="yyyy-MM-dd"
  61. class="date-input"
  62. />
  63. </div>
  64. </div>
  65. <div class="info-row">
  66. <span class="info-label">有效期截止</span>
  67. <div class="input-wrapper">
  68. <el-date-picker
  69. v-model="newExpiryDate"
  70. type="date"
  71. placeholder="选择有效期"
  72. format="yyyy-MM-dd"
  73. value-format="yyyy-MM-dd"
  74. class="date-input"
  75. />
  76. </div>
  77. </div>
  78. <div class="info-row">
  79. <span class="info-label">关联单号1</span>
  80. <span class="info-value">{{ stockInfo.orderref1 || '-' }}</span>
  81. </div>
  82. <div class="info-row">
  83. <span class="info-label">关联单号2</span>
  84. <span class="info-value">{{ stockInfo.orderref2 || '-' }}</span>
  85. </div>
  86. <div class="info-row">
  87. <span class="info-label">批次号</span>
  88. <span class="info-value">{{ stockInfo.batchNo || ''}}</span>
  89. </div>
  90. </div>
  91. <!-- 底部操作按钮 -->
  92. <div class="bottom-actions" v-if="stockInfo.labelCode">
  93. <button class="action-btn primary" @click="confirmChange">
  94. 确定
  95. </button>
  96. <button class="action-btn secondary" @click="cancelChange">
  97. 取消
  98. </button>
  99. </div>
  100. </div>
  101. </template>
  102. <script>
  103. import { getStockInfoByLabelCode, savePropertyChange } from "@/api/property-change/property-change.js";
  104. import { getCurrentWarehouse } from '@/utils'
  105. import moment from 'moment';
  106. export default {
  107. data() {
  108. return {
  109. scanCode: '',
  110. stockInfo: {},
  111. newLocationId: '',
  112. newProductionDate: '',
  113. newExpiryDate: ''
  114. };
  115. },
  116. methods: {
  117. // 处理扫描
  118. handleScan() {
  119. if (!this.scanCode.trim()) {
  120. return;
  121. }
  122. this.getStockInfo(this.scanCode.trim());
  123. this.scanCode = '';
  124. },
  125. // 获取库存信息
  126. getStockInfo(labelCode) {
  127. const params = {
  128. labelCode: labelCode,
  129. site: localStorage.getItem('site'),
  130. warehouseId: getCurrentWarehouse()
  131. };
  132. getStockInfoByLabelCode(params).then(({ data }) => {
  133. if (data && data.code === 0) {
  134. this.stockInfo = data.data;
  135. // 初始化输入值为当前值
  136. this.newLocationId = this.stockInfo.locationId || '';
  137. this.newProductionDate = this.stockInfo.productionDate ? moment(this.stockInfo.productionDate).format('YYYY-MM-DD') : '';
  138. this.newExpiryDate = this.stockInfo.expiryDate ? moment(this.stockInfo.expiryDate).format('YYYY-MM-DD') : '';
  139. this.$message.success('获取标签信息成功');
  140. } else {
  141. this.$message.error(data.msg || '未找到该标签的库存信息');
  142. this.stockInfo = {};
  143. }
  144. }).catch(error => {
  145. console.error('获取库存信息失败:', error);
  146. this.$message.error('获取库存信息失败');
  147. this.stockInfo = {};
  148. });
  149. },
  150. // 聚焦到下一个输入框
  151. focusNextInput() {
  152. // 可以根据需要实现聚焦逻辑
  153. },
  154. // 确认变动
  155. confirmChange() {
  156. if (!this.stockInfo.labelCode) {
  157. this.$message.warning('请先扫描标签条码');
  158. return;
  159. }
  160. // 检查是否有变动
  161. const oldLocationId = this.stockInfo.locationId || '';
  162. const oldProductionDate = this.stockInfo.productionDate ? moment(this.stockInfo.productionDate).format('YYYY-MM-DD') : '';
  163. const oldExpiryDate = this.stockInfo.expiryDate ? moment(this.stockInfo.expiryDate).format('YYYY-MM-DD') : '';
  164. const hasLocationChange = this.newLocationId !== oldLocationId;
  165. const hasProductionDateChange = this.newProductionDate !== oldProductionDate;
  166. const hasExpiryDateChange = this.newExpiryDate !== oldExpiryDate;
  167. if (!hasLocationChange && !hasProductionDateChange && !hasExpiryDateChange) {
  168. this.$message.warning('没有检测到属性变动,请修改后再提交');
  169. return;
  170. }
  171. // 构建保存参数
  172. const params = {
  173. site: localStorage.getItem('site'),
  174. buNo: this.stockInfo.buNo,
  175. warehouseId: getCurrentWarehouse(),
  176. labelCode: this.stockInfo.labelCode,
  177. partNo: this.stockInfo.partNo,
  178. batchNo: this.stockInfo.batchNo,
  179. quantity: this.stockInfo.qtyOnHand,
  180. oldLocationId: oldLocationId,
  181. newLocationId: this.newLocationId,
  182. oldProductionDate: oldProductionDate,
  183. newProductionDate: this.newProductionDate,
  184. oldExpiryDate: oldExpiryDate,
  185. newExpiryDate: this.newExpiryDate
  186. };
  187. // 确认对话框
  188. let changeMsg = '确认进行以下属性变动?\n';
  189. if (hasLocationChange) {
  190. changeMsg += `库位: ${oldLocationId}${this.newLocationId}\n`;
  191. }
  192. if (hasProductionDateChange) {
  193. changeMsg += `生产日期: ${oldProductionDate}${this.newProductionDate}\n`;
  194. }
  195. if (hasExpiryDateChange) {
  196. changeMsg += `有效期: ${oldExpiryDate}${this.newExpiryDate}\n`;
  197. }
  198. this.$confirm(changeMsg, '确认变动', {
  199. confirmButtonText: '确定',
  200. cancelButtonText: '取消',
  201. type: 'warning'
  202. }).then(() => {
  203. this.saveChange(params);
  204. }).catch(() => {
  205. // 用户取消
  206. });
  207. },
  208. // 保存变动
  209. saveChange(params) {
  210. savePropertyChange(params).then(({ data }) => {
  211. if (data && data.code === 0) {
  212. this.$message.success('操作成功');
  213. this.resetForm();
  214. } else {
  215. this.$message.error(data.msg || '保存失败');
  216. }
  217. }).catch(error => {
  218. console.error('保存属性变动失败:', error);
  219. this.$message.error('保存失败');
  220. });
  221. },
  222. // 取消变动
  223. cancelChange() {
  224. if (this.stockInfo.labelCode) {
  225. this.$confirm('确定要取消当前操作吗?', '提示', {
  226. confirmButtonText: '确定',
  227. cancelButtonText: '继续操作',
  228. type: 'warning'
  229. }).then(() => {
  230. this.resetForm();
  231. }).catch(() => {
  232. // 用户选择继续操作
  233. });
  234. } else {
  235. this.$router.back();
  236. }
  237. },
  238. // 重置表单
  239. resetForm() {
  240. this.stockInfo = {};
  241. this.newLocationId = '';
  242. this.newProductionDate = '';
  243. this.newExpiryDate = '';
  244. // 聚焦扫描框
  245. this.$nextTick(() => {
  246. if (this.$refs.scanInput) {
  247. this.$refs.scanInput.focus();
  248. }
  249. });
  250. }
  251. },
  252. mounted() {
  253. // 聚焦扫描框
  254. this.$nextTick(() => {
  255. if (this.$refs.scanInput) {
  256. this.$refs.scanInput.focus();
  257. }
  258. });
  259. }
  260. };
  261. </script>
  262. <style scoped>
  263. .pda-container {
  264. width: 100vw;
  265. height: 100vh;
  266. display: flex;
  267. flex-direction: column;
  268. background: #f5f5f5;
  269. }
  270. /* 头部栏 */
  271. .header-bar {
  272. display: flex;
  273. justify-content: space-between;
  274. align-items: center;
  275. padding: 8px 16px;
  276. background: #17B3A3;
  277. color: white;
  278. height: 40px;
  279. min-height: 40px;
  280. }
  281. .header-left {
  282. display: flex;
  283. align-items: center;
  284. cursor: pointer;
  285. font-size: 16px;
  286. font-weight: 500;
  287. }
  288. .header-left i {
  289. margin-right: 8px;
  290. font-size: 18px;
  291. }
  292. .header-right {
  293. cursor: pointer;
  294. font-size: 16px;
  295. font-weight: 500;
  296. }
  297. /* 搜索容器 */
  298. .search-container {
  299. padding: 12px 16px;
  300. background: white;
  301. }
  302. .search-container .el-input {
  303. width: 100%;
  304. }
  305. /* 标签信息卡片 */
  306. .label-info-card {
  307. background: white;
  308. margin: 8px 16px;
  309. padding: 16px;
  310. border-radius: 8px;
  311. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  312. flex: 1;
  313. overflow-y: auto;
  314. }
  315. .info-row {
  316. display: flex;
  317. align-items: center;
  318. margin-bottom: 2px;
  319. min-height: 40px;
  320. }
  321. .info-label {
  322. width: 80px;
  323. font-size: 14px;
  324. color: #333;
  325. font-weight: 500;
  326. flex-shrink: 0;
  327. }
  328. .info-value {
  329. flex: 1;
  330. font-size: 14px;
  331. color: #666;
  332. margin-left: 12px;
  333. }
  334. .status-frozen {
  335. color: #ff4949;
  336. font-weight: 500;
  337. }
  338. .status-normal {
  339. color: #17B3A3;
  340. font-weight: 500;
  341. }
  342. .input-wrapper {
  343. flex: 1;
  344. margin-left: 12px;
  345. }
  346. .location-input {
  347. width: 100%;
  348. }
  349. .location-input ::v-deep .el-input__inner {
  350. border: 1px solid #17B3A3;
  351. border-radius: 4px;
  352. font-size: 14px;
  353. padding: 8px 12px;
  354. }
  355. .date-input {
  356. width: 100%;
  357. }
  358. .date-input ::v-deep .el-input__inner {
  359. border: 1px solid #17B3A3;
  360. border-radius: 4px;
  361. font-size: 14px;
  362. padding: 8px 12px;
  363. }
  364. /* 底部操作按钮 */
  365. .bottom-actions {
  366. display: flex;
  367. padding: 16px;
  368. gap: 12px;
  369. background: white;
  370. margin-top: auto;
  371. }
  372. .action-btn {
  373. flex: 1;
  374. padding: 12px;
  375. border-radius: 20px;
  376. font-size: 14px;
  377. cursor: pointer;
  378. transition: all 0.2s ease;
  379. border: none;
  380. }
  381. .action-btn.primary {
  382. background: #17B3A3;
  383. color: white;
  384. }
  385. .action-btn.primary:hover {
  386. background: #0d8f7f;
  387. }
  388. .action-btn.secondary {
  389. background: white;
  390. color: #17B3A3;
  391. border: 1px solid #17B3A3;
  392. }
  393. .action-btn.secondary:hover {
  394. background: #17B3A3;
  395. color: white;
  396. }
  397. .action-btn:active {
  398. transform: scale(0.98);
  399. }
  400. /* 响应式设计 */
  401. @media (max-width: 360px) {
  402. .header-bar {
  403. padding: 8px 12px;
  404. }
  405. .search-container {
  406. padding: 8px 12px;
  407. }
  408. .label-info-card {
  409. margin: 6px 12px;
  410. padding: 12px;
  411. }
  412. .info-label {
  413. width: 70px;
  414. font-size: 13px;
  415. }
  416. .info-value {
  417. font-size: 13px;
  418. }
  419. .bottom-actions {
  420. padding: 12px;
  421. }
  422. }
  423. </style>