|
|
<template>
|
|
|
<view class="page-container">
|
|
|
<NavBar :title="'新增备件盘点单'" />
|
|
|
|
|
|
<!-- 盘点时间 -->
|
|
|
<view class="section-title-bar">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title"><text class="required">*</text>盘点时间</text>
|
|
|
</view>
|
|
|
<view class="form-row-card datetime-picker-card">
|
|
|
<uni-datetime-picker
|
|
|
v-model="checkTime"
|
|
|
type="datetime"
|
|
|
placeholder="请选择盘点时间"
|
|
|
:clear-icon="false"
|
|
|
return-type="string"
|
|
|
/>
|
|
|
</view>
|
|
|
|
|
|
<!-- 生成来源 -->
|
|
|
<view class="section-title-bar">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title"><text class="required">*</text>生成来源</text>
|
|
|
</view>
|
|
|
<view class="radio-group">
|
|
|
<view :class="['radio-item', sourceType === 1 ? 'radio-active' : '']" @click="switchSourceType(1)">
|
|
|
<text class="radio-text">按库存</text>
|
|
|
</view>
|
|
|
<view :class="['radio-item', sourceType === 2 ? 'radio-active' : '']" @click="switchSourceType(2)">
|
|
|
<text class="radio-text">按产品</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 按库存:仓库/库区/盘点项 -->
|
|
|
<template v-if="sourceType === 1">
|
|
|
<view class="section-title-bar">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title">仓库/库区/盘点项</text>
|
|
|
</view>
|
|
|
<view class="select-row-card">
|
|
|
<view class="warehouse-area-rows">
|
|
|
<!-- 仓库(固定为备件仓,不可选) -->
|
|
|
<view class="warehouse-area-row">
|
|
|
<text class="warehouse-area-label">仓库</text>
|
|
|
<view class="warehouse-area-dropdown">
|
|
|
<view class="dropdown-input">
|
|
|
<text class="dropdown-value">备件仓</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 库区 -->
|
|
|
<view class="warehouse-area-row">
|
|
|
<text class="warehouse-area-label">库区</text>
|
|
|
<view class="warehouse-area-dropdown">
|
|
|
<view class="dropdown-input" @click="goSelectArea">
|
|
|
<text :class="['dropdown-value', { placeholder: !selectedAreas.length }]">
|
|
|
{{ selectedAreas.length ? selectedAreas.map(a => a.label).join('、') : '请选择' }}
|
|
|
</text>
|
|
|
<text class="dropdown-arrow">▶</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 盘点项 -->
|
|
|
<view class="warehouse-area-row" v-if="selectedAreas.length">
|
|
|
<text class="warehouse-area-label">盘点项</text>
|
|
|
<view class="item-select-btn" @click="goSelectItems">
|
|
|
<text class="item-select-text">{{ items.length ? `已选${items.length}项` : '点击选择' }}</text>
|
|
|
<text class="dropdown-arrow">▼</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<!-- 按产品:产品/盘点项 -->
|
|
|
<template v-if="sourceType === 2">
|
|
|
<view class="section-title-bar">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title"><text class="required">*</text>产品/盘点项</text>
|
|
|
</view>
|
|
|
<view class="select-row-card">
|
|
|
<view class="warehouse-area-rows">
|
|
|
<!-- 产品 -->
|
|
|
<view class="warehouse-area-row">
|
|
|
<text class="warehouse-area-label">产品</text>
|
|
|
<view class="warehouse-area-dropdown" @click="goSelectProduct">
|
|
|
<view class="dropdown-input">
|
|
|
<text :class="['dropdown-value', { placeholder: !selectedProducts.length }]">
|
|
|
{{ selectedProducts.length ? selectedProducts.map(p => p.name).join('、') : '请选择' }}
|
|
|
</text>
|
|
|
<text class="dropdown-arrow">▼</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 盘点项 -->
|
|
|
<view class="warehouse-area-row" v-if="selectedProducts.length">
|
|
|
<text class="warehouse-area-label">盘点项</text>
|
|
|
<view class="warehouse-area-dropdown" @click="goSelectItemsByProduct">
|
|
|
<view class="dropdown-input">
|
|
|
<text class="dropdown-value">{{ items.length ? `已选择 ${items.length} 项` : '请选择' }}</text>
|
|
|
<text class="dropdown-arrow">▼</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<!-- 已选盘点项列表 -->
|
|
|
<view class="section-title-bar" v-if="items.length">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title">已选盘点项({{ items.length }})</text>
|
|
|
</view>
|
|
|
<view v-if="items.length" class="item-list">
|
|
|
<view v-for="(item, idx) in items" :key="idx" class="item-card">
|
|
|
<view class="item-header">
|
|
|
<text class="item-name">{{ textValue(item.productName) }}</text>
|
|
|
<text class="item-code">{{ textValue(item.productBarCode) }}</text>
|
|
|
</view>
|
|
|
<view class="item-body">
|
|
|
<view class="item-row">
|
|
|
<text class="item-lbl">仓库</text>
|
|
|
<text class="item-val">{{ textValue(item.warehouseName) }}</text>
|
|
|
</view>
|
|
|
<view class="item-row">
|
|
|
<text class="item-lbl">库区</text>
|
|
|
<text class="item-val">{{ textValue(item.areaName) }}</text>
|
|
|
</view>
|
|
|
<view class="item-row">
|
|
|
<text class="item-lbl">账面数量</text>
|
|
|
<text class="item-val highlight">{{ textValue(item.stockCount) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="item-delete" @click="items.splice(idx, 1)">
|
|
|
<text class="item-delete-text">删除</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view v-if="loadingProducts" class="hint">加载中...</view>
|
|
|
|
|
|
<!-- 备注 -->
|
|
|
<view class="section-title-bar">
|
|
|
<view class="section-bar-line"></view>
|
|
|
<text class="section-title">备注</text>
|
|
|
</view>
|
|
|
<view class="form-row-card remark-card">
|
|
|
<textarea v-model="remark" class="remark-textarea" placeholder="请输入备注" :maxlength="200" auto-height />
|
|
|
</view>
|
|
|
|
|
|
<!-- 底部提交 -->
|
|
|
<view class="bottom-actions" v-if="items.length">
|
|
|
<view class="bottom-btn cancel-btn" @click="handleCancel">取消</view>
|
|
|
<view class="bottom-btn confirm-btn" @click="handleSubmit">保存</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { ref } from 'vue'
|
|
|
import { onShow } from '@dcloudio/uni-app'
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
import UniDatetimePicker from '@dcloudio/uni-ui/lib/uni-datetime-picker/uni-datetime-picker.vue'
|
|
|
import { getWarehousePage } from '@/api/mes/moldget'
|
|
|
import { createCheck } from '@/api/mes/sparepartCheck'
|
|
|
|
|
|
// 盘点时间
|
|
|
const now = new Date()
|
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
|
const checkTime = ref(`${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`)
|
|
|
|
|
|
// 生成来源 & 分类
|
|
|
const sourceType = ref(1)
|
|
|
const categoryType = ref(3)
|
|
|
|
|
|
function switchSourceType(type) {
|
|
|
sourceType.value = type
|
|
|
items.value = []
|
|
|
selectedProducts.value = []
|
|
|
selectedAreas.value = []
|
|
|
}
|
|
|
|
|
|
// 仓库
|
|
|
const warehouseOptions = ref([])
|
|
|
const selectedWarehouse = ref(null)
|
|
|
|
|
|
// 库区(多选)
|
|
|
const selectedAreas = ref([])
|
|
|
|
|
|
// 产品(按产品模式,多选)
|
|
|
const selectedProducts = ref([])
|
|
|
|
|
|
// 已选盘点项
|
|
|
const items = ref([])
|
|
|
const remark = ref('')
|
|
|
|
|
|
function textValue(v) {
|
|
|
if (v === 0) return '0'
|
|
|
if (v == null) return '-'
|
|
|
const s = String(v).trim()
|
|
|
return s || '-'
|
|
|
}
|
|
|
|
|
|
// 加载仓库(自动选定备件仓)
|
|
|
async function loadWarehouses() {
|
|
|
try {
|
|
|
const res = await getWarehousePage({ pageNo: 1, pageSize: 100, categoryType: 3 })
|
|
|
const data = Array.isArray(res) ? res : (Array.isArray(res?.data) ? res.data : (Array.isArray(res?.data?.list) ? res.data.list : []))
|
|
|
warehouseOptions.value = data.map(w => ({ value: w.id, label: w.name || String(w.id) }))
|
|
|
if (warehouseOptions.value.length && !selectedWarehouse.value) {
|
|
|
selectedWarehouse.value = warehouseOptions.value[0]
|
|
|
}
|
|
|
} catch (e) {
|
|
|
console.error('loadWarehouses error', e)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 跳转库区选择页
|
|
|
function goSelectArea() {
|
|
|
getApp().globalData._checkAreaSelectResult = null
|
|
|
getApp().globalData._checkAreaWarehouseId = selectedWarehouse.value?.value || ''
|
|
|
getApp().globalData._checkAreaSelected = selectedAreas.value.length
|
|
|
? JSON.parse(JSON.stringify(selectedAreas.value))
|
|
|
: null
|
|
|
uni.navigateTo({
|
|
|
url: `/pages_function/pages/sparepartCheck/areaSelect`
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 跳转盘点项选择页
|
|
|
function goSelectItems() {
|
|
|
if (!selectedAreas.value.length) return
|
|
|
getApp().globalData._checkItemSelectResult = null
|
|
|
getApp().globalData._checkItemSelected = items.value.length
|
|
|
? JSON.parse(JSON.stringify(items.value))
|
|
|
: null
|
|
|
const areaIds = selectedAreas.value.map(a => a.value).join(',')
|
|
|
uni.navigateTo({
|
|
|
url: `/pages_function/pages/sparepartCheck/itemSelect?warehouseId=${selectedWarehouse.value.value}&areaIds=${areaIds}`
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 选择产品(跳转产品多选页)
|
|
|
function goSelectProduct() {
|
|
|
getApp().globalData._checkProductSelectResult = null
|
|
|
// 传入当前已选产品,让选择页显示勾选状态
|
|
|
getApp().globalData._checkProductSelected = selectedProducts.value.length
|
|
|
? JSON.parse(JSON.stringify(selectedProducts.value))
|
|
|
: null
|
|
|
uni.navigateTo({
|
|
|
url: `/pages_function/pages/sparepartCheck/productSelect`
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 按产品选择盘点项(跳转页面,传多个productId)
|
|
|
function goSelectItemsByProduct() {
|
|
|
if (!selectedProducts.value.length) return
|
|
|
getApp().globalData._checkItemSelectResult = null
|
|
|
getApp().globalData._checkItemSelected = items.value.length
|
|
|
? JSON.parse(JSON.stringify(items.value))
|
|
|
: null
|
|
|
const ids = selectedProducts.value.map(p => p.id).join(',')
|
|
|
uni.navigateTo({
|
|
|
url: `/pages_function/pages/sparepartCheck/itemSelectByProduct?productIds=${ids}`
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 提交
|
|
|
async function handleSubmit() {
|
|
|
if (!items.value.length) {
|
|
|
uni.showToast({ title: '请先选择盘点项', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
|
|
|
const submitItems = items.value.map(item => ({
|
|
|
warehouseId: Number(item.warehouseId),
|
|
|
areaId: item.areaId ? Number(item.areaId) : undefined,
|
|
|
productId: Number(item.productId),
|
|
|
productBarCode: item.productBarCode || '',
|
|
|
productName: item.productName || '',
|
|
|
stockCount: item.stockCount != null ? Number(item.stockCount) : 0,
|
|
|
actualCount: null,
|
|
|
productPrice: item.productPrice != null ? Number(item.productPrice) : 0,
|
|
|
count: 0,
|
|
|
remark: ''
|
|
|
}))
|
|
|
|
|
|
try {
|
|
|
uni.showLoading({ title: '保存中...', mask: true })
|
|
|
await createCheck({
|
|
|
checkTime: checkTime.value,
|
|
|
sourceType: sourceType.value,
|
|
|
categoryType: categoryType.value,
|
|
|
checkStatus: 0,
|
|
|
remark: remark.value || '',
|
|
|
items: submitItems
|
|
|
})
|
|
|
uni.hideLoading()
|
|
|
uni.showToast({ title: '创建成功', icon: 'success' })
|
|
|
setTimeout(() => uni.navigateBack(), 1500)
|
|
|
} catch (e) {
|
|
|
uni.hideLoading()
|
|
|
const msg = e?.message || e?.data?.msg || '创建失败'
|
|
|
uni.showToast({ title: String(msg).substring(0, 50), icon: 'none' })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleCancel() { uni.navigateBack() }
|
|
|
|
|
|
onShow(() => {
|
|
|
loadWarehouses()
|
|
|
// 接收产品选择返回
|
|
|
const productResult = getApp().globalData?._checkProductSelectResult
|
|
|
if (productResult && Array.isArray(productResult)) {
|
|
|
selectedProducts.value = productResult.map(p => ({
|
|
|
id: p.id,
|
|
|
name: p.name || '备件'
|
|
|
}))
|
|
|
items.value = []
|
|
|
getApp().globalData._checkProductSelectResult = null
|
|
|
}
|
|
|
// 接收库区选择返回
|
|
|
const areaResult = getApp().globalData?._checkAreaSelectResult
|
|
|
if (areaResult && Array.isArray(areaResult)) {
|
|
|
selectedAreas.value = areaResult.map(a => ({
|
|
|
value: a.id,
|
|
|
label: a.name || '库区'
|
|
|
}))
|
|
|
items.value = []
|
|
|
getApp().globalData._checkAreaSelectResult = null
|
|
|
}
|
|
|
// 接收盘点项选择返回
|
|
|
const itemResult = getApp().globalData?._checkItemSelectResult
|
|
|
if (itemResult && Array.isArray(itemResult)) {
|
|
|
const existingKeys = new Set(items.value.map(i => `${i.productId || i.id}_${i.warehouseId || ''}_${i.areaId || ''}`))
|
|
|
const newItems = itemResult.filter(i => !existingKeys.has(`${i.productId || i.id}_${i.warehouseId || ''}_${i.areaId || ''}`))
|
|
|
if (newItems.length) {
|
|
|
items.value = [...items.value, ...newItems]
|
|
|
}
|
|
|
getApp().globalData._checkItemSelectResult = null
|
|
|
}
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.page-container {
|
|
|
min-height: 100vh;
|
|
|
background: #f5f6f8;
|
|
|
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
|
|
|
}
|
|
|
|
|
|
.section-title-bar {
|
|
|
display: flex; align-items: center; gap: 10rpx;
|
|
|
padding: 24rpx 24rpx 18rpx;
|
|
|
}
|
|
|
.section-bar-line { width: 6rpx; height: 32rpx; border-radius: 3rpx; background: #2563eb; flex-shrink: 0; }
|
|
|
.section-title { font-size: 30rpx; font-weight: 700; color: #1a1a1a; }
|
|
|
.required { color: #ef4444; }
|
|
|
|
|
|
.form-row-card {
|
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
|
background: #fff; border-radius: 16rpx; padding: 26rpx 24rpx; margin: 0 24rpx 16rpx;
|
|
|
box-shadow: 0 4rpx 16rpx rgba(15,23,42,0.04);
|
|
|
font-size: 28rpx; color: #374151;
|
|
|
.placeholder { color: #9ca3af; }
|
|
|
}
|
|
|
.form-row-arrow { font-size: 20rpx; color: #999; }
|
|
|
.datetime-picker-card {
|
|
|
flex-direction: column;
|
|
|
align-items: stretch;
|
|
|
padding: 16rpx 24rpx;
|
|
|
:deep(.uni-date) {
|
|
|
width: 100%;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 单选组 */
|
|
|
.radio-group { display: flex; gap: 12rpx; padding: 0 24rpx 16rpx; }
|
|
|
.radio-item {
|
|
|
padding: 16rpx 36rpx;
|
|
|
border-radius: 12rpx;
|
|
|
background: #fff;
|
|
|
border: 2rpx solid #e5e7eb;
|
|
|
font-size: 26rpx;
|
|
|
color: #6b7280;
|
|
|
text-align: center;
|
|
|
&.radio-active {
|
|
|
background: #1f4b79;
|
|
|
border-color: #1f4b79;
|
|
|
.radio-text { color: #fff; }
|
|
|
}
|
|
|
}
|
|
|
.radio-text { font-size: 26rpx; color: #6b7280; }
|
|
|
|
|
|
/* 盘点项列表 */
|
|
|
.item-list { padding: 0 24rpx; }
|
|
|
.item-card {
|
|
|
background: #fff; border-radius: 14rpx; padding: 20rpx; margin-bottom: 16rpx;
|
|
|
box-shadow: 0 2rpx 12rpx rgba(15,23,42,0.04);
|
|
|
position: relative;
|
|
|
}
|
|
|
.item-header { margin-bottom: 14rpx; padding-bottom: 14rpx; border-bottom: 1rpx solid #f0f0f0; }
|
|
|
.item-name { font-size: 28rpx; font-weight: 600; color: #1a1a1a; display: block; }
|
|
|
.item-code { font-size: 24rpx; color: #9ca3af; margin-top: 4rpx; display: block; }
|
|
|
.item-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12rpx;
|
|
|
&:last-child { margin-bottom: 0; }
|
|
|
}
|
|
|
.item-lbl { font-size: 26rpx; color: #6b7280; }
|
|
|
.item-val { font-size: 26rpx; color: #374151; &.highlight { font-weight: 700; color: #1f4b79; } }
|
|
|
.item-delete { position: absolute; top: 20rpx; right: 20rpx; }
|
|
|
.item-delete-text { font-size: 24rpx; color: #dc2626; }
|
|
|
|
|
|
.remark-card { padding: 20rpx 24rpx; }
|
|
|
.remark-textarea { width: 100%; min-height: 120rpx; font-size: 27rpx; color: #374151; line-height: 1.6; box-sizing: border-box; }
|
|
|
|
|
|
.hint { padding: 120rpx 0; text-align: center; color: #94a3b8; font-size: 28rpx; }
|
|
|
|
|
|
.bottom-actions {
|
|
|
position: fixed; left: 0; right: 0; bottom: 0; display: flex; gap: 18rpx;
|
|
|
padding: 18rpx 24rpx calc(18rpx + env(safe-area-inset-bottom));
|
|
|
background: #fff; box-shadow: 0 -8rpx 24rpx rgba(15,23,42,0.06); z-index: 99;
|
|
|
}
|
|
|
.bottom-btn { flex: 1; height: 84rpx; line-height: 84rpx; text-align: center; border-radius: 16rpx; font-size: 30rpx; font-weight: 600;
|
|
|
&:active { opacity: 0.85; }
|
|
|
}
|
|
|
.cancel-btn { background: #eef2f7; color: #475569; }
|
|
|
.confirm-btn { background: #1f4b79; color: #fff; }
|
|
|
|
|
|
/* 下拉 */
|
|
|
.select-row-card { background: #fff; border-radius: 16rpx; padding: 24rpx; margin: 0 24rpx 16rpx; box-shadow: 0 4rpx 16rpx rgba(15,23,42,0.04); min-height: 0 !important; height: auto !important; }
|
|
|
.warehouse-area-rows { display: flex; flex-direction: column; gap: 16rpx; }
|
|
|
.warehouse-area-row { display: flex; align-items: center; }
|
|
|
.warehouse-area-label { width: 100rpx; font-size: 26rpx; color: #6b7280; flex-shrink: 0; }
|
|
|
.warehouse-area-dropdown { flex: 1; position: relative; }
|
|
|
.dropdown-input { display: flex; align-items: center; min-height: 64rpx !important; height: 64rpx !important; 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; }
|
|
|
.dropdown-panel { position: absolute; top: 68rpx; left: 0; right: 0; z-index: 100; 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; overflow-y: auto; }
|
|
|
.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; }
|
|
|
}
|
|
|
.dropdown-item-text { font-size: 27rpx; color: #333; }
|
|
|
.dropdown-check { font-size: 28rpx; color: #2563eb; font-weight: 700; }
|
|
|
.dropdown-empty { padding: 32rpx; text-align: center; color: #999; font-size: 26rpx; }
|
|
|
|
|
|
/* 盘点项选择按钮 */
|
|
|
.item-select-btn {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
height: 64rpx;
|
|
|
padding: 0 20rpx;
|
|
|
border: 1rpx solid #e0e0e0;
|
|
|
border-radius: 10rpx;
|
|
|
background: #f9fafb;
|
|
|
}
|
|
|
.item-select-text {
|
|
|
font-size: 27rpx;
|
|
|
color: #333;
|
|
|
}
|
|
|
.item-select-btn .dropdown-arrow {
|
|
|
font-size: 20rpx;
|
|
|
color: #999;
|
|
|
}
|
|
|
</style>
|