factory_mp/pages/mpr/material_requisition_form.vue

320 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 物资领用 -->
<template>
<view class="form-page">
<scroll-view scroll-y class="form-scroll">
<uni-forms v-model="form" label-width="200rpx" ref="customForm">
<!-- 审批状态卡片 -->
<view class="form-card" v-if="form.ticket_">
<ticketd :ticket_="form.ticket_"></ticketd>
</view>
<!-- 基本信息 -->
<view class="form-card">
<view class="form-card-title">基本信息</view>
<uni-forms-item label="编号" v-if="form.number">
<span>{{form.number}}</span>
</uni-forms-item>
<uni-forms-item label="领用部门">
<span>{{form.belong_dept_name || userDeptName}}</span>
</uni-forms-item>
<uni-forms-item label="操作人">
<span>{{form.create_by_name || userName}}</span>
</uni-forms-item>
<uni-forms-item label="填报时间" required>
<picker mode="date" :value="form.req_date" @change="onDateChange" v-if="mode!='show'">
<view class="picker-value">{{form.req_date || '请选择日期'}}</view>
</picker>
<span v-else>{{form.req_date}}</span>
</uni-forms-item>
<uni-forms-item label="领取人">
<uni-easyinput v-model="form.collector" placeholder="请输入领取人" v-if="mode!='show'"/>
<span v-else>{{form.collector}}</span>
</uni-forms-item>
<uni-forms-item label="备注">
<textarea placeholder-style="color:#c0c4cc" v-model="form.note" placeholder="请输入备注" class="form-textarea" v-if="mode!='show'"/>
<span v-else>{{form.note}}</span>
</uni-forms-item>
</view>
<!-- 领用物品明细 -->
<view class="form-card">
<view class="form-card-title">领用物品</view>
<view v-if="mode!='show'" class="item-add-row">
<button class="item-add-btn item-add-btn-stock" @click="openStockPicker" size="mini">从库存选择</button>
<button class="item-add-btn" @click="addItem" size="mini">+ 手动添加</button>
</view>
<view v-for="(item, idx) in items" :key="idx" class="item-card">
<view class="item-card-header">
<text class="item-card-index">第{{idx+1}}项 {{item.is_stock_item ? '(库存)' : ''}}</text>
<text v-if="mode!='show'" class="item-card-del" @click="delItem(idx)">删除</text>
</view>
<uni-forms-item label="物资名称" required>
<uni-easyinput v-model="item.name" placeholder="物资名称" :disabled="item.is_stock_item" v-if="mode!='show'"/>
<span v-else>{{item.name}}</span>
</uni-forms-item>
<uni-forms-item label="领用类型">
<uni-easyinput v-model="item.req_type" placeholder="领用类型" v-if="mode!='show'"/>
<span v-else>{{item.req_type}}</span>
</uni-forms-item>
<uni-forms-item label="规格型号">
<uni-easyinput v-model="item.spec" placeholder="规格型号" :disabled="item.is_stock_item" v-if="mode!='show'"/>
<span v-else>{{item.spec}}</span>
</uni-forms-item>
<uni-forms-item label="单位">
<uni-easyinput v-model="item.unit" placeholder="单位" :disabled="item.is_stock_item" v-if="mode!='show'"/>
<span v-else>{{item.unit}}</span>
</uni-forms-item>
<uni-forms-item label="库存量" v-if="item.is_stock_item">
<span style="color:#9CA3AF;">{{item.stock_quantity}}</span>
</uni-forms-item>
<uni-forms-item label="领用量" required>
<uni-number-box v-model="item.quantity" :min="0" :max="item.is_stock_item ? Number(item.stock_quantity) : 99999" :step="1" v-if="mode!='show'"/>
<span v-else>{{item.quantity}}</span>
</uni-forms-item>
<uni-forms-item label="备注">
<uni-easyinput v-model="item.note" placeholder="备注" v-if="mode!='show'"/>
<span v-else>{{item.note}}</span>
</uni-forms-item>
</view>
<view v-if="items.length === 0" class="empty-hint">暂无领用物品</view>
</view>
</uni-forms>
</scroll-view>
<view class="footer_fixed">
<button v-if="mode=='edit'" class="form-btn form-btn-danger" @click="handleDel" :loading="saveLoading" :disabled="saveLoading">
删除
</button>
<button v-if="mode!='show'" class="form-btn form-btn-primary" @click="handleSave" :loading="saveLoading" :disabled="saveLoading">
提交审批
</button>
<ticketd_b :workflow_key="'wf_material_requis'" v-if="form.ticket_ && mode == 'show'" :t_id="form.id" :ticket_="form.ticket_"
:ticket_data="ticket_data" @success="()=>{uni.navigateBack()}" ref="ticketd_b"></ticketd_b>
</view>
<!-- 库存选择弹窗 -->
<uni-popup ref="stockPopup" type="bottom" background-color="#fff">
<view class="stock-popup">
<view class="stock-popup-header">
<text class="stock-popup-title">选择库存物品</text>
<text class="stock-popup-close" @click="closeStockPicker">关闭</text>
</view>
<view class="stock-search-row">
<uni-easyinput v-model="stockSearch" placeholder="搜索名称/规格/供应商" @confirm="loadStockList" prefixIcon="search"/>
</view>
<scroll-view scroll-y class="stock-list">
<view v-for="stock in stockList" :key="stock.id" class="stock-item" @click="pickStock(stock)" hover-class="stock-item-hover">
<view class="stock-item-name">{{stock.name}}</view>
<view class="stock-item-info">
<text v-if="stock.spec">规格: {{stock.spec}}</text>
<text>库存: {{stock.quantity}} {{stock.unit}}</text>
</view>
<view class="stock-item-info" v-if="stock.warehouse_name || stock.supplier_name">
<text v-if="stock.warehouse_name">仓库: {{stock.warehouse_name}}</text>
<text v-if="stock.supplier_name">供应商: {{stock.supplier_name}}</text>
</view>
</view>
<view v-if="stockList.length === 0 && stockLoadStatus !== 'loading'" class="empty-hint">暂无可用库存</view>
<uni-load-more v-if="stockLoadStatus === 'loading'" status="loading"/>
</scroll-view>
</view>
</uni-popup>
</view>
</template>
<script>
import ticketd_b from "../wf/ticketd_b.vue"
import ticketd from "../wf/ticketd.vue"
export default {
components: { ticketd_b, ticketd },
data(){
return{
saveLoading: false,
mode:"add",
t_id: null,
form:{},
items:[],
ticket_data:{},
userName:'',
userDeptName:'',
// 库存选择
stockSearch:'',
stockList:[],
stockLoadStatus: 'more',
}
},
computed: {
isEditable() {
return this.mode === 'add' || this.mode === 'edit'
}
},
async onLoad(options) {
let userInfo = uni.getStorageSync("userInfo")
this.userName = userInfo.name
this.userDeptName = userInfo.belong_dept_name || ''
this.mode = options.mode || 'add'
this.t_id = options.t_id || null
if(this.t_id) {
const res = await this.$api.materialRequisitionItem(this.t_id)
this.form = res
this.items = (res.items_ || []).map(i => ({
...i,
stock_id: i.stock,
stock_quantity: '—',
}))
if(res.ticket_ && res.ticket_.state_.type == 1 && res.create_by == userInfo.id) {
this.mode = "edit"
} else {
this.mode = "show"
}
}
},
methods:{
onDateChange(e) {
this.form.req_date = e.detail.value
},
// 库存选择相关
async openStockPicker() {
this.stockSearch = ''
this.stockList = []
this.stockLoadStatus = 'loading'
await this.loadStockList()
this.$refs.stockPopup.open()
},
closeStockPicker() {
this.$refs.stockPopup.close()
},
async loadStockList() {
this.stockLoadStatus = 'loading'
try {
let params = { page: 0 }
if(this.stockSearch) {
params.search = this.stockSearch
}
console.log('加载库存列表, 参数:', JSON.stringify(params))
const res = await this.$api.warehouseStockList(params)
console.log('库存列表响应:', JSON.stringify(res).substring(0, 200))
const list = Array.isArray(res) ? res : (res.results || [])
// 过滤掉库存为0和已选择的
const existingIds = new Set(this.items.filter(i=>i.is_stock_item).map(i=>i.stock_id))
this.stockList = list.filter(s => Number(s.quantity) > 0 && !existingIds.has(s.id))
console.log('过滤后库存数量:', this.stockList.length)
this.stockLoadStatus = 'noMore'
} catch(e) {
console.error('加载库存失败:', e)
this.stockLoadStatus = 'noMore'
}
},
pickStock(stock) {
this.items.push({
is_stock_item: true,
stock_id: stock.id,
name: stock.name,
spec: stock.spec || '',
unit: stock.unit || '',
stock_quantity: stock.quantity,
quantity: 0,
req_type: '',
note: '',
})
// 从列表中移除已选
this.stockList = this.stockList.filter(s => s.id !== stock.id)
uni.showToast({ title: `已添加: ${stock.name}`, icon: 'none' })
},
// 手动添加
addItem() {
this.items.push({
is_stock_item: false,
name: '', req_type: '', spec: '', unit: '',
quantity: 0, note: ''
})
},
delItem(idx) {
this.items.splice(idx, 1)
},
async handleDel(){
await this.$api.materialRequisitionDelete(this.form.id)
uni.navigateBack()
},
async handleSave(){
if(!this.form.req_date) {
uni.showToast({ title: '请选择填报时间', icon: 'none' }); return
}
if(this.items.length === 0) {
uni.showToast({ title: '请至少添加一项领用物品', icon: 'none' }); return
}
for(let i = 0; i < this.items.length; i++) {
if(!this.items[i].name) {
uni.showToast({ title: `${i+1}项:请填写物资名称`, icon: 'none' }); return
}
if(!this.items[i].quantity || this.items[i].quantity <= 0) {
uni.showToast({ title: `${i+1}领用量必须大于0`, icon: 'none' }); return
}
if(this.items[i].is_stock_item && Number(this.items[i].quantity) > Number(this.items[i].stock_quantity)) {
uni.showToast({ title: `${i+1}项:领用量超过库存量`, icon: 'none' }); return
}
}
this.saveLoading = true
try {
const payload = {
req_date: this.form.req_date,
collector: this.form.collector,
note: this.form.note,
items: this.items.map(row => ({
is_stock_item: row.is_stock_item,
stock: row.is_stock_item ? row.stock_id : null,
req_type: row.req_type,
name: row.name,
spec: row.spec,
unit: row.unit,
quantity: row.quantity,
note: row.note,
}))
}
if(this.mode === 'edit') {
await this.$api.materialRequisitionUpdate(this.form.id, payload)
} else {
await this.$api.materialRequisitionCreate(payload)
}
uni.navigateBack()
} catch(e) {
uni.showToast({ title: '提交失败', icon: 'none' })
} finally {
this.saveLoading = false
}
},
}
}
</script>
<style lang="scss" scoped>
.form-page { min-height: 100vh; background: #F0F2F5; }
.form-scroll { padding: 0; padding-bottom: 180rpx; }
.form-card { background: #fff; border-radius: 0; padding: 24rpx; margin-bottom: 0; box-shadow: none; }
.form-card-title { font-size: 30rpx; font-weight: 600; color: #1F2937; margin-bottom: 16rpx; padding-left: 16rpx; border-left: 6rpx solid #2BA471; }
.form-textarea { width: 100%; min-height: 120rpx; border: 2rpx solid #E5E7EB; border-radius: 12rpx; padding: 16rpx; font-size: 28rpx; color: #374151; box-sizing: border-box; background: #F9FAFB; }
.form-btn { flex: 1; height: 80rpx; line-height: 80rpx; border-radius: 14rpx !important; font-size: 28rpx; font-weight: 500; border: none !important; }
.form-btn-primary { background: linear-gradient(135deg, #2BA471, #1F8C5E) !important; color: #fff !important; box-shadow: 0 4rpx 12rpx rgba(43,164,113,0.2); }
.form-btn-danger { background: linear-gradient(135deg, #EF4444, #DC2626) !important; color: #fff !important; box-shadow: 0 4rpx 12rpx rgba(239,68,68,0.3); }
.picker-value { padding: 12rpx 16rpx; background: #F9FAFB; border: 2rpx solid #E5E7EB; border-radius: 8rpx; font-size: 28rpx; color: #374151; }
.item-add-row { display: flex; justify-content: flex-end; margin-bottom: 16rpx; gap: 16rpx; }
.item-add-btn { background: #2BA471 !important; color: #fff !important; font-size: 24rpx; border: none !important; border-radius: 8rpx !important; padding: 0 24rpx !important; height: 56rpx; line-height: 56rpx; }
.item-add-btn-stock { background: #3594E6 !important; }
.item-card { background: #F9FAFB; border-radius: 12rpx; padding: 20rpx; margin-bottom: 16rpx; border: 1rpx solid #E5E7EB; }
.item-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
.item-card-index { font-size: 26rpx; font-weight: 600; color: #2BA471; }
.item-card-del { font-size: 24rpx; color: #EF4444; }
.empty-hint { text-align: center; padding: 40rpx 0; color: #9CA3AF; font-size: 26rpx; }
// 库存选择弹窗
.stock-popup { height: 70vh; display: flex; flex-direction: column; }
.stock-popup-header { display: flex; justify-content: space-between; align-items: center; padding: 24rpx; border-bottom: 1rpx solid #E5E7EB; }
.stock-popup-title { font-size: 32rpx; font-weight: 600; color: #1F2937; }
.stock-popup-close { font-size: 28rpx; color: #9CA3AF; padding: 8rpx 16rpx; }
.stock-search-row { padding: 16rpx 24rpx; }
.stock-list { flex: 1; padding: 0 24rpx; }
.stock-item { padding: 20rpx; margin-bottom: 12rpx; background: #F9FAFB; border-radius: 12rpx; border: 1rpx solid #E5E7EB; }
.stock-item-hover { background: #E0F5EA; border-color: #2BA471; }
.stock-item-name { font-size: 28rpx; font-weight: 600; color: #1F2937; margin-bottom: 8rpx; }
.stock-item-info { display: flex; gap: 24rpx; font-size: 24rpx; color: #6B7280; margin-top: 4rpx; }
</style>