| | |
| | | <template> |
| | | <up-popup |
| | | v-model:show="show" |
| | | mode="bottom" |
| | | :round="20" |
| | | :safeAreaInsetBottom="true" |
| | | @close="handleClose" |
| | | @open="handleOpen" |
| | | > |
| | | <view class="dispatch-modal"> |
| | | <!-- 头部 --> |
| | | <view class="modal-header"> |
| | | <text class="modal-title">生产派工</text> |
| | | <view class="close-btn" @click="handleClose"> |
| | | <up-icon name="close" size="20" color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 表单内容 --> |
| | | <view class="modal-content"> |
| | | <up-form |
| | | :model="form" |
| | | ref="formRef" |
| | | :rules="rules" |
| | | labelWidth="120" |
| | | > |
| | | <!-- 项目基本信息 --> |
| | | <view class="form-section"> |
| | | <text class="section-title">项目信息</text> |
| | | <up-form-item label="项目名称" prop="projectName"> |
| | | <up-input |
| | | v-model="form.projectName" |
| | | disabled |
| | | placeholder="项目名称" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="产品大类" prop="productCategory"> |
| | | <up-input |
| | | v-model="form.productCategory" |
| | | disabled |
| | | placeholder="产品大类" |
| | | /> |
| | | </up-form-item> |
| | | </view> |
| | | |
| | | <!-- 数量信息 --> |
| | | <view class="form-section"> |
| | | <text class="section-title">数量信息</text> |
| | | <up-form-item label="总数量" prop="quantity"> |
| | | <up-input |
| | | v-model="form.quantity" |
| | | disabled |
| | | placeholder="总数量" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="待排产数量" prop="pendingQuantity"> |
| | | <up-input |
| | | v-model="form.pendingQuantity" |
| | | disabled |
| | | placeholder="待排产数量" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="本次排产数量" prop="schedulingNum" required> |
| | | <up-number-box |
| | | v-model="form.schedulingNum" |
| | | :min="0" |
| | | :max="form.pendingQuantity" |
| | | :step="0.1" |
| | | :precision="2" |
| | | @change="handleNumChange" |
| | | /> |
| | | </up-form-item> |
| | | </view> |
| | | |
| | | <!-- 派工信息 --> |
| | | <view class="form-section"> |
| | | <text class="section-title">派工信息</text> |
| | | <up-form-item label="派工人" prop="schedulingUserId" required> |
| | | <up-input |
| | | v-model="selectedUserName" |
| | | placeholder="请选择派工人" |
| | | readonly |
| | | @click="showUserPicker = true" |
| | | suffixIcon="arrow-down" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="派工日期" prop="schedulingDate" required> |
| | | <up-input |
| | | v-model="form.schedulingDate" |
| | | placeholder="请选择派工日期" |
| | | readonly |
| | | @click="showDatePicker = true" |
| | | suffixIcon="calendar" |
| | | /> |
| | | </up-form-item> |
| | | </view> |
| | | </up-form> |
| | | </view> |
| | | |
| | | <!-- 底部按钮 --> |
| | | <view class="modal-footer"> |
| | | <up-button |
| | | @click="handleClose" |
| | | text="取消" |
| | | type="info" |
| | | plain |
| | | :customStyle="{ marginRight: '12px', flex: 1 }" |
| | | /> |
| | | <up-button |
| | | @click="handleConfirm" |
| | | text="确认派工" |
| | | type="primary" |
| | | :customStyle="{ flex: 1 }" |
| | | :loading="submitting" |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 人员选择器 --> |
| | | <up-picker |
| | | v-model="showUserPicker" |
| | | :columns="userColumns" |
| | | @confirm="handleUserSelect" |
| | | @cancel="showUserPicker = false" |
| | | /> |
| | | |
| | | <!-- 日期选择器 --> |
| | | <up-datetime-picker |
| | | v-model="showDatePicker" |
| | | mode="date" |
| | | @confirm="handleDateSelect" |
| | | @cancel="showDatePicker = false" |
| | | /> |
| | | </up-popup> |
| | | <up-popup v-model:show="show" |
| | | mode="bottom" |
| | | :round="20" |
| | | :safeAreaInsetBottom="true" |
| | | @close="handleClose" |
| | | @open="handleOpen"> |
| | | <view class="dispatch-modal"> |
| | | <!-- 头部 --> |
| | | <view class="modal-header"> |
| | | <text class="modal-title">生产派工</text> |
| | | <view class="close-btn" |
| | | @click="handleClose"> |
| | | <up-icon name="close" |
| | | size="20" |
| | | color="#999"></up-icon> |
| | | </view> |
| | | </view> |
| | | <!-- 表单内容 --> |
| | | <view class="modal-content"> |
| | | <up-form :model="form" |
| | | ref="formRef" |
| | | :rules="rules" |
| | | labelWidth="120"> |
| | | <!-- 项目基本信息 --> |
| | | <view class="form-section"> |
| | | <text class="section-title">项目信息</text> |
| | | <up-form-item label="项目名称" |
| | | prop="projectName"> |
| | | <up-input v-model="form.projectName" |
| | | disabled |
| | | placeholder="项目名称" /> |
| | | </up-form-item> |
| | | <up-form-item label="产品大类" |
| | | prop="productCategory"> |
| | | <up-input v-model="form.productCategory" |
| | | disabled |
| | | placeholder="产品大类" /> |
| | | </up-form-item> |
| | | </view> |
| | | <!-- 数量信息 --> |
| | | <view class="form-section"> |
| | | <text class="section-title">数量信息</text> |
| | | <up-form-item label="总数量" |
| | | prop="quantity"> |
| | | <up-input v-model="form.quantity" |
| | | disabled |
| | | placeholder="总数量" /> |
| | | </up-form-item> |
| | | <up-form-item label="待排产数量" |
| | | prop="pendingQuantity"> |
| | | <up-input v-model="form.pendingQuantity" |
| | | disabled |
| | | placeholder="待排产数量" /> |
| | | </up-form-item> |
| | | <up-form-item label="本次排产数量" |
| | | prop="schedulingNum" |
| | | required> |
| | | <up-number-box v-model="form.schedulingNum" |
| | | :min="0" |
| | | :max="form.pendingQuantity" |
| | | :step="0.1" |
| | | :precision="2" |
| | | @change="handleNumChange" /> |
| | | </up-form-item> |
| | | </view> |
| | | <!-- 派工信息 --> |
| | | <view class="form-section"> |
| | | <text class="section-title">派工信息</text> |
| | | <up-form-item label="派工人" |
| | | prop="schedulingUserId" |
| | | required> |
| | | <up-input v-model="selectedUserName" |
| | | placeholder="请选择派工人" |
| | | readonly |
| | | @click="showUserPicker = true" |
| | | suffixIcon="arrow-down" /> |
| | | </up-form-item> |
| | | <up-form-item label="派工日期" |
| | | prop="schedulingDate" |
| | | required> |
| | | <up-input v-model="form.schedulingDate" |
| | | placeholder="请选择派工日期" |
| | | readonly |
| | | @click="showDatePicker = true" |
| | | suffixIcon="calendar" /> |
| | | </up-form-item> |
| | | </view> |
| | | </up-form> |
| | | </view> |
| | | <!-- 底部按钮 --> |
| | | <view class="modal-footer"> |
| | | <up-button @click="handleClose" |
| | | text="取消" |
| | | type="info" |
| | | plain |
| | | :customStyle="{ marginRight: '12px', flex: 1 }" /> |
| | | <up-button @click="handleConfirm" |
| | | text="确认派工" |
| | | type="primary" |
| | | :customStyle="{ flex: 1 }" |
| | | :loading="submitting" /> |
| | | </view> |
| | | </view> |
| | | <!-- 人员选择器 --> |
| | | <up-picker v-model="showUserPicker" |
| | | :columns="userColumns" |
| | | @confirm="handleUserSelect" |
| | | @cancel="showUserPicker = false" /> |
| | | <!-- 日期选择器 --> |
| | | <up-datetime-picker v-model="showDatePicker" |
| | | mode="date" |
| | | @confirm="handleDateSelect" |
| | | @cancel="showDatePicker = false" /> |
| | | </up-popup> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, getCurrentInstance } from 'vue'; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { productionDispatch } from "@/api/productionManagement/productionOrder.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import dayjs from "dayjs"; |
| | | import { ref, reactive, computed, getCurrentInstance } from "vue"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | // import { productionDispatch } from "@/api/productionManagement/productionOrder.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const emit = defineEmits(['confirm']); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const emit = defineEmits(["confirm"]); |
| | | |
| | | // 弹窗显示状态 |
| | | const show = ref(false); |
| | | const submitting = ref(false); |
| | | // 弹窗显示状态 |
| | | const show = ref(false); |
| | | const submitting = ref(false); |
| | | |
| | | // 选择器显示状态 |
| | | const showUserPicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | // 选择器显示状态 |
| | | const showUserPicker = ref(false); |
| | | const showDatePicker = ref(false); |
| | | |
| | | // 用户列表 |
| | | const userList = ref([]); |
| | | const userColumns = computed(() => [ |
| | | userList.value.map(user => ({ |
| | | label: user.nickName, |
| | | value: user.userId |
| | | })) |
| | | ]); |
| | | // 用户列表 |
| | | const userList = ref([]); |
| | | const userColumns = computed(() => [ |
| | | userList.value.map(user => ({ |
| | | label: user.nickName, |
| | | value: user.userId, |
| | | })), |
| | | ]); |
| | | |
| | | // 选中的用户名称(用于显示) |
| | | const selectedUserName = computed(() => { |
| | | const user = userList.value.find(u => u.userId === form.schedulingUserId); |
| | | return user ? user.nickName : ''; |
| | | }); |
| | | // 选中的用户名称(用于显示) |
| | | const selectedUserName = computed(() => { |
| | | const user = userList.value.find(u => u.userId === form.schedulingUserId); |
| | | return user ? user.nickName : ""; |
| | | }); |
| | | |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | projectName: "", |
| | | productCategory: "", |
| | | quantity: "", |
| | | schedulingNum: 0, |
| | | schedulingUserId: "", |
| | | schedulingDate: "", |
| | | pendingQuantity: 0, |
| | | id: "" // 原始记录ID |
| | | }); |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | projectName: "", |
| | | productCategory: "", |
| | | quantity: "", |
| | | schedulingNum: 0, |
| | | schedulingUserId: "", |
| | | schedulingDate: "", |
| | | pendingQuantity: 0, |
| | | id: "", // 原始记录ID |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = reactive({ |
| | | schedulingNum: [ |
| | | { required: true, message: "请输入排产数量", trigger: "blur" } |
| | | ], |
| | | schedulingUserId: [ |
| | | { required: true, message: "请选择派工人", trigger: "change" } |
| | | ], |
| | | schedulingDate: [ |
| | | { required: true, message: "请选择派工日期", trigger: "change" } |
| | | ] |
| | | }); |
| | | // 表单验证规则 |
| | | const rules = reactive({ |
| | | schedulingNum: [ |
| | | { required: true, message: "请输入排产数量", trigger: "blur" }, |
| | | ], |
| | | schedulingUserId: [ |
| | | { required: true, message: "请选择派工人", trigger: "change" }, |
| | | ], |
| | | schedulingDate: [ |
| | | { required: true, message: "请选择派工日期", trigger: "change" }, |
| | | ], |
| | | }); |
| | | |
| | | // 表单引用 |
| | | const formRef = ref(); |
| | | // 表单引用 |
| | | const formRef = ref(); |
| | | |
| | | // 打开弹窗 |
| | | const open = async (rowData) => { |
| | | try { |
| | | // 加载用户列表 |
| | | const res = await userListNoPageByTenantId(); |
| | | userList.value = res.data || []; |
| | | |
| | | // 填充表单数据 |
| | | Object.assign(form, { |
| | | ...rowData, |
| | | schedulingNum: 0, |
| | | schedulingUserId: userStore.id, |
| | | schedulingDate: dayjs().format("YYYY-MM-DD") |
| | | }); |
| | | |
| | | show.value = true; |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: '加载用户列表失败', |
| | | icon: 'error' |
| | | }); |
| | | } |
| | | }; |
| | | // 打开弹窗 |
| | | const open = async rowData => { |
| | | try { |
| | | // 加载用户列表 |
| | | const res = await userListNoPageByTenantId(); |
| | | userList.value = res.data || []; |
| | | |
| | | // 处理数量变化 |
| | | const handleNumChange = (value) => { |
| | | if (value > form.pendingQuantity) { |
| | | form.schedulingNum = form.pendingQuantity; |
| | | uni.showToast({ |
| | | title: '排产数量不可大于待排产数量', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }; |
| | | // 填充表单数据 |
| | | Object.assign(form, { |
| | | ...rowData, |
| | | schedulingNum: 0, |
| | | schedulingUserId: userStore.id, |
| | | schedulingDate: dayjs().format("YYYY-MM-DD"), |
| | | }); |
| | | |
| | | // 处理用户选择 |
| | | const handleUserSelect = (params) => { |
| | | if (params.value && params.value.length > 0) { |
| | | form.schedulingUserId = params.value[0]; |
| | | } |
| | | showUserPicker.value = false; |
| | | }; |
| | | show.value = true; |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: "加载用户列表失败", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 处理日期选择 |
| | | const handleDateSelect = (params) => { |
| | | if (params.value) { |
| | | form.schedulingDate = dayjs(params.value).format("YYYY-MM-DD"); |
| | | } |
| | | showDatePicker.value = false; |
| | | }; |
| | | // 处理数量变化 |
| | | const handleNumChange = value => { |
| | | if (value > form.pendingQuantity) { |
| | | form.schedulingNum = form.pendingQuantity; |
| | | uni.showToast({ |
| | | title: "排产数量不可大于待排产数量", |
| | | icon: "none", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 确认派工 |
| | | const handleConfirm = async () => { |
| | | try { |
| | | // 表单验证 |
| | | const valid = await formRef.value?.validate(); |
| | | if (!valid) return; |
| | | |
| | | if (form.schedulingNum <= 0) { |
| | | uni.showToast({ |
| | | title: '排产数量必须大于0', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | submitting.value = true; |
| | | |
| | | // 提交派工数据 |
| | | await productionDispatch(form); |
| | | |
| | | uni.showToast({ |
| | | title: '派工成功', |
| | | icon: 'success' |
| | | }); |
| | | |
| | | handleClose(); |
| | | emit('confirm'); |
| | | |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: '派工失败', |
| | | icon: 'error' |
| | | }); |
| | | } finally { |
| | | submitting.value = false; |
| | | } |
| | | }; |
| | | // 处理用户选择 |
| | | const handleUserSelect = params => { |
| | | if (params.value && params.value.length > 0) { |
| | | form.schedulingUserId = params.value[0]; |
| | | } |
| | | showUserPicker.value = false; |
| | | }; |
| | | |
| | | // 弹窗打开事件 |
| | | const handleOpen = () => { |
| | | // 弹窗打开时的处理 |
| | | }; |
| | | // 处理日期选择 |
| | | const handleDateSelect = params => { |
| | | if (params.value) { |
| | | form.schedulingDate = dayjs(params.value).format("YYYY-MM-DD"); |
| | | } |
| | | showDatePicker.value = false; |
| | | }; |
| | | |
| | | // 关闭弹窗 |
| | | const handleClose = () => { |
| | | show.value = false; |
| | | showUserPicker.value = false; |
| | | showDatePicker.value = false; |
| | | |
| | | // 重置表单 |
| | | Object.assign(form, { |
| | | projectName: "", |
| | | productCategory: "", |
| | | quantity: "", |
| | | schedulingNum: 0, |
| | | schedulingUserId: "", |
| | | schedulingDate: "", |
| | | pendingQuantity: 0, |
| | | id: "" |
| | | }); |
| | | }; |
| | | // 确认派工 |
| | | const handleConfirm = async () => { |
| | | try { |
| | | // 表单验证 |
| | | const valid = await formRef.value?.validate(); |
| | | if (!valid) return; |
| | | |
| | | // 暴露方法 |
| | | defineExpose({ |
| | | open |
| | | }); |
| | | if (form.schedulingNum <= 0) { |
| | | uni.showToast({ |
| | | title: "排产数量必须大于0", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | submitting.value = true; |
| | | |
| | | // 提交派工数据 |
| | | // await productionDispatch(form); |
| | | |
| | | uni.showToast({ |
| | | title: "派工成功", |
| | | icon: "success", |
| | | }); |
| | | |
| | | handleClose(); |
| | | emit("confirm"); |
| | | } catch (error) { |
| | | uni.showToast({ |
| | | title: "派工失败", |
| | | icon: "error", |
| | | }); |
| | | } finally { |
| | | submitting.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 弹窗打开事件 |
| | | const handleOpen = () => { |
| | | // 弹窗打开时的处理 |
| | | }; |
| | | |
| | | // 关闭弹窗 |
| | | const handleClose = () => { |
| | | show.value = false; |
| | | showUserPicker.value = false; |
| | | showDatePicker.value = false; |
| | | |
| | | // 重置表单 |
| | | Object.assign(form, { |
| | | projectName: "", |
| | | productCategory: "", |
| | | quantity: "", |
| | | schedulingNum: 0, |
| | | schedulingUserId: "", |
| | | schedulingDate: "", |
| | | pendingQuantity: 0, |
| | | id: "", |
| | | }); |
| | | }; |
| | | |
| | | // 暴露方法 |
| | | defineExpose({ |
| | | open, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dispatch-modal { |
| | | background: #ffffff; |
| | | border-radius: 20px 20px 0 0; |
| | | max-height: 80vh; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | .dispatch-modal { |
| | | background: #ffffff; |
| | | border-radius: 20px 20px 0 0; |
| | | max-height: 80vh; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .modal-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 20px 20px 0 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | padding-bottom: 16px; |
| | | margin-bottom: 20px; |
| | | } |
| | | .modal-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 20px 20px 0 20px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | padding-bottom: 16px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .modal-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | .modal-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .close-btn { |
| | | padding: 4px; |
| | | |
| | | &:active { |
| | | opacity: 0.7; |
| | | } |
| | | } |
| | | .close-btn { |
| | | padding: 4px; |
| | | |
| | | .modal-content { |
| | | flex: 1; |
| | | padding: 0 20px; |
| | | overflow-y: auto; |
| | | } |
| | | &:active { |
| | | opacity: 0.7; |
| | | } |
| | | } |
| | | |
| | | .form-section { |
| | | margin-bottom: 24px; |
| | | } |
| | | .modal-content { |
| | | flex: 1; |
| | | padding: 0 20px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .section-title { |
| | | display: block; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 16px; |
| | | padding-left: 8px; |
| | | border-left: 3px solid #2979ff; |
| | | } |
| | | .form-section { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .modal-footer { |
| | | display: flex; |
| | | gap: 12px; |
| | | padding: 20px; |
| | | border-top: 1px solid #f0f0f0; |
| | | background: #fafafa; |
| | | } |
| | | .section-title { |
| | | display: block; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 16px; |
| | | padding-left: 8px; |
| | | border-left: 3px solid #2979ff; |
| | | } |
| | | |
| | | // uView 组件样式调整 |
| | | :deep(.up-form-item) { |
| | | margin-bottom: 20px; |
| | | } |
| | | .modal-footer { |
| | | display: flex; |
| | | gap: 12px; |
| | | padding: 20px; |
| | | border-top: 1px solid #f0f0f0; |
| | | background: #fafafa; |
| | | } |
| | | |
| | | :deep(.up-input) { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | // uView 组件样式调整 |
| | | :deep(.up-form-item) { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | :deep(.up-input--disabled) { |
| | | background: #f0f0f0; |
| | | color: #999; |
| | | } |
| | | :deep(.up-input) { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | :deep(.up-number-box) { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | :deep(.up-input--disabled) { |
| | | background: #f0f0f0; |
| | | color: #999; |
| | | } |
| | | |
| | | :deep(.up-number-box) { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | } |
| | | </style> |