| | |
| | | <view class="production-dispatching"> |
| | | <!-- 使用通用页面头部组件 --> |
| | | <PageHeader title="生产派工" @back="goBack" /> |
| | | |
| | | <!-- 炒机状态展示 --> |
| | | <view class="machines-section"> |
| | | <view class="section-title">炒机状态</view> |
| | | <view class="machines-grid"> |
| | | <view v-for="machine in machines" :key="machine.id" class="machine-card"> |
| | | <view class="machine-title">炒机{{ machine.id }}</view> |
| | | <view class="machine-metrics"> |
| | | <view class="metric-item"> |
| | | <text class="metric-label">总量(kg)</text> |
| | | <up-input |
| | | v-model="machineTotal[machine.key]" |
| | | type="number" |
| | | placeholder="请输入总量" |
| | | border="surround" |
| | | size="mini" |
| | | class="metric-input" |
| | | @change="updateMachineVacant(machine.key)" |
| | | /> |
| | | </view> |
| | | <view class="metric-item"> |
| | | <text class="metric-label">正在生产(kg)</text> |
| | | <text class="metric-value">{{ machineInProduction[machine.key] }}</text> |
| | | </view> |
| | | <view class="metric-item"> |
| | | <text class="metric-label">空余量(kg)</text> |
| | | <text class="metric-value">{{ machineVacant[machine.key] }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 损耗率设置 --> |
| | | <view class="loss-rate-section"> |
| | | <view class="section-title">损耗率设置</view> |
| | | <view class="loss-rate-content"> |
| | | <view class="loss-rate-item"> |
| | | <up-button |
| | | class="loss-rate-btn" |
| | | type="primary" |
| | | plain |
| | | size="small" |
| | | @click="showLossRateSheet = true" |
| | | >{{ lossRate ? `损耗率: ${lossRate}%` : '请选择损耗率' }}</up-button> |
| | | <up-action-sheet |
| | | :show="showLossRateSheet" |
| | | :actions="lossRateOptions" |
| | | @select="onLossRateSelect" |
| | | title="选择损耗率" |
| | | @close="showLossRateSheet = false" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="save-section"> |
| | | <up-button type="primary" @click="saveMachineTotals" size="normal" class="save-btn">保存炒机设置</up-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 搜索区域 --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <view class="search-form"> |
| | | <view class="search-item"> |
| | | <text class="search-label">客户名称</text> |
| | | <up-input |
| | | class="search-text" |
| | | placeholder="请输入客户名称搜索" |
| | | v-model="searchForm.customerName" |
| | | placeholder="请输入客户名称" |
| | | @change="handleQuery" |
| | | clearable |
| | | border="surround" |
| | | class="search-input" |
| | | /> |
| | | </view> |
| | | <view class="filter-button" @click="handleQuery"> |
| | | <up-icon name="search" size="24" color="#999"></up-icon> |
| | | <view class="search-item"> |
| | | <text class="search-label">项目名称</text> |
| | | <up-input |
| | | v-model="searchForm.projectName" |
| | | placeholder="请输入项目名称" |
| | | @change="handleQuery" |
| | | clearable |
| | | border="surround" |
| | | class="search-input" |
| | | /> |
| | | </view> |
| | | <view class="search-buttons"> |
| | | <up-button type="primary" @click="handleQuery" size="small" class="search-btn">搜索</up-button> |
| | | <up-button @click="handleReset" size="small" class="reset-btn">重置</up-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 批量操作区域 --> |
| | | <view class="batch-actions-section" v-if="showBatchActions"> |
| | | <view class="batch-info"> |
| | | <text class="batch-text">已选择 {{ selectedItems.length }} 个项目</text> |
| | | </view> |
| | | <view class="batch-buttons"> |
| | | <up-button type="primary" size="small" @click="handleAutoDispatch" class="batch-btn">自动派单</up-button> |
| | | <up-button type="default" size="small" @click="clearSelection" class="batch-btn">取消选择</up-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 全选操作区域 --> |
| | | <view class="select-all-section" v-if="tableData.length > 0"> |
| | | <view class="select-all-content"> |
| | | <up-checkbox |
| | | v-model="isAllSelected" |
| | | @change="toggleAllSelection" |
| | | label="全选" |
| | | class="select-all-checkbox" |
| | | :disabled="tableData.length === 0 || tableData.filter(item => item.pendingQuantity > 0 && item.speculativeTradingName).length === 0" |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 生产派工列表 --> |
| | | <view class="ledger-list" v-if="tableData.length > 0"> |
| | | <view v-for="(item, index) in tableData" :key="item.id || index"> |
| | | <view v-for="(item, index) in tableData" :key="item.id || index" class="list-item"> |
| | | <view class="ledger-item"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | <!-- 选择复选框 --> |
| | | <view class="item-checkbox"> |
| | | <up-checkbox |
| | | :model-value="selectedItems.some(selected => selected.id === item.id)" |
| | | @change="(checked) => toggleItemSelection(item, checked)" |
| | | :disabled="item.pendingQuantity <= 0 || !item.speculativeTradingName" |
| | | shape="circle" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="item-content"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.salesContractNo }}</text> |
| | | </view> |
| | | <text class="item-id">{{ item.salesContractNo }}</text> |
| | | <text class="entry-date">{{ item.entryDate }}</text> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">录入日期</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | <view class="item-details"> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">客户合同号</text> |
| | | <text class="detail-value">{{ item.customerContractNo }}</text> |
| | | </view> --> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客户名称</text> |
| | | <text class="detail-value">{{ item.customerName }}</text> |
| | | </view> |
| | | <!-- <view class="detail-row"> |
| | | <text class="detail-label">项目名称</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> --> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产品大类</text> |
| | | <text class="detail-value">{{ item.productCategory }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">规格型号</text> |
| | | <text class="detail-value">{{ item.specificationModel }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">绑定机器</text> |
| | | <text class="detail-value">{{ item.speculativeTradingName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">单位</text> |
| | | <text class="detail-value">{{ item.unit }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">录入日期</text> |
| | | <text class="detail-value">{{ item.entryDate }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">数量</text> |
| | | <text class="detail-value">{{ item.quantity }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">排产数量</text> |
| | | <text class="detail-value highlight">{{ item.schedulingNum }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">待排数量</text> |
| | | <text class="detail-value" :class="{ 'danger': item.pendingQuantity <= 0 }">{{ item.pendingQuantity }}</text> |
| | | </view> |
| | | |
| | | <!-- 操作按钮区域 --> |
| | | <view class="action-buttons"> |
| | | <up-button |
| | | type="primary" |
| | | size="small" |
| | | @click="handleDispatch(item)" |
| | | class="action-btn" |
| | | :disabled="item.pendingQuantity <= 0 || !item.speculativeTradingName" |
| | | > |
| | | {{ item.pendingQuantity <= 0 ? '无需派工' : !item.speculativeTradingName ? '未绑定机器' : '生产派工' }} |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客户合同号</text> |
| | | <text class="detail-value">{{ item.customerContractNo }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">客户名称</text> |
| | | <text class="detail-value">{{ item.customerName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">项目名称</text> |
| | | <text class="detail-value">{{ item.projectName }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产品大类</text> |
| | | <text class="detail-value">{{ item.productCategory }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">规格型号</text> |
| | | <text class="detail-value">{{ item.specificationModel }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">总数量</text> |
| | | <text class="detail-value">{{ item.quantity }} {{ item.unit }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">排产数量</text> |
| | | <text class="detail-value highlight">{{ item.schedulingNum }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">待排数量</text> |
| | | <text class="detail-value" :class="{ 'danger': item.pendingQuantity <= 0 }">{{ item.pendingQuantity }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 操作按钮区域 --> |
| | | <view class="action-buttons"> |
| | | <up-button |
| | | type="primary" |
| | | size="small" |
| | | @click="handleDispatch(item)" |
| | | class="action-btn" |
| | | :disabled="item.pendingQuantity <= 0" |
| | | > |
| | | 生产派工 |
| | | </up-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="no-data"> |
| | | <text>暂无生产派工数据</text> |
| | | <up-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"> |
| | | <text class="no-data-text">暂无生产派工数据</text> |
| | | </up-empty> |
| | | </view> |
| | | |
| | | <!-- 派工弹窗 --> |
| | | <DispatchModal ref="dispatchModalRef" @confirm="handleDispatchConfirm" /> |
| | | |
| | | <!-- 自动派单弹窗 --> |
| | | <AutoDispatchDia ref="autoDispatchDia" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import dayjs from "dayjs"; |
| | | import {schedulingListPage} from "@/api/productionManagement/productionOrder.js"; |
| | | import {schedulingListPage, schedulingList, addSpeculatTrading, updateSpeculatTrading, getLossRate, addLossRate, updateLossRate} from "@/api/productionManagement/productionOrder.js"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import DispatchModal from "./components/DispatchModal.vue"; |
| | | import AutoDispatchDia from "./components/autoDispatchDia.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 加载状态 |
| | |
| | | // 列表数据 |
| | | const tableData = ref([]); |
| | | |
| | | // 选择相关数据 |
| | | const selectedItems = ref([]); |
| | | const isAllSelected = ref(false); |
| | | const showBatchActions = ref(false); |
| | | |
| | | // 搜索表单数据 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | customerName: "", |
| | | projectName: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // 分页配置 |
| | | const page = reactive({ |
| | | current: -1, |
| | | size: -1, |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | |
| | | // 炒机状态数据 |
| | | const machineTotal = reactive({ |
| | | m1: 0, |
| | | m2: 0, |
| | | m3: 0, |
| | | m4: 0, |
| | | }) |
| | | |
| | | const machineInProduction = reactive({ |
| | | m1: 0, |
| | | m2: 0, |
| | | m3: 0, |
| | | m4: 0, |
| | | }) |
| | | |
| | | const machineVacant = reactive({ |
| | | m1: 0, |
| | | m2: 0, |
| | | m3: 0, |
| | | m4: 0, |
| | | }) |
| | | |
| | | // 炒机配置数组 |
| | | const machines = [ |
| | | { id: 1, key: 'm1' }, |
| | | { id: 2, key: 'm2' }, |
| | | { id: 3, key: 'm3' }, |
| | | { id: 4, key: 'm4' } |
| | | ] |
| | | |
| | | // 是否有查询数据(用于判断是新增还是修改) |
| | | const hasQueryData = ref(false); |
| | | |
| | | // 损耗率相关数据 |
| | | const lossRate = ref(""); // 当前选择的损耗率 |
| | | const showLossRateSheet = ref(false); // 控制损耗率选择面板显示 |
| | | const lossRateOptions = ref([ |
| | | { name: "6%", value: "6" }, |
| | | { name: "7%", value: "7" }, |
| | | { name: "8%", value: "8" }, |
| | | { name: "9%", value: "9" }, |
| | | { name: "10%", value: "10" } |
| | | ]); |
| | | const lossRateData = ref(null); // 损耗率查询返回的数据 |
| | | |
| | | // 派工弹窗引用 |
| | | const dispatchModalRef = ref(); |
| | | |
| | | // 自动派单弹窗引用 |
| | | const autoDispatchDia = ref(); |
| | | |
| | | // 通用提示函数 |
| | | const showLoadingToast = (message) => { |
| | |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 重置搜索 |
| | | const handleReset = () => { |
| | | searchForm.value.customerName = ""; |
| | | searchForm.value.projectName = ""; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 查询列表 |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 获取炒机生产数据 |
| | | const getMachineProductionData = () => { |
| | | schedulingList().then((res) => { |
| | | if (res.data && Array.isArray(res.data)) { |
| | | // 重置数据 |
| | | machineInProduction.m1 = 0; |
| | | machineInProduction.m2 = 0; |
| | | machineInProduction.m3 = 0; |
| | | machineInProduction.m4 = 0; |
| | | |
| | | // 处理炒机数据 |
| | | res.data.forEach(item => { |
| | | const machineId = Number(item.id); |
| | | if (machineId >= 1 && machineId <= 4) { |
| | | const machineKey = `m${machineId}`; |
| | | |
| | | if (item.workLoad !== null && item.workLoad !== undefined) { |
| | | machineTotal[machineKey] = Number(item.workLoad) || 0; |
| | | } |
| | | |
| | | if (item.currentWorkLoad !== null && item.currentWorkLoad !== undefined) { |
| | | machineInProduction[machineKey] = Number(item.currentWorkLoad) || 0; |
| | | } |
| | | |
| | | // 计算空余量 |
| | | machineVacant[machineKey] = machineTotal[machineKey] - machineInProduction[machineKey]; |
| | | } |
| | | }); |
| | | } |
| | | }).catch(err => { |
| | | console.error('获取炒机数据失败:', err); |
| | | }); |
| | | }; |
| | | |
| | | // 损耗率选择事件 |
| | | const onLossRateSelect = (action) => { |
| | | lossRate.value = action.value; |
| | | showLossRateSheet.value = false; |
| | | console.log('选择了损耗率:', action.name, '值:', action.value); |
| | | }; |
| | | |
| | | // 获取损耗率数据 |
| | | const getLossRateData = () => { |
| | | getLossRate().then((res) => { |
| | | if (res.data) { |
| | | lossRateData.value = res.data; |
| | | // 设置当前选择的损耗率 |
| | | if (res.data.rate !== null && res.data.rate !== undefined) { |
| | | lossRate.value = res.data.rate.toString(); |
| | | } |
| | | } |
| | | }).catch(err => { |
| | | console.error('获取损耗率失败:', err); |
| | | }); |
| | | }; |
| | | |
| | | // 获取列表数据 |
| | |
| | | loading.value = true; |
| | | showLoadingToast('加载中...'); |
| | | |
| | | // 构造请求参数 |
| | | const params = { ...searchForm.value, ...page }; |
| | | |
| | | schedulingListPage(params).then((res) => { |
| | | loading.value = false; |
| | | closeToast(); |
| | | |
| | | // 处理每条数据,增加pendingQuantity字段 |
| | | tableData.value = (res.data.records || []).map(item => ({ |
| | | ...item, |
| | | pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0) |
| | | })); |
| | | ...item, |
| | | pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0) |
| | | })).filter(item => item.pendingQuantity > 0); |
| | | |
| | | page.total = res.data.total || 0; |
| | | |
| | | // 获取炒机数据 |
| | | getMachineProductionData(); |
| | | |
| | | // 获取损耗率数据 |
| | | getLossRateData(); |
| | | |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | closeToast(); |
| | |
| | | return; |
| | | } |
| | | |
| | | if (!item.speculativeTradingName) { |
| | | uni.showToast({ |
| | | title: '该项目未绑定机器,无法派工', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | dispatchModalRef.value?.open(item); |
| | | }; |
| | | |
| | |
| | | getList(); // 刷新列表 |
| | | }; |
| | | |
| | | // 更新炒机空余量 |
| | | const updateMachineVacant = (machineKey) => { |
| | | machineVacant[machineKey] = (Number(machineTotal[machineKey]) || 0) - (Number(machineInProduction[machineKey]) || 0); |
| | | }; |
| | | |
| | | // 获取炒机查询数据 |
| | | const getMachineQueryData = (machineId) => { |
| | | // 这里需要根据实际情况从查询数据中获取对应的炒机数据 |
| | | // 暂时返回一个模拟数据 |
| | | return { |
| | | id: machineId, |
| | | name: `炒机${machineId}`, |
| | | workLoad: machineTotal[`m${machineId}`] || 0, |
| | | currentWorkLoad: machineInProduction[`m${machineId}`] || 0 |
| | | }; |
| | | }; |
| | | |
| | | // 保存损耗率设置 |
| | | const saveLossRate = () => { |
| | | if (!lossRate.value) { |
| | | console.log('未选择损耗率,跳过保存'); |
| | | return Promise.resolve(); |
| | | } |
| | | |
| | | const lossRateDataToSave = { |
| | | rate: parseFloat(lossRate.value) || 0 |
| | | }; |
| | | |
| | | // 如果有查询到的损耗率数据,说明是修改操作,需要传递id |
| | | if (lossRateData.value && lossRateData.value.id) { |
| | | lossRateDataToSave.id = lossRateData.value.id; |
| | | } |
| | | |
| | | console.log('保存损耗率数据:', lossRateDataToSave); |
| | | |
| | | // 根据是否有损耗率数据决定调用新增接口还是修改接口 |
| | | const saveLossApi = lossRateData.value && lossRateData.value.id ? updateLossRate : addLossRate; |
| | | const successMessage = lossRateData.value && lossRateData.value.id ? '损耗率修改成功' : '损耗率新增成功'; |
| | | |
| | | return saveLossApi(lossRateDataToSave).then(res => { |
| | | console.log('损耗率保存成功:', res); |
| | | uni.showToast({ |
| | | title: successMessage, |
| | | icon: 'success' |
| | | }); |
| | | |
| | | // 更新损耗率数据 |
| | | if (res.data) { |
| | | lossRateData.value = res.data; |
| | | } |
| | | |
| | | return res; |
| | | }).catch(err => { |
| | | console.error('损耗率保存失败:', err); |
| | | uni.showToast({ |
| | | title: '损耗率保存失败', |
| | | icon: 'none' |
| | | }); |
| | | throw err; |
| | | }); |
| | | }; |
| | | |
| | | // 保存炒机总量设置 |
| | | const saveMachineTotals = () => { |
| | | // 构造保存数据数组,使用machines数组循环构建 |
| | | const saveData = machines.map(machine => { |
| | | const machineData = { |
| | | name: `炒机${machine.id}`, // 炒机名称 |
| | | workLoad: machineTotal[machine.key] || 0, // 总量 |
| | | vacant: machineVacant[machine.key] || 0 // 空余量 |
| | | }; |
| | | |
| | | // 如果是修改操作,需要传递id字段 |
| | | if (hasQueryData.value) { |
| | | // 这里需要从查询数据中获取对应的id |
| | | // 假设查询数据中每个炒机数据都有id字段 |
| | | const queryData = getMachineQueryData(machine.id); |
| | | if (queryData && queryData.id) { |
| | | machineData.id = queryData.id; |
| | | } |
| | | } |
| | | |
| | | return machineData; |
| | | }); |
| | | |
| | | console.log('保存炒机设置数据:', saveData); |
| | | |
| | | // 根据是否有查询数据决定调用新增接口还是修改接口 |
| | | const saveApi = hasQueryData.value ? updateSpeculatTrading : addSpeculatTrading; |
| | | const successMessage = hasQueryData.value ? '炒机设置修改成功' : '炒机设置新增成功'; |
| | | |
| | | console.log(`调用接口: ${hasQueryData.value ? '修改' : '新增'}`); |
| | | |
| | | // 先保存损耗率,再保存炒机设置 |
| | | saveLossRate().then(() => { |
| | | // 调用后端API保存炒机设置 |
| | | return saveApi(saveData); |
| | | }).then(res => { |
| | | uni.showToast({ |
| | | title: successMessage, |
| | | icon: 'success' |
| | | }); |
| | | console.log('保存成功:', res); |
| | | |
| | | // 保存成功后,设置hasQueryData为true,下次保存将调用修改接口 |
| | | if (!hasQueryData.value) { |
| | | hasQueryData.value = true; |
| | | } |
| | | }).catch(err => { |
| | | uni.showToast({ |
| | | title: '保存失败', |
| | | icon: 'none' |
| | | }); |
| | | console.error('保存失败:', err); |
| | | }); |
| | | }; |
| | | |
| | | // 切换单个项目选择状态 |
| | | const toggleItemSelection = (item, checked) => { |
| | | // 仅允许选择已绑定机器且待派数量>0的项目 |
| | | if (!item.speculativeTradingName || item.pendingQuantity <= 0) return; |
| | | |
| | | console.log('切换选择状态:', item.id, checked); |
| | | |
| | | // 使用更严格的比较逻辑,确保ID唯一性 |
| | | const index = selectedItems.value.findIndex(selected => { |
| | | // 深度比较对象,确保是同一个项目 |
| | | return JSON.stringify(selected) === JSON.stringify(item); |
| | | }); |
| | | |
| | | if (checked) { |
| | | // 如果选中且不在选中列表中,则添加 |
| | | if (index === -1) { |
| | | selectedItems.value.push({...item}); // 创建新对象,避免引用问题 |
| | | console.log('添加项目后选中数量:', selectedItems.value.length); |
| | | } |
| | | } else { |
| | | // 如果取消选中且在选中列表中,则移除 |
| | | if (index > -1) { |
| | | selectedItems.value.splice(index, 1); |
| | | console.log('移除项目后选中数量:', selectedItems.value.length); |
| | | } |
| | | } |
| | | |
| | | console.log('当前选中项目列表:', selectedItems.value.map(s => s.id)); |
| | | updateAllSelectedStatus(); |
| | | updateBatchActionsVisibility(); |
| | | }; |
| | | |
| | | // 切换全选状态 |
| | | const toggleAllSelection = () => { |
| | | if (isAllSelected.value) { |
| | | selectedItems.value = []; |
| | | } else { |
| | | selectedItems.value = tableData.value.filter(item => item.pendingQuantity > 0 && item.speculativeTradingName).map(item => ({ ...item })); |
| | | } |
| | | isAllSelected.value = !isAllSelected.value; |
| | | updateBatchActionsVisibility(); |
| | | }; |
| | | |
| | | // 更新全选状态 |
| | | const updateAllSelectedStatus = () => { |
| | | const selectableItems = tableData.value.filter(item => item.pendingQuantity > 0 && item.speculativeTradingName); |
| | | if (selectableItems.length > 0 && selectedItems.value.length === selectableItems.length && |
| | | selectableItems.every(item => selectedItems.value.some(selected => selected.id === item.id))) { |
| | | isAllSelected.value = true; |
| | | } else { |
| | | isAllSelected.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 更新批量操作显示状态 |
| | | const updateBatchActionsVisibility = () => { |
| | | showBatchActions.value = selectedItems.value.length > 0; |
| | | }; |
| | | |
| | | // 清空选择 |
| | | const clearSelection = () => { |
| | | selectedItems.value = []; |
| | | isAllSelected.value = false; |
| | | showBatchActions.value = false; |
| | | }; |
| | | |
| | | // 获取选中的项目 |
| | | const getSelectedItems = () => { |
| | | return selectedItems.value; |
| | | }; |
| | | |
| | | // 处理自动派单 |
| | | const handleAutoDispatch = () => { |
| | | if (selectedItems.value.length === 0) { |
| | | uni.showToast({ |
| | | title: '请选择要派工的项目', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // 检查是否所有选中项目都有绑定机器 |
| | | const unboundItems = selectedItems.value.filter(item => !item.speculativeTradingName); |
| | | if (unboundItems.length > 0) { |
| | | uni.showToast({ |
| | | title: '所选项目中有未绑定机器的项目,无法自动派单', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // 确保传递的是完整的选中项目数组 |
| | | autoDispatchDia.value?.openDialog([...selectedItems.value]); |
| | | }; |
| | | |
| | | // 页面显示时加载数据 |
| | | onShow(() => { |
| | | // 加载列表数据 |
| | | getList(); |
| | | clearSelection(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/styles/sales-common.scss'; |
| | | |
| | | // 生产派工页面样式 |
| | | .production-dispatching { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | position: relative; |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | // 列表项样式 |
| | | // 损耗率设置区域 |
| | | .loss-rate-section { |
| | | background: #ffffff; |
| | | border: 1rpx solid #e4e7ed; |
| | | border-radius: 12rpx; |
| | | padding: 32rpx; |
| | | margin-top: 24rpx; |
| | | margin-bottom: 32rpx; |
| | | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .loss-rate-section .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .loss-rate-section .loss-rate-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24rpx; |
| | | } |
| | | |
| | | .loss-rate-section .loss-rate-content .loss-rate-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 24rpx; |
| | | } |
| | | |
| | | .loss-rate-section .loss-rate-content .loss-rate-label { |
| | | font-size: 30rpx; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | min-width: 140rpx; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .loss-rate-section .loss-rate-content .loss-rate-btn { |
| | | min-width: 260rpx; |
| | | font-size: 28rpx; |
| | | height: 64rpx; |
| | | line-height: 64rpx; |
| | | border-radius: 8rpx; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | // 炒机状态区域 |
| | | .machines-section { |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .machines-section .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .machines-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .machine-card { |
| | | background: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 24rpx; |
| | | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | border: 1rpx solid #e9ecef; |
| | | } |
| | | |
| | | .machine-title { |
| | | font-size: 28rpx; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | text-align: center; |
| | | margin-bottom: 20rpx; |
| | | padding-bottom: 16rpx; |
| | | border-bottom: 2rpx solid #3498db; |
| | | } |
| | | |
| | | .machine-metrics { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16rpx; |
| | | } |
| | | |
| | | .metric-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 8rpx 0; |
| | | } |
| | | |
| | | .metric-label { |
| | | font-size: 24rpx; |
| | | color: #6c757d; |
| | | } |
| | | |
| | | .metric-value { |
| | | font-size: 26rpx; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | } |
| | | |
| | | // 输入框样式 |
| | | .metric-input { |
| | | width: 120rpx; |
| | | text-align: right; |
| | | } |
| | | |
| | | // 保存区域 |
| | | .save-section { |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-top: 30rpx; |
| | | padding-top: 20rpx; |
| | | border-top: 1rpx solid #e9ecef; |
| | | } |
| | | |
| | | .save-btn { |
| | | min-width: 200rpx; |
| | | } |
| | | |
| | | // 搜索区域 |
| | | .search-section { |
| | | background: #ffffff; |
| | | border-radius: 16rpx; |
| | | padding: 24rpx; |
| | | margin-bottom: 30rpx; |
| | | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .search-form { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .search-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .search-label { |
| | | font-size: 26rpx; |
| | | color: #606266; |
| | | min-width: 140rpx; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | } |
| | | |
| | | .search-buttons { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | justify-content: flex-end; |
| | | margin-top: 10rpx; |
| | | } |
| | | |
| | | .search-btn, .reset-btn { |
| | | min-width: 120rpx; |
| | | } |
| | | |
| | | // 列表样式 |
| | | .ledger-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24rpx; |
| | | } |
| | | |
| | | .list-item { |
| | | background: #ffffff; |
| | | border-radius: 16rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .ledger-item { |
| | | .detail-value.highlight { |
| | | color: #ff6b35; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .detail-value.danger { |
| | | color: #ee0a24; |
| | | font-weight: 600; |
| | | } |
| | | padding: 0; |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 24rpx 24rpx 0 24rpx; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | } |
| | | |
| | | .document-icon { |
| | | width: 48rpx; |
| | | height: 48rpx; |
| | | background: #409eff; |
| | | border-radius: 8rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .item-id { |
| | | font-size: 28rpx; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .entry-date { |
| | | font-size: 24rpx; |
| | | color: #909399; |
| | | } |
| | | |
| | | .item-details { |
| | | padding: 24rpx; |
| | | } |
| | | |
| | | .detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 12rpx 0; |
| | | border-bottom: 1rpx solid #f5f5f5; |
| | | } |
| | | |
| | | .detail-row:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .detail-label { |
| | | font-size: 26rpx; |
| | | color: #606266; |
| | | } |
| | | |
| | | .detail-value { |
| | | font-size: 26rpx; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .detail-value.highlight { |
| | | color: #ff6b35; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .detail-value.danger { |
| | | color: #ee0a24; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .action-buttons { |
| | | padding: 0 24rpx 24rpx 24rpx; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .action-btn { |
| | | min-width: 180rpx; |
| | | } |
| | | |
| | | // 批量操作区域样式 |
| | | .batch-actions-section { |
| | | background: #e8f4ff; |
| | | border: 1rpx solid #409eff; |
| | | border-radius: 12rpx; |
| | | padding: 20rpx 24rpx; |
| | | margin-bottom: 24rpx; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .batch-actions-section .batch-text { |
| | | font-size: 28rpx; |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .batch-actions-section .batch-buttons { |
| | | display: flex; |
| | | gap: 16rpx; |
| | | } |
| | | |
| | | .batch-actions-section .batch-btn { |
| | | min-width: 140rpx; |
| | | } |
| | | |
| | | // 全选操作区域样式 |
| | | .select-all-section { |
| | | background: #ffffff; |
| | | border-radius: 12rpx; |
| | | padding: 20rpx 24rpx; |
| | | margin-bottom: 16rpx; |
| | | border: 1rpx solid #e4e7ed; |
| | | } |
| | | |
| | | .select-all-section .select-all-content { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .select-all-section .select-all-checkbox { |
| | | font-size: 28rpx; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | // 列表项选择框样式 |
| | | .ledger-item { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | padding: 0; |
| | | } |
| | | |
| | | .item-checkbox { |
| | | padding: 24rpx 16rpx 0 24rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .item-content { |
| | | flex: 1; |
| | | } |
| | | |
| | | // 空状态 |
| | | .no-data { |
| | | padding: 100rpx 0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .no-data-text { |
| | | font-size: 28rpx; |
| | | color: #909399; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | // 点击编辑区域样式 |
| | | .metric-value-container { |
| | | cursor: pointer; |
| | | min-width: 120rpx; |
| | | text-align: right; |
| | | padding: 4rpx 8rpx; |
| | | border-radius: 4rpx; |
| | | transition: all 0.2s ease; |
| | | } |
| | | |
| | | .metric-value-container:hover { |
| | | background-color: #f0f8ff; |
| | | border: 1rpx solid #409eff; |
| | | } |
| | | |
| | | // 适配 uView 组件样式 |
| | | :deep(.up-input) { |
| | | background: transparent; |
| | | } |
| | | </style> |
| | | |
| | | :deep(.up-input__content) { |
| | | background: #f8f9fa; |
| | | } |
| | | </style> |