diff --git a/src/api/mes/productOutbound.js b/src/api/mes/productOutbound.js new file mode 100644 index 0000000..b3c5bf3 --- /dev/null +++ b/src/api/mes/productOutbound.js @@ -0,0 +1,51 @@ +import request from '@/utils/request' + +const PRODUCT_OUT_TYPE = '产品出库' + +export function createProductOutbound(data) { + return request({ + url: '/admin-api/erp/stock-out/create', + method: 'post', + data: { ...data, outType: PRODUCT_OUT_TYPE } + }) +} + +export function getProductOutboundPage(params = {}) { + return request({ + url: '/admin-api/erp/stock-out/page', + method: 'get', + params: { ...params, outType: PRODUCT_OUT_TYPE } + }) +} + +export function getProductOutboundDetail(id) { + return request({ + url: '/admin-api/erp/stock-out/get', + method: 'get', + params: { id } + }) +} + +export function submitProductOutbound(data) { + return request({ + url: '/admin-api/erp/stock-out/submit', + method: 'put', + data + }) +} + +export function auditProductOutbound(data) { + return request({ + url: '/admin-api/erp/stock-out/audit', + method: 'put', + data + }) +} + +export function getPalletPage(params = {}) { + return request({ + url: '/admin-api/erp/pallet/page', + method: 'get', + params + }) +} diff --git a/src/locales/en-US.js b/src/locales/en-US.js index d81da51..d11a0f3 100644 --- a/src/locales/en-US.js +++ b/src/locales/en-US.js @@ -1418,7 +1418,7 @@ export default { statusAuditing: 'Pending Review', statusStored: 'Stored', statusRejected: 'Rejected', - searchDocumentPlaceholder: 'Enter document No.', + searchDocumentPlaceholder: 'Enter inbound No.', searchProductPlaceholder: 'Search product name/code/spec', searchTaskPlaceholder: 'Search task order No.', searchTaskProductPlaceholder: 'Search product name/code', @@ -1525,5 +1525,107 @@ export default { enterRatedLoadWeight: 'Enter rated load', createSuccess: 'Created successfully', createPalletFailed: 'Failed to create pallet' - } -} + }, + productOutbound: { + moduleName: 'Product Outbound', + createTitle: 'New Product Outbound', + detailTitle: 'Product Outbound Detail', + selectProductTitle: 'Select Product', + selectPalletTitle: 'Select Pallet', + all: 'All', + reset: 'Reset', + clear: 'Clear', + loading: 'Loading...', + loadingMore: 'Loading more...', + noMoreData: 'No more data', + confirm: 'Confirm', + cancel: 'Cancel', + back: 'Back', + submit: 'OK', + submitting: 'Submitting...', + tip: 'Tip', + choose: 'Please select', + statusPending: 'Pending Outbound', + statusAuditing: 'Pending Review', + statusStored: 'Outbounded', + statusRejected: 'Rejected', + searchDocumentPlaceholder: 'Enter outbound No.', + searchProductPlaceholder: 'Search product name/code/spec', + searchPalletPlaceholder: 'Search pallet code', + productInfo: 'Product Info', + outboundInfo: 'Outbound Info', + outboundTime: 'Outbound Time', + selectOutboundTime: 'Select outbound time', + operator: 'Operator', + selectOperator: 'Select operator', + outboundQuantity: 'Outbound Qty', + reviewer: 'Reviewer', + auditor: 'Reviewer', + documentNo: 'Document No.', + outboundType: 'Outbound Type', + remark: 'Remark', + remarkPlaceholder: 'Enter remark', + attachment: 'Attachment', + chooseFile: 'Choose File', + itemList: 'Outbound Items', + addProduct: 'Add Product', + product: 'Product', + pallet: 'Pallet', + packageCount: 'Packages', + pieceCount: 'Pieces', + outboundPackageCount: 'Outbound Packages', + outboundPieceCount: 'Outbound Pieces', + availablePackageCount: 'Available Packages', + enterOutboundPackageCount: 'Enter outbound packages', + palletQuantity: 'Pallet Qty', + emptyAddProduct: 'Please add product', + emptyOutboundList: 'No product outbound records', + emptyItemList: 'No outbound items', + confirmOutbound: 'Confirm Outbound', + outboundSuccess: 'Outbound success', + saveFailed: 'Save failed', + loadFailed: 'Load failed', + detailLoadFailed: 'Detail load failed', + operationFailed: 'Operation failed', + submitAudit: 'Submit Review', + auditPass: 'Approve', + auditReject: 'Reject', + auditPassSuccess: 'Approved', + auditRejectSuccess: 'Rejected', + submitAuditSuccess: 'Submitted for review', + submitFailed: 'Submit failed', + selectAuditor: 'Select reviewer', + selectOutboundStatus: 'Select outbound status', + confirmAuditPass: 'Approve this product outbound order?', + confirmAuditReject: 'Reject this product outbound order?', + noDetailId: 'No detail ID', + completeProductPalletInfo: 'Complete product and pallet info', + addProductFirst: 'Add product first', + selectProduct: 'Select product', + selectPallet: 'Select pallet', + selectPalletFirst: 'Select pallet first', + selectProductFirst: 'Select product first', + completePalletInfo: 'Complete pallet warehouse/location/package info', + productAdded: 'Product added', + code: 'Code', + packagingScheme: 'Packaging Scheme', + palletPackageQuantity: 'Packages per Pallet', + packageQuantity: 'Pieces per Package', + selectedPalletCount: '{count} pallets selected', + selectedCount: '{count} selected', + packageUnit: '{count} packages', + pieceUnit: '{count} pieces', + warehouse: 'Warehouse', + area: 'Area', + location: 'Location', + emptyProduct: 'No product data', + emptyPallet: 'No pallets', + spec: 'Spec', + unit: 'Unit', + palletCode: 'Pallet Code', + palletType: 'Pallet Type', + palletStatus: 'Pallet Status', + outMode: 'Outbound Mode', + outModeWholePallet: 'Whole Pallet', + outModeSplitPallet: 'Split Pallet' + }} diff --git a/src/locales/index.js b/src/locales/index.js index 1fcc551..4dade9c 100644 --- a/src/locales/index.js +++ b/src/locales/index.js @@ -143,7 +143,10 @@ const literalMap = { '选择托盘': 'productInbound.selectPalletTitle', '新增托盘': 'productInbound.createPalletTitle', '选择任务单': 'productInbound.selectTaskTitle', - '选择任务产品': 'productInbound.selectTaskProductTitle' + '选择任务产品': 'productInbound.selectTaskProductTitle', + '产品出库': 'productOutbound.moduleName', + '新增产品出库': 'productOutbound.createTitle', + '产品出库详情': 'productOutbound.detailTitle' } export function getCurrentLocale() { diff --git a/src/locales/zh-CN.js b/src/locales/zh-CN.js index bacfb81..8214e5e 100644 --- a/src/locales/zh-CN.js +++ b/src/locales/zh-CN.js @@ -1421,7 +1421,7 @@ export default { statusAuditing: '待审核', statusStored: '已入库', statusRejected: '已驳回', - searchDocumentPlaceholder: '请输入单据编号', + searchDocumentPlaceholder: '请输入入库单号', searchProductPlaceholder: '搜索产品名称/编码/规格', searchTaskPlaceholder: '搜索任务单号', searchTaskProductPlaceholder: '搜索产品名称/编码', @@ -1529,6 +1529,109 @@ export default { createSuccess: '新增成功', createPalletFailed: '新增托盘失败' }, + productOutbound: { + moduleName: '产品出库', + createTitle: '新增产品出库', + detailTitle: '产品出库详情', + selectProductTitle: '选择产品', + selectPalletTitle: '选择托盘', + all: '全部', + reset: '重置', + clear: '清空', + loading: '加载中...', + loadingMore: '加载更多...', + noMoreData: '没有更多数据', + confirm: '确认', + cancel: '取消', + back: '返回', + submit: '确定', + submitting: '提交中...', + tip: '提示', + choose: '请选择', + statusPending: '待出库', + statusAuditing: '待审核', + statusStored: '已出库', + statusRejected: '已驳回', + searchDocumentPlaceholder: '请输入出库单号', + searchProductPlaceholder: '搜索产品名称/编码/规格', + searchPalletPlaceholder: '搜索托盘码', + productInfo: '产品信息', + outboundInfo: '出库信息', + outboundTime: '出库时间', + selectOutboundTime: '请选择出库时间', + operator: '经办人', + selectOperator: '请选择经办人', + outboundQuantity: '出库数量', + reviewer: '审核人', + auditor: '审核人', + documentNo: '单据编号', + outboundType: '出库类型', + remark: '备注', + remarkPlaceholder: '请输入备注', + attachment: '附件', + chooseFile: '选择文件', + itemList: '出库清单', + addProduct: '添加产品', + product: '产品', + pallet: '托盘', + packageCount: '包数', + pieceCount: '个数', + outboundPackageCount: '出库包数', + outboundPieceCount: '出库个数', + availablePackageCount: '可用包数', + enterOutboundPackageCount: '请输入出库包数', + palletQuantity: '托盘数量', + emptyAddProduct: '请添加产品', + emptyOutboundList: '暂无产品出库单', + emptyItemList: '暂无出库清单', + confirmOutbound: '确认出库', + outboundSuccess: '出库成功', + saveFailed: '保存失败', + loadFailed: '加载失败', + detailLoadFailed: '详情加载失败', + operationFailed: '操作失败', + submitAudit: '提交审核', + auditPass: '审核通过', + auditReject: '审核驳回', + auditPassSuccess: '审核通过', + auditRejectSuccess: '已驳回', + submitAuditSuccess: '提交审核成功', + submitFailed: '提交失败', + selectAuditor: '请选择审核人', + selectOutboundStatus: '选择出库状态', + confirmAuditPass: '确认审核通过该产品出库单?', + confirmAuditReject: '确认驳回该产品出库单?', + noDetailId: '暂无详情ID', + completeProductPalletInfo: '请完善产品和托盘信息', + addProductFirst: '请先添加产品', + selectProduct: '请选择产品', + selectPallet: '请选择托盘', + selectPalletFirst: '请先选择托盘', + selectProductFirst: '请先选择产品', + completePalletInfo: '请完善托盘仓库/库位/包数', + productAdded: '已添加产品', + code: '编码', + packagingScheme: '包装方案', + palletPackageQuantity: '每托包数', + packageQuantity: '每包个数', + selectedPalletCount: '已选择 {count} 个托盘', + selectedCount: '已选 {count} 个', + packageUnit: '{count} 包', + pieceUnit: '{count} 个', + warehouse: '仓库', + area: '库区', + location: '库位', + emptyProduct: '暂无产品数据', + emptyPallet: '暂无托盘', + spec: '规格', + unit: '单位', + palletCode: '托盘码', + palletType: '托盘类型', + palletStatus: '托盘状态', + outMode: '出库方式', + outModeWholePallet: '整托出库', + outModeSplitPallet: '拆托出库' + }, sparepartInbound: { moduleName: '备件入库', tabPending: '待入库', diff --git a/src/pages.json b/src/pages.json index 8cfc4a9..6a978d1 100644 --- a/src/pages.json +++ b/src/pages.json @@ -458,6 +458,48 @@ "navigationStyle": "custom" } }, + { + "path": "productOutbound/index", + "style": { + "navigationBarTitleText": "产品出库", + "navigationStyle": "custom" + } + }, + { + "path": "productOutbound/create", + "style": { + "navigationBarTitleText": "新增产品出库", + "navigationStyle": "custom" + } + }, + { + "path": "productOutbound/detail", + "style": { + "navigationBarTitleText": "产品出库详情", + "navigationStyle": "custom" + } + }, + { + "path": "productOutbound/productSelect", + "style": { + "navigationBarTitleText": "选择产品", + "navigationStyle": "custom" + } + }, + { + "path": "productOutbound/productConfirm", + "style": { + "navigationBarTitleText": "新增产品出库", + "navigationStyle": "custom" + } + }, + { + "path": "productOutbound/palletSelect", + "style": { + "navigationBarTitleText": "选择托盘", + "navigationStyle": "custom" + } + }, { "path": "sparepartOutbound/index", "style": { diff --git a/src/pages_function/pages/moldRepair/userSelect.vue b/src/pages_function/pages/moldRepair/userSelect.vue index 1b9251b..a4724eb 100644 --- a/src/pages_function/pages/moldRepair/userSelect.vue +++ b/src/pages_function/pages/moldRepair/userSelect.vue @@ -124,7 +124,7 @@ function handleConfirm() { return } // 根据来源使用不同的 globalData key - const keyMap = { sparepartInbound: '_sparepartInboundUserSelectResult', sparepartOutbound: '_sparepartOutboundUserSelectResult', materialInbound: '_materialInboundUserSelectResult' } + const keyMap = { productInbound: '_productInboundUserSelectResult', productOutbound: '_productOutboundUserSelectResult', sparepartInbound: '_sparepartInboundUserSelectResult', sparepartOutbound: '_sparepartOutboundUserSelectResult', materialInbound: '_materialInboundUserSelectResult' } const key = keyMap[fromSource.value] || '_moldRepairUserSelectResult' getApp().globalData[key] = { field: field.value === 'confirmBy' ? 'confirmBy' : 'acceptedBy', diff --git a/src/pages_function/pages/productInbound/index.vue b/src/pages_function/pages/productInbound/index.vue index 3544190..b4a007c 100644 --- a/src/pages_function/pages/productInbound/index.vue +++ b/src/pages_function/pages/productInbound/index.vue @@ -95,23 +95,9 @@ *{{ t('productInbound.auditor') }} - - {{ selectedAuditor ? selectedAuditor.label : t('productInbound.choose') }} - v - - - - {{ u.label }} - - - - + + {{ selectedAuditor ? selectedAuditor.label : t('productInbound.choose') }} + @@ -158,7 +144,6 @@ import { onReady, onShow, onUnload } from '@dcloudio/uni-app' import { useI18n } from 'vue-i18n' import NavBar from '@/components/common/NavBar.vue' import { auditProductInbound, getProductInboundPage, submitProductInbound } from '@/api/mes/productInbound' -import { getSimpleUserList } from '@/api/mes/moldget' const { t } = useI18n() const selectedStatus = ref('') @@ -244,10 +229,11 @@ async function fetchList(reset) { if (pageNo.value === 1) loading.value = true else loadingMore.value = true try { + const keyword = searchKeyword.value.trim() const res = await getProductInboundPage({ pageNo: pageNo.value, pageSize: pageSize.value, - no: searchKeyword.value.trim() || undefined, + no: keyword || undefined, status: selectedStatus.value || undefined }) const page = normalizePageData(res) @@ -346,9 +332,7 @@ async function handleReject(item) { const showAuditModal = ref(false) const currentAuditItem = ref(null) -const auditorOptions = ref([]) const selectedAuditor = ref(null) -const showAuditorDropdown = ref(false) const auditRemark = ref('') function openSubmitAudit(item) { @@ -356,32 +340,24 @@ function openSubmitAudit(item) { selectedAuditor.value = null auditRemark.value = '' showAuditModal.value = true - loadAuditorOptions() } function closeAuditModal() { showAuditModal.value = false currentAuditItem.value = null - showAuditorDropdown.value = false } -function toggleAuditorDropdown() { - showAuditorDropdown.value = !showAuditorDropdown.value +function goSelectAuditor() { + const suffix = selectedAuditor.value?.value ? `&selectedId=${encodeURIComponent(String(selectedAuditor.value.value))}` : '' + uni.navigateTo({ url: `/pages_function/pages/moldRepair/userSelect?field=auditUser&from=productInbound${suffix}` }) } -function selectAuditor(u) { - selectedAuditor.value = u - showAuditorDropdown.value = false -} -async function loadAuditorOptions() { - if (auditorOptions.value.length) return - try { - const res = await getSimpleUserList() - const data = Array.isArray(res) ? res : (Array.isArray(res?.data) ? res.data : []) - auditorOptions.value = data.map((u) => ({ - value: u.id || u.userId, - label: u.nickname || u.userName || u.name || String(u.id || '') - })) - } catch (e) { - console.error('loadAuditorOptions error', e) +function consumeSelectedAuditor() { + const result = getApp().globalData?._productInboundUserSelectResult + if (!result?.user) return + const user = result.user + selectedAuditor.value = { + value: user.id || user.userId, + label: user.nickname || user.userName || user.name || String(user.id || '') } + getApp().globalData._productInboundUserSelectResult = null } async function confirmSubmitAudit() { if (!selectedAuditor.value) { @@ -433,7 +409,10 @@ onReady(() => { focusKeywordNoKeyboard() }) -onShow(() => fetchList(true)) +onShow(() => { + consumeSelectedAuditor() + fetchList(true) +}) onUnload(() => clearSearchTimer()) @@ -475,8 +454,8 @@ onUnload(() => clearSearchTimer()) .go-top-btn { position: fixed; right: 28rpx; bottom: calc(140rpx + env(safe-area-inset-bottom)); width: 92rpx; height: 92rpx; border-radius: 46rpx; background: rgba(255, 255, 255, 0.96); box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.12); display: flex; align-items: center; justify-content: center; } .add-btn { position: fixed; right: 28rpx; bottom: calc(56rpx + env(safe-area-inset-bottom)); width: 92rpx; height: 92rpx; border-radius: 46rpx; background: #1f4b79; box-shadow: 0 14rpx 30rpx rgba(24, 63, 108, 0.24); display: flex; align-items: center; justify-content: center; } .add-icon { color: #fff; font-size: 64rpx; line-height: 1; margin-top: -4rpx; } -.modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); z-index: 999; display: flex; align-items: center; justify-content: center; } -.modal-card { width: 640rpx; background: #fff; border-radius: 20rpx; box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15); } +.modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); z-index: 999; display: flex; align-items: flex-end; justify-content: center; } +.modal-card { width: 100%; background: #fff; border-radius: 28rpx 28rpx 0 0; box-shadow: 0 -12rpx 42rpx rgba(0, 0, 0, 0.14); padding-bottom: env(safe-area-inset-bottom); } .modal-header { display: flex; align-items: center; justify-content: space-between; padding: 32rpx 32rpx 20rpx; border-bottom: 1rpx solid #f0f0f0; } .modal-title { font-size: 32rpx; font-weight: 700; color: #1a1a1a; } .modal-close { font-size: 36rpx; color: #999; padding: 8rpx; } @@ -485,16 +464,10 @@ onUnload(() => clearSearchTimer()) .modal-field:last-child { margin-bottom: 0; } .modal-label { font-size: 28rpx; color: #374151; font-weight: 500; margin-bottom: 14rpx; display: block; } .required { color: #ef4444; } -.modal-dropdown { position: relative; display: flex; align-items: center; justify-content: space-between; height: 80rpx; padding: 0 24rpx; background: #f8fafc; border: 1rpx solid #e0e0e0; border-radius: 12rpx; font-size: 28rpx; color: #374151; } +.modal-select-field { display: flex; align-items: center; justify-content: space-between; height: 80rpx; padding: 0 24rpx; background: #f8fafc; border: 1rpx solid #e0e0e0; border-radius: 12rpx; box-sizing: border-box; } +.modal-select-text { flex: 1; min-width: 0; font-size: 28rpx; color: #374151; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .placeholder { color: #bbb; } -.dropdown-arrow { font-size: 20rpx; color: #999; } .modal-textarea { width: 100%; min-height: var(--app-textarea-field-height, 100rpx); padding: 20rpx; background: #f8fafc; border: 1rpx solid #e0e0e0; border-radius: 12rpx; font-size: 27rpx; color: #374151; box-sizing: border-box; } -.dropdown-panel { position: absolute; top: 100%; left: 0; right: 0; z-index: 200; margin-top: 4rpx; background: #fff; border: 1rpx solid #e0e0e0; border-radius: 12rpx; box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.1); overflow: hidden; } -.dropdown-scroll { height: 300rpx; } -.dropdown-item { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; border-bottom: 1rpx solid #f0f0f0; } -.dropdown-item.active { background: #f0f5ff; } -.dropdown-item-text { font-size: 27rpx; color: #333; } -.dropdown-check { font-size: 28rpx; color: #2563eb; font-weight: 700; } .modal-footer { display: flex; gap: 18rpx; padding: 24rpx 32rpx; border-top: 1rpx solid #f0f0f0; } .modal-btn { flex: 1; height: 80rpx; line-height: 80rpx; text-align: center; border-radius: 14rpx; font-size: 30rpx; font-weight: 600; } .cancel-btn { background: #f0f0f0; color: #6b7280; } diff --git a/src/pages_function/pages/productOutbound/create.vue b/src/pages_function/pages/productOutbound/create.vue new file mode 100644 index 0000000..defb250 --- /dev/null +++ b/src/pages_function/pages/productOutbound/create.vue @@ -0,0 +1,372 @@ +