| | |
| | | <input |
| | | id="pdacode" |
| | | type="text" |
| | | ref="barcodeInput" |
| | | v-model="barcode" |
| | | placeholder="请扫码(12位以下为托盘码)" |
| | | :focus="focusState" |
| | | @focus="focusFn" |
| | | @blur="focusState = false" |
| | | placeholder="请扫码" |
| | | /> |
| | | </view> |
| | | </view> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <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="textareainput"> |
| | | <textarea |
| | | style="max-height: 15px;overflow: hidden;" |
| | | id="pdacode" |
| | | type="text" |
| | | v-model="thingCode" |
| | | placeholder="请扫码" |
| | | @input="handleBarcodeInput" |
| | | /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 物料信息输入栏(对应后端MatList) --> |
| | | <view class="form-card material-card"> |
| | | <view class="card-title">物料信息</view> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 操作按钮组 --> |
| | | <view class="btn-group"> |
| | | <button class="operate-btn scan-btn" @click="startScan">开始扫码</button> |
| | | <button class="operate-btn submit-btn" @click="submit">成品入库</button> |
| | | </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 为空,调用接口后才填充数据 |
| | | 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; |
| | | } |
| | | }, |
| | | |
| | | data() { |
| | | return { |
| | | thingCode:'', |
| | | focusState: false, |
| | | baseUrl: '', |
| | | barcodeNum:'', |
| | | barcode: '', // 托盘码 |
| | | palletizingNo: '1', // 码垛位编号 |
| | | boxType: '', // 木箱类型 |
| | | token: '', |
| | | barcode: '', |
| | | palletizingNo: '1', |
| | | boxType: '', |
| | | matList: { |
| | | matnr: '', // 规格 |
| | | maktx: '', // 物料名称 |
| | | batch: '', // 木箱编号 |
| | | model: '', // 卷编号 |
| | | position: '1', // 木箱码垛位置 |
| | | weight: '', // 净重 |
| | | roughWeight: '', // 毛重 |
| | | anfme: 1.0, // 默认值1.0 |
| | | rollExtent: '', // 卷长度 |
| | | joint: 0, // 接头 |
| | | matnr: '', |
| | | maktx: '', |
| | | batch: '', |
| | | model: '', |
| | | position: '1', |
| | | weight: '', |
| | | roughWeight: '', |
| | | anfme: 1.0, |
| | | rollExtent: '', |
| | | joint: 0, |
| | | cutting: 2, |
| | | qualified: 0, |
| | | modelFront: '', // 分切前箱编号 |
| | | batchFront: '' ,// 分切前卷号 |
| | | modelFront: '', |
| | | batchFront: '' |
| | | }, |
| | | // 初始化 mode 为空数组 → 页面加载时箱型无数据 |
| | | mode: [], |
| | | index_mode: 0, |
| | | index_num: 0, |
| | | submitData: {} ,// 最终提交给后端的数据 |
| | | scanBuffer: '', // 添加扫码缓冲区 |
| | | scanTimeout: null, // 扫码超时定时器 |
| | | isScanning: false, // 扫码状态标志 |
| | | scanMode: 'tray', // 扫码模式: tray-托盘码, material-物料码 |
| | | lastFillTarget: null // 记录上次填充的目标 |
| | | submitData: {}, |
| | | scanBuffer: '', |
| | | scanTimeout: null, |
| | | lastScanTime: 0, |
| | | isProcessingScan: false |
| | | } |
| | | }, |
| | | 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) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 设置扫码监听 |
| | | setupScanListener() { |
| | | // 移除之前的监听 |
| | | this.removeScanListener(); |
| | | // 检查登录状态 |
| | | checkLoginStatus() { |
| | | this.token = uni.getStorageSync('token'); |
| | | this.baseUrl = uni.getStorageSync('baseUrl'); |
| | | |
| | | // 监听键盘输入事件 |
| | | document.addEventListener('keydown', this.handleKeyDown); |
| | | }, |
| | | |
| | | // 移除监听 |
| | | removeScanListener() { |
| | | document.removeEventListener('keydown', this.handleKeyDown); |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | } |
| | | }, |
| | | |
| | | // 键盘事件处理 |
| | | handleKeyDown(e) { |
| | | // 排除功能键(F1-F12等) |
| | | if (e.key.length > 1 && !['Enter', 'Tab'].includes(e.key)) { |
| | | 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); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 处理扫码结果 |
| | | processScanCode() { |
| | | if (!this.scanBuffer) return; |
| | | |
| | | const scanResult = this.scanBuffer.trim(); |
| | | this.scanBuffer = ''; |
| | | |
| | | // 判断扫码长度决定填充位置 |
| | | 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; |
| | | }, 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); |
| | | } |
| | | }, |
| | | |
| | | // 智能填充物料字段 |
| | | smartFillMaterialFields(code) { |
| | | // 根据业务逻辑判断,这里假设长条码可能是卷号或箱号 |
| | | // 如果卷号为空,优先填充卷号 |
| | | if (!this.matList.model) { |
| | | this.matList.model = code; |
| | | } |
| | | // 如果卷号已有值但箱号为空,填充箱号 |
| | | else if (!this.matList.batch) { |
| | | this.matList.batch = code; |
| | | } |
| | | // 如果两者都有值,提示用户 |
| | | else { |
| | | 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(); |
| | | console.log('箱型接口返回:', res.data); |
| | | |
| | | uni.showToast({ |
| | | title: '物料信息扫码成功', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | if (res.data && res.data.code === 200) { |
| | | if (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() !== ''); |
| | | |
| | | console.log('提取的箱型列表:', this.mode); |
| | | |
| | | if (this.mode.length === 0) { |
| | | uni.showToast({ |
| | | title: '无可用箱型', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | } else { |
| | | console.warn('返回数据格式不符,不是数组:', res.data.data); |
| | | 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]; |
| | | console.log('选择的箱型:', this.boxType); |
| | | }, |
| | | |
| | | 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 = ''; |
| | | handleBarcodeInput(e) { |
| | | // 在uni-app中,textarea的input事件可能是e.detail.value |
| | | const value = e.detail ? e.detail.value : e.target.value; |
| | | this.thingCode = value; |
| | | this.parseBarcodeContent(value); |
| | | }, |
| | | |
| | | // 重置物料信息 |
| | | parseBarcodeContent(content) { |
| | | if (!content) return; |
| | | |
| | | // 按行分割内容 |
| | | const lines = content.split('\n').filter(line => line.trim() !== ''); |
| | | |
| | | console.log('解析的行数据:', lines); |
| | | |
| | | // 根据你提供的样例格式解析 |
| | | // 格式为: |
| | | // 2511080305A22B6 (卷号) |
| | | // Y251113764 (箱号) |
| | | // 5μm×1050mm (规格) |
| | | // 8700 m (长度) |
| | | // 418.0 kg (净重) |
| | | // 441.0 kg (毛重) |
| | | // 0 个 (接头) |
| | | |
| | | if (lines.length >= 7) { |
| | | // 卷号 (第一行) |
| | | this.matList.model = lines[0].trim(); |
| | | |
| | | // 箱号 (第二行) |
| | | this.matList.batch = lines[1].trim(); |
| | | |
| | | // 规格 (第三行) |
| | | this.matList.matnr = lines[2].trim(); |
| | | |
| | | // 长度 (第四行) - 提取数字部分 |
| | | const lengthMatch = lines[3].match(/([\d.]+)/); |
| | | if (lengthMatch) { |
| | | this.matList.rollExtent = lengthMatch[1]; |
| | | } |
| | | |
| | | // 净重 (第五行) - 提取数字部分 |
| | | const weightMatch = lines[4].match(/([\d.]+)/); |
| | | if (weightMatch) { |
| | | this.matList.weight = weightMatch[1]; |
| | | } |
| | | |
| | | // 毛重 (第六行) - 提取数字部分 |
| | | const roughWeightMatch = lines[5].match(/([\d.]+)/); |
| | | if (roughWeightMatch) { |
| | | this.matList.roughWeight = roughWeightMatch[1]; |
| | | } |
| | | |
| | | // 接头 (第七行) - 提取数字部分 |
| | | const jointMatch = lines[6].match(/([\d.]+)/); |
| | | if (jointMatch) { |
| | | this.matList.joint = jointMatch[1]; |
| | | } |
| | | |
| | | console.log('解析后的数据:', this.matList); |
| | | } else if (lines.length > 0) { |
| | | // 如果行数不够,也可以尝试智能匹配 |
| | | this.autoMatchFields(lines); |
| | | } |
| | | }, |
| | | |
| | | // 智能匹配方法,用于处理可能的不规则数据 |
| | | autoMatchFields(lines) { |
| | | lines.forEach((line, index) => { |
| | | line = line.trim(); |
| | | |
| | | // 根据内容特征匹配 |
| | | if (index === 0 && !this.matList.model) { |
| | | // 假设第一行总是卷号 |
| | | this.matList.model = line; |
| | | } else if (index === 1 && !this.matList.batch) { |
| | | // 假设第二行总是箱号 |
| | | this.matList.batch = line; |
| | | } else if (line.includes('μm') || line.includes('mm') || line.includes('×')) { |
| | | // 包含规格特征的 |
| | | this.matList.matnr = line; |
| | | } else if (line.includes('m') && line.match(/[\d.]+ m/)) { |
| | | // 长度特征 |
| | | const match = line.match(/([\d.]+)/); |
| | | if (match) this.matList.rollExtent = match[1]; |
| | | } else if (line.includes('kg') && line.match(/[\d.]+ kg/)) { |
| | | // 重量特征,需要区分净重和毛重 |
| | | const match = line.match(/([\d.]+)/); |
| | | if (match) { |
| | | const weightValue = match[1]; |
| | | if (!this.matList.weight) { |
| | | this.matList.weight = weightValue; |
| | | } else if (!this.matList.roughWeight) { |
| | | this.matList.roughWeight = weightValue; |
| | | } |
| | | } |
| | | } else if (line.includes('个') && line.match(/[\d.]+ 个/)) { |
| | | // 接头特征 |
| | | const match = line.match(/([\d.]+)/); |
| | | if (match) this.matList.joint = match[1]; |
| | | } |
| | | }); |
| | | }, |
| | | // 重置表单 |
| | | resetForm() { |
| | | this.barcode = ''; |
| | | this.boxType = ''; |
| | | this.index_mode = 0; |
| | | this.matList = { |
| | | matnr: '', |
| | | maktx: '', |
| | | batch: '', |
| | | model: '', |
| | | position: '1', |
| | | position: '1', |
| | | weight: '', |
| | | roughWeight: '', |
| | | anfme: 1.0, |
| | | anfme: 1.0, |
| | | rollExtent: '', |
| | | joint: 0, |
| | | cutting: 2, |
| | | qualified: 0, |
| | | joint: 0, |
| | | cutting: 2, |
| | | qualified: 0, |
| | | modelFront: '', |
| | | batchFront: '' |
| | | }; |
| | | this.scanBuffer = ''; |
| | | this.focusState = false; |
| | | }, |
| | | |
| | | // 提交数据 |
| | | submit() { |
| | | // 校验必填项 |
| | | // 1. 验证登录 |
| | | if (!this.checkLoginStatus()) return; |
| | | |
| | | // 2. 验证必填项 |
| | | if (!this.barcode) { |
| | | uni.showToast({title: '请扫描托盘编码', icon: 'none'}); |
| | | return; |
| | |
| | | return; |
| | | } |
| | | |
| | | if (!this.matList.model || !this.matList.batch) { |
| | | uni.showToast({title: '请填写卷号和箱号', icon: 'none'}); |
| | | if (!this.matList.model) { |
| | | uni.showToast({title: '请填写卷号', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | // 数据类型转换 |
| | | if (!this.matList.batch) { |
| | | uni.showToast({title: '请填写箱号', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | // 3. 准备数据 |
| | | 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 |
| | | joint: this.matList.joint ? Number(this.matList.joint) : 0 |
| | | }; |
| | | |
| | | // 组装提交数据 |
| | | this.submitData = { |
| | | const submitData = { |
| | | barcode: this.barcode, |
| | | palletizingNo: this.palletizingNo, |
| | | boxType: this.boxType, |
| | | matList: [matItem] |
| | | }; |
| | | |
| | | console.log('提交数据:', this.submitData); |
| | | console.log('提交数据:', submitData); |
| | | |
| | | // 提交请求 |
| | | // 4. 发送请求 |
| | | 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(); |
| | | console.log('提交返回:', res.data); |
| | | |
| | | 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.message || '提交失败', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('请求失败详情:', err); |
| | | uni.showToast({title: '网络请求失败', icon: 'none'}); |
| | | uni.hideLoading(); |
| | | console.error('提交失败:', err); |
| | | uni.showToast({ |
| | | title: '网络错误', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | |
| | | padding: 24rpx; |
| | | transition: all 0.2s ease; |
| | | } |
| | | |
| | | .textareainput{ |
| | | width: 60%; |
| | | height: 15px; |
| | | font-size: 28rpx; |
| | | padding: 16rpx 12rpx; |
| | | border: 1rpx solid #e5e7eb; |
| | | border-radius: 8rpx; |
| | | transition: border-color 0.2s ease; |
| | | } |
| | | /* 物料信息卡片特殊样式 */ |
| | | .material-card { |
| | | padding: 0; |
| | | } |
| | | |
| | | .card-title { |
| | | font-size: 30rpx; |
| | | font-weight: 500; |
| | |
| | | |
| | | /* 物料输入框 */ |
| | | .material-input { |
| | | width: 45%; |
| | | font-size: 28rpx; |
| | | color: #1f2937; |
| | | padding: 16rpx 12rpx; |