| | |
| | | <template> |
| | | <view class="sockpage"> |
| | | <view class="column"> |
| | | <!-- 托盘编码 --> |
| | | <view class="form-card"> |
| | | <view class="form-row"> |
| | | <view class="label"> |
| | | <text class="required">*</text>托盘编码: |
| | | </view> |
| | | <view class="picker-wrap" hover-class="picker-hover"> |
| | | <view class="picker-wrap"> |
| | | <view class="input-box"> |
| | | <input |
| | | id="pdacode" |
| | | type="text" |
| | | ref="barcodeInput" |
| | | v-model="barcode" |
| | | placeholder="请扫码(12位以下为托盘码)" |
| | | :focus="focusState" |
| | | @focus="focusFn" |
| | | @blur="focusState = false" |
| | | type="text" |
| | | v-model="barcode" |
| | | placeholder="请扫码" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 箱型栏(对应后端boxType) --> |
| | | <!-- 箱型选择 --> |
| | | <view class="form-card"> |
| | | <view class="form-row"> |
| | | <view class="label"> |
| | | <text class="required">*</text>箱 型: |
| | | <text class="required">*</text>箱型: |
| | | </view> |
| | | <view class="picker-wrap" hover-class="picker-hover"> |
| | | <view class="picker-wrap"> |
| | | <picker @change="modePickerChange" :value="index_mode" :range="mode"> |
| | | <view class="input-box"> |
| | | <text class="input-placeholder" v-if="!mode.length || !mode[index_mode]">请选择箱型</text> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 物料信息输入栏(对应后端MatList) --> |
| | | <!-- 物料码扫码(隐藏但可用) --> |
| | | <!-- <view class="form-card" style="height: 0; overflow: hidden; padding: 0; margin: 0;"> --> |
| | | <view class="form-card" style=" overflow: hidden; padding: 0; margin: 0;"> |
| | | <view class="form-row"> |
| | | <view class="label"> |
| | | <text class="required">*</text>物料码: |
| | | </view> |
| | | <view class="picker-wrap"> |
| | | <view class="input-box"> |
| | | <input |
| | | type="text" |
| | | v-model="scanBuffer" |
| | | placeholder="请扫码" |
| | | @input="handleMaterialScan" |
| | | ref="scanInput" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 物料信息 --> |
| | | <view class="form-card material-card"> |
| | | <view class="card-title">物料信息</view> |
| | | <view class="material-grid"> |
| | | <view class="material-item"> |
| | | <text class="material-label"><text class="required">*</text>卷号:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="matList.model" |
| | | placeholder="请扫码或输入卷号" |
| | | placeholder-class="input-placeholder" |
| | | ref="modelInput" |
| | | /> |
| | | |
| | | <!-- 多物料列表 --> |
| | | <view class="material-list" v-if="matList.length > 0"> |
| | | <view class="material-item" v-for="(mat, index) in matList" :key="mat.id"> |
| | | <view class="material-header"> |
| | | <text class="material-title">物料 {{ index + 1 }}</text> |
| | | <view class="material-actions"> |
| | | <view class="action-btn" @click="removeMaterial(index)">删除</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="material-grid"> |
| | | <view class="material-item"> |
| | | <text class="material-label"><text class="required">*</text>位置:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="mat.position" |
| | | placeholder="请选择位置" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label"><text class="required">*</text>箱号:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="mat.batch" |
| | | placeholder="请扫码或输入箱号" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label"><text class="required">*</text>卷号:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="mat.model" |
| | | placeholder="请扫码或输入卷号" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">规格:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="mat.matnr" |
| | | placeholder="请输入规格" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">长度:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="mat.rollExtent" |
| | | placeholder="请输入长度" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">净重:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="mat.weight" |
| | | placeholder="请输入净重" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">毛重:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="mat.roughWeight" |
| | | placeholder="请输入毛重" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">接头:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="mat.joint" |
| | | placeholder="请输入接头数" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label"><text class="required">*</text>箱号:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="matList.batch" |
| | | placeholder="请扫码或输入箱号" |
| | | placeholder-class="input-placeholder" |
| | | ref="batchInput" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">规格:</text> |
| | | <input |
| | | class="material-input" |
| | | type="text" |
| | | v-model="matList.matnr" |
| | | placeholder="请输入规格" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">长度:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.rollExtent" |
| | | placeholder="请输入长度" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">净重:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.weight" |
| | | placeholder="请输入净重" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">毛重:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.roughWeight" |
| | | placeholder="请输入毛重" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | | <text class="material-label">接头:</text> |
| | | <input |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.joint" |
| | | placeholder="请输入接头数" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 空状态提示 --> |
| | | <view class="empty-state" v-else> |
| | | <view class="empty-icon">📦</view> |
| | | <text class="empty-text">暂无物料信息</text> |
| | | <text class="empty-tip">请扫描物料码添加物料</text> |
| | | </view> |
| | | |
| | | <!-- 扫码按钮 --> |
| | | <view class="scan-btn" @click="focusScanInput"> |
| | | <text class="scan-icon">+</text> |
| | | <text class="scan-text">扫码添加物料</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 操作按钮组 --> |
| | | <view class="btn-group"> |
| | | <button class="operate-btn scan-btn" @click="startScan">开始扫码</button> |
| | | <button class="operate-btn submit-btn" @click="submit">成品入库</button> |
| | | <button class="operate-btn submit-btn" @click="submit">组托</button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 扫码确认弹窗 --> |
| | | <view class="modal-mask" v-if="showScanModal" @click="closeModal"> |
| | | <view class="modal-content" @click.stop> |
| | | <view class="modal-header"> |
| | | <text class="modal-title">扫码确认</text> |
| | | </view> |
| | | <view class="modal-body"> |
| | | <view class="scan-preview"> |
| | | <text class="scan-label">扫描到物料:</text> |
| | | <text class="scan-code">{{ tempMaterial.model || '未知物料' }}</text> |
| | | </view> |
| | | |
| | | <view class="preview-grid"> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">箱号:</text> |
| | | <text class="preview-value">{{ tempMaterial.batch || '未识别' }}</text> |
| | | </view> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">卷号:</text> |
| | | <text class="preview-value">{{ tempMaterial.model || '未识别' }}</text> |
| | | </view> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">规格:</text> |
| | | <text class="preview-value">{{ tempMaterial.matnr || '未识别' }}</text> |
| | | </view> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">长度:</text> |
| | | <text class="preview-value">{{ tempMaterial.rollExtent || '未识别' }}</text> |
| | | </view> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">净重:</text> |
| | | <text class="preview-value">{{ tempMaterial.weight || '未识别' }}</text> |
| | | </view> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">毛重:</text> |
| | | <text class="preview-value">{{ tempMaterial.roughWeight || '未识别' }}</text> |
| | | </view> |
| | | <view class="preview-item"> |
| | | <text class="preview-label">接头:</text> |
| | | <text class="preview-value">{{ tempMaterial.joint || '未识别' }}</text> |
| | | </view> |
| | | |
| | | <view class="preview-item full-width"> |
| | | <text class="preview-label">物料位置:</text> |
| | | <view class="position-selector" @click.stop="togglePositionPicker"> |
| | | <view class="position-display"> |
| | | <text v-if="positionIndex >= 0">{{ positionOptions[positionIndex].label }}</text> |
| | | <text v-else class="placeholder">请选择位置</text> |
| | | <text class="position-arrow">▼</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 新增:自定义选择器弹出层 --> |
| | | <view v-if="showPositionPicker" class="custom-picker-overlay" @click.stop="closePositionPicker"> |
| | | <view class="custom-picker-content" @click.stop> |
| | | <view class="custom-picker-header"> |
| | | <text class="custom-picker-title">选择物料位置</text> |
| | | <view class="custom-picker-close" @click="closePositionPicker">×</view> |
| | | </view> |
| | | <view class="custom-picker-list"> |
| | | <view |
| | | v-for="(item, index) in positionOptions" |
| | | :key="index" |
| | | class="custom-picker-item" |
| | | :class="{'selected': positionIndex === index}" |
| | | @click="selectPosition(index)" |
| | | > |
| | | <text class="custom-picker-text">{{ item.label }}</text> |
| | | <text v-if="positionIndex === index" class="custom-picker-check">✓</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="modal-footer"> |
| | | <button class="modal-btn cancel" @click="cancelAddMaterial">取消</button> |
| | | <button class="modal-btn confirm" @click="confirmAddMaterial">确定添加</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { onLoad } from '../../uni_modules/uview-ui/libs/mixin/mixin' |
| | | |
| | | export default { |
| | | onLoad(){ |
| | | // 先读取缓存,再执行请求 |
| | | this.baseUrl = uni.getStorageSync('baseUrl'); |
| | | this.token = uni.getStorageSync('token'); |
| | | // 页面加载时 mode 为空,调用接口后才填充数据 |
| | | this.POSTinfo(); |
| | | onLoad(options) { |
| | | this.checkLoginStatus(); |
| | | this.POSTinfo(); |
| | | }, |
| | | |
| | | mounted() { |
| | | this.setupScanListener(); |
| | | onShow() { |
| | | this.checkLoginStatus(); |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | this.removeScanListener(); |
| | | onHide() { |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | this.scanTimeout = null; |
| | | } |
| | | }, |
| | | |
| | | onReady() { |
| | | // 在页面准备就绪后初始化扫码监听 |
| | | setTimeout(() => { |
| | | this.initScanListener(); |
| | | }, 500); |
| | | }, |
| | | |
| | | data() { |
| | | return { |
| | | focusState: false, |
| | | baseUrl: '', |
| | | barcodeNum:'', |
| | | barcode: '', // 托盘码 |
| | | palletizingNo: '1', // 码垛位编号 |
| | | boxType: '', // 木箱类型 |
| | | matList: { |
| | | matnr: '', // 规格 |
| | | maktx: '', // 物料名称 |
| | | batch: '', // 木箱编号 |
| | | model: '', // 卷编号 |
| | | position: '1', // 木箱码垛位置 |
| | | weight: '', // 净重 |
| | | roughWeight: '', // 毛重 |
| | | anfme: 1.0, // 默认值1.0 |
| | | rollExtent: '', // 卷长度 |
| | | joint: 0, // 接头 |
| | | cutting: 2, |
| | | qualified: 0, |
| | | modelFront: '', // 分切前箱编号 |
| | | batchFront: '' ,// 分切前卷号 |
| | | }, |
| | | // 初始化 mode 为空数组 → 页面加载时箱型无数据 |
| | | mode: [], |
| | | token: '', |
| | | barcode: '', |
| | | boxType: '', |
| | | mode: [], |
| | | index_mode: 0, |
| | | index_num: 0, |
| | | submitData: {} ,// 最终提交给后端的数据 |
| | | scanBuffer: '', // 添加扫码缓冲区 |
| | | scanTimeout: null, // 扫码超时定时器 |
| | | isScanning: false, // 扫码状态标志 |
| | | scanMode: 'tray', // 扫码模式: tray-托盘码, material-物料码 |
| | | lastFillTarget: null // 记录上次填充的目标 |
| | | |
| | | // 扫码相关 |
| | | scanBuffer: '', |
| | | scanTimeout: null, |
| | | |
| | | // 物料列表 |
| | | matList: [], |
| | | materialId: 1, |
| | | |
| | | // 弹窗相关 |
| | | showScanModal: false, |
| | | tempMaterial: { |
| | | model: '', |
| | | batch: '', |
| | | matnr: '', |
| | | rollExtent: '', |
| | | weight: '', |
| | | roughWeight: '', |
| | | joint: 0, |
| | | position: '1' // 新增:物料位置,默认值为"1"(左) |
| | | }, |
| | | |
| | | // 新增:物料位置下拉框相关数据 |
| | | positionIndex: 0, // 默认选中第一个(左) |
| | | |
| | | // 新增:控制自定义选择器显示 |
| | | showPositionPicker: false, |
| | | positionOptions: [ |
| | | { label: '1', value: '1' }, |
| | | { label: '2', value: '2' } |
| | | ] |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | POSTinfo(){ |
| | | uni.request({ |
| | | url: this.baseUrl + '/mobile/box/type/complete/auth/v2', |
| | | method:'POST', |
| | | header: { |
| | | 'Content-Type': 'application/json' |
| | | }, |
| | | success:(res)=>{ |
| | | // 接口返回后才填充箱型数据 |
| | | this.mode = res.data.data.map(item => item.boxSpecs || '').filter(item => item); |
| | | console.log('提取的箱型数据:', this.mode); |
| | | }, |
| | | fail:(err)=>{ |
| | | console.error('获取箱型失败:', err) |
| | | // 新增:切换位置选择器显示 |
| | | togglePositionPicker() { |
| | | this.showPositionPicker = !this.showPositionPicker; |
| | | }, |
| | | |
| | | // 新增:关闭位置选择器 |
| | | closePositionPicker() { |
| | | this.showPositionPicker = false; |
| | | }, |
| | | |
| | | // 新增:选择位置 |
| | | selectPosition(index) { |
| | | this.positionIndex = index; |
| | | this.tempMaterial.position = this.positionOptions[index].value; |
| | | this.closePositionPicker(); |
| | | }, |
| | | |
| | | // 初始化扫码监听 |
| | | initScanListener() { |
| | | // 在uni-app中,可以通过ref获取DOM节点,但要注意平台差异 |
| | | // 这里我们通过一个安全的方法来聚焦输入框 |
| | | this.$nextTick(() => { |
| | | const input = this.$refs.scanInput; |
| | | if (input && typeof input.focus === 'function') { |
| | | input.focus(); |
| | | } |
| | | }) |
| | | }); |
| | | }, |
| | | |
| | | // 设置扫码监听 |
| | | setupScanListener() { |
| | | // 移除之前的监听 |
| | | this.removeScanListener(); |
| | | // 聚焦扫码输入框 |
| | | focusScanInput() { |
| | | // 使用uni-app的安全方法 |
| | | this.$nextTick(() => { |
| | | const input = this.$refs.scanInput; |
| | | if (input) { |
| | | // 在uni-app中,input可能是一个组件实例 |
| | | // 我们需要获取其DOM节点或使用平台特定的方法 |
| | | // 这里我们尝试调用focus,如果不可用则跳过 |
| | | try { |
| | | if (typeof input.focus === 'function') { |
| | | input.focus(); |
| | | } else if (input.$el && typeof input.$el.focus === 'function') { |
| | | // 如果是Vue组件实例 |
| | | input.$el.focus(); |
| | | } |
| | | } catch (error) { |
| | | console.log('聚焦失败,但不影响功能:', error); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 处理扫码输入 |
| | | handleMaterialScan(e) { |
| | | const value = e.detail ? e.detail.value : e.target.value; |
| | | |
| | | // 监听键盘输入事件 |
| | | document.addEventListener('keydown', this.handleKeyDown); |
| | | }, |
| | | |
| | | // 移除监听 |
| | | removeScanListener() { |
| | | document.removeEventListener('keydown', this.handleKeyDown); |
| | | // 防抖处理 |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | } |
| | | |
| | | this.scanTimeout = setTimeout(() => { |
| | | if (value && value.trim()) { |
| | | this.parseBarcodeContent(value.trim()); |
| | | this.showScanModal = true; |
| | | this.scanBuffer = ''; // 清空缓冲区 |
| | | |
| | | // 每次打开弹窗时重置位置选择为默认值 |
| | | this.positionIndex = 0; |
| | | this.tempMaterial.position = '1'; |
| | | } |
| | | }, 300); |
| | | }, |
| | | |
| | | // 键盘事件处理 |
| | | handleKeyDown(e) { |
| | | // 排除功能键(F1-F12等) |
| | | if (e.key.length > 1 && !['Enter', 'Tab'].includes(e.key)) { |
| | | // 解析条码内容 |
| | | parseBarcodeContent(content) { |
| | | // const lines = content.split(/\\n/).filter(line => line.trim() !== ''); |
| | | const lines = content.split(' ').filter(line => line.trim().length > 0); |
| | | |
| | | // 清空临时数据 |
| | | this.tempMaterial = { |
| | | model: '', |
| | | batch: '', |
| | | matnr: '', |
| | | rollExtent: '', |
| | | weight: '', |
| | | roughWeight: '', |
| | | joint: 0, |
| | | position: '1' // 重置位置为默认值 |
| | | }; |
| | | |
| | | // 使用正则表达式提取键值对[6,7](@ref) |
| | | const extractKeyValue = (line) => { |
| | | const match = line.match(/([^:]+):(.+)/); |
| | | if (match && match.length === 3) { |
| | | return { |
| | | key: match[1].trim(), |
| | | value: match[2].trim() |
| | | }; |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | // 提取数字(包括小数)[6,7](@ref) |
| | | const extractNumber = (str) => { |
| | | const match = str.match(/[\d.]+/); |
| | | return match ? match[0] : ''; |
| | | }; |
| | | |
| | | // 遍历每一行,解析键值对 |
| | | lines.forEach(line => { |
| | | const keyValue = extractKeyValue(line); |
| | | if (keyValue) { |
| | | const { key, value } = keyValue; |
| | | |
| | | switch (key) { |
| | | case '卷号': |
| | | this.tempMaterial.model = value; |
| | | break; |
| | | case '箱号': |
| | | this.tempMaterial.batch = value; |
| | | break; |
| | | case '规格': |
| | | this.tempMaterial.matnr = value; |
| | | break; |
| | | case '长度': |
| | | this.tempMaterial.rollExtent = extractNumber(value); |
| | | break; |
| | | case '净重': |
| | | this.tempMaterial.weight = extractNumber(value); |
| | | break; |
| | | case '毛重': |
| | | this.tempMaterial.roughWeight = extractNumber(value); |
| | | break; |
| | | case '接头': |
| | | this.tempMaterial.joint = extractNumber(value) || 0; |
| | | break; |
| | | default: |
| | | // 可以处理未知键或忽略 |
| | | break; |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 确认添加物料 |
| | | confirmAddMaterial() { |
| | | this.closePositionPicker(); |
| | | if (!this.tempMaterial.model) { |
| | | uni.showToast({ |
| | | title: '卷号不能为空', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // PDA扫码通常以回车结束 |
| | | if (e.key === 'Enter') { |
| | | e.preventDefault(); // 阻止默认行为 |
| | | this.processScanCode(); |
| | | } else { |
| | | // 累积扫码字符(排除Shift等修饰键) |
| | | if (!e.ctrlKey && !e.altKey && !e.metaKey && e.key.length === 1) { |
| | | this.scanBuffer += e.key; |
| | | |
| | | // 重置超时定时器 |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | } |
| | | |
| | | // 设置超时(假设扫码间隔超过200ms表示一次扫码完成) |
| | | this.scanTimeout = setTimeout(() => { |
| | | this.processScanCode(); |
| | | }, 200); |
| | | } |
| | | } |
| | | // 检查是否已存在相同物料 |
| | | const exists = this.matList.some(item => |
| | | item.model === this.tempMaterial.model && |
| | | item.batch === this.tempMaterial.batch |
| | | ); |
| | | |
| | | if (exists) { |
| | | uni.showToast({ |
| | | title: '该物料已存在', |
| | | icon: 'none' |
| | | }); |
| | | this.closeModal(); |
| | | return; |
| | | }; |
| | | |
| | | // 添加到物料列表 |
| | | const newMaterial = { |
| | | id: this.materialId++, |
| | | ...this.tempMaterial, |
| | | position: (this.matList.length + 1).toString(), |
| | | cutting: 2, |
| | | qualified: 0 |
| | | }; |
| | | |
| | | this.matList.push(newMaterial); |
| | | |
| | | uni.showToast({ |
| | | title: '物料添加成功', |
| | | icon: 'success' |
| | | }); |
| | | |
| | | this.closeModal(); |
| | | }, |
| | | |
| | | // 处理扫码结果 |
| | | processScanCode() { |
| | | if (!this.scanBuffer) return; |
| | | // 取消添加 |
| | | cancelAddMaterial() { |
| | | this.closeModal(); |
| | | }, |
| | | |
| | | // 关闭弹窗 |
| | | closeModal() { |
| | | this.showPositionPicker = false; |
| | | this.showScanModal = false; |
| | | this.tempMaterial = { |
| | | model: '', |
| | | batch: '', |
| | | matnr: '', |
| | | rollExtent: '', |
| | | weight: '', |
| | | roughWeight: '', |
| | | joint: 0, |
| | | position: '1' // 重置位置 |
| | | }; |
| | | |
| | | const scanResult = this.scanBuffer.trim(); |
| | | this.scanBuffer = ''; |
| | | // 重置位置选择 |
| | | this.positionIndex = 0; |
| | | |
| | | // 判断扫码长度决定填充位置 |
| | | if (scanResult.length <= 12) { |
| | | // 12位以下填充到托盘编码 |
| | | this.barcode = scanResult; |
| | | this.lastFillTarget = 'tray'; |
| | | uni.showToast({ |
| | | title: '托盘码已填充', |
| | | icon: 'success', |
| | | duration: 1000 |
| | | }); |
| | | } else { |
| | | // 12位以上填充到物料信息 |
| | | this.autoFillMaterialInfo(scanResult); |
| | | this.lastFillTarget = 'material'; |
| | | uni.showToast({ |
| | | title: '物料码已填充', |
| | | icon: 'success', |
| | | duration: 1000 |
| | | }); |
| | | } |
| | | |
| | | // 清除定时器 |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | this.scanTimeout = null; |
| | | } |
| | | |
| | | // 自动失焦 |
| | | // 重新聚焦到扫码输入框 |
| | | setTimeout(() => { |
| | | this.focusState = false; |
| | | this.focusScanInput(); |
| | | }, 100); |
| | | }, |
| | | |
| | | // 自动填充物料信息 |
| | | autoFillMaterialInfo(code) { |
| | | // 尝试解析二维码内容 |
| | | if (code.includes('卷号:') && code.includes('箱号:')) { |
| | | // 如果是带格式的二维码 |
| | | const parseResult = this.parseQrCodeText(code); |
| | | this.matList = { ...this.matList, ...parseResult }; |
| | | console.log('解析后的物料信息:', parseResult); |
| | | } else { |
| | | // 如果是纯数字/条码,智能填充 |
| | | this.smartFillMaterialFields(code); |
| | | } |
| | | // 删除物料 |
| | | removeMaterial(index) { |
| | | uni.showModal({ |
| | | title: '确认删除', |
| | | content: '确定要删除这个物料吗?', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | this.matList.splice(index, 1); |
| | | uni.showToast({ |
| | | title: '删除成功', |
| | | icon: 'success' |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 智能填充物料字段 |
| | | smartFillMaterialFields(code) { |
| | | // 根据业务逻辑判断,这里假设长条码可能是卷号或箱号 |
| | | // 如果卷号为空,优先填充卷号 |
| | | if (!this.matList.model) { |
| | | this.matList.model = code; |
| | | } |
| | | // 如果卷号已有值但箱号为空,填充箱号 |
| | | else if (!this.matList.batch) { |
| | | this.matList.batch = code; |
| | | } |
| | | // 如果两者都有值,提示用户 |
| | | else { |
| | | // 检查登录状态 |
| | | checkLoginStatus() { |
| | | this.token = uni.getStorageSync('token'); |
| | | this.baseUrl = uni.getStorageSync('baseUrl'); |
| | | |
| | | if (!this.token || !this.baseUrl) { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: `扫描到长条码: ${code}\n请选择填充位置:`, |
| | | showCancel: true, |
| | | cancelText: '卷号', |
| | | confirmText: '箱号', |
| | | content: '请先登录', |
| | | showCancel: false, |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | this.matList.batch = code; |
| | | } else if (res.cancel) { |
| | | this.matList.model = code; |
| | | uni.navigateTo({ |
| | | url: '/pages/login/login' |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | return false; |
| | | } |
| | | return true; |
| | | }, |
| | | |
| | | // 解析二维码文本(用于扫货物码功能) |
| | | parseQrCodeText(qrText) { |
| | | const result = {}; |
| | | const lines = qrText.split('\n'); |
| | | // 获取箱型数据 |
| | | POSTinfo() { |
| | | if (!this.checkLoginStatus()) return; |
| | | |
| | | lines.forEach(line => { |
| | | if (line.includes('卷号:')) { |
| | | result.model = line.split('卷号:')[1]?.trim() || ''; |
| | | } |
| | | if (line.includes('箱号:')) { |
| | | result.batch = line.split('箱号:')[1]?.trim() || ''; |
| | | } |
| | | if (line.includes('规格:')) { |
| | | result.matnr = line.split('规格:')[1]?.trim() || ''; |
| | | } |
| | | // 可以根据需要添加更多字段的解析 |
| | | uni.showLoading({ |
| | | title: '加载中...' |
| | | }); |
| | | |
| | | return result; |
| | | }, |
| | | |
| | | // 手动开始扫码 |
| | | startScan() { |
| | | // 清空当前值并聚焦 |
| | | this.scanBuffer = ''; |
| | | |
| | | this.$nextTick(() => { |
| | | this.focusState = true; |
| | | |
| | | // 延迟确保input已聚焦 |
| | | setTimeout(() => { |
| | | const input = document.getElementById('pdacode'); |
| | | if (input) { |
| | | input.focus(); |
| | | input.select(); |
| | | } |
| | | }, 100); |
| | | }); |
| | | }, |
| | | |
| | | // 修改focusFn |
| | | focusFn() { |
| | | this.focusState = true; |
| | | this.scanBuffer = ''; // 聚焦时清空缓冲区 |
| | | }, |
| | | |
| | | // 保留原有的扫货物码功能 |
| | | scantwo() { |
| | | uni.scanCode({ |
| | | autoDecodeCharSet: true, |
| | | scanType: ['qrCode', 'barCode'], |
| | | uni.request({ |
| | | url: this.baseUrl + '/mobile/box/type/complete/auth/v2', |
| | | method: 'POST', |
| | | header: { |
| | | 'Content-Type': 'application/json', |
| | | 'Authorization': 'Bearer ' + this.token |
| | | }, |
| | | success: (res) => { |
| | | const qrResult = res.result.trim(); |
| | | this.autoFillMaterialInfo(qrResult); |
| | | uni.hideLoading(); |
| | | |
| | | uni.showToast({ |
| | | title: '物料信息扫码成功', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | if (res.data && res.data.code === 200 && Array.isArray(res.data.data)) { |
| | | this.mode = res.data.data |
| | | .map(item => item.boxSpecs || item.boxType || item.typeName || item.name || '') |
| | | .filter(item => item && item.trim() !== ''); |
| | | if (this.mode.length === 0) { |
| | | uni.showToast({ |
| | | title: '无可用箱型', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | } else { |
| | | uni.showToast({ |
| | | title: res.data?.message || '获取箱型失败', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.log('扫码失败:', err); |
| | | uni.hideLoading(); |
| | | console.error('获取箱型失败:', err); |
| | | uni.showToast({ |
| | | title: '扫码失败或取消', |
| | | title: '网络错误', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | |
| | | |
| | | modePickerChange(e) { |
| | | this.index_mode = e.detail.value; |
| | | this.boxType = this.mode[this.index_mode]; // 同步到boxType |
| | | this.boxType = this.mode[this.index_mode]; |
| | | }, |
| | | |
| | | numPickerChange(e) { |
| | | this.index_num = e.detail.value; |
| | | }, |
| | | |
| | | // 修改重置方法 |
| | | resetForm() { |
| | | this.barcodeNum = ''; |
| | | this.barcode = ''; |
| | | this.boxType = ''; |
| | | this.index_mode = 0; |
| | | this.index_num = 0; |
| | | this.submitData = {}; |
| | | this.mode = []; |
| | | this.lastFillTarget = null; |
| | | this.scanBuffer = ''; |
| | | |
| | | // 重置物料信息 |
| | | this.matList = { |
| | | matnr: '', |
| | | maktx: '', |
| | | batch: '', |
| | | model: '', |
| | | position: '1', |
| | | weight: '', |
| | | roughWeight: '', |
| | | anfme: 1.0, |
| | | rollExtent: '', |
| | | joint: 0, |
| | | cutting: 2, |
| | | qualified: 0, |
| | | modelFront: '', |
| | | batchFront: '' |
| | | }; |
| | | }, |
| | | |
| | | // 提交数据 |
| | | submit() { |
| | | // 校验必填项 |
| | | if (!this.checkLoginStatus()) return; |
| | | |
| | | // 验证必填项 |
| | | if (!this.barcode) { |
| | | uni.showToast({title: '请扫描托盘编码', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | |
| | | this.boxType = 999; |
| | | if (!this.boxType) { |
| | | uni.showToast({title: '请选择箱型', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | if (!this.matList.model || !this.matList.batch) { |
| | | uni.showToast({title: '请填写卷号和箱号', icon: 'none'}); |
| | | if (this.matList.length === 0) { |
| | | uni.showToast({title: '请至少添加一个物料', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | // 数据类型转换 |
| | | const matItem = { |
| | | ...this.matList, |
| | | weight: this.matList.weight ? Number(this.matList.weight) : null, |
| | | roughWeight: this.matList.roughWeight ? Number(this.matList.roughWeight) : null, |
| | | rollExtent: this.matList.rollExtent ? Number(this.matList.rollExtent) : null, |
| | | joint: this.matList.joint ? Number(this.matList.joint) : 0, |
| | | anfme: 1.0, |
| | | cutting: 2, |
| | | qualified: 0 |
| | | }; |
| | | // 验证每个物料的必填字段 |
| | | for (let i = 0; i < this.matList.length; i++) { |
| | | const mat = this.matList[i]; |
| | | if (!mat.model) { |
| | | uni.showToast({title: `物料${i+1}卷号不能为空`, icon: 'none'}); |
| | | return; |
| | | } |
| | | if (!mat.batch) { |
| | | uni.showToast({title: `物料${i+1}箱号不能为空`, icon: 'none'}); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 组装提交数据 |
| | | this.submitData = { |
| | | // 准备提交数据 |
| | | const submitData = { |
| | | barcode: this.barcode, |
| | | palletizingNo: this.palletizingNo, |
| | | boxType: this.boxType, |
| | | matList: [matItem] |
| | | matList: this.matList.map(mat => ({ |
| | | ...mat, |
| | | weight: mat.weight ? Number(mat.weight) : null, |
| | | roughWeight: mat.roughWeight ? Number(mat.roughWeight) : null, |
| | | rollExtent: mat.rollExtent ? Number(mat.rollExtent) : null, |
| | | joint: mat.joint ? Number(mat.joint) : 0, |
| | | position: mat.position // 包含位置信息 |
| | | })) |
| | | }; |
| | | |
| | | console.log('提交数据:', this.submitData); |
| | | console.log('提交数据:', submitData); |
| | | |
| | | // 提交请求 |
| | | // 发送请求 |
| | | uni.showLoading({ |
| | | title: '提交中...', |
| | | mask: true |
| | | }); |
| | | |
| | | uni.request({ |
| | | url: this.baseUrl + '/mobile/truss/comd/auth/v2', |
| | | method: 'POST', |
| | | header: {'Content-Type': 'application/json'}, |
| | | data: this.submitData, |
| | | header: { |
| | | 'Content-Type': 'application/json', |
| | | 'Authorization': 'Bearer ' + this.token |
| | | }, |
| | | data: submitData, |
| | | success: (res) => { |
| | | console.log('后端返回:', res); |
| | | uni.hideLoading(); |
| | | |
| | | if (res.data && res.data.code === 200) { |
| | | uni.showToast({title: '提交成功', icon: 'success'}); |
| | | this.resetForm(); |
| | | if (res.data.code === 200) { |
| | | uni.showToast({ |
| | | title: '提交成功', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | |
| | | // 清空表单 |
| | | setTimeout(() => { |
| | | this.resetForm(); |
| | | }, 1500); |
| | | } else { |
| | | uni.showToast({title: res.data.message || '提交失败', icon: 'none'}); |
| | | uni.showToast({ |
| | | title: res.data.msg || '提交失败', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('请求失败详情:', err); |
| | | uni.showToast({title: '网络请求失败', icon: 'none'}); |
| | | uni.hideLoading(); |
| | | console.error('提交失败:', err); |
| | | uni.showToast({ |
| | | title: '网络错误', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 重置表单 |
| | | resetForm() { |
| | | this.barcode = ''; |
| | | this.boxType = ''; |
| | | this.index_mode = 0; |
| | | this.matList = []; |
| | | this.materialId = 1; |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* 全局样式 */ |
| | | |
| | | /* 物料位置选择器样式 */ |
| | | .full-width { |
| | | grid-column: 1 / -1; /* 占满整行 */ |
| | | margin-top: 8rpx; |
| | | position: relative; |
| | | z-index: 100; /* 确保在弹窗内部层级正常 */ |
| | | } |
| | | |
| | | .position-selector { |
| | | width: 200rpx; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .position-picker { |
| | | width: 200rpx; |
| | | } |
| | | |
| | | .position-display { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 8rpx 16rpx; |
| | | border: 1rpx solid #e5e7eb; |
| | | border-radius: 6rpx; |
| | | background-color: #ffffff; |
| | | color: #333; |
| | | font-size: 28rpx; |
| | | min-height: 40rpx; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .placeholder { |
| | | color: #999; |
| | | } |
| | | |
| | | .position-arrow { |
| | | font-size: 20rpx; |
| | | color: #666; |
| | | margin-left: 8rpx; |
| | | } |
| | | /* 全局样式 - 保持原有风格 */ |
| | | .sockpage { |
| | | background-color: #f5f7fa; |
| | | padding: 24rpx; |
| | | padding: 20rpx; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | /* 表单列容器 */ |
| | | .column { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24rpx; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | /* 表单卡片 */ |
| | | .form-card { |
| | | background: #ffffff; |
| | | border-radius: 12rpx; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06); |
| | | padding: 24rpx; |
| | | transition: all 0.2s ease; |
| | | border-radius: 8rpx; |
| | | padding: 20rpx; |
| | | box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | /* 物料信息卡片特殊样式 */ |
| | | .form-row { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | .label { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: 500; |
| | | min-width: 140rpx; |
| | | } |
| | | |
| | | .required { |
| | | color: #e64340; |
| | | margin-right: 4rpx; |
| | | } |
| | | |
| | | .picker-wrap { |
| | | flex: 1; |
| | | } |
| | | |
| | | .input-box { |
| | | border: 1rpx solid #e5e7eb; |
| | | border-radius: 6rpx; |
| | | padding: 16rpx 20rpx; |
| | | background: #fff; |
| | | } |
| | | |
| | | .input-placeholder { |
| | | color: #999; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .input-value { |
| | | color: #333; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | /* 物料信息卡片 */ |
| | | .material-card { |
| | | padding: 0; |
| | | } |
| | |
| | | .card-title { |
| | | font-size: 30rpx; |
| | | font-weight: 500; |
| | | color: #1f2937; |
| | | padding: 24rpx 24rpx 16rpx; |
| | | color: #333; |
| | | padding: 20rpx 20rpx 16rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | /* 表单行 */ |
| | | .form-row { |
| | | /* 物料列表 */ |
| | | .material-list { |
| | | padding: 0 20rpx; |
| | | } |
| | | |
| | | .material-item { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .material-header { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | } |
| | | |
| | | /* 标签样式 */ |
| | | .label { |
| | | font-size: 28rpx; |
| | | color: #374151; |
| | | font-weight: 500; |
| | | min-width: 140rpx; |
| | | } |
| | | |
| | | /* 必填项标红 */ |
| | | .required { |
| | | color: #ef4444; |
| | | margin-right: 4rpx; |
| | | } |
| | | |
| | | /* 选择器容器 */ |
| | | .picker-wrap { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 16rpx; |
| | | padding-bottom: 12rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | /* 选择器hover态 */ |
| | | .picker-hover { |
| | | background-color: #f9fafb; |
| | | border-radius: 8rpx; |
| | | } |
| | | |
| | | /* 输入框容器 */ |
| | | .input-box { |
| | | flex: 1; |
| | | .material-title { |
| | | font-size: 28rpx; |
| | | padding: 16rpx 12rpx; |
| | | border: 1rpx solid #e5e7eb; |
| | | border-radius: 8rpx; |
| | | transition: border-color 0.2s ease; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | |
| | | .input-box:focus-within { |
| | | border-color: #f97316; |
| | | .material-actions { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | | /* 输入值样式 */ |
| | | .input-value { |
| | | color: #1f2937; |
| | | } |
| | | |
| | | /* 扫码图标 */ |
| | | .scan-icon { |
| | | .action-btn { |
| | | font-size: 24rpx; |
| | | margin-left: 8rpx; |
| | | color: #6b7280; |
| | | color: #e64340; |
| | | padding: 6rpx 12rpx; |
| | | border-radius: 4rpx; |
| | | background: #fff0f0; |
| | | } |
| | | |
| | | /* 物料信息网格布局 */ |
| | | /* 物料网格 */ |
| | | .material-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 20rpx; |
| | | padding: 24rpx; |
| | | gap: 16rpx; |
| | | } |
| | | |
| | | /* 物料信息项 */ |
| | | .material-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8rpx; |
| | | } |
| | | |
| | | /* 物料标签 */ |
| | | .material-label { |
| | | font-size: 26rpx; |
| | | color: #4b5563; |
| | | color: #666; |
| | | } |
| | | |
| | | /* 物料输入框 */ |
| | | .material-input { |
| | | font-size: 28rpx; |
| | | color: #1f2937; |
| | | padding: 16rpx 12rpx; |
| | | color: #333; |
| | | padding: 12rpx; |
| | | border: 1rpx solid #e5e7eb; |
| | | border-radius: 8rpx; |
| | | border-radius: 6rpx; |
| | | background-color: #ffffff; |
| | | transition: border-color 0.2s ease; |
| | | } |
| | | |
| | | .material-input:focus { |
| | | border-color: #f97316; |
| | | outline: none; |
| | | /* 空状态 */ |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 40rpx 20rpx; |
| | | } |
| | | |
| | | /* 按钮组 */ |
| | | .btn-group { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | justify-content: center; |
| | | margin-top: 16rpx; |
| | | padding: 8rpx 0; |
| | | .empty-icon { |
| | | font-size: 60rpx; |
| | | display: block; |
| | | margin-bottom: 20rpx; |
| | | opacity: 0.5; |
| | | } |
| | | |
| | | /* 操作按钮通用样式 */ |
| | | .operate-btn { |
| | | flex: 1; |
| | | max-width: 200rpx; |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | .empty-text { |
| | | display: block; |
| | | font-size: 28rpx; |
| | | font-weight: 500; |
| | | border-radius: 12rpx; |
| | | border: none; |
| | | transition: all 0.2s ease; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #999; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .empty-tip { |
| | | display: block; |
| | | font-size: 24rpx; |
| | | color: #ccc; |
| | | } |
| | | |
| | | /* 扫码按钮 */ |
| | | .scan-btn { |
| | | background-color: #3b82f6; |
| | | color: #ffffff; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 10rpx; |
| | | padding: 20rpx; |
| | | margin: 20rpx 20rpx 20rpx; |
| | | background: #f0f7ff; |
| | | border: 1rpx dashed #1989fa; |
| | | border-radius: 8rpx; |
| | | color: #1989fa; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .scan-btn:active { |
| | | background-color: #2563eb; |
| | | transform: scale(0.98); |
| | | .scan-icon { |
| | | font-size: 36rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | /* 提交按钮 */ |
| | | .submit-btn { |
| | | background-color: #f97316; |
| | | color: #ffffff; |
| | | /* 按钮组 */ |
| | | .btn-group { |
| | | margin-top: 20rpx; |
| | | padding: 8rpx 0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .submit-btn:active { |
| | | background-color: #ea580c; |
| | | transform: scale(0.98); |
| | | .operate-btn { |
| | | width: 200rpx; |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | font-size: 30rpx; |
| | | font-weight: 500; |
| | | border-radius: 8rpx; |
| | | border: none; |
| | | background: #333; |
| | | color: #fff; |
| | | } |
| | | |
| | | /* 适配小屏幕 */ |
| | | @media (max-width: 375px) { |
| | | /* 弹窗样式 */ |
| | | .modal-mask { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .modal-content { |
| | | background: #fff; |
| | | border-radius: 12rpx; |
| | | width: 600rpx; |
| | | max-width: 90%; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .modal-header { |
| | | padding: 30rpx 30rpx 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .modal-title { |
| | | font-size: 32rpx; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | |
| | | .modal-body { |
| | | padding: 30rpx; |
| | | } |
| | | |
| | | .scan-preview { |
| | | margin-bottom: 20rpx; |
| | | padding-bottom: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .scan-label { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | display: block; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .scan-code { |
| | | font-size: 32rpx; |
| | | color: #333; |
| | | font-weight: bold; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .preview-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 15rpx; |
| | | } |
| | | |
| | | .preview-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .preview-label { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | } |
| | | |
| | | .preview-value { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .modal-footer { |
| | | display: flex; |
| | | border-top: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .modal-btn { |
| | | flex: 1; |
| | | height: 90rpx; |
| | | line-height: 90rpx; |
| | | font-size: 30rpx; |
| | | border: none; |
| | | background: transparent; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | .modal-btn.cancel { |
| | | color: #999; |
| | | border-right: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .modal-btn.confirm { |
| | | color: #1989fa; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* 自定义选择器样式 */ |
| | | .custom-picker-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 9999; /* 确保在最上层 */ |
| | | } |
| | | |
| | | .custom-picker-content { |
| | | background-color: #ffffff; |
| | | border-radius: 12rpx; |
| | | width: 600rpx; |
| | | max-width: 80%; |
| | | max-height: 70vh; |
| | | overflow: hidden; |
| | | box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15); |
| | | animation: fadeIn 0.3s ease; |
| | | } |
| | | |
| | | @keyframes fadeIn { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateY(20rpx); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | |
| | | .custom-picker-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .custom-picker-title { |
| | | font-size: 32rpx; |
| | | font-weight: 500; |
| | | color: #333; |
| | | } |
| | | |
| | | .custom-picker-close { |
| | | font-size: 40rpx; |
| | | color: #999; |
| | | line-height: 1; |
| | | padding: 0 10rpx; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .custom-picker-list { |
| | | max-height: 50vh; |
| | | overflow-y: auto; |
| | | padding: 0; |
| | | } |
| | | |
| | | .custom-picker-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 25rpx 30rpx; |
| | | border-bottom: 1rpx solid #f5f5f5; |
| | | cursor: pointer; |
| | | transition: background-color 0.2s; |
| | | } |
| | | |
| | | .custom-picker-item:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .custom-picker-item:hover { |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | .custom-picker-item.selected { |
| | | background-color: #f0f7ff; |
| | | color: #1989fa; |
| | | } |
| | | |
| | | .custom-picker-text { |
| | | font-size: 30rpx; |
| | | } |
| | | |
| | | .custom-picker-check { |
| | | font-size: 28rpx; |
| | | color: #1989fa; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | /* 弹窗样式调整,确保层级关系 */ |
| | | .modal-mask { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 1000; /* 弹窗基础层级 */ |
| | | } |
| | | |
| | | .modal-content { |
| | | position: relative; /* 确保子元素相对定位 */ |
| | | z-index: 1001; /* 比蒙版高一级 */ |
| | | background: #fff; |
| | | border-radius: 12rpx; |
| | | width: 600rpx; |
| | | max-width: 90%; |
| | | overflow: visible; /* 改为 visible 允许选择器弹出 */ |
| | | } |
| | | |
| | | /* 响应式适配 */ |
| | | @media (max-width: 750rpx) { |
| | | .material-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .operate-btn { |
| | | max-width: 160rpx; |
| | | height: 72rpx; |
| | | line-height: 72rpx; |
| | | font-size: 26rpx; |
| | | .preview-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .modal-content { |
| | | width: 90%; |
| | | } |
| | | .custom-picker-content { |
| | | width: 90%; |
| | | max-width: 90%; |
| | | } |
| | | |
| | | .custom-picker-item { |
| | | padding: 20rpx 25rpx; |
| | | } |
| | | } |
| | | </style> |