<template>
|
<view class="rm-form-page">
|
<PageHeader :title="operationType === 'add' ? '新增原料检' : '编辑原料检'" @back="goBack" />
|
|
<scroll-view scroll-y class="content-scroll">
|
<view class="section-card">
|
<view class="section-title">基础信息</view>
|
|
<view class="form-row">
|
<text class="form-label required">产品名称</text>
|
<view class="selector-trigger" @click="openProductSelector" :class="{ disabled: operationType === 'edit' }">
|
<text class="selector-text" :class="{ placeholder: !form.productName }">
|
{{ form.productName || '请选择产品' }}
|
</text>
|
<up-icon name="arrow-right" size="16" color="#999"></up-icon>
|
</view>
|
</view>
|
|
<view class="form-row">
|
<text class="form-label required">规格型号</text>
|
<up-input v-model="form.model" disabled placeholder="请选择产品后自动带出" />
|
</view>
|
|
<view class="form-row">
|
<text class="form-label">单位</text>
|
<up-input v-model="form.unit" disabled placeholder="自动带出" />
|
</view>
|
|
<view class="form-row">
|
<text class="form-label required">批号</text>
|
<up-input v-model="form.batchNo" placeholder="请输入批号" />
|
</view>
|
|
<view class="form-row">
|
<text class="form-label required">检验类型</text>
|
<view class="selector-trigger" @click="showCheckTypeSheet = true">
|
<text class="selector-text" :class="{ placeholder: form.checkType === '' || form.checkType == null }">
|
{{ checkTypeLabel }}
|
</text>
|
<up-icon name="arrow-down" size="14" color="#999"></up-icon>
|
</view>
|
</view>
|
|
<view class="form-row">
|
<text class="form-label required">检测结果</text>
|
<view class="selector-trigger" @click="showCheckResultSheet = true">
|
<text class="selector-text" :class="{ placeholder: form.checkResult === '' || form.checkResult == null }">
|
{{ checkResultLabel }}
|
</text>
|
<up-icon name="arrow-down" size="14" color="#999"></up-icon>
|
</view>
|
</view>
|
|
<view class="form-row">
|
<text class="form-label required">检验员</text>
|
<up-input v-model="form.checkUserName" placeholder="请输入检验员" />
|
</view>
|
|
<view class="form-row">
|
<text class="form-label required">检测日期</text>
|
<view class="selector-trigger" @click="openCheckDatePicker">
|
<text class="selector-text" :class="{ placeholder: !form.checkTime }">
|
{{ form.checkTime || '请选择检测日期' }}
|
</text>
|
<up-icon name="calendar" size="16" color="#999"></up-icon>
|
</view>
|
</view>
|
</view>
|
|
<view class="section-card">
|
<view class="section-title row-between">
|
<text>检测项目</text>
|
<view class="btn-inline" @click="openItemSelector">添加检测项目</view>
|
</view>
|
|
<view v-if="inspectItems.length > 0">
|
<view v-for="(it, idx) in inspectItems" :key="it.id || idx" class="item-card">
|
<view class="item-head">
|
<text class="item-name">{{ it.name }}</text>
|
<view class="item-del" @click="removeItem(it.id)">删除</view>
|
</view>
|
<view class="item-row"><text class="l">单位</text><text class="r">{{ it.unit || '-' }}</text></view>
|
<view class="item-row"><text class="l">标准值</text><text class="r">{{ it.standardValue || '-' }}</text></view>
|
<view class="item-row"><text class="l">内控值</text><text class="r">{{ it.internalControl || '-' }}</text></view>
|
<view class="item-row input-row">
|
<text class="l">化验值</text>
|
<up-input v-model="it.testValue" placeholder="请输入" class="test-value-input" />
|
</view>
|
</view>
|
</view>
|
<view v-else class="no-data">请添加检测项目</view>
|
</view>
|
</scroll-view>
|
|
<view class="bottom-bar">
|
<view class="btn-submit" :class="{ disabled: submitLoading }" @click="handleSubmit">
|
{{ submitLoading ? '提交中...' : '保存' }}
|
</view>
|
</view>
|
|
<!-- 产品选择弹窗(复用 pageModel 接口) -->
|
<up-popup :show="showProductPopup" mode="bottom" @close="showProductPopup = false">
|
<view class="popup-wrap">
|
<view class="popup-header">
|
<text class="popup-title">选择产品</text>
|
</view>
|
<view class="popup-search">
|
<up-input v-model="productQuery.productName" placeholder="产品大类" clearable />
|
<up-input v-model="productQuery.model" placeholder="型号名称" clearable />
|
<view class="popup-search-btn" @click="loadProductList">搜索</view>
|
</view>
|
<scroll-view scroll-y class="popup-list">
|
<view v-for="row in productList" :key="row.id" class="popup-item" @click="selectProduct(row)">
|
<view class="popup-item-top">
|
<text class="popup-item-name">{{ row.productName }}</text>
|
<text class="popup-item-unit">{{ row.unit }}</text>
|
</view>
|
<view class="popup-item-sub">型号:{{ row.model }}</view>
|
</view>
|
<view v-if="!productLoading && productList.length === 0" class="no-data">暂无数据</view>
|
</scroll-view>
|
</view>
|
</up-popup>
|
|
<!-- 检测项目选择弹窗(简化:从检测项维护表里选) -->
|
<up-popup :show="showItemPopup" mode="bottom" @close="showItemPopup = false">
|
<view class="popup-wrap">
|
<view class="popup-header">
|
<text class="popup-title">选择检测项目</text>
|
</view>
|
<view class="popup-search">
|
<up-input v-model="itemQuery.name" placeholder="检测项目名称" clearable />
|
<view class="popup-search-btn" @click="loadItemList">搜索</view>
|
</view>
|
<scroll-view scroll-y class="popup-list">
|
<view
|
v-for="row in itemList"
|
:key="row.id"
|
class="popup-item"
|
:class="{ selected: isItemSelected(row.id) }"
|
@click="toggleItem(row)"
|
>
|
<view class="popup-item-top">
|
<text class="popup-item-name">{{ row.name }}</text>
|
<view class="right-wrap">
|
<text class="popup-item-unit">{{ row.unit }}</text>
|
<up-icon
|
v-if="isItemSelected(row.id)"
|
name="checkbox-mark"
|
size="18"
|
color="#2979ff"
|
></up-icon>
|
</view>
|
</view>
|
<view class="popup-item-sub">标准值:{{ row.standardValue || '-' }}|内控值:{{ row.internalControl || '-' }}</view>
|
</view>
|
<view v-if="!itemLoading && itemList.length === 0" class="no-data">暂无数据</view>
|
</scroll-view>
|
<view class="popup-footer">
|
<view class="btn-cancel" @click="showItemPopup = false">取消</view>
|
<view class="btn-ok" @click="confirmItems">确定</view>
|
</view>
|
</view>
|
</up-popup>
|
|
<!-- 选择器:检验类型/结果 -->
|
<up-action-sheet :actions="checkTypeActions" :show="showCheckTypeSheet" @close="showCheckTypeSheet = false" @select="onSelectCheckType" title="检验类型" />
|
<up-action-sheet :actions="checkResultActions" :show="showCheckResultSheet" @close="showCheckResultSheet = false" @select="onSelectCheckResult" title="检测结果" />
|
|
<!-- 日期选择 -->
|
<up-popup :show="showCheckDatePicker" mode="bottom" @close="showCheckDatePicker = false">
|
<up-datetime-picker :show="true" v-model="checkDateValue" mode="date" @confirm="onCheckDateConfirm" @cancel="showCheckDatePicker = false" />
|
</up-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { computed, reactive, ref } from 'vue'
|
import { onLoad } from '@dcloudio/uni-app'
|
import dayjs from 'dayjs'
|
import PageHeader from '@/components/PageHeader.vue'
|
import useUserStore from '@/store/modules/user'
|
import { productModelList } from '@/api/basicData/productModel.js'
|
import { qualityInspectItemListPage } from '@/api/qualityManagement/inspectItem.js'
|
import { createRawMaterial, findRawMaterialDetail, updateRawMaterial } from '@/api/qualityManagement/rawMaterial.js'
|
|
const userStore = useUserStore()
|
|
const operationType = ref('add')
|
const submitLoading = ref(false)
|
|
const form = reactive({
|
id: null,
|
productId: '',
|
productModelId: '',
|
productName: '',
|
model: '',
|
unit: '',
|
batchNo: '',
|
checkType: '',
|
checkResult: '',
|
checkUserName: userStore?.nickName || '',
|
checkTime: ''
|
})
|
|
const inspectItems = ref([]) // qualityInspectItem
|
|
// 检验类型/结果
|
const checkTypeActions = [
|
{ name: '入厂检', value: 0 },
|
{ name: '车间检', value: 1 },
|
{ name: '出厂检', value: 2 }
|
]
|
const checkResultActions = [
|
{ name: '合格', value: 1 },
|
{ name: '不合格', value: 0 }
|
]
|
const showCheckTypeSheet = ref(false)
|
const showCheckResultSheet = ref(false)
|
const checkTypeLabel = computed(() => checkTypeActions.find(a => a.value === form.checkType)?.name || '请选择')
|
const checkResultLabel = computed(() => checkResultActions.find(a => a.value === form.checkResult)?.name || '请选择')
|
const onSelectCheckType = (e) => { form.checkType = e.value; showCheckTypeSheet.value = false }
|
const onSelectCheckResult = (e) => { form.checkResult = e.value; showCheckResultSheet.value = false }
|
|
// 日期选择
|
const showCheckDatePicker = ref(false)
|
const checkDateValue = ref(Date.now())
|
const openCheckDatePicker = () => {
|
checkDateValue.value = form.checkTime ? dayjs(form.checkTime, 'YYYY-MM-DD').valueOf() : Date.now()
|
showCheckDatePicker.value = true
|
}
|
const onCheckDateConfirm = (e) => {
|
form.checkTime = dayjs(e.value).format('YYYY-MM-DD')
|
showCheckDatePicker.value = false
|
}
|
|
// 产品选择
|
const showProductPopup = ref(false)
|
const productQuery = reactive({ productName: '', model: '' })
|
const productList = ref([])
|
const productLoading = ref(false)
|
const openProductSelector = () => {
|
if (operationType.value === 'edit') return
|
showProductPopup.value = true
|
if (productList.value.length === 0) loadProductList()
|
}
|
const loadProductList = () => {
|
productLoading.value = true
|
productModelList({ productName: productQuery.productName || '', model: productQuery.model || '', current: 1, size: 20 })
|
.then(res => {
|
const data = res?.records || res?.data?.records || []
|
productList.value = Array.isArray(data) ? data : []
|
})
|
.finally(() => { productLoading.value = false })
|
}
|
const selectProduct = (row) => {
|
form.productId = row.productId
|
form.productModelId = row.id
|
form.productName = row.productName
|
form.model = row.model
|
form.unit = row.unit
|
showProductPopup.value = false
|
}
|
|
// 检测项目选择
|
const showItemPopup = ref(false)
|
const itemQuery = reactive({ name: '' })
|
const itemList = ref([])
|
const itemLoading = ref(false)
|
const selectedItemIds = ref(new Set())
|
const isItemSelected = (id) => selectedItemIds.value.has(id)
|
const openItemSelector = () => {
|
showItemPopup.value = true
|
selectedItemIds.value = new Set(inspectItems.value.map(i => i.id))
|
if (itemList.value.length === 0) loadItemList()
|
}
|
const loadItemList = () => {
|
itemLoading.value = true
|
qualityInspectItemListPage({ name: itemQuery.name || null, current: 1, size: 50 })
|
.then(res => {
|
const records = res?.data?.records || []
|
itemList.value = Array.isArray(records) ? records : []
|
})
|
.finally(() => { itemLoading.value = false })
|
}
|
const toggleItem = (row) => {
|
const set = selectedItemIds.value
|
if (set.has(row.id)) set.delete(row.id)
|
else set.add(row.id)
|
// 简单提示选中状态(可按需做 icon)
|
}
|
const confirmItems = () => {
|
const set = selectedItemIds.value
|
const existingMap = new Map(inspectItems.value.map(i => [i.id, i]))
|
const next = []
|
for (const id of set) {
|
const exist = existingMap.get(id)
|
if (exist) next.push(exist)
|
else {
|
const row = itemList.value.find(r => r.id === id)
|
if (row) next.push({ ...row, testValue: '' })
|
}
|
}
|
inspectItems.value = next
|
showItemPopup.value = false
|
}
|
const removeItem = (id) => {
|
inspectItems.value = inspectItems.value.filter(i => i.id !== id)
|
}
|
|
const validate = () => {
|
if (!form.productModelId) return '请选择产品'
|
if (!form.batchNo) return '请输入批号'
|
if (form.checkType === '' || form.checkType == null) return '请选择检验类型'
|
if (form.checkResult === '' || form.checkResult == null) return '请选择检测结果'
|
if (!form.checkUserName) return '请输入检验员'
|
if (!form.checkTime) return '请选择检测日期'
|
if (!inspectItems.value.length) return '请添加检测项目'
|
return ''
|
}
|
|
const handleSubmit = () => {
|
if (submitLoading.value) return
|
const msg = validate()
|
if (msg) {
|
uni.showToast({ title: msg, icon: 'none' })
|
return
|
}
|
submitLoading.value = true
|
const payload = { ...form, qualityInspectItem: inspectItems.value }
|
const api = operationType.value === 'add' ? createRawMaterial : updateRawMaterial
|
api(payload)
|
.then(() => {
|
uni.showToast({ title: '保存成功', icon: 'success' })
|
setTimeout(() => uni.navigateBack(), 400)
|
})
|
.catch(() => {
|
uni.showToast({ title: '保存失败', icon: 'none' })
|
})
|
.finally(() => { submitLoading.value = false })
|
}
|
|
onLoad((options) => {
|
operationType.value = options?.type || 'add'
|
const id = options?.id
|
if (operationType.value === 'edit' && id) {
|
findRawMaterialDetail(id).then(res => {
|
const d = res?.data || {}
|
form.id = d.id
|
form.productId = d.productId
|
form.productModelId = d.productModelId
|
form.productName = d.productName
|
form.model = d.model
|
form.unit = d.unit
|
form.batchNo = d.batchNo
|
form.checkType = d.checkType
|
form.checkResult = d.checkResult
|
form.checkUserName = d.checkUserName || form.checkUserName
|
form.checkTime = d.checkTime
|
inspectItems.value = Array.isArray(d.qualityInspectItem) ? d.qualityInspectItem : []
|
})
|
}
|
})
|
|
const goBack = () => uni.navigateBack()
|
</script>
|
|
<style lang="scss" scoped>
|
.rm-form-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 110rpx; }
|
.content-scroll { height: calc(100vh - 110rpx); }
|
.section-card { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
|
.section-title { font-size: 28rpx; font-weight: 500; color: #333; margin-bottom: 12rpx; display: flex; align-items: center; }
|
.row-between { justify-content: space-between; }
|
.btn-inline { font-size: 26rpx; color: #2979ff; }
|
.form-row { margin-bottom: 22rpx; }
|
.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-trigger.disabled { opacity: 0.6; }
|
.selector-text { font-size: 28rpx; color: #333; }
|
.selector-text.placeholder { color: #999; }
|
.no-data { text-align: center; padding: 40rpx 0; color: #999; font-size: 26rpx; }
|
.item-card { padding: 18rpx 0; border-top: 1rpx solid #eee; }
|
.item-head { display: flex; justify-content: space-between; align-items: center; padding: 6rpx 0 10rpx; }
|
.item-name { font-size: 28rpx; font-weight: 500; color: #333; }
|
.item-del { font-size: 26rpx; color: #f56c6c; }
|
.item-row { display: flex; justify-content: space-between; align-items: center; padding: 6rpx 0; font-size: 26rpx; }
|
.item-row .l { color: #666; }
|
.item-row .r { color: #333; }
|
.input-row { align-items: center; }
|
.test-value-input {
|
flex: 0 0 220rpx;
|
}
|
:deep(.test-value-input .u-input),
|
:deep(.test-value-input .up-input) {
|
width: 100%;
|
}
|
.bottom-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04); }
|
.btn-submit { height: 88rpx; border-radius: 999rpx; background: #2979ff; color: #fff; font-size: 30rpx; display: flex; align-items: center; justify-content: center; }
|
.btn-submit.disabled { opacity: 0.6; }
|
.popup-wrap { 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-search { padding: 16rpx 24rpx; display: grid; grid-template-columns: 1fr 1fr 140rpx; gap: 16rpx; align-items: center; }
|
.popup-search-btn { height: 72rpx; border-radius: 12rpx; background: #2979ff; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 28rpx; }
|
.popup-list { max-height: 60vh; padding: 0 24rpx 24rpx; }
|
.popup-item { padding: 18rpx 0; border-bottom: 1rpx solid #f0f0f0; }
|
.popup-item.selected { background: rgba(41, 121, 255, 0.06); }
|
.popup-item-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6rpx; }
|
.right-wrap { display: inline-flex; align-items: center; gap: 12rpx; }
|
.popup-item-name { font-size: 28rpx; color: #333; }
|
.popup-item-unit { font-size: 24rpx; color: #999; }
|
.popup-item-sub { font-size: 24rpx; color: #666; }
|
.popup-footer { display: flex; gap: 24rpx; padding: 16rpx 24rpx 24rpx; }
|
.btn-cancel { flex: 1; text-align: center; padding: 24rpx; background: #f0f0f0; border-radius: 12rpx; }
|
.btn-ok { flex: 1; text-align: center; padding: 24rpx; background: #2979ff; color: #fff; border-radius: 12rpx; }
|
</style>
|