font-weight失效移动安卓处理方法及原因

文章详细介绍了在移动安卓设备上遇到font-weight样式不生效的问题,分析了原因并提出了解决方案,包括使用特定的字体格式或者调整CSS属性等方法。

font-weight失效移动安卓处理方法 转载其他
博客地址:https://blog.youkuaiyun.com/weixin_41697143/article/details/104517239

<template> <view class="container"> <view class="banner-section"> <image class="banner-image" src="/static/IntegrityCulture.png" /> </view> <view class="title">廉洁文化资讯</view> <view class="news-list"> <scroll-view scroll-y="true" :enhanced="true" :use-passive="true" :show-scrollbar="false" class="scroll-container" > <view v-for="item in newsList" :key="item.id" class="news-item" @click="navigateToDetail(item)" > <view class="news-content"> <text class="news-title">{{ item.title }}</text> <text class="news-summary">{{ item.summary }}</text> <text class="news-date">{{ item.publishDate }}</text> </view> <view class="status-box" :class="item.read ? 'read' : 'unread'" > {{ item.read ? '已阅' : '未阅' }} </view> </view> </scroll-view> </view> </view> </template> <script setup> import { ref, onMounted } from 'vue'; const newsList = ref([]); const fetchData = () => { setTimeout(() => { newsList.value = [ { id: 1, title: '廉洁从业,从我做起', summary: '廉洁从业是每一位员工应尽的责任和义务,本文介绍了公司廉洁文化建设的相关要求和实践。', publishDate: '2024-09-10', read: true }, { id: 2, title: '加强反腐倡廉,构建阳光企业', summary: '公司持续加强反腐倡廉工作,推动形成风清气正的企业文化氛围。', publishDate: '2024-09-08', read: false }, { id: 3, title: '廉洁文化进班组,清风正气润人心', summary: '公司组织廉洁文化进班组活动,通过宣传、培训等方式提升员工廉洁意识。', publishDate: '2024-09-05', read: false } ]; }, 500); }; onMounted(() => { fetchData(); // 添加被动事件监听器(可选) setTimeout(() => { const scrollEl = uni.createSelectorQuery().select('.scroll-container'); scrollEl.boundingClientRect(data => { if (data) { console.log('滚动容器高度:', data.height); } }).exec(); }, 300); }); const navigateToDetail = (item) => { uni.navigateTo({ url: `/pages/IntegrityCulture/IntegrityCultureDetail/IntegrityCultureDetail?id=${item.id}` }); }; </script> <style scoped> /* 被动事件优化 */ scroll-view { touch-action: pan-y; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; } .container { padding: 30rpx; background-color: #f9f9f9; height: 100vh; overflow: hidden; box-sizing: border-box; } .banner-section { margin-bottom: 40rpx; border-radius: 16rpx; overflow: hidden; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1); } .banner-image { width: 100%; height: 320rpx; display: block; } .title { font-size: 36rpx; font-weight: bold; text-align: center; margin-bottom: 40rpx; } .news-list { background-color: #fff; border-radius: 16rpx; overflow: hidden; height: calc(100vh - 500rpx); min-height: 300rpx; } .scroll-container { height: 100%; } .news-item { padding: 30rpx; position: relative; display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 1rpx solid #eee; } .news-content { flex: 1; padding-right: 20rpx; } .news-title { font-size: 32rpx; font-weight: bold; color: #333; display: block; margin-bottom: 10rpx; } .news-summary { font-size: 28rpx; color: #666; display: block; margin-bottom: 10rpx; line-height: 1.5; } .news-date { font-size: 24rpx; color: #999; display: block; } .status-box { padding: 8rpx 16rpx; font-size: 24rpx; border-radius: 8rpx; min-width: 80rpx; text-align: center; } .status-box.read { background-color: rgba(76, 175, 80, 0.1); color: #4caf50; } .status-box.unread { background-color: rgba(244, 67, 54, 0.1); color: #f44336; } /* 移动设备优化 */ @media (max-height: 600px) { .news-list { height: calc(100vh - 450rpx); } } </style> 我想要news-list板块内部滚动不影响页面其他内容,同时去掉滚动条
08-15
<template> <!--首次加载数据效果--> <up-loading-page loadingText="" :loading="onloading" bgColor="#fff" iconSize="80px" zIndex="999" image="https://m.nocexpo.com/statics/js/modules/h5/jinn/loading.gif"> </up-loading-page> <!-- <root> --> <root v-if="!onloading"> <gb :message="gbConfig.message"></gb> <view class="container" :style="`height: ${screenArInfo.middleHeight}px;overflow:hidden;`"> <view scroll-y class="scroll-view"> <view class="head" :style="`width:${headSize.right}px`"> <view class="first"> <view class="lan"></view> <view class="first-c"> <view>仅看差异</view> <view class="switch-container"> <switch :checked="showOnlyDiff" @change="toggleDiffMode" color="rgba(16, 98, 202, 1)" style="transform:scale(0.7)"></switch> </view> </view> <view class="lan"></view> </view> <view class="data"> <view class="data-c"> <view class="item" v-for="(product, index) in products" :key="index" :class="dingFlag && index === 0?'item_first':''"> <!--固定右侧的小白条--> <view class="ding-right"></view> <view :class="`item-c item-${index}`"> <view class="icon"> <view class="din" @click.stop="toggleDing(index)"> <pic v-if="dingFlag && index === 0" src="icon_ding_active.png" prefix="image" cstyle="width:25.44rpx;height:25.42rpx" /> <pic v-else src="icon_ding.png" prefix="image" cstyle="width:25rpx;height:25rpx" /> {{ dingFlag && index === 0 ? '钉住' : '钉在左侧' }} </view> <!-- 使用view包装pic组件以确保事件能正确触发 --> <view @click.stop="removeProduct(index)" class="close"> <pic src="icon_close.png" prefix="image" cstyle="width:16.45rpx;height:16.01rpx" /> </view> </view> <view class="title"> {{ product.seriesName }} </view> <view class="grey"> <!-- {{ product.brand }} --> {{ product.name }} </view> </view> </view> <!-- <view class="last" @click="addProduct"> <view>添加机型</view> <view class="i-img"> <pic src="icon_add.png" prefix="image" cstyle="width:34rpx;height:34rpx" /> </view> </view>--> </view> </view> </view> <!-- <view class="hr"></view> --> <view class="data-list"> <view class="datas"> <view class="group-name" :style="`width:${headSize.right-5}px`"> <view>参数</view> </view> <view class="group-data" :style="`width:${headSize.right-5}px`"> <view class="data"> <view class="item"> <view class="d">品牌</view> <view class="d" v-for="(product, index) in products" :key="index" :class="dingFlag && index==0?'item_first':''"> {{ product.brand }} </view> </view> <view class="item"> <view class="d">价格</view> <view class="d" v-for="(product, index) in products" :class="dingFlag && index==0?'item_first':''"> {{ convertPrice(product) }} </view> </view> </view> </view> </view> <view class="datas" v-for="(group, groupIndex) in filteredAttributeGroups" :key="groupIndex"> <view class="group-name" :style="`width:${headSize.right-5}px`"> <view>{{ group.groupName }}</view> </view> <view class="group-data" :style="`width:${headSize.right-5}px`"> <view class="data"> <view class="item" v-for="(attr, attrIndex) in group.attributes" :key="attrIndex"> <view class="d">{{ attr.attrName }}</view> <view class="d" v-for="(product, productIndex) in products" :key="productIndex" :class="`${attr.isDiff?'diff':''} ${dingFlag && productIndex==0?'item_first':''}`"> {{ getProductAttributeValue(product, group.groupName, attr.attrName) }} </view> </view> </view> </view> </view> </view> <view v-if="!screenArInfo.boolStripe" style="height: 20rpx;"></view> </view> </view> </root> </template> <script setup> import { ref, reactive, computed, onMounted } from "vue" import { useStore } from 'vuex'; import infoApi from '@/api/product/equipment/info.js'; import utilApi from "@/utils/util.js"; import classifyApi from '@/api/productseries.js'; const gbConfig = ref({ psize: {}, message: { type: 1, TitleText: "设备参数", url: "pages/Choose/parameter/index" } }) const onloading = ref(true); const dingFlag = ref(false); const showOnlyDiff = ref(false); // 控制是否仅显示差异 const products = ref([]); const attributeGroups = ref([]); const screenArInfo = computed(() => { return useStore().getters.getScreenInfo }) const headSize = ref({}); const productIds = ref(''); // 计算属性,根据showOnlyDiff过滤属性组 const filteredAttributeGroups = computed(() => { if (!showOnlyDiff.value) { // 如果不显示差异,则返回所有属性组 return attributeGroups.value; } else { // 如果仅显示差异,则过滤掉isDiff为false的属性组和属性 return attributeGroups.value .filter(group => group.isDiff) // 过滤属性组 .map(group => { // 过滤属性 const filteredAttributes = group.attributes.filter(attr => attr.isDiff); return { ...group, attributes: filteredAttributes }; }) .filter(group => group.attributes.length > 0); // 过滤掉没有属性的组 } }); // 根据系列id获取所有型号id const dataAll = ref([]); const IDS = ref(''); const getSeriesId = async (id) => { const params = { seriesId: id, } try { const res = await classifyApi.getAllId(params); if (res.data.code === 0) { console.log(res.data.list, 'res---'); // 检查数据结构,确保products正确赋值 if (Array.isArray(res.data.list)) { // 假设res.data.list中的每个item需要映射到products的格式 // 创建一个临时数组存储转换后的数据 const formattedProducts = []; // 遍历响应数据 res.data.list.forEach(item => { // 检查item是否包含products数组 if (item.products && Array.isArray(item.products)) { // 将每个product添加到formattedProducts中,并确保包含模板中使用的属性 item.products.forEach(product => { // 确保产品对象包含必要的属性,如seriesName, name, brand等 formattedProducts.push({ id: product.id, seriesName: product.seriesName || item.seriesName || '未知系列', name: product.name || '未知型号', brand: product.brand || '未知品牌', quoted: product.quoted || 2, // 默认暂无报价 price: product.price || 0, attributes: product.attributes || {} }); }); } else { // 如果item本身就是产品对象,则直接添加 formattedProducts.push({ id: item.id, seriesName: item.seriesName || '未知系列', name: item.name || '未知型号', brand: item.brand || '未知品牌', quoted: item.quoted || 2, price: item.price || 0, attributes: item.attributes || {} }); } }); // 赋值给products products.value = formattedProducts; // 重新生成attributeGroups if (formattedProducts.length > 0) { regenerateAttributeGroups(); } console.log('处理后的产品数据:', products.value); } // 计算高度 setTimeout(() => { getHeight() }, 100); } onloading.value = false; // 加载完成后关闭加载状态 } catch (error) { console.error('获取系列型号ID失败:', error); onloading.value = false; // 出错时也关闭加载状态 } } /** * 切换差异显示模式 */ function toggleDiffMode(event) { showOnlyDiff.value = event.detail.value; } /** * 添加产品 */ function addProduct() { uni.navigateTo({ url: '/pages/product/equipment/list/list' }) } /** * 删除指定索引的产品 */ function removeProduct(index) { // 检查产品数量是否大于1,否则不允许删除 if (products.value.length <= 1) { console.log('至少保留一个设备'); // 这里可以添加用户提示,比如使用uni.showToast uni.showToast({ title: '至少保留一个设备', icon: 'none', duration: 2000 }); return; } // 检查索引是否有效 if (index < 0 || index >= products.value.length) { console.error('删除型号无效:', index); return; } // 从products数组中删除指定索引的产品 products.value.splice(index, 1); console.log('products after removal:', products.value); // 重新生成attributeGroups数据 regenerateAttributeGroups(); // 确保onloading状态正确(在某些情况下可能需要) onloading.value = false; } /** * 钉住/取消钉住功能 * @param {Number} index - 要钉住的产品索引 */ async function toggleDing(index) { // 如果点击的是第一个产品(索引为0) if (index === 0) { // 切换钉住状态 dingFlag.value = !dingFlag.value; // 如果钉住状态为true,确保该产品保持在第一位 if (dingFlag.value && products.value.length > 1) { // 第一个产品已经是索引0,无需移动 console.log('产品已钉住'); } } else { // 如果点击的不是第一个产品,则需要将该产品移动到第一位并钉住 if (index >= 0 && index < products.value.length) { // 获取要移动的产品 const productToMove = products.value[index]; // 从原位置删除并添加到第一位 products.value.splice(index, 1); products.value.unshift(productToMove); // 设置钉住状态 dingFlag.value = true; // 重新生成属性组 regenerateAttributeGroups(); console.log('产品已移动到第一位并钉住'); } } } /** * 根据当前products重新生成attributeGroups */ function regenerateAttributeGroups() { // 创建一个映射来存储所有属性组和属性 const attributeMap = new Map(); // 遍历所有产品,收集所有的属性组和属性 products.value.forEach(product => { if (product.attributes) { Object.keys(product.attributes).forEach(groupName => { // 如果属性组不存在,则创建它 if (!attributeMap.has(groupName)) { attributeMap.set(groupName, new Set()); } // 将该组下的所有属性添加到集合中 const groupAttrs = product.attributes[groupName]; Object.keys(groupAttrs).forEach(attrName => { attributeMap.get(groupName).add(attrName); }); }); } }); // 将Map转换为与原来格式相同的数组 const newAttributeGroups = []; attributeMap.forEach((attrsSet, groupName) => { const attributes = []; attrsSet.forEach(attrName => { // 检查该属性是否在所有产品中都有不同的值 let isDiff = checkAttributeIsDiff(groupName, attrName); attributes.push({ attrName: attrName, isDiff: isDiff }); }); // 检查该属性组是否在不同产品中有不同的属性 let groupIsDiff = attributes.some(attr => attr.isDiff); newAttributeGroups.push({ groupName: groupName, attributes: attributes, isDiff: groupIsDiff }); }); // 更新attributeGroups attributeGroups.value = newAttributeGroups; // 重新计算高度 // setTimeout(() => { // getHeight(); // }, 1000); } /** * 检查指定属性在所有产品中是否具有不同值 */ function checkAttributeIsDiff(groupName, attrName) { let values = []; // 收集所有产品的该属性值 products.value.forEach(product => { if (product.attributes && product.attributes[groupName] && product.attributes[groupName][attrName]) { values.push(product.attributes[groupName][attrName]); } else { values.push(undefined); } }); // 检查是否所有定义的值都相同 const firstValue = values[0]; return !values.every(value => value === firstValue); } /** * 获取对比数据 */ // async function fetchContrastData() { // if (!productIds.value) return; // try { // const response = await infoApi.getproductcontrast(productIds.value) // if (response.data.code === 0) { // products.value = response.data.list[0].products || [] // attributeGroups.value = response.data.list[0].attributeGroups || [] // onloading.value = false; // 关闭加载动画 // // 计算高度 // setTimeout(() => { // getHeight() // }, 100); // } // } catch (error) { // console.error('获取对比数据失败:', error) // onloading.value = false; // 出错时也关闭加载动画 // } // } function convertPrice(item) { if (item.quoted === 1) { return "¥" + utilApi.handlePrice(item.price) + "起" } else if (item.quoted === 0) { return "询价" } else { return "暂无报价" } } /** * 此方法数据加载后执行,获取元素成开的高度 */ function getHeight() { const query = uni.createSelectorQuery(); // query.selectAll('.head').boundingClientRect(data => { // headSize.value = data[0] // }).exec(); // console.log(headSize.value.right, 2222222222, ".item-" + (products.value.length - 1)); if (!headSize.value.right) { query.selectAll('.item-' + (products.value.length - 1)).boundingClientRect(data => { headSize.value = data[0]; }).exec(); } } function getProductAttributeValue(product, groupName, attrName) { if (product.attributes && product.attributes[groupName] && product.attributes[groupName][attrName] && product.attributes[groupName][attrName].length > 0) { return product.attributes[groupName][attrName]; } else { return '-'; } } // 存储页面参数 const pageOptions = ref({}); onMounted(() => { // 获取URL参数 const pages = getCurrentPages() const currentPage = pages[pages.length - 1] pageOptions.value = currentPage.options || {} productIds.value = pageOptions.value.productIds || '' // 确保有seriesId参数再调用 if (pageOptions.value.seriesId) { getSeriesId(pageOptions.value.seriesId); } else { console.warn('缺少seriesId参数'); onloading.value = false; } }); </script> <style lang="scss" scoped> .container { background-color: #fff; } .scroll-view { height: 100%; overflow-y: auto; .data-list { padding-top: 10rpx; padding-bottom: 2rpx; .datas { .group-name { font-size: 28rpx; line-height: 54rpx; font-weight: 600; display: flex; view:first-child { position: sticky; left: 26rpx; } } .group-data { display: flex; align-items: center; padding-right: 16rpx; position: relative; box-shadow: 0px 4rpx 6rpx rgba(0, 0, 0, 0.1); .data { border-top: 2rpx solid rgba(209, 224, 255, 1); .item { display: flex; padding-left: 26rpx; .d { display: flex; justify-content: center; align-items: center; flex: 0 0 auto; text-align: center; width: 200rpx; flex-shrink: 0; padding: 10rpx 16rpx; font-size: 24rpx; min-height: 60rpx; color: rgba(56, 56, 56, 1); border-right: 2rpx solid rgba(209, 224, 255, 1); border-bottom: 2rpx solid rgba(209, 224, 255, 1); word-wrap: break-word; word-break: break-word; white-space: pre-wrap; .ding-right { display: none; } } .d:last-child { margin-right: 0px; } .d:first-child { position: sticky; left: 26rpx; flex-shrink: 0; width: 175rpx; flex: 0 0 auto; font-size: 28rpx; z-index: 4; color: rgba(0, 0, 0, 1); background: rgba(239, 244, 255, 1); border-left: 2rpx solid rgba(209, 224, 255, 1); &:before { position: absolute; content: ""; z-index: 3; top: -4%; left: -28rpx; height: 106%; width: 26rpx; background-color: #fff; } } .d.diff { background: rgba(255, 245, 242, 1); color: rgba(16, 98, 202, 1); } } .item_first { position: sticky; left: 233rpx; background-color: #fff; } } } } } } .hr { box-shadow: 0px 4rpx 6rpx rgba(0, 0, 0, 0.1); position: fixed; left: 0px; top: 363rpx; width: 100%; z-index: 10; height: 2rpx; background: #fff; } .head { display: flex; padding: 0rpx 0px 18rpx 0rpx; position: sticky; top: 0px; z-index: 9; height: 160rpx; background-color: #fff; box-shadow: 0px 4rpx 6rpx rgba(0, 0, 0, 0.1); .first { position: sticky; left: 0rpx; display: flex; .lan { width: 36rpx; height: 165rpx; position: relative; z-index: 8; background-color: #fff; margin-right: -10rpx; } .lan:last-child { width: 24rpx; margin-left: -10rpx; margin-right: 0px; } .first-c { width: 200rpx; height: 160rpx; flex: 0 0 auto; z-index: 9; border-radius: 12rpx; background: rgba(255, 245, 242, 1); color: rgba(0, 0, 0, 1); font-size: 24rpx; text-align: center; border: 2rpx solid rgba(255, 230, 224, 1); view:first-child { font-size: 24rpx; padding: 36rpx 0px 0px; } view:last-child { display: flex; align-items: center; justify-content: center; height: 46rpx; margin-top: 16rpx; } } } .data { flex: 0 0 auto; .data-c { width: 100%; display: flex; align-items: center; .item { flex-shrink: 0; margin: 0px 7rpx; .ding-right { display: none; } .item-c { width: 208rpx; height: 160rpx; padding: 0px 0rpx 0rpx 10rpx; display: inline-block; border-radius: 12rpx; background: rgba(239, 244, 255, 1); border: 2rpx solid rgba(209, 224, 255, 1); .icon { display: flex; justify-content: space-between; .din { display: flex; align-items: center; font-size: 20rpx; gap: 10rpx; color: rgba(97, 97, 97, 1); } .close { padding: 15rpx; } } .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: rgba(46, 45, 45, 1); font-size: 24rpx; font-weight: 600; padding-right: 10rpx; } .grey { word-wrap: break-word; height: 70rpx; word-break: normal; font-size: 24rpx; margin-top: 5rpx; line-height: 35rpx; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; /* 限制最多显示3行 */ overflow: hidden; text-overflow: ellipsis; /* 可选,部分浏览器不生效 */ word-break: break-word; color: rgba(56, 56, 56, 1); padding-right: 10rpx; } } } .item:first-child { margin-left: 0px; } .item_first { position: sticky; left: 242rpx; margin-left: 0px; .item-c { position: relative; z-index: 4; } .ding-right { position: absolute; width: 16rpx; height: 100%; right: -10rpx; top: 0px; z-index: 3; display: block; background: #fff; } } } } } .param-title { color: rgba(0, 0, 0, 1); font-size: 28rpx; font-weight: 555; padding: 20rpx 26rpx; } </style>在安卓正常显示 在ios会出现横向滚动时纵向也会滚动的情况 分析代码 找出什么原因
10-31
<template> <view class="bind-container"> <!-- 长者信息 --> <view class="section"> <text class="section-title">长者信息</text> <view class="info-row"> <text class="label">姓名:</text> <text class="value">{{ name }}</text> </view> <view class="info-row"> <text class="label">电话:</text> <text class="value">{{ phone }}</text> </view> </view> <!-- 设备绑定指引 --> <view class="section"> <text class="section-title">设备绑定</text> <view class="step">1、插入SIM卡:打开卡托,插入运营商SIM卡(确保SIM卡开通VoLTE功能)</view> <view class="step">2、开机:长按电源键开机</view> <view class="step">3、点击下方“扫一扫”按钮,扫描包装盒或设备上86或76开头的IMEI串码</view> </view> <!-- 扫一扫 --> <view class="scan-area" @click="scanCode"> <image src="/static/images/scan.png" class="scan-icon" mode="widthFix"></image> <text class="scan-text">扫一扫</text> </view> <view class="step1">多次扫码成功,请在下方手动输入IMEI串码</view> <!-- IMEI 输入框 --> <input class="imei-input" v-model="imei" placeholder="设备IMEI串码(15位数字)" type="text" maxlength="15" @input="onImeiInput" /> <!-- 绑定按钮 --> <button class="bind-btn" @click="handleBind" :disabled="!imei.trim()">绑定设备</button> <!-- H5 摄像头扫码弹窗(仅 H5 显示) --> <view v-if="showScanner" class="scanner-overlay" @click.stop="closeScanner"> <!-- 新增一个专门放 video 的容器 --> <view class="video-container"></view> <view class="scanner-box"> <view class="line"></view> </view> <view class="close-btn">×</view> </view> </view> </template> <script> import { bindDevice } from '@/api/family'; import { BrowserMultiFormatReader } from '@zxing/library'; export default { data() { return { id: '', name: '', phone: '', imei: '', showScanner: false, codeReader: null, isScanning: false }; }, onLoad(options) { this.id = options.id || ''; this.name = decodeURIComponent(options.name || ''); this.phone = options.phone || ''; }, methods: { scanCode() { // 判断是否为 App 或 小程序环境(有 plus 或 wx 对象) const isApp = typeof window !== 'undefined' && (window.plus || window.__wxjs_environment); const isH5 = !isApp; if (isH5) { // ✅ 正确判断:只有 localhost / 127.0.0.1 允许 HTTP,其他 HTTP 都不行 const isLocalhost = ['localhost', '127.0.0.1'].includes(location.hostname); const isSecure = location.protocol === 'https:'; if (!isSecure && !isLocalhost) { uni.showToast({ title: '请通过 HTTPS 或 localhost 访问以使用扫码功能', icon: 'none', duration: 3000 }); return; } // ✅ localhost 或 HTTPS 下,允许调用摄像头 this.startH5Scan(); } else { // App / 小程序:使用 uni.scanCode uni.scanCode({ onlyFromCamera: true, scanType: ['qrCode', 'barCode'], success: (res) => { const result = (res.result || '').trim(); const imeiMatch = result.match(/\b\d{15}\b/); this.imei = imeiMatch ? imeiMatch[0] : result; }, fail: (err) => { console.warn('扫码失败:', err); uni.showToast({ title: '扫码失败,请手动输入 IMEI', icon: 'none' }); } }); } }, async startH5Scan() { if (this.isScanning) return; this.showScanner = true; this.isScanning = true; await this.$nextTick(); // 等待 DOM 渲染 let video = document.getElementById('qr-video'); if (!video) { video = document.createElement('video'); video.id = 'qr-video'; video.setAttribute('autoplay', ''); video.setAttribute('playsinline', ''); video.style.width = '100%'; video.style.maxWidth = '600px'; video.style.display = 'block'; video.style.background = '#000'; // ✅ 关键修改:查找 .video-container,而不是 .scanner-overlay const container = document.querySelector('.video-container'); if (!container) { console.error('未找到 .video-container'); this.closeScanner(); return; } // 只清空 container 内的内容(防止重复插入) container.innerHTML = ''; container.appendChild(video); } this.codeReader = new BrowserMultiFormatReader(); try { const devices = await this.codeReader.getVideoInputDevices(); if (devices.length === 0) { throw new Error('未检测到摄像头'); } let deviceId = devices[0].deviceId; const backCam = devices.find(d => d.label.toLowerCase().includes('back')); if (backCam) deviceId = backCam.deviceId; this.codeReader.decodeFromVideoDevice( deviceId, 'qr-video', (result, err) => { if (result) { this.handleScanResult(result.getText()); } } ); } catch (err) { console.error('启动摄像头失败:', err); uni.showToast({ title: '无法访问摄像头,请允许权限并使用 HTTPS', icon: 'none' }); this.closeScanner(); } }, onImeiInput(e) { let val = e.detail.value; // 只保留数字 val = val.replace(/\D/g, ''); // 最多15位 if (val.length > 15) val = val.slice(0, 15); this.imei = val; // 确保最终值是干净的 }, handleScanResult(text) { if (!text) return; const raw = text.trim(); console.log('扫码结果:', raw); const matches = raw.match(/\d{15}/); if (matches) { this.imei = matches[0]; // 强制刷新输入框(可选) this.$nextTick(() => { // 如果需要,可以 focus 输入框 // 但 uni-app 不支持直接 focus H5 input,除非用 ref + $el }); } else { uni.showToast({ title: '未识别到15位 IMEI', icon: 'none' }); } this.closeScanner(); }, closeScanner() { if (this.codeReader) { this.codeReader.reset(); this.codeReader = null; } this.isScanning = false; this.showScanner = false; // 确保 video 被移除 const video = document.getElementById('qr-video'); if (video) video.remove(); // 关键:强制触发一次 DOM 更新(有时 showScanner=false 不立即生效) this.$nextTick(); }, async handleBind() { const imei = this.imei.trim(); const studentId = this.id; if (!studentId) { uni.showToast({ title: '长者信息异常', icon: 'none' }); return; } if (!imei) { uni.showToast({ title: '请输入或扫描 IMEI', icon: 'none' }); return; } if (!/^\d{15}$/.test(imei)) { uni.showToast({ title: 'IMEI 应为15位数字', icon: 'none' }); return; } try { uni.showLoading({ title: '绑定中...' }); const res = await bindDevice({ studentId: parseInt(studentId, 10), imei: imei }); uni.hideLoading(); if (res.errcode === 0) { uni.showToast({ title: '绑定成功', icon: 'success' }); setTimeout(() => uni.navigateBack(), 800); } else { uni.showToast({ title: res.errtext || '绑定失败', icon: 'none' }); } } catch (error) { uni.hideLoading(); console.error('绑定请求异常:', error); uni.showToast({ title: '网络错误,请稍后再试', icon: 'none' }); } } }, // 页面销毁时清理资源 onUnload() { if (this.codeReader) { this.codeReader.reset(); } } }; </script> <style scoped> .bind-container { padding: 20rpx; } .section { background: #fff; border-radius: 12rpx; padding: 20rpx; margin-bottom: 20rpx; } .section-title { font-weight: bold; font-size: 32rpx; margin-bottom: 20rpx; display: block; } .info-row { display: flex; margin-bottom: 10rpx; } .label { color: #666; width: 120rpx; } .value { flex: 1; color: #333; } .step { font-size: 28rpx; color: #555; margin: 12rpx 0; line-height: 1.5; } .scan-area { background: #f0f7ff; border-radius: 16rpx; padding: 30rpx 0; text-align: center; margin: 30rpx 0; display: flex; flex-direction: column; /* 垂直排列 */ align-items: center; justify-content: center; } .scan-icon { width: 80rpx; height: 80rpx; margin-bottom: 10rpx; } .scan-text { font-size: 30rpx; color: #007AFF; } .step1 { font-size: 24rpx; color: #999; text-align: center; margin: 20rpx 0; } .imei-input { border: 1px solid #ddd; border-radius: 8rpx; padding: 20rpx; font-size: 28rpx; width: 100%; box-sizing: border-box; margin: 20rpx 0; } .bind-btn { background: #007AFF; color: white; border: none; border-radius: 8rpx; padding: 24rpx; font-size: 32rpx; width: 100%; } /* H5 扫码弹窗样式 */ .scanner-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: #000; z-index: 9999; display: flex; justify-content: center; align-items: center; } .video-container { width: 100%; max-width: 600rpx; position: relative; /* 为 scanner-box 的 absolute 定位提供参考 */ } #qr-video { width: 100%; display: block; background: #000; } .scanner-box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 300rpx; height: 300rpx; border: 2rpx solid rgba(0,255,0,0.5); box-shadow: 0 0 0 1000rpx rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; pointer-events: none; /* 防止遮挡点击事件 */ } .line { width: 200rpx; height: 4rpx; background: #0f0; animation: scanMove 2s infinite linear; } @keyframes scanMove { 0% { transform: translateY(-150rpx); } 100% { transform: translateY(150rpx); } } .close-btn { position: absolute; top: 40rpx; right: 40rpx; color: white; font-size: 60rpx; font-weight: bold; } </style>帮我搜索一下为什么不能手动输入
11-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值