| src/api/inventoryManagement/stockInRecord.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/inventoryManagement/stockOutRecord.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inventoryManagement/dispatchLog/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inventoryManagement/receiptManagement/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/inventoryManagement/stockInRecord.js
@@ -25,3 +25,12 @@ data: ids }) } // 编辑入库(用于库存台账编辑) export const editStockInStock = (data) => { return request({ url: '/stockInRecord/editStockInStock', method: 'POST', data }) } src/api/inventoryManagement/stockOutRecord.js
@@ -17,3 +17,12 @@ data: ids }) } // 编辑出库(库存台账编辑) export const editStockOut = (data) => { return request({ url: '/stockOutRecord/editStockOut', method: 'POST', data }) } src/pages/inventoryManagement/dispatchLog/index.vue
@@ -40,13 +40,26 @@ <view class="card-body"> <view class="row"><text class="l">规格型号</text><text class="r">{{ item.model }}</text></view> <view class="row"><text class="l">单位</text><text class="r">{{ item.unit }}</text></view> <view class="row"><text class="l">出库数量</text><text class="r highlight">{{ item.stockOutNum }}</text></view> <view class="row"><text class="l">出库数量</text><text class="r highlight">{{ item.netWeight }}</text></view> <view class="row"><text class="l">采购员</text><text class="r">{{ item.purchaser || item.createBy }}</text></view> <view class="row" v-if="item.recordType !== undefined"><text class="l">来源</text><text class="r">{{ getRecordType(item.recordType) || item.recordType }}</text></view> </view> </view> <view class="card-actions"> <view class="btn-delete" @click.stop="handleDeleteSingle(item)">删除</view> <view v-if="isQualifiedRow(item) && hasDispatchEdit" class="btn-edit" @click.stop="handleEdit(item)" > 编辑 </view> <view v-if="hasDispatchCancel" class="btn-delete" @click.stop="handleDeleteSingle(item)" > 删除 </view> </view> </view> </view> @@ -56,14 +69,91 @@ <view class="load-more-wrap" v-if="tableData.length > 0"> <u-loadmore :status="loadStatus" @loadmore="loadMore" /> </view> <!-- 编辑弹窗(参照 PC) --> <up-popup :show="showEditModal" mode="bottom" @close="showEditModal = false"> <view class="edit-popup"> <view class="popup-header"> <text class="popup-title">编辑出库</text> </view> <scroll-view scroll-y class="popup-body"> <view class="form-row"> <text class="form-label required">车牌号</text> <up-input v-model="editForm.licensePlateNo" placeholder="请输入车牌号" /> </view> <view class="form-row"> <text class="form-label required">毛重(吨)</text> <up-input v-model="editForm.grossWeight" type="digit" placeholder="请输入毛重" @blur="computeNetWeightEdit" /> </view> <view class="form-row"> <text class="form-label required">皮重(吨)</text> <up-input v-model="editForm.tareWeight" type="digit" placeholder="请输入皮重" @blur="computeNetWeightEdit" /> </view> <view class="form-row"> <text class="form-label required">净重(吨)</text> <up-input v-model="editForm.netWeight" type="digit" placeholder="自动计算" disabled /> </view> <view class="form-row"> <text class="form-label required">过磅日期</text> <view class="selector-trigger" @click="openWeighingDatePicker"> <text class="selector-text" :class="{ placeholder: !editForm.weighingDate }"> {{ editForm.weighingDate || '请选择过磅日期' }} </text> <up-icon name="calendar" size="16" color="#999" /> </view> </view> <view class="form-row"> <text class="form-label required">过磅员</text> <up-input v-model="editForm.weighingOperator" placeholder="请输入过磅员" /> </view> <view class="form-row"> <text class="form-label">备注</text> <up-input v-model="editForm.remark" type="textarea" placeholder="选填" /> </view> </scroll-view> <view class="popup-footer"> <view class="btn-cancel" @click="showEditModal = false">取消</view> <view class="btn-ok" @click="handleEditSubmit">确认</view> </view> <up-popup :show="showWeighingDatePicker" mode="bottom" @close="showWeighingDatePicker = false"> <up-datetime-picker :show="true" v-model="weighingDateValue" mode="datetime" @confirm="onWeighingDateConfirm" @cancel="showWeighingDatePicker = false" /> </up-popup> </view> </up-popup> </view> </template> <script setup> import { ref, reactive, toRefs } from 'vue' import { computed, ref, reactive, toRefs, watch } from 'vue' import { onShow, onReachBottom } from '@dcloudio/uni-app' import PageHeader from '@/components/PageHeader.vue' import { getStockOutPage, delStockOut } from '@/api/inventoryManagement/stockOutRecord.js' import dayjs from 'dayjs' import { checkPermi } from '@/utils/permission' import { getStockOutPage, delStockOut, editStockOut } from '@/api/inventoryManagement/stockOutRecord.js' import { findAllQualifiedStockOutRecordTypeOptions } from '@/api/basicData/enum.js' @@ -81,6 +171,10 @@ }) const { searchForm } = toRefs(data) // 权限控制(参照 PC) const hasDispatchEdit = computed(() => checkPermi(['dispatch_edit'])) const hasDispatchCancel = computed(() => checkPermi(['dispatch_cancel'])) function getRecordType(recordType) { if (recordType == null || recordType === '') return '' return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || '' @@ -95,6 +189,10 @@ .catch(() => { stockRecordTypeOptions.value = [] }) } function isQualifiedRow(row) { return row?.recordType === 0 || row?.recordType === '0' } const getList = () => { @@ -160,6 +258,7 @@ } const handleDeleteSingle = (item) => { if (!hasDispatchCancel.value) return uni.showModal({ title: '删除', content: '确认删除该条出库记录?', @@ -175,6 +274,82 @@ }) } } }) } // ---------------- 编辑 ---------------- const showEditModal = ref(false) const showWeighingDatePicker = ref(false) const weighingDateValue = ref(Date.now()) const editForm = reactive({ id: null, licensePlateNo: '', grossWeight: '', tareWeight: '', netWeight: '', weighingDate: '', weighingOperator: '', remark: '' }) const computeNetWeightEdit = () => { const gross = Number(editForm.grossWeight) const tare = Number(editForm.tareWeight) if (Number.isFinite(gross) && Number.isFinite(tare)) { const net = gross - tare const safeNet = Number(net.toFixed(2)) editForm.netWeight = safeNet > 0 ? safeNet : 0 } } watch( () => [showEditModal.value, editForm.grossWeight, editForm.tareWeight], () => { if (showEditModal.value) computeNetWeightEdit() } ) const openWeighingDatePicker = () => { weighingDateValue.value = editForm.weighingDate ? dayjs(editForm.weighingDate).valueOf() : Date.now() showWeighingDatePicker.value = true } const onWeighingDateConfirm = (e) => { editForm.weighingDate = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss') showWeighingDatePicker.value = false } const handleEdit = (row) => { Object.assign(editForm, row || {}) // 以当前毛重/皮重为准计算净重 computeNetWeightEdit() showEditModal.value = true } const handleEditSubmit = () => { if (!hasDispatchEdit.value) return if (!editForm.licensePlateNo) return uni.showToast({ title: '请输入车牌号', icon: 'none' }) if (!editForm.grossWeight && editForm.grossWeight !== 0) return uni.showToast({ title: '请输入毛重', icon: 'none' }) if (!editForm.tareWeight && editForm.tareWeight !== 0) return uni.showToast({ title: '请输入皮重', icon: 'none' }) if (!editForm.weighingDate) return uni.showToast({ title: '请选择过磅日期', icon: 'none' }) if (!editForm.weighingOperator) return uni.showToast({ title: '请输入过磅员', icon: 'none' }) uni.showLoading({ title: '保存中...', mask: true }) const { stockOutNum, ...payload } = editForm editStockOut(payload) .then(() => { uni.showToast({ title: '编辑成功', icon: 'success' }) showEditModal.value = false getList() }) .catch(() => { uni.showToast({ title: '编辑失败', icon: 'none' }) }) .finally(() => { uni.hideLoading() }) } @@ -263,11 +438,26 @@ display: flex; justify-content: center; align-items: center; width: 100%; text-align: center; } .btn-delete { font-size: 28rpx; color: #f56c6c; padding: 12rpx 32rpx; margin: 0 auto; display: inline-flex; align-items: center; justify-content: center; } .btn-edit { font-size: 28rpx; color: #2979ff; padding: 12rpx 32rpx; margin: 0 auto; display: inline-flex; align-items: center; justify-content: center; } .no-data { text-align: center; @@ -276,4 +466,78 @@ font-size: 28rpx; } .load-more-wrap { padding: 24rpx 0 40rpx; } /* 编辑弹窗样式 */ .edit-popup { background: #fff; border-radius: 24rpx 24rpx 0 0; padding-bottom: env(safe-area-inset-bottom); } .popup-header { padding: 24rpx; border-bottom: 1rpx solid #eee; text-align: center; } .popup-title { font-size: 30rpx; font-weight: 500; color: #333; } .popup-body { padding: 24rpx 24rpx 0; max-height: 60vh; } .popup-footer { display: flex; gap: 24rpx; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); border-top: 1rpx solid #eee; } .btn-cancel, .btn-ok { flex: 1; height: 88rpx; border-radius: 999rpx; display: flex; align-items: center; justify-content: center; font-size: 30rpx; } .btn-cancel { background: #f0f0f0; color: #666; } .btn-ok { background: #2979ff; color: #fff; } .form-row { margin-bottom: 24rpx; } .form-label { display: block; font-size: 26rpx; color: #666; margin-bottom: 12rpx; } .form-label.required:before { content: '*'; color: #f56c6c; margin-right: 6rpx; } .selector-trigger { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; background: #f5f5f5; border-radius: 12rpx; } .selector-text { font-size: 28rpx; color: #333; } .selector-text.placeholder { color: #999; } </style> src/pages/inventoryManagement/receiptManagement/index.vue
@@ -46,7 +46,20 @@ </view> </view> <view class="card-actions"> <view class="btn-delete" @click.stop="handleDeleteSingle(item)">删除</view> <view v-if="isQualifiedRow(item) && hasReceiptEdit" class="btn-edit" @click.stop="handleEdit(item)" > 编辑 </view> <view v-if="hasReceiptCancel" class="btn-delete" @click.stop="handleDeleteSingle(item)" > 删除 </view> </view> </view> </view> @@ -56,20 +69,82 @@ <view class="load-more-wrap" v-if="tableData.length > 0"> <u-loadmore :status="loadStatus" @loadmore="loadMore" /> </view> <!-- 编辑弹窗 --> <up-popup :show="showEditModal" mode="bottom" @close="showEditModal = false"> <view class="edit-popup"> <view class="popup-header"> <text class="popup-title">编辑入库</text> </view> <scroll-view scroll-y class="popup-body"> <view class="form-row"> <text class="form-label required">车牌号</text> <up-input v-model="editForm.licensePlateNo" placeholder="请输入车牌号" /> </view> <view class="form-row"> <text class="form-label required">毛重(吨)</text> <up-input v-model="editForm.grossWeight" type="digit" placeholder="请输入毛重" @blur="computeNetWeightEdit" /> </view> <view class="form-row"> <text class="form-label required">皮重(吨)</text> <up-input v-model="editForm.tareWeight" type="digit" placeholder="请输入皮重" @blur="computeNetWeightEdit" /> </view> <view class="form-row"> <text class="form-label required">净重(吨)</text> <up-input v-model="editForm.netWeight" type="digit" placeholder="自动计算" disabled /> </view> <view class="form-row"> <text class="form-label required">过磅日期</text> <view class="selector-trigger" @click="openWeighingDatePicker"> <text class="selector-text" :class="{ placeholder: !editForm.weighingDate }"> {{ editForm.weighingDate || '请选择过磅日期' }} </text> <up-icon name="calendar" size="16" color="#999" /> </view> </view> <view class="form-row"> <text class="form-label required">过磅员</text> <up-input v-model="editForm.weighingOperator" placeholder="请输入过磅员" /> </view> <view class="form-row"> <text class="form-label">备注</text> <up-input v-model="editForm.remark" type="textarea" placeholder="选填" /> </view> </scroll-view> <view class="popup-footer"> <view class="btn-cancel" @click="showEditModal = false">取消</view> <view class="btn-ok" @click="handleEditSubmit">确认</view> </view> <!-- 过磅日期选择 --> <up-popup :show="showWeighingDatePicker" mode="bottom" @close="showWeighingDatePicker = false"> <up-datetime-picker :show="true" v-model="weighingDateValue" mode="datetime" @confirm="onWeighingDateConfirm" @cancel="showWeighingDatePicker = false" /> </up-popup> </view> </up-popup> </view> </template> <script setup> import { ref, reactive, toRefs } from 'vue' import { computed, ref, reactive, toRefs, watch } from 'vue' import { onShow, onReachBottom } from '@dcloudio/uni-app' import dayjs from 'dayjs' import PageHeader from '@/components/PageHeader.vue' import { getStockInRecordListPage, batchDeleteStockInRecords batchDeleteStockInRecords, editStockInStock } from '@/api/inventoryManagement/stockInRecord.js' import { findAllQualifiedStockInRecordTypeOptions } from '@/api/basicData/enum.js' import { checkPermi } from '@/utils/permission' const stockRecordTypeOptions = ref([]) const currentType = () => '0' @@ -77,6 +152,10 @@ const tableData = ref([]) const total = ref(0) const loadStatus = ref('loadmore') // loadmore | loading | nomore | error // 权限控制 const hasReceiptEdit = computed(() => checkPermi(['receipt_edit'])) const hasReceiptCancel = computed(() => checkPermi(['receipt_cancel'])) const page = reactive({ current: 1, size: 4 }) const data = reactive({ @@ -89,6 +168,10 @@ function getRecordType(recordType) { if (recordType == null || recordType === '') return '' return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || '' } function isQualifiedRow(row) { return row?.recordType === 0 || row?.recordType === '0' } function fetchRecordTypeOptions() { @@ -181,6 +264,77 @@ }) } } }) } // ---------------- 编辑入库 ---------------- const showEditModal = ref(false) const showWeighingDatePicker = ref(false) const weighingDateValue = ref(Date.now()) const editForm = reactive({ id: null, licensePlateNo: '', grossWeight: '', tareWeight: '', netWeight: '', weighingDate: '', weighingOperator: '', remark: '' }) const computeNetWeightEdit = () => { const gross = Number(editForm.grossWeight) const tare = Number(editForm.tareWeight) if (Number.isFinite(gross) && Number.isFinite(tare)) { const net = gross - tare const safeNet = Number(net.toFixed(2)) editForm.netWeight = safeNet > 0 ? safeNet : 0 } } watch( () => [editForm.grossWeight, editForm.tareWeight], () => { if (showEditModal.value) computeNetWeightEdit() } ) const openWeighingDatePicker = () => { weighingDateValue.value = editForm.weighingDate ? dayjs(editForm.weighingDate).valueOf() : Date.now() showWeighingDatePicker.value = true } const onWeighingDateConfirm = (e) => { editForm.weighingDate = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss') showWeighingDatePicker.value = false } const handleEdit = (row) => { Object.assign(editForm, row || {}) computeNetWeightEdit() showEditModal.value = true } const handleEditSubmit = () => { if (!editForm.licensePlateNo) return uni.showToast({ title: '请输入车牌号', icon: 'none' }) if (editForm.grossWeight === '' || Number(editForm.grossWeight) < 0) return uni.showToast({ title: '请输入毛重', icon: 'none' }) if (editForm.tareWeight === '' || Number(editForm.tareWeight) < 0) return uni.showToast({ title: '请输入皮重', icon: 'none' }) if (!editForm.weighingDate) return uni.showToast({ title: '请选择过磅日期', icon: 'none' }) if (!editForm.weighingOperator) return uni.showToast({ title: '请输入过磅员', icon: 'none' }) uni.showLoading({ title: '保存中...', mask: true }) editStockInStock({ ...editForm }) .then(() => { uni.showToast({ title: '编辑成功', icon: 'success' }) showEditModal.value = false getList() }) .catch(() => { uni.showToast({ title: '编辑失败', icon: 'none' }) }) .finally(() => { uni.hideLoading() }) } @@ -302,6 +456,16 @@ align-items: center; justify-content: center; } .btn-edit { font-size: 28rpx; color: #2979ff; padding: 12rpx 32rpx; margin: 0 auto; display: inline-flex; align-items: center; justify-content: center; } .no-data { text-align: center; padding: 80rpx 0; @@ -311,4 +475,78 @@ .load-more-wrap { padding: 24rpx 0 40rpx; } /* 编辑弹窗样式 */ .edit-popup { background: #fff; border-radius: 24rpx 24rpx 0 0; padding-bottom: env(safe-area-inset-bottom); } .popup-header { padding: 24rpx; border-bottom: 1rpx solid #eee; text-align: center; } .popup-title { font-size: 30rpx; font-weight: 500; color: #333; } .popup-body { padding: 24rpx 24rpx 0; max-height: 60vh; } .popup-footer { display: flex; gap: 24rpx; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); border-top: 1rpx solid #eee; } .btn-cancel, .btn-ok { flex: 1; height: 88rpx; border-radius: 999rpx; display: flex; align-items: center; justify-content: center; font-size: 30rpx; } .btn-cancel { background: #f0f0f0; color: #666; } .btn-ok { background: #2979ff; color: #fff; } .form-row { margin-bottom: 24rpx; } .form-label { display: block; font-size: 26rpx; color: #666; margin-bottom: 12rpx; } .form-label.required:before { content: '*'; color: #f56c6c; margin-right: 6rpx; } .selector-trigger { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; background: #f5f5f5; border-radius: 12rpx; } .selector-text { font-size: 28rpx; color: #333; } .selector-text.placeholder { color: #999; } </style>