<template>
|
<view>
|
<up-popup v-model:show="dialogFormVisible" mode="bottom" round="12" @close="closeDia" :customStyle="{ height: '85vh' }">
|
<view class="dia-container">
|
<view class="dia-header">
|
<text class="title">工序排产</text>
|
<up-button size="mini" @click="addRow" type="primary">新增</up-button>
|
<text class="pending">待排产数量:{{ pendingNum }}</text>
|
</view>
|
|
<scroll-view class="rows" scroll-y>
|
<view v-for="(row, index) in tableData" :key="index" class="row-card">
|
<view class="row-header">
|
<text class="row-index">#{{ index + 1 }}</text>
|
<up-button size="mini" type="error" plain @click="removeRow(index)">删除</up-button>
|
</view>
|
|
<up-form>
|
<up-form-item label="工序" label-width="80">
|
<up-input v-model="row.process" placeholder="请输入工序" />
|
</up-form-item>
|
<up-form-item label="单位" label-width="80">
|
<up-input v-model="row.unit" placeholder="请输入单位" />
|
</up-form-item>
|
<up-form-item label="口味/品名/规格" label-width="110">
|
<up-input v-model="row.type" placeholder="请输入" />
|
</up-form-item>
|
<up-form-item label="排产数量" label-width="80">
|
<uni-number-box v-model="row.schedulingNum" :min="0" :step="0.1" style="width: 100%;" background="#2979FF" color="#fff" />
|
</up-form-item>
|
<up-form-item label="工时定额" label-width="80">
|
<uni-number-box v-model="row.workHours" :min="0" :step="0.1" style="width: 100%;" background="#2979FF" color="#fff" />
|
</up-form-item>
|
<up-form-item label="排产日期" label-width="80">
|
<view class="select-box" @click.stop="openDatePicker(index)">
|
<view class="label" :class="[row.schedulingDate?'':'un-value']">
|
{{row.schedulingDate||'选择日期'}}
|
</view>
|
<view class="value">
|
<up-icon name="calendar"></up-icon>
|
</view>
|
</view>
|
<!-- <up-input v-model="row.schedulingDate" placeholder="选择日期" :border="false" readonly @click="openDatePicker(index)" />
|
<template #right>
|
<up-icon name="calendar" @click="openDatePicker(index)"></up-icon>
|
</template> -->
|
</up-form-item>
|
<up-form-item label="排产人" label-width="80">
|
<view class="select-box" @click.stop="openUserPicker(index)">
|
<view class="label" :class="[row.schedulingUserName?'':'un-value']">
|
{{row.schedulingUserName||'选择人员'}}
|
</view>
|
<view class="value">
|
<up-icon name="arrow-right"></up-icon>
|
</view>
|
</view>
|
<!-- <up-input v-model="row.schedulingUserName" placeholder="选择人员" readonly @click="openUserPicker(index)" />
|
<template #right>
|
<up-icon name="arrow-right" @click="openUserPicker(index)"></up-icon>
|
</template> -->
|
</up-form-item>
|
<up-form-item label="备注" label-width="80">
|
<up-input v-model="row.remark" placeholder="请输入备注" />
|
</up-form-item>
|
</up-form>
|
</view>
|
</scroll-view>
|
|
<view class="summary">
|
<text>排产数量合计:{{ totalSchedulingNum }}</text>
|
</view>
|
|
<view class="dia-footer">
|
<up-button type="primary" @click="submitForm">确认</up-button>
|
<up-button @click="closeDia">取消</up-button>
|
</view>
|
</view>
|
</up-popup>
|
|
<!-- 日期选择器(up 系列) -->
|
<up-popup :show="datePicker.show" mode="bottom" @close="datePicker.show = false">
|
<up-datetime-picker :show="true" v-model="datePicker.valueData" mode="date" @confirm="onDateConfirm" @cancel="datePicker.show = false" />
|
</up-popup>
|
|
<!-- 人员选择器(up 系列 action-sheet) -->
|
<up-action-sheet
|
:show="userPicker.show"
|
:actions="userActionList"
|
title="选择人员"
|
@select="onUserSelect"
|
@close="userPicker.show = false"
|
/>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, getCurrentInstance, computed } from 'vue'
|
import { userListNoPageByTenantId } from '@/api/system/user.js'
|
import { processScheduling } from '@/api/productionManagement/operationScheduling.js'
|
const { proxy } = getCurrentInstance()
|
const emit = defineEmits(['close'])
|
|
const dialogFormVisible = ref(false)
|
const operationType = ref('')
|
const tableData = ref([])
|
const unitFromRow = ref('')
|
const idFromRow = ref('')
|
const specificationModelFromRow = ref('')
|
const pendingNum = ref(0)
|
const userList = ref([])
|
const receive = ref('')
|
|
// pickers
|
const datePicker = ref({ show: false, valueData: Date.now(), rowIndex: -1 })
|
|
const userPicker = ref({ show: false, rowIndex: -1 })
|
|
// ActionSheet 数据
|
const userActionList = computed(() => {
|
return userList.value.map(u => ({ name: u.nickName, value: u.userId }))
|
})
|
|
const totalSchedulingNum = computed(() => {
|
return tableData.value.reduce((sum, r) => sum + (Number(r.schedulingNum || 0)), 0)
|
})
|
|
const userLabel = (uid) => {
|
const u = userList.value.find(u => u.userId === uid)
|
return u ? u.nickName : ''
|
}
|
|
// 打开弹框
|
const openDialog = (type, row) => {
|
operationType.value = type
|
dialogFormVisible.value = true
|
userListNoPageByTenantId().then((res) => {
|
userList.value = res.data
|
})
|
|
pendingNum.value = row?.pendingNum ?? 0
|
unitFromRow.value = row?.unit ?? ''
|
idFromRow.value = row?.id ?? ''
|
specificationModelFromRow.value = row?.specificationModel ?? ''
|
tableData.value = [createRow()]
|
}
|
|
const createRow = () => ({
|
id: idFromRow.value,
|
process: '',
|
schedulingDate: '',
|
schedulingNum: null,
|
schedulingUserId: '',
|
schedulingUserName: '',
|
workHours: null,
|
unit: unitFromRow.value,
|
remark: '',
|
type: specificationModelFromRow.value,
|
})
|
|
const openDatePicker = (idx) => {
|
datePicker.value.rowIndex = idx
|
datePicker.value.valueData = Date.now()
|
datePicker.value.show = true
|
}
|
const onDateConfirm = (e) => {
|
const val = e.value
|
const d = new Date(val)
|
const y = d.getFullYear()
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
const day = String(d.getDate()).padStart(2, '0')
|
const str = `${y}-${m}-${day}`
|
if (datePicker.value.rowIndex > -1) {
|
tableData.value[datePicker.value.rowIndex].schedulingDate = str
|
}
|
datePicker.value.show = false
|
}
|
|
const openUserPicker = (idx) => {
|
userPicker.value.rowIndex = idx
|
userPicker.value.show = true
|
}
|
const onUserSelect = (item) => {
|
if (item && userPicker.value.rowIndex > -1) {
|
const row = tableData.value[userPicker.value.rowIndex]
|
row.schedulingUserId = item.value
|
row.schedulingUserName = item.name
|
}
|
userPicker.value.show = false
|
}
|
|
const submitForm = () => {
|
// 1. 检查每一行是否填写完整
|
for (let i = 0; i < tableData.value.length; i++) {
|
const row = tableData.value[i]
|
if (!row.process || !row.schedulingDate || row.schedulingNum === '' || row.schedulingNum === null || !row.schedulingUserId || row.workHours === '' || row.workHours === null || !row.unit) {
|
uni.showToast({ title: `第${i + 1}行数据未填写完整`, icon: 'none' })
|
return
|
}
|
}
|
// 2. 合计排产数量
|
const total = tableData.value.reduce((sum, row) => sum + Number(row.schedulingNum || 0), 0)
|
if (total > Number(pendingNum.value)) {
|
uni.showToast({ title: '排产数量合计不能超过待排产数量', icon: 'none' })
|
return
|
}
|
// 3. 拼装数据
|
const submitData = tableData.value.map(row => {
|
const { loss, ...rest } = row
|
return { ...rest, receive: receive.value }
|
})
|
processScheduling(submitData).then(() => {
|
uni.showToast({ title: '提交成功', icon: 'success' })
|
closeDia()
|
})
|
}
|
|
// 关闭弹框
|
const closeDia = () => {
|
dialogFormVisible.value = false
|
receive.value = ''
|
tableData.value = []
|
unitFromRow.value = ''
|
idFromRow.value = ''
|
specificationModelFromRow.value = ''
|
pendingNum.value = 0
|
emit('close')
|
}
|
defineExpose({ openDialog })
|
|
const addRow = () => {
|
tableData.value.push(createRow())
|
}
|
const removeRow = (index) => {
|
tableData.value.splice(index, 1)
|
}
|
</script>
|
|
<style scoped lang="scss">
|
.dia-container {
|
padding: 12px;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
}
|
.dia-header {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
margin-bottom: 10px;
|
}
|
.dia-header .title {
|
font-weight: 600;
|
font-size: 16px;
|
color: #333;
|
}
|
.dia-header .pending {
|
margin-left: auto;
|
color: #666;
|
font-size: 12px;
|
}
|
.rows {
|
display: flex;
|
flex-direction: column;
|
gap: 10px;
|
overflow-y: auto;
|
-webkit-overflow-scrolling: touch;
|
flex: 1;
|
min-height: 0;
|
}
|
.row-card {
|
background: #f5f5f5;
|
border-radius: 10px;
|
padding: 20rpx;
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
box-sizing: border-box;
|
// border: 1px solid #f5f5f5;
|
margin-bottom: 20rpx;
|
}
|
.row-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 6px;
|
}
|
.row-index {
|
color: #999;
|
font-size: 12px;
|
}
|
.summary {
|
padding: 10px 0;
|
color: #333;
|
font-size: 14px;
|
text-align: right;
|
}
|
.dia-footer {
|
display: flex;
|
gap: 10px;
|
justify-content: flex-end;
|
padding-top: 8px;
|
}
|
.select-box{
|
display: flex;
|
flex-direction: row;
|
align-items: center;
|
justify-content: space-between;
|
flex: 1;
|
border-radius: 4px;
|
box-sizing: border-box;
|
padding: 6px 9px;
|
border-width: 0.5px !important;
|
border-color: #dadbde !important;
|
border-style: solid;
|
height: 100%;
|
.label{
|
min-height: 1.4em;
|
color: rgb(48, 49, 51);
|
font-size: 15px;
|
}
|
.un-value{
|
color: #999;
|
}
|
}
|
</style>
|