ry_app/package_a/enter/enter.vue

635 lines
15 KiB
Vue
Raw Normal View History

2026-01-19 16:52:24 +08:00
<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>
2026-01-21 15:09:06 +08:00
<button class="btn no-code" @click="goNoCode">/</button>
2026-01-19 16:52:24 +08:00
</view>
2026-01-21 15:09:06 +08:00
<!-- 条码扫描区域 -->
2026-01-19 16:52:24 +08:00
<view class="scan-area">
<view class="scan-tip">对准商品条码自动识别</view>
2026-01-21 15:09:06 +08:00
<!-- App环境扫码 -->
2026-01-19 16:52:24 +08:00
<!-- #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 -->
2026-01-21 15:09:06 +08:00
<!-- H5环境提示 -->
2026-01-19 16:52:24 +08:00
<!-- #ifndef APP-PLUS -->
<view class="h5-tip">
<text class="tip-text">扫码功能需要在App中使用</text>
<button class="scan-btn" @click="startScan"></button>
</view>
<!-- #endif -->
</view>
2026-01-21 15:09:06 +08:00
<!-- 已录入商品列表 -->
<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>
2026-01-19 16:52:24 +08:00
<text class="empty-title">暂未录入商品</text>
<text class="empty-desc">请在上方选择商品录入方式</text>
2026-01-21 15:09:06 +08:00
</view>
<!-- 提交按钮 -->
<view class="submit-area">
<button class="submit-btn" @click="submitList" :disabled="goodsList.length === 0">提交清单</button>
2026-01-19 16:52:24 +08:00
</view>
</view>
</template>
<script>
export default {
data() {
return {
flashOn: false,
scanPaused: false,
isScanning: false,
2026-01-21 15:09:06 +08:00
barcodeInstance: null,
goodsList: [
{
name: '怡宝饮用纯净水350ml',
barcode: '6901285991240',
price: 1,
quantity: 1,
2026-01-26 18:58:10 +08:00
image: 'http://193.112.94.36:8099/static/images/yibao.png'
2026-01-21 15:09:06 +08:00
}
]
2026-01-19 16:52:24 +08:00
};
},
mounted() {
// #ifdef APP-PLUS
2026-01-21 15:09:06 +08:00
console.log('页面 mounted 触发');
// 关键修复:等待 Webview 完全渲染后再初始化
this.$nextTick(() => {
setTimeout(() => {
this.initScan();
}, 300); // 延迟 300ms 确保原生层准备就绪
});
2026-01-19 16:52:24 +08:00
// #endif
},
onShow() {
// #ifdef APP-PLUS
2026-01-21 15:09:06 +08:00
// 从后台返回或从设置返回时,重新检查
if (!this.barcodeInstance) {
this.$nextTick(() => {
setTimeout(() => {
this.requestCameraAuth().then(() => {
this.initBarcodeScan();
}).catch(err => {
console.log('权限不足,无法重新初始化');
});
}, 300);
});
}
2026-01-19 16:52:24 +08:00
// #endif
},
onUnload() {
2026-01-21 15:09:06 +08:00
this.destroyScan();
2026-01-19 16:52:24 +08:00
},
methods: {
goBack() {
2026-01-21 15:09:06 +08:00
this.destroyScan();
2026-01-19 16:52:24 +08:00
uni.navigateBack({ delta: 1 });
},
2026-01-21 15:09:06 +08:00
goImport() {
2026-03-28 21:37:27 +08:00
uni.navigateTo({ url: '/package_a/import/import' });
2026-01-21 15:09:06 +08:00
},
goNoCode() {
2026-03-28 21:37:27 +08:00
uni.navigateTo({ url: '/package_a/NoCode/NoCode' });
2026-01-21 15:09:06 +08:00
},
2026-01-19 16:52:24 +08:00
goRecord() {
uni.showToast({ title: '记录功能开发中', icon: 'none' });
},
initScan() {
this.requestCameraAuth().then(() => {
this.initBarcodeScan();
}).catch(err => {
2026-01-21 15:09:06 +08:00
console.error('权限获取失败:', err);
2026-01-19 16:52:24 +08:00
uni.showModal({
title: '权限提示',
2026-01-21 15:09:06 +08:00
content: '请前往设置开启相机权限',
2026-01-19 16:52:24 +08:00
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
2026-01-21 15:09:06 +08:00
this.openAppSettings();
2026-01-19 16:52:24 +08:00
}
}
});
});
},
requestCameraAuth() {
return new Promise((resolve, reject) => {
2026-01-21 15:09:06 +08:00
// ... 这里保持原来的权限申请代码不变 ...
// (如果不确定可以保留之前修复的代码)
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) {
2026-01-19 16:52:24 +08:00
resolve();
} else {
2026-01-21 15:09:06 +08:00
plus.android.requestPermissions(['android.permission.CAMERA'], (res) => {
res[0].granted ? resolve() : reject('用户拒绝');
}, reject);
2026-01-19 16:52:24 +08:00
}
2026-01-21 15:09:06 +08:00
} else {
resolve();
2026-01-19 16:52:24 +08:00
}
});
},
2026-01-21 15:09:06 +08:00
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' });
2026-01-19 16:52:24 +08:00
}
2026-01-21 15:09:06 +08:00
},
initBarcodeScan() {
if (!plus || !plus.barcode) return;
this.destroyScan();
2026-01-19 16:52:24 +08:00
this.isScanning = true;
const sysInfo = uni.getSystemInfoSync();
const statusBarHeight = sysInfo.statusBarHeight || 0;
const navBarHeight = 44;
const totalNavBarHeight = statusBarHeight + navBarHeight;
2026-01-21 15:09:06 +08:00
// 调试:打印高度计算结果
console.log('高度计算:', { statusBarHeight, navBarHeight, totalNavBarHeight });
2026-01-19 16:52:24 +08:00
2026-01-21 15:09:06 +08:00
// 调整位置:确保扫码框在搜索栏下方
const scanTop = totalNavBarHeight + 80;
const scanWidth = sysInfo.windowWidth - 30;
const scanHeight = 250; // 固定高度测试避免宽高比计算错误导致高度为0
// 创建控件
2026-01-19 16:52:24 +08:00
const barcode = plus.barcode.create('barcode',
2026-01-21 15:09:06 +08:00
[plus.barcode.CODE_128, plus.barcode.EAN_13, plus.barcode.EAN_8],
2026-01-19 16:52:24 +08:00
{
top: scanTop,
left: 15,
width: scanWidth,
height: scanHeight,
2026-01-21 15:09:06 +08:00
scanbarColor: '#e60012',
frameColor: '#e60012',
2026-01-19 16:52:24 +08:00
background: '#000',
2026-01-21 15:09:06 +08:00
zIndex: 9999 // 强制置顶
2026-01-19 16:52:24 +08:00
}
);
barcode.onmarked = (type, result) => {
2026-01-21 15:09:06 +08:00
if (result) {
console.log('扫码结果:', result);
this.addGoodsByBarcode(result);
2026-01-19 16:52:24 +08:00
}
2026-01-21 15:09:06 +08:00
};
// 关键:获取当前 Webview 并挂载
const currentWebview = this.$scope.$getAppWebview();
if (currentWebview) {
currentWebview.append(barcode);
console.log('扫码控件已挂载到 Webview');
} else {
console.error('获取 Webview 失败,无法挂载');
}
2026-01-19 16:52:24 +08:00
2026-01-21 15:09:06 +08:00
barcode.start();
this.barcodeInstance = barcode;
2026-01-19 16:52:24 +08:00
},
2026-01-21 15:09:06 +08:00
destroyScan() {
if (this.barcodeInstance) {
this.barcodeInstance.close();
this.barcodeInstance = null;
}
this.isScanning = false;
2026-01-19 16:52:24 +08:00
},
2026-01-21 15:09:06 +08:00
toggleFlash() {
if (this.barcodeInstance) {
this.flashOn = !this.flashOn;
this.barcodeInstance.setFlash(this.flashOn);
}
2026-01-19 16:52:24 +08:00
},
2026-01-21 15:09:06 +08:00
pauseScan() {
if (this.barcodeInstance) {
this.scanPaused = !this.scanPaused;
this.scanPaused ? this.barcodeInstance.pause() : this.barcodeInstance.resume();
2026-01-19 16:52:24 +08:00
}
},
2026-01-21 15:09:06 +08:00
addGoodsByBarcode(barcode) {
const mockGoods = {
name: '测试商品-' + barcode,
barcode: barcode,
price: 10,
quantity: 1,
2026-01-26 18:58:10 +08:00
image: 'http://193.112.94.36:8099/static/images/yibao.png'
2026-01-21 15:09:06 +08:00
};
this.goodsList.push(mockGoods);
uni.showToast({ title: '识别成功', icon: 'success' });
},
2026-01-19 16:52:24 +08:00
2026-01-21 15:09:06 +08:00
removeGoods(index) {
this.goodsList.splice(index, 1);
2026-01-19 16:52:24 +08:00
},
2026-01-21 15:09:06 +08:00
submitList() {
uni.showToast({ title: '提交成功', icon: 'success' });
setTimeout(() => uni.navigateBack(), 1000);
2026-01-19 16:52:24 +08:00
}
}
};
</script>
<style scoped>
2026-01-21 15:09:06 +08:00
/* 页面基础样式 */
2026-01-19 16:52:24 +08:00
.input-list-page {
width: 100%;
2026-01-21 15:09:06 +08:00
min-height: 100vh;
2026-01-19 16:52:24 +08:00
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
2026-01-21 15:09:06 +08:00
/* 顶部导航栏 */
2026-01-19 16:52:24 +08:00
.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;
}
2026-01-21 15:09:06 +08:00
/* 功能操作栏 */
2026-01-19 16:52:24 +08:00
.operation-bar {
2026-01-21 15:09:06 +08:00
margin-top: calc(var(--status-bar-height) + 44px);
2026-01-19 16:52:24 +08:00
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;
}
2026-01-21 15:09:06 +08:00
/* 扫码区域关键修复增加z-index */
2026-01-19 16:52:24 +08:00
.scan-area {
width: 100%;
padding: 15px;
box-sizing: border-box;
position: relative;
margin-top: 10px;
2026-01-21 15:09:06 +08:00
background-color: #fff;
z-index: 1; /* 确保扫码区域在最上层 */
2026-01-19 16:52:24 +08:00
}
.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;
2026-01-21 15:09:06 +08:00
z-index: 2; /* 关键修复:确保扫码容器层级 */
2026-01-19 16:52:24 +08:00
}
.scan-controls {
position: absolute;
bottom: 15px;
left: 0;
width: 100%;
display: flex;
2026-01-21 15:09:06 +08:00
justify-content: center;
gap: 40px;
2026-01-19 16:52:24 +08:00
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;
}
2026-01-21 15:09:06 +08:00
/* H5端提示 */
2026-01-19 16:52:24 +08:00
.h5-tip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
2026-01-21 15:09:06 +08:00
background-color: #f9f9f9;
2026-01-19 16:52:24 +08:00
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;
}
2026-01-21 15:09:06 +08:00
/* 商品列表 */
.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;
}
/* 空状态 */
2026-01-19 16:52:24 +08:00
.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;
2026-01-21 15:09:06 +08:00
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;
2026-01-19 16:52:24 +08:00
}
</style>