635 lines
15 KiB
Vue
635 lines
15 KiB
Vue
<template>
|
||
<view class="input-list-page">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="navbar">
|
||
<view class="nav-left" @click="goBack">
|
||
<uni-icons type="left" size="20" color="#fff"></uni-icons>
|
||
</view>
|
||
<view class="nav-title">录入清单</view>
|
||
<view class="nav-right" @click="goRecord">
|
||
<uni-icons type="redo" size="18" color="#fff"></uni-icons>
|
||
<text class="record-text">记录</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 功能操作栏 -->
|
||
<view class="operation-bar">
|
||
<view class="search-box">
|
||
<uni-icons type="search" size="16" color="#999"></uni-icons>
|
||
<input type="text" placeholder="搜索商品名称、条码" placeholder-class="search-placeholder" />
|
||
</view>
|
||
<button class="btn batch-import" @click="goImport">批量导入</button>
|
||
<button class="btn no-code" @click="goNoCode">无码/生鲜</button>
|
||
</view>
|
||
|
||
<!-- 条码扫描区域 -->
|
||
<view class="scan-area">
|
||
<view class="scan-tip">对准商品条码,自动识别</view>
|
||
|
||
<!-- App环境扫码 -->
|
||
<!-- #ifdef APP-PLUS -->
|
||
<view class="scan-view" id="barcode-view"></view>
|
||
<view class="scan-controls" v-if="isScanning">
|
||
<view class="control-item" @click.stop="toggleFlash">
|
||
<uni-icons type="flash" size="22" color="#fff"></uni-icons>
|
||
<text class="control-text">{{ flashOn ? '关闭手电' : '开启手电' }}</text>
|
||
</view>
|
||
<view class="control-item" @click.stop="pauseScan">
|
||
<uni-icons type="pause" size="22" color="#fff"></uni-icons>
|
||
<text class="control-text">{{ scanPaused ? '继续扫码' : '暂停扫码' }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- #endif -->
|
||
|
||
<!-- H5环境提示 -->
|
||
<!-- #ifndef APP-PLUS -->
|
||
<view class="h5-tip">
|
||
<text class="tip-text">扫码功能需要在App中使用</text>
|
||
<button class="scan-btn" @click="startScan">开始扫码</button>
|
||
</view>
|
||
<!-- #endif -->
|
||
</view>
|
||
|
||
<!-- 已录入商品列表 -->
|
||
<view class="goods-list" v-if="goodsList.length > 0">
|
||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||
<view class="goods-info">
|
||
<image :src="item.image || '/static/default-goods.png'" class="goods-img"></image>
|
||
<view class="goods-detail">
|
||
<text class="goods-name">{{ item.name }}</text>
|
||
<text class="goods-barcode">{{ item.barcode }}</text>
|
||
<text class="goods-price">¥{{ item.price }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="goods-actions">
|
||
<view class="action-btn remove" @click="removeGoods(index)">移除商品</view>
|
||
</view>
|
||
<view class="goods-form">
|
||
<view class="form-item">
|
||
<text class="label required">进货数量</text>
|
||
<input type="number" v-model="item.quantity" class="input" placeholder="请输入数量" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">进货价</text>
|
||
<input type="number" v-model="item.price" class="input" placeholder="请输入价格" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view class="empty-area" v-else>
|
||
<text class="empty-title">暂未录入商品</text>
|
||
<text class="empty-desc">请在上方选择商品录入方式</text>
|
||
</view>
|
||
|
||
<!-- 提交按钮 -->
|
||
<view class="submit-area">
|
||
<button class="submit-btn" @click="submitList" :disabled="goodsList.length === 0">提交清单</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
flashOn: false,
|
||
scanPaused: false,
|
||
isScanning: false,
|
||
barcodeInstance: null,
|
||
goodsList: [
|
||
{
|
||
name: '怡宝饮用纯净水350ml',
|
||
barcode: '6901285991240',
|
||
price: 1,
|
||
quantity: 1,
|
||
image: 'http://193.112.94.36:8099/static/images/yibao.png'
|
||
}
|
||
]
|
||
};
|
||
},
|
||
mounted() {
|
||
// #ifdef APP-PLUS
|
||
console.log('页面 mounted 触发');
|
||
// 关键修复:等待 Webview 完全渲染后再初始化
|
||
this.$nextTick(() => {
|
||
setTimeout(() => {
|
||
this.initScan();
|
||
}, 300); // 延迟 300ms 确保原生层准备就绪
|
||
});
|
||
// #endif
|
||
},
|
||
onShow() {
|
||
// #ifdef APP-PLUS
|
||
// 从后台返回或从设置返回时,重新检查
|
||
if (!this.barcodeInstance) {
|
||
this.$nextTick(() => {
|
||
setTimeout(() => {
|
||
this.requestCameraAuth().then(() => {
|
||
this.initBarcodeScan();
|
||
}).catch(err => {
|
||
console.log('权限不足,无法重新初始化');
|
||
});
|
||
}, 300);
|
||
});
|
||
}
|
||
// #endif
|
||
},
|
||
onUnload() {
|
||
this.destroyScan();
|
||
},
|
||
methods: {
|
||
goBack() {
|
||
this.destroyScan();
|
||
uni.navigateBack({ delta: 1 });
|
||
},
|
||
goImport() {
|
||
uni.navigateTo({ url: '/pages/import/import' });
|
||
},
|
||
goNoCode() {
|
||
uni.navigateTo({ url: '/pages/NoCode/NoCode' });
|
||
},
|
||
goRecord() {
|
||
uni.showToast({ title: '记录功能开发中', icon: 'none' });
|
||
},
|
||
|
||
initScan() {
|
||
this.requestCameraAuth().then(() => {
|
||
this.initBarcodeScan();
|
||
}).catch(err => {
|
||
console.error('权限获取失败:', err);
|
||
uni.showModal({
|
||
title: '权限提示',
|
||
content: '请前往设置开启相机权限',
|
||
confirmText: '去设置',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.openAppSettings();
|
||
}
|
||
}
|
||
});
|
||
});
|
||
},
|
||
|
||
requestCameraAuth() {
|
||
return new Promise((resolve, reject) => {
|
||
// ... 这里保持原来的权限申请代码不变 ...
|
||
// (如果不确定可以保留之前修复的代码)
|
||
const platform = uni.getSystemInfoSync().platform;
|
||
if (platform === 'android') {
|
||
const main = plus.android.runtimeMainActivity();
|
||
const Permission = plus.android.importClass('android.Manifest.permission');
|
||
const PackageManager = plus.android.importClass('android.content.pm.PackageManager');
|
||
if (main.checkSelfPermission(Permission.CAMERA) === PackageManager.PERMISSION_GRANTED) {
|
||
resolve();
|
||
} else {
|
||
plus.android.requestPermissions(['android.permission.CAMERA'], (res) => {
|
||
res[0].granted ? resolve() : reject('用户拒绝');
|
||
}, reject);
|
||
}
|
||
} else {
|
||
resolve();
|
||
}
|
||
});
|
||
},
|
||
|
||
openAppSettings() {
|
||
// ... 这里保持原来的跳转设置代码不变 ...
|
||
try {
|
||
plus.android.importClass('android.content.Intent');
|
||
plus.android.importClass('android.provider.Settings');
|
||
plus.android.importClass('android.net.Uri');
|
||
const mainActivity = plus.android.runtimeMainActivity();
|
||
const intent = new android.content.Intent();
|
||
intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||
const uri = android.net.Uri.fromParts('package', mainActivity.getPackageName(), null);
|
||
intent.setData(uri);
|
||
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||
mainActivity.startActivity(intent);
|
||
} catch (e) {
|
||
uni.showToast({ title: '跳转失败,请手动开启', icon: 'none' });
|
||
}
|
||
},
|
||
|
||
initBarcodeScan() {
|
||
if (!plus || !plus.barcode) return;
|
||
|
||
this.destroyScan();
|
||
this.isScanning = true;
|
||
|
||
const sysInfo = uni.getSystemInfoSync();
|
||
const statusBarHeight = sysInfo.statusBarHeight || 0;
|
||
const navBarHeight = 44;
|
||
const totalNavBarHeight = statusBarHeight + navBarHeight;
|
||
|
||
// 调试:打印高度计算结果
|
||
console.log('高度计算:', { statusBarHeight, navBarHeight, totalNavBarHeight });
|
||
|
||
// 调整位置:确保扫码框在搜索栏下方
|
||
const scanTop = totalNavBarHeight + 80;
|
||
const scanWidth = sysInfo.windowWidth - 30;
|
||
const scanHeight = 250; // 固定高度测试,避免宽高比计算错误导致高度为0
|
||
|
||
// 创建控件
|
||
const barcode = plus.barcode.create('barcode',
|
||
[plus.barcode.CODE_128, plus.barcode.EAN_13, plus.barcode.EAN_8],
|
||
{
|
||
top: scanTop,
|
||
left: 15,
|
||
width: scanWidth,
|
||
height: scanHeight,
|
||
scanbarColor: '#e60012',
|
||
frameColor: '#e60012',
|
||
background: '#000',
|
||
zIndex: 9999 // 强制置顶
|
||
}
|
||
);
|
||
|
||
barcode.onmarked = (type, result) => {
|
||
if (result) {
|
||
console.log('扫码结果:', result);
|
||
this.addGoodsByBarcode(result);
|
||
}
|
||
};
|
||
|
||
// 关键:获取当前 Webview 并挂载
|
||
const currentWebview = this.$scope.$getAppWebview();
|
||
if (currentWebview) {
|
||
currentWebview.append(barcode);
|
||
console.log('扫码控件已挂载到 Webview');
|
||
} else {
|
||
console.error('获取 Webview 失败,无法挂载');
|
||
}
|
||
|
||
barcode.start();
|
||
this.barcodeInstance = barcode;
|
||
},
|
||
|
||
destroyScan() {
|
||
if (this.barcodeInstance) {
|
||
this.barcodeInstance.close();
|
||
this.barcodeInstance = null;
|
||
}
|
||
this.isScanning = false;
|
||
},
|
||
|
||
toggleFlash() {
|
||
if (this.barcodeInstance) {
|
||
this.flashOn = !this.flashOn;
|
||
this.barcodeInstance.setFlash(this.flashOn);
|
||
}
|
||
},
|
||
|
||
pauseScan() {
|
||
if (this.barcodeInstance) {
|
||
this.scanPaused = !this.scanPaused;
|
||
this.scanPaused ? this.barcodeInstance.pause() : this.barcodeInstance.resume();
|
||
}
|
||
},
|
||
|
||
addGoodsByBarcode(barcode) {
|
||
const mockGoods = {
|
||
name: '测试商品-' + barcode,
|
||
barcode: barcode,
|
||
price: 10,
|
||
quantity: 1,
|
||
image: 'http://193.112.94.36:8099/static/images/yibao.png'
|
||
};
|
||
this.goodsList.push(mockGoods);
|
||
uni.showToast({ title: '识别成功', icon: 'success' });
|
||
},
|
||
|
||
removeGoods(index) {
|
||
this.goodsList.splice(index, 1);
|
||
},
|
||
|
||
submitList() {
|
||
uni.showToast({ title: '提交成功', icon: 'success' });
|
||
setTimeout(() => uni.navigateBack(), 1000);
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 页面基础样式 */
|
||
.input-list-page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 顶部导航栏 */
|
||
.navbar {
|
||
width: 100%;
|
||
height: calc(var(--status-bar-height) + 44px);
|
||
background-color: #e60012;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 15px;
|
||
box-sizing: border-box;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 999;
|
||
}
|
||
|
||
.nav-left, .nav-right {
|
||
display: flex;
|
||
align-items: center;
|
||
color: #fff;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
}
|
||
|
||
.record-text {
|
||
font-size: 14px;
|
||
margin-left: 5px;
|
||
color: #fff;
|
||
}
|
||
|
||
/* 功能操作栏 */
|
||
.operation-bar {
|
||
margin-top: calc(var(--status-bar-height) + 44px);
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 10px 15px;
|
||
box-sizing: border-box;
|
||
background-color: #fff;
|
||
gap: 10px;
|
||
}
|
||
|
||
.search-box {
|
||
flex: 1;
|
||
height: 32px;
|
||
border: 1px solid #eee;
|
||
border-radius: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 10px;
|
||
background-color: #f9f9f9;
|
||
}
|
||
|
||
.search-placeholder {
|
||
font-size: 14px;
|
||
color: #999;
|
||
}
|
||
|
||
.btn {
|
||
height: 32px;
|
||
padding: 0 12px;
|
||
font-size: 14px;
|
||
border-radius: 4px;
|
||
background-color: #f5f5f5;
|
||
border: 1px solid #eee;
|
||
color: #333;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 扫码区域(关键修复:增加z-index) */
|
||
.scan-area {
|
||
width: 100%;
|
||
padding: 15px;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
margin-top: 10px;
|
||
background-color: #fff;
|
||
z-index: 1; /* 确保扫码区域在最上层 */
|
||
}
|
||
|
||
.scan-tip {
|
||
position: absolute;
|
||
top: 15px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background-color: rgba(0,0,0,0.7);
|
||
color: #fff;
|
||
font-size: 14px;
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
z-index: 10;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.scan-view {
|
||
width: 100%;
|
||
height: 300px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
background-color: #000;
|
||
z-index: 2; /* 关键修复:确保扫码容器层级 */
|
||
}
|
||
|
||
.scan-controls {
|
||
position: absolute;
|
||
bottom: 15px;
|
||
left: 0;
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 40px;
|
||
padding: 0 15px;
|
||
box-sizing: border-box;
|
||
z-index: 10;
|
||
}
|
||
|
||
.control-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
color: #fff;
|
||
cursor: pointer;
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
min-width: 60px;
|
||
}
|
||
|
||
.control-text {
|
||
font-size: 12px;
|
||
margin-top: 5px;
|
||
text-align: center;
|
||
}
|
||
|
||
/* H5端提示 */
|
||
.h5-tip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 60px 20px;
|
||
background-color: #f9f9f9;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.tip-text {
|
||
font-size: 16px;
|
||
color: #333;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.scan-btn {
|
||
padding: 12px 30px;
|
||
background-color: #e60012;
|
||
color: #fff;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* 商品列表 */
|
||
.goods-list {
|
||
flex: 1;
|
||
padding: 15px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.goods-item {
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.goods-info {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.goods-img {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 4px;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.goods-detail {
|
||
flex: 1;
|
||
}
|
||
|
||
.goods-name {
|
||
font-size: 16px;
|
||
color: #333;
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.goods-barcode {
|
||
font-size: 12px;
|
||
color: #999;
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.goods-price {
|
||
font-size: 14px;
|
||
color: #e60012;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.goods-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 6px 12px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.remove {
|
||
background-color: #fef0f0;
|
||
color: #ff4d4f;
|
||
border: 1px solid #ffccc7;
|
||
}
|
||
|
||
/* 商品表单 */
|
||
.goods-form {
|
||
display: flex;
|
||
gap: 20px;
|
||
}
|
||
|
||
.form-item {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.label {
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.required::before {
|
||
content: '*';
|
||
color: #e60012;
|
||
margin-right: 2px;
|
||
}
|
||
|
||
.input {
|
||
height: 32px;
|
||
border: 1px solid #eee;
|
||
border-radius: 4px;
|
||
padding: 0 8px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-area {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 16px;
|
||
color: #333;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.empty-desc {
|
||
font-size: 14px;
|
||
color: #999;
|
||
}
|
||
|
||
/* 提交按钮区域 */
|
||
.submit-area {
|
||
padding: 15px;
|
||
box-sizing: border-box;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
height: 44px;
|
||
background-color: #e60012;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.submit-btn:disabled {
|
||
background-color: #ccc;
|
||
color: #999;
|
||
}
|
||
</style> |