|
|
|
|
@ -48,7 +48,7 @@
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-item">
|
|
|
|
|
<text class="info-label">当前库存:</text>
|
|
|
|
|
<text class="info-value stock-highlight">{{ textValue(selectedSparepart.stock) }}{{ textUnit(selectedSparepart.minStockUnitName) }}</text>
|
|
|
|
|
<text class="info-value stock-highlight">{{ selectedSparepart.count != null ? selectedSparepart.count : 0 }}{{ textUnit(selectedSparepart.unitName || selectedSparepart.minStockUnitName || '个') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
@ -60,7 +60,7 @@
|
|
|
|
|
</view>
|
|
|
|
|
<view class="detail-col">
|
|
|
|
|
<text class="detail-label">库存单位:</text>
|
|
|
|
|
<text class="detail-value">{{ textValue(selectedSparepart.unitName || selectedSparepart.minStockUnitName) }}</text>
|
|
|
|
|
<text class="detail-value">{{ textValue(selectedSparepart.unitName || selectedSparepart.minStockUnitName || '个') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="detail-row">
|
|
|
|
|
@ -92,6 +92,14 @@
|
|
|
|
|
<text class="iconfont icon-shield purpose-icon"></text>
|
|
|
|
|
<text class="purpose-text">保养领用</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view
|
|
|
|
|
class="purpose-item"
|
|
|
|
|
:class="{ active: selectedPurpose === 'other' }"
|
|
|
|
|
@click="setPurpose('other')"
|
|
|
|
|
>
|
|
|
|
|
<text class="iconfont icon-box purpose-icon"></text>
|
|
|
|
|
<text class="purpose-text">其他出库</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 关联信息(维修领用/保养领用) -->
|
|
|
|
|
@ -101,6 +109,7 @@
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="selectedPurpose === 'repair' || selectedPurpose === 'maintain'" class="select-row-card warehouse-area-card">
|
|
|
|
|
<view class="warehouse-area-rows">
|
|
|
|
|
<!-- 关联设备 -->
|
|
|
|
|
<view class="warehouse-area-row">
|
|
|
|
|
<text class="warehouse-area-label">关联设备</text>
|
|
|
|
|
<view class="warehouse-area-dropdown" @click="toggleDeviceDropdown">
|
|
|
|
|
@ -110,15 +119,17 @@
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="showDeviceDropdown" class="dropdown-panel">
|
|
|
|
|
<scroll-view scroll-y class="dropdown-scroll">
|
|
|
|
|
<view v-for="item in deviceOptions" :key="item.value" class="dropdown-item" :class="{ active: selectedDevice?.value === item.value }" @click.stop="handleSelectDevice(item)">
|
|
|
|
|
<view v-for="item in filteredDeviceOptions" :key="item.value" class="dropdown-item" :class="{ active: selectedDevice?.value === item.value }" @click.stop="handleSelectDevice(item)">
|
|
|
|
|
<text class="dropdown-item-text">{{ item.label }}</text>
|
|
|
|
|
<text v-if="selectedDevice?.value === item.value" class="dropdown-check">✓</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="!deviceOptions.length" class="dropdown-empty">暂无设备数据</view>
|
|
|
|
|
<view v-if="deviceLoading" class="dropdown-empty">加载中...</view>
|
|
|
|
|
<view v-else-if="!filteredDeviceOptions.length" class="dropdown-empty">{{ selectedPurpose === 'maintain' ? '暂无保养设备' : '暂无设备数据' }}</view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<!-- 维修单号/保养单号 -->
|
|
|
|
|
<view class="warehouse-area-row">
|
|
|
|
|
<text class="warehouse-area-label">{{ selectedPurpose === 'repair' ? '维修单号' : '保养单号' }}</text>
|
|
|
|
|
<view class="warehouse-area-dropdown" @click="toggleOrderDropdown">
|
|
|
|
|
@ -218,9 +229,10 @@ import { useI18n } from 'vue-i18n'
|
|
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
|
|
import { getSimpleUserList } from '@/api/mes/moldget'
|
|
|
|
|
import { getDeviceLedgerList } from '@/api/mes/moldoperate'
|
|
|
|
|
import { getEquipmentRepairListByDeviceId, getEquipmentMaintenanceByDeviceId } from '@/api/mes/equipment'
|
|
|
|
|
import { getEquipmentRepairListByDeviceId, getMaintenanceTicketPage } from '@/api/mes/equipment'
|
|
|
|
|
import { getDvRepairPage } from '@/api/mes/dvrepair'
|
|
|
|
|
import { getSparepartDetail } from '@/api/mes/sparepart'
|
|
|
|
|
import { getSparepartDetail, getSparepartStockCount } from '@/api/mes/sparepart'
|
|
|
|
|
import { createSparepartOutbound } from '@/api/mes/sparepartOutbound'
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
@ -228,6 +240,19 @@ const selectedSparepart = ref({})
|
|
|
|
|
const outboundQty = ref(null)
|
|
|
|
|
const selectedPurpose = ref('repair')
|
|
|
|
|
|
|
|
|
|
async function loadStockCount() {
|
|
|
|
|
const id = selectedSparepart.value.id
|
|
|
|
|
if (!id) return
|
|
|
|
|
try {
|
|
|
|
|
const res = await getSparepartStockCount(id)
|
|
|
|
|
const count = res?.data ?? res
|
|
|
|
|
selectedSparepart.value.count = (count != null) ? Number(count) : 0
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取库存失败:', e)
|
|
|
|
|
selectedSparepart.value.count = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setPurpose(value) {
|
|
|
|
|
selectedPurpose.value = value
|
|
|
|
|
// 切换用途时清空关联信息
|
|
|
|
|
@ -251,6 +276,18 @@ const selectedRepairOrder = ref(null)
|
|
|
|
|
const maintainOrderOptions = ref([])
|
|
|
|
|
const selectedMaintainOrder = ref(null)
|
|
|
|
|
|
|
|
|
|
// 保养模式:有保养记录的设备ID集合
|
|
|
|
|
const maintainDeviceIds = ref(new Set())
|
|
|
|
|
const deviceLoading = ref(false)
|
|
|
|
|
|
|
|
|
|
// 根据模式过滤设备列表
|
|
|
|
|
const filteredDeviceOptions = computed(() => {
|
|
|
|
|
if (selectedPurpose.value === 'maintain') {
|
|
|
|
|
return deviceOptions.value.filter(d => maintainDeviceIds.value.has(d.value))
|
|
|
|
|
}
|
|
|
|
|
return deviceOptions.value
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 通用的订单下拉显示状态(根据当前用途自动切换)
|
|
|
|
|
const showOrderDropdown = ref(false)
|
|
|
|
|
const currentOrder = computed(() =>
|
|
|
|
|
@ -299,11 +336,37 @@ function stockUnitLabel(item) {
|
|
|
|
|
|
|
|
|
|
function handleScan() {
|
|
|
|
|
uni.scanCode({
|
|
|
|
|
onlyFromCamera: true,
|
|
|
|
|
onlyFromCamera: false,
|
|
|
|
|
scanType: ['barCode', 'qrCode'],
|
|
|
|
|
success: (res) => {
|
|
|
|
|
console.log('扫码结果:', res.result)
|
|
|
|
|
uni.showToast({ title: '扫码成功: ' + res.result, icon: 'none' })
|
|
|
|
|
success: async (res) => {
|
|
|
|
|
const code = (res.result || '').trim()
|
|
|
|
|
if (!code) return
|
|
|
|
|
let sparepartId = null
|
|
|
|
|
if (code.toUpperCase().startsWith('SPARE-')) {
|
|
|
|
|
sparepartId = code.replace(/SPARE-/i, '')
|
|
|
|
|
} else {
|
|
|
|
|
const idMatch = code.match(/(\d+)$/)
|
|
|
|
|
if (idMatch) sparepartId = idMatch[1]
|
|
|
|
|
}
|
|
|
|
|
if (!sparepartId) {
|
|
|
|
|
uni.showToast({ title: '无法识别备件码', icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const apiRes = await getSparepartDetail(sparepartId)
|
|
|
|
|
const detail = apiRes?.data || apiRes
|
|
|
|
|
if (detail && detail.id) {
|
|
|
|
|
selectedSparepart.value = detail
|
|
|
|
|
outboundQty.value = null
|
|
|
|
|
loadStockCount()
|
|
|
|
|
uni.showToast({ title: '已加载: ' + (detail.name || ''), icon: 'success', duration: 1500 })
|
|
|
|
|
} else {
|
|
|
|
|
uni.showToast({ title: '未找到备件: ' + sparepartId, icon: 'none' })
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('[备件出库] 扫码查询备件失败:', e)
|
|
|
|
|
uni.showToast({ title: '扫码失败', icon: 'none' })
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fail: () => {
|
|
|
|
|
uni.showToast({ title: '扫码失败', icon: 'none' })
|
|
|
|
|
@ -321,9 +384,35 @@ function handleSelectSparepart() {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设备下拉
|
|
|
|
|
// 设备下拉(维修模式加载全部设备,保养模式加载有保养记录的设备)
|
|
|
|
|
function toggleDeviceDropdown() {
|
|
|
|
|
showDeviceDropdown.value = !showDeviceDropdown.value
|
|
|
|
|
if (showDeviceDropdown.value && selectedPurpose.value === 'maintain' && maintainDeviceIds.value.size === 0) {
|
|
|
|
|
loadMaintainDeviceIds()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载有保养记录的设备ID(从保养工单中提取设备名)
|
|
|
|
|
async function loadMaintainDeviceIds() {
|
|
|
|
|
deviceLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await getMaintenanceTicketPage({})
|
|
|
|
|
const data = res && res.data !== undefined ? res.data : res
|
|
|
|
|
const records = Array.isArray(data) ? data : (data?.list || data?.records || [])
|
|
|
|
|
// 从保养工单提取设备名,匹配到 deviceOptions 中的设备ID
|
|
|
|
|
const deviceNames = new Set(records.map(r => r.deviceName).filter(Boolean))
|
|
|
|
|
const ids = new Set()
|
|
|
|
|
for (const d of deviceOptions.value) {
|
|
|
|
|
if (deviceNames.has(d.label) || deviceNames.has(d.deviceName)) {
|
|
|
|
|
ids.add(d.value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
maintainDeviceIds.value = ids
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('loadMaintainDeviceIds error', e)
|
|
|
|
|
} finally {
|
|
|
|
|
deviceLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleSelectDevice(item) {
|
|
|
|
|
@ -397,25 +486,20 @@ async function loadMaintainOrders(deviceCode) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保养单列表(根据设备名称加载保养工单)
|
|
|
|
|
async function loadMaintainOrdersById(deviceId) {
|
|
|
|
|
if (!deviceId) return
|
|
|
|
|
try {
|
|
|
|
|
const res = await getEquipmentMaintenanceByDeviceId(deviceId)
|
|
|
|
|
const deviceName = selectedDevice.value?.deviceName || selectedDevice.value?.label || ''
|
|
|
|
|
const res = await getMaintenanceTicketPage({ deviceName })
|
|
|
|
|
const data = res && res.data !== undefined ? res.data : res
|
|
|
|
|
let list = []
|
|
|
|
|
if (Array.isArray(data)) { list = data }
|
|
|
|
|
else if (data && Array.isArray(data.data)) { list = data.data }
|
|
|
|
|
else if (data && data.data && Array.isArray(data.data.list)) { list = data.data.list }
|
|
|
|
|
else if (data && data.data && Array.isArray(data.data.rows)) { list = data.data.rows }
|
|
|
|
|
else if (data && data.data && Array.isArray(data.data.records)) { list = data.data.records }
|
|
|
|
|
else if (data && Array.isArray(data.list)) { list = data.list }
|
|
|
|
|
else if (data && Array.isArray(data.rows)) { list = data.rows }
|
|
|
|
|
else if (data && Array.isArray(data.records)) { list = data.records }
|
|
|
|
|
const list = Array.isArray(data) ? data : (data?.list || data?.records || [])
|
|
|
|
|
maintainOrderOptions.value = list.map((m) => ({
|
|
|
|
|
value: m.id,
|
|
|
|
|
label: m.maintenanceCode || m.code || m.maintenanceNo || m.no || String(m.id || '')
|
|
|
|
|
label: m.planNo || String(m.id || ''),
|
|
|
|
|
deviceId: m.deviceId,
|
|
|
|
|
deviceName: m.deviceName || ''
|
|
|
|
|
}))
|
|
|
|
|
console.log('保养单列表:', JSON.stringify(maintainOrderOptions.value))
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('loadMaintainOrdersById error', e)
|
|
|
|
|
}
|
|
|
|
|
@ -514,21 +598,75 @@ async function handleSubmit() {
|
|
|
|
|
uni.showToast({ title: '请选择出库用途', icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// TODO: 调用出库提交接口(后续补充)
|
|
|
|
|
uni.showToast({ title: t('functionCommon.createSuccess'), icon: 'success' })
|
|
|
|
|
|
|
|
|
|
const inputCount = Number(outboundQty.value)
|
|
|
|
|
const convertRatio = Number(selectedSparepart.value.purchaseUnitConvertQuantity) || 1
|
|
|
|
|
const count = inputCount * convertRatio
|
|
|
|
|
|
|
|
|
|
const submitData = {
|
|
|
|
|
outTime: Date.now(),
|
|
|
|
|
stockUserId: String(selectedOperator.value.value),
|
|
|
|
|
status: 0,
|
|
|
|
|
totalCount: count,
|
|
|
|
|
totalPrice: 0,
|
|
|
|
|
items: [{
|
|
|
|
|
productId: selectedSparepart.value.id,
|
|
|
|
|
productName: selectedSparepart.value.name || '',
|
|
|
|
|
productBarCode: selectedSparepart.value.barCode || '',
|
|
|
|
|
productUnitName: selectedSparepart.value.unitName || selectedSparepart.value.purchaseUnitName || '',
|
|
|
|
|
purchaseUnitName: selectedSparepart.value.purchaseUnitName || '',
|
|
|
|
|
purchaseUnitConvertQuantity: convertRatio,
|
|
|
|
|
inputCount: inputCount,
|
|
|
|
|
count: count
|
|
|
|
|
}]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 出库用途
|
|
|
|
|
submitData.purpose = selectedPurpose.value
|
|
|
|
|
|
|
|
|
|
// 关联设备
|
|
|
|
|
if (selectedDevice.value) {
|
|
|
|
|
submitData.deviceId = selectedDevice.value.value
|
|
|
|
|
submitData.deviceName = selectedDevice.value.deviceName || selectedDevice.value.label || ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关联维修单/保养单
|
|
|
|
|
if (selectedPurpose.value === 'repair' && selectedRepairOrder.value) {
|
|
|
|
|
submitData.repairId = selectedRepairOrder.value.value
|
|
|
|
|
submitData.repairNo = selectedRepairOrder.value.label
|
|
|
|
|
} else if (selectedPurpose.value === 'maintain' && selectedMaintainOrder.value) {
|
|
|
|
|
submitData.maintenanceId = selectedMaintainOrder.value.value
|
|
|
|
|
submitData.maintenanceNo = selectedMaintainOrder.value.label
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
uni.showLoading({ title: '提交中...', mask: true })
|
|
|
|
|
await createSparepartOutbound(submitData)
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
uni.showToast({ title: t('functionCommon.createSuccess'), icon: 'success' })
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
}, 1500)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
const msg = e?.message || e?.data?.msg || e?.response?.data?.msg || t('functionCommon.saveFailed')
|
|
|
|
|
console.error('出库提交失败:', e)
|
|
|
|
|
uni.showToast({ title: String(msg).substring(0, 50), icon: 'none' })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onShow(async () => {
|
|
|
|
|
const selectResult = getApp().globalData?._sparepartSelectResult
|
|
|
|
|
if (selectResult) {
|
|
|
|
|
selectedSparepart.value = selectResult
|
|
|
|
|
// 补充获取备件详情(含供应商、图片等)
|
|
|
|
|
// 补充获取备件详情(含供应商、库存等完整信息)
|
|
|
|
|
if (selectResult.id) {
|
|
|
|
|
try {
|
|
|
|
|
const res = await getSparepartDetail(selectResult.id)
|
|
|
|
|
const detail = res?.data || res
|
|
|
|
|
if (detail && detail.suppliers) {
|
|
|
|
|
if (detail) {
|
|
|
|
|
selectedSparepart.value = { ...selectedSparepart.value, ...detail }
|
|
|
|
|
loadStockCount()
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取备件详情失败:', e)
|
|
|
|
|
@ -685,8 +823,8 @@ onHide(() => {
|
|
|
|
|
.dropdown-input { display: flex; align-items: center; height: 64rpx; padding: 0 20rpx; border: 1rpx solid #e0e0e0; border-radius: 10rpx; background: #f9fafb; }
|
|
|
|
|
.dropdown-value { flex: 1; font-size: 27rpx; color: #333; &.placeholder { color: #bbb; } }
|
|
|
|
|
.dropdown-arrow { font-size: 20rpx; color: #999; flex-shrink: 0; }
|
|
|
|
|
.dropdown-panel { position: absolute; top: 68rpx; left: 0; right: 0; z-index: 99; background: #fff; border: 1rpx solid #e0e0e0; border-radius: 12rpx; box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.1); overflow: hidden; }
|
|
|
|
|
.dropdown-scroll { max-height: 360rpx; }
|
|
|
|
|
.dropdown-panel { position: absolute; top: 68rpx; left: 0; right: 0; z-index: 200; 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: 360rpx; }
|
|
|
|
|
.dropdown-item { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
&:last-child { border-bottom: 0; }
|
|
|
|
|
&.active { background: #f0f5ff; }
|
|
|
|
|
|