功能修改
This commit is contained in:
parent
e66dc97ac3
commit
04816e1fd0
|
|
@ -580,6 +580,17 @@
|
|||
"navigationBarTitleText": "OCR查询"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/devicesSearch/camera",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "相机识别",
|
||||
"app-plus": {
|
||||
"orientation": ["portrait-primary"],
|
||||
"background": "#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/devicesSearch/codeSearch",
|
||||
"style": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,646 @@
|
|||
<template>
|
||||
<view class="camera-container">
|
||||
<!-- 相机预览背景 -->
|
||||
<view class="camera-preview" :style="cameraStyle"></view>
|
||||
|
||||
<!-- 顶部状态栏占位 -->
|
||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
|
||||
<!-- 顶部提示文字 -->
|
||||
<view class="top-tip">
|
||||
<text class="tip-text">请将识别编码置于取景框内,完成扫描</text>
|
||||
</view>
|
||||
|
||||
<!-- 取景框 -->
|
||||
<view class="viewfinder-container">
|
||||
<view class="viewfinder">
|
||||
<!-- 四个角的装饰 -->
|
||||
<view class="corner corner-top-left"></view>
|
||||
<view class="corner corner-top-right"></view>
|
||||
<view class="corner corner-bottom-left"></view>
|
||||
<view class="corner corner-bottom-right"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部控制区域 -->
|
||||
<view class="bottom-controls">
|
||||
<!-- 返回按钮 -->
|
||||
<view class="control-btn" @click="goBack">
|
||||
<text class="control-icon">✕</text>
|
||||
</view>
|
||||
|
||||
<!-- 拍照按钮 -->
|
||||
<view class="photo-btn" @click="takePicture" :class="{ 'taking': isTaking }">
|
||||
<view class="photo-btn-inner"></view>
|
||||
</view>
|
||||
|
||||
<!-- 相册按钮 -->
|
||||
<view class="control-btn" @click="openGallery">
|
||||
<text class="control-icon">📷</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载提示 -->
|
||||
<view v-if="isProcessing" class="loading-overlay">
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">正在识别中...</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 在camera-container内部添加隐藏的canvas -->
|
||||
<canvas
|
||||
canvas-id="imageCanvas"
|
||||
style="position: fixed; top: -9999px; left: -9999px; width: 1px; height: 1px;"
|
||||
></canvas>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import ImageUtils from "../../services/utils/imageUtils";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: 0,
|
||||
screenWidth: 0,
|
||||
screenHeight: 0,
|
||||
isCameraActive: false,
|
||||
isTaking: false,
|
||||
isProcessing: false,
|
||||
cameraPreviewOptions: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
camera: 'rear',
|
||||
tapPhoto: false,
|
||||
previewDrag: false,
|
||||
toBack: true,
|
||||
alpha: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
cameraStyle() {
|
||||
return {
|
||||
width: this.screenWidth + 'px',
|
||||
height: this.screenHeight + 'px'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.initCamera();
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.getSystemInfo();
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.stopCamera();
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.stopCamera();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 获取系统信息
|
||||
getSystemInfo() {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||
this.screenWidth = systemInfo.screenWidth;
|
||||
this.screenHeight = systemInfo.screenHeight;
|
||||
|
||||
// 更新相机预览配置
|
||||
this.cameraPreviewOptions.width = this.screenWidth;
|
||||
this.cameraPreviewOptions.height = this.screenHeight;
|
||||
|
||||
// 启动相机
|
||||
this.startCamera();
|
||||
},
|
||||
|
||||
// 初始化相机
|
||||
initCamera() {
|
||||
// 检查相机权限
|
||||
// #ifdef APP-PLUS
|
||||
const main = plus.android.runtimeMainActivity();
|
||||
const Intent = plus.android.importClass('android.content.Intent');
|
||||
const Settings = plus.android.importClass('android.provider.Settings');
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 启动相机预览
|
||||
startCamera() {
|
||||
if (typeof window.CameraPreview === 'undefined') {
|
||||
uni.showToast({
|
||||
title: '相机插件未安装',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
window.CameraPreview.startCamera(
|
||||
this.cameraPreviewOptions,
|
||||
() => {
|
||||
console.log('Camera started successfully');
|
||||
this.isCameraActive = true;
|
||||
},
|
||||
(err) => {
|
||||
console.error('Camera start error:', err);
|
||||
uni.showToast({
|
||||
title: '相机启动失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// 停止相机预览
|
||||
stopCamera() {
|
||||
if (this.isCameraActive && typeof window.CameraPreview !== 'undefined') {
|
||||
window.CameraPreview.stopCamera(
|
||||
() => {
|
||||
console.log('Camera stopped successfully');
|
||||
this.isCameraActive = false;
|
||||
},
|
||||
(err) => {
|
||||
console.error('Camera stop error:', err);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// 拍照
|
||||
takePicture() {
|
||||
if (!this.isCameraActive || this.isTaking) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isTaking = true;
|
||||
|
||||
window.CameraPreview.takePicture(
|
||||
{
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
quality: 50 // 直接在这里设置压缩质量,相当于Android的compress质量
|
||||
},
|
||||
(base64Data) => {
|
||||
this.isTaking = false;
|
||||
this.onCameraSuccess(base64Data);
|
||||
},
|
||||
(err) => {
|
||||
this.isTaking = false;
|
||||
console.error('Take picture error:', err);
|
||||
uni.showToast({
|
||||
title: '拍照失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// 打开相册
|
||||
openGallery() {
|
||||
this.stopCamera();
|
||||
|
||||
navigator.camera.getPicture(
|
||||
(imageData) => {
|
||||
// 这里imageData可能是文件路径或base64数据
|
||||
this.onCameraSuccess(imageData);
|
||||
},
|
||||
this.onCameraError,
|
||||
{
|
||||
quality: 50, // 设置压缩质量
|
||||
destinationType: navigator.camera.DestinationType.FILE_URI, // 获取文件路径
|
||||
sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY,
|
||||
allowEdit: false,
|
||||
encodingType: navigator.camera.EncodingType.JPEG,
|
||||
targetWidth: 1024,
|
||||
targetHeight: 1024
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// 拍照成功回调
|
||||
async onCameraSuccess(imageData) {
|
||||
this.isProcessing = true;
|
||||
|
||||
try {
|
||||
// 使用工具类处理图片,相当于Android的完整处理流程
|
||||
const processedBase64 = await ImageUtils.processImage(imageData, {
|
||||
maxWidth: 1024,
|
||||
maxHeight: 1024,
|
||||
quality: 50,
|
||||
outputFormat: 'base64'
|
||||
});
|
||||
|
||||
// OCR识别
|
||||
const params = {
|
||||
image: processedBase64,
|
||||
jiju_type: '',
|
||||
auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
|
||||
};
|
||||
|
||||
uni.request({
|
||||
url: '/material/app/ocr/getOcrCode',
|
||||
method: 'POST',
|
||||
data: params,
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
success: (res) => {
|
||||
this.isProcessing = false;
|
||||
const { data: resData } = res;
|
||||
|
||||
if (resData.data && resData.data.data && resData.data.data.result) {
|
||||
const result = resData.data.data.result;
|
||||
uni.showToast({
|
||||
title: '识别成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({
|
||||
success: () => {
|
||||
uni.$emit('ocrResult', {
|
||||
success: true,
|
||||
result: result,
|
||||
image: processedBase64
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '识别失败:' + (resData.data?.msg || '未知错误'),
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.startCamera();
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
this.isProcessing = false;
|
||||
console.error('OCR request failed:', err);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.startCamera();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
this.isProcessing = false;
|
||||
console.error('Image processing failed:', error);
|
||||
uni.showToast({
|
||||
title: '图片处理失败',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.startCamera();
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
|
||||
// 拍照失败回调
|
||||
onCameraError(message) {
|
||||
console.error('Camera error:', message);
|
||||
uni.showToast({
|
||||
title: '操作失败: ' + message,
|
||||
icon: 'none'
|
||||
});
|
||||
// 重新启动相机
|
||||
this.startCamera();
|
||||
},
|
||||
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
|
||||
// 将图片转换为Base64字符串(相当于Android的bitmapToString)
|
||||
async imageToBase64(imagePath, quality = 50) {
|
||||
try {
|
||||
// 如果已经是base64格式,直接返回
|
||||
if (imagePath.startsWith('data:image/')) {
|
||||
return imagePath.split(',')[1];
|
||||
}
|
||||
|
||||
// 使用uni.compressImage压缩图片
|
||||
const compressResult = await new Promise((resolve, reject) => {
|
||||
uni.compressImage({
|
||||
src: imagePath,
|
||||
quality: quality, // 压缩质量 0-100
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
|
||||
// 读取压缩后的文件并转换为base64
|
||||
const fileManager = uni.getFileSystemManager();
|
||||
const base64Data = await new Promise((resolve, reject) => {
|
||||
fileManager.readFile({
|
||||
filePath: compressResult.tempFilePath,
|
||||
encoding: 'base64',
|
||||
success: (res) => resolve(res.data),
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
|
||||
return base64Data;
|
||||
} catch (error) {
|
||||
console.error('Image to base64 conversion failed:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 获取缩放后的图片(相当于Android的getSmallBitmap)
|
||||
async getSmallImage(imagePath, maxWidth = 1024, maxHeight = 1024) {
|
||||
try {
|
||||
return await this.resizeImage(imagePath, maxWidth, maxHeight);
|
||||
} catch (error) {
|
||||
console.error('Get small image failed:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 使用Canvas调整图片尺寸
|
||||
async resizeImage(imagePath, maxWidth, maxHeight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 创建canvas上下文
|
||||
const ctx = uni.createCanvasContext('imageCanvas', this);
|
||||
|
||||
// 获取图片信息
|
||||
uni.getImageInfo({
|
||||
src: imagePath,
|
||||
success: (imageInfo) => {
|
||||
const { width, height } = imageInfo;
|
||||
|
||||
// 计算缩放比例(相当于Android的calculateInSampleSize)
|
||||
const scale = this.calculateScale(width, height, maxWidth, maxHeight);
|
||||
const newWidth = Math.floor(width * scale);
|
||||
const newHeight = Math.floor(height * scale);
|
||||
|
||||
// 设置canvas尺寸
|
||||
ctx.canvas.width = newWidth;
|
||||
ctx.canvas.height = newHeight;
|
||||
|
||||
// 绘制缩放后的图片
|
||||
ctx.drawImage(imagePath, 0, 0, newWidth, newHeight);
|
||||
ctx.draw(false, () => {
|
||||
// 导出canvas为临时文件
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: 'imageCanvas',
|
||||
fileType: 'jpg',
|
||||
quality: 0.8,
|
||||
success: (res) => {
|
||||
resolve(res.tempFilePath);
|
||||
},
|
||||
fail: reject
|
||||
}, this);
|
||||
});
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 计算缩放比例(相当于Android的calculateInSampleSize)
|
||||
calculateScale(originalWidth, originalHeight, maxWidth, maxHeight) {
|
||||
if (originalWidth <= maxWidth && originalHeight <= maxHeight) {
|
||||
return 1; // 不需要缩放
|
||||
}
|
||||
|
||||
const widthScale = maxWidth / originalWidth;
|
||||
const heightScale = maxHeight / originalHeight;
|
||||
|
||||
// 选择较小的缩放比例,确保图片完全适应限制尺寸
|
||||
return Math.min(widthScale, heightScale);
|
||||
},
|
||||
|
||||
// 压缩图片质量
|
||||
async compressImageQuality(imagePath, quality = 50) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.compressImage({
|
||||
src: imagePath,
|
||||
quality: quality,
|
||||
compressedWidth: 1024,
|
||||
compressedHeight: 1024,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.camera-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #000;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.camera-preview {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.top-tip {
|
||||
position: absolute;
|
||||
top: 80rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
padding: 20rpx 30rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.viewfinder-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.viewfinder {
|
||||
position: relative;
|
||||
width: 500rpx;
|
||||
height: 600rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.corner {
|
||||
position: absolute;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 6rpx solid #4b8eff;
|
||||
}
|
||||
|
||||
.corner-top-left {
|
||||
top: -6rpx;
|
||||
left: -6rpx;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
border-top-left-radius: 20rpx;
|
||||
}
|
||||
|
||||
.corner-top-right {
|
||||
top: -6rpx;
|
||||
right: -6rpx;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
border-top-right-radius: 20rpx;
|
||||
}
|
||||
|
||||
.corner-bottom-left {
|
||||
bottom: -6rpx;
|
||||
left: -6rpx;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-bottom-left-radius: 20rpx;
|
||||
}
|
||||
|
||||
.corner-bottom-right {
|
||||
bottom: -6rpx;
|
||||
right: -6rpx;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
border-bottom-right-radius: 20rpx;
|
||||
}
|
||||
|
||||
.bottom-controls {
|
||||
position: absolute;
|
||||
bottom: 100rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 100rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.control-icon {
|
||||
color: #fff;
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.photo-btn {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 6rpx solid #fff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.photo-btn.taking {
|
||||
transform: scale(0.9);
|
||||
background-color: rgba(75, 142, 255, 0.8);
|
||||
}
|
||||
|
||||
.photo-btn-inner {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.photo-btn.taking .photo-btn-inner {
|
||||
background-color: #4b8eff;
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
padding: 60rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 6rpx solid #f3f3f3;
|
||||
border-top: 6rpx solid #4b8eff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #333;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,491 +1,318 @@
|
|||
<template>
|
||||
<view class="page-container">
|
||||
<!-- <view>
|
||||
<live-pusher v-once id="livePusher" ref="livePusher" class="livePusher" mode="FHD" beauty="0" whiteness="0"
|
||||
device-position="back" :auto-focus="false" :muted="true" :enable-camera="true" :enable-mic="true"
|
||||
:zoom="true" :style="[{height: '350rpx' ,width:'750rpx'}]">
|
||||
</live-pusher>
|
||||
</view> -->
|
||||
<view class="table-list-item">
|
||||
<view class="scan-btn" @click="handleInstruct('shutter')">
|
||||
<text style="color: #FFF;">开始识别</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="table-list-item">
|
||||
<uni-row :gutter="24" style="display: flex; align-items: center">
|
||||
<uni-col :span="6">
|
||||
<text>设备编码</text>
|
||||
</uni-col>
|
||||
<uni-col :span="10">
|
||||
<view>
|
||||
<uni-easyinput v-model="queryCodeParams.maCode" placeholder="请输入内容" />
|
||||
</view>
|
||||
</uni-col>
|
||||
<uni-col :span="6">
|
||||
<view class="coding-btn" style="padding: 10rpx 0;color: #fe9a09;background-color: #fff7eb;border: 1px solid #fe9a09;"
|
||||
@click="getCode()">
|
||||
<text style="color: #fe9a09;text-align: center;">编码检索</text>
|
||||
</view>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
<div v-if="optionList.length > 1" class="select-container">
|
||||
<uni-data-select
|
||||
v-model="queryCodeParams.maId"
|
||||
:localdata="optionList"
|
||||
@change="changeTag"
|
||||
placeholder="请选择相关联编号"
|
||||
class="data-select"
|
||||
:searchable="false"
|
||||
></uni-data-select>
|
||||
</div>
|
||||
</view>
|
||||
<view class="table-list-item">
|
||||
<!-- <scroll-view class="scroll-view" scroll-y="true" style="height: 650rpx;"> -->
|
||||
<uni-forms :model="codeData" label-width="100" :border="true">
|
||||
<uni-forms-item label="物资名称:" name="materialType">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialType }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="物资类型:" name="materialName">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialName }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="规格型号:" name="materialModel">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialModel }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="设备编码:" name="maCode">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maCode }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="检验结论:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="本次检修时间:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="下次检修时间:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="检修员:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="检验员:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="联系电话:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="领料工程:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="领料单位:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<!-- </scroll-view> -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="page-container">
|
||||
<view class="table-list-item">
|
||||
<view class="scan-btn" @click="openCustomCamera">
|
||||
<text style="color: #FFF;">开始识别</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="table-list-item">
|
||||
<uni-row :gutter="24" style="display: flex; align-items: center">
|
||||
<uni-col :span="6">
|
||||
<text>设备编码</text>
|
||||
</uni-col>
|
||||
<uni-col :span="10">
|
||||
<view>
|
||||
<uni-easyinput v-model="queryCodeParams.maCode" placeholder="请输入内容" />
|
||||
</view>
|
||||
</uni-col>
|
||||
<uni-col :span="6">
|
||||
<view class="coding-btn" style="padding: 10rpx 0;color: #fe9a09;background-color: #fff7eb;border: 1px solid #fe9a09;"
|
||||
@click="getCode()">
|
||||
<text style="color: #fe9a09;text-align: center;">编码检索</text>
|
||||
</view>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
<div v-if="optionList.length > 1" class="select-container">
|
||||
<uni-data-select
|
||||
v-model="queryCodeParams.maId"
|
||||
:localdata="optionList"
|
||||
@change="changeTag"
|
||||
placeholder="请选择相关联编号"
|
||||
class="data-select"
|
||||
:searchable="false"
|
||||
></uni-data-select>
|
||||
</div>
|
||||
</view>
|
||||
|
||||
<view class="table-list-item">
|
||||
<uni-forms :model="codeData" label-width="100" :border="true">
|
||||
<uni-forms-item label="物资名称:" name="materialType">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialType }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="物资类型:" name="materialName">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialName }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="规格型号:" name="materialModel">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialModel }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="设备编码:" name="maCode">
|
||||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maCode }}</text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="检验结论:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="本次检修时间:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="下次检修时间:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="检修员:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="检验员:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="联系电话:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="领料工程:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="领料单位:" name="statusName">
|
||||
<text style="height: 100%;display: flex;align-items: center;"></text>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
livePusher: null,
|
||||
ready: true,
|
||||
cameraHeight: '', //相机画面宽度
|
||||
coverImage: null,
|
||||
queryParams:{},
|
||||
queryCodeParams:{
|
||||
maCode:"",
|
||||
maId:""
|
||||
},
|
||||
codeData:{},
|
||||
optionList:[],
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
// this.queryParams = JSON.parse(options.queryParams)
|
||||
// this.queryCodeParams.typeId = this.queryParams.typeId;
|
||||
},
|
||||
// onReady() {
|
||||
// this.cameraHeight = uni.getSystemInfoSync().screenHeight * 0.22
|
||||
// console.log(this.cameraHeight)
|
||||
// this.init()
|
||||
// },
|
||||
methods: {
|
||||
//初始化相机
|
||||
// init() {
|
||||
// this.livePusher = uni.createLivePusherContext('livePusher', this);
|
||||
// console.log(this.livePusher)
|
||||
// setTimeout(() => {
|
||||
// this.startPreview()
|
||||
// }, 1000)
|
||||
// },
|
||||
// 开始相机
|
||||
// startPreview() {
|
||||
// this.livePusher.startPreview({
|
||||
// success: () => {
|
||||
// console.log('相机初始化成功');
|
||||
// switch (plus.os.name) {
|
||||
// case 'Android':
|
||||
// break;
|
||||
// case 'iOS':
|
||||
// this.livePusher.switchCamera()
|
||||
// break
|
||||
// }
|
||||
// },
|
||||
// fail: (err) => {
|
||||
// console.log(err)
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
//截屏
|
||||
handleInstruct(instruct) {
|
||||
if (this.ready) {
|
||||
this.ready = false
|
||||
this.livePusher.snapshot({
|
||||
success: (res) => {
|
||||
console.log(res)
|
||||
this.ready = true
|
||||
this.$emit('getImage', res.message.tempImagePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
handleInstruct() {
|
||||
uni.showActionSheet({
|
||||
itemList: ['拍照', '从相册选择'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
this.getCamera()
|
||||
} else if (res.tapIndex === 1) {
|
||||
// 从相册选择
|
||||
this.getPhoto()
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('操作菜单选择失败:', err)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
getCamera() {
|
||||
console.log(1)
|
||||
navigator.camera.getPicture(this.onCameraSuccess, this.onCameraError, {
|
||||
quality: 50,
|
||||
destinationType: window.Camera.DestinationType.DATA_URL,
|
||||
sourceType: window.Camera.PictureSourceType.CAMERA,
|
||||
})
|
||||
},
|
||||
getPhoto() {
|
||||
console.log(2)
|
||||
navigator.camera.getPicture(this.onCameraSuccess, this.onCameraError, {
|
||||
quality: 50,
|
||||
destinationType: window.Camera.DestinationType.DATA_URL,
|
||||
sourceType: window.Camera.PictureSourceType.SAVEDPHOTOALBUM,
|
||||
})
|
||||
},
|
||||
onCameraError(message) {
|
||||
console.log(message)
|
||||
},
|
||||
onCameraSuccess(file) {
|
||||
// const file1 = "data:image/jpeg;base64," + file;
|
||||
const file1 = file
|
||||
let params = {
|
||||
image: file1,
|
||||
jiju_type: '',
|
||||
auth_lic:
|
||||
'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4=',
|
||||
}
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
queryParams: {},
|
||||
queryCodeParams: {
|
||||
maCode: "",
|
||||
maId: ""
|
||||
},
|
||||
codeData: {},
|
||||
optionList: []
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
// 监听OCR识别结果
|
||||
uni.$on('ocrResult', this.handleOcrResult);
|
||||
},
|
||||
|
||||
uni.request({
|
||||
url: '/material/app/ocr/getOcrCode',
|
||||
method: 'post',
|
||||
data: params,
|
||||
header: {
|
||||
'Content-Type': 'application/json', // 根据后端要求设置请求头,常见的 POST 请求数据格式为 JSON
|
||||
},
|
||||
success: (res) => {
|
||||
const { data: resData } = res
|
||||
onUnload() {
|
||||
// 移除事件监听
|
||||
uni.$off('ocrResult', this.handleOcrResult);
|
||||
},
|
||||
|
||||
if (resData.data.data.result) {
|
||||
// this.queryCodeParams.maCode = resData.data.data.result
|
||||
this.queryCodeParams.maCode = resData.data.data.result
|
||||
this.$nextTick(() => {
|
||||
this.getCode();
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '识别失败!' + resData.data.msg,
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: '请求失败:' + err.errMsg,
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 打开自定义相机页面
|
||||
openCustomCamera() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/devicesSearch/camera'
|
||||
});
|
||||
},
|
||||
|
||||
//编码检索
|
||||
async getCode(){
|
||||
let param = {
|
||||
maCode: this.queryCodeParams.maCode,
|
||||
}
|
||||
uni.request({
|
||||
url: '/material/ma_machine/getHisByCode',
|
||||
method: 'get',
|
||||
data: param,
|
||||
success: response => {
|
||||
response = response.data;
|
||||
if (response.data && response.data.length !== 0) {
|
||||
this.optionList = response.data.map(option => ({
|
||||
value: option.maId,
|
||||
text: option.maCode
|
||||
}))
|
||||
if(response.data.length === 1){
|
||||
this.codeData = response.data[0]
|
||||
}
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: '未查询到该编号信息',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
fail: err => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
// 处理OCR识别结果
|
||||
handleOcrResult(result) {
|
||||
if (result.success && result.result) {
|
||||
this.queryCodeParams.maCode = result.result;
|
||||
uni.showToast({
|
||||
title: '识别成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
},
|
||||
// 自动执行编码检索
|
||||
this.$nextTick(() => {
|
||||
this.getCode();
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '识别失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
changeTag(){
|
||||
if (!this.queryCodeParams.maId) return
|
||||
|
||||
try {
|
||||
let param = {
|
||||
maId: this.queryCodeParams.maId,
|
||||
}
|
||||
uni.request({
|
||||
url: '/material/ma_machine/getHisByCode',
|
||||
method: 'get',
|
||||
data: param,
|
||||
success: response => {
|
||||
response = response.data;
|
||||
if (response.data && response.data.length !== 0) {
|
||||
this.codeData = response.data[0]
|
||||
// 编码检索
|
||||
async getCode() {
|
||||
let param = {
|
||||
maCode: this.queryCodeParams.maCode,
|
||||
}
|
||||
uni.request({
|
||||
url: '/material/ma_machine/getHisByCode',
|
||||
method: 'get',
|
||||
data: param,
|
||||
success: response => {
|
||||
response = response.data;
|
||||
if (response.data && response.data.length !== 0) {
|
||||
this.optionList = response.data.map(option => ({
|
||||
value: option.maId,
|
||||
text: option.maCode
|
||||
}))
|
||||
if (response.data.length === 1) {
|
||||
this.codeData = response.data[0]
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '未查询到该编号信息',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: err => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: '获取编号信息失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
fail: err => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("获取编号信息失败", error)
|
||||
uni.showToast({
|
||||
title: '获取信息失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changeTag() {
|
||||
if (!this.queryCodeParams.maId) return
|
||||
|
||||
try {
|
||||
let param = {
|
||||
maId: this.queryCodeParams.maId,
|
||||
}
|
||||
uni.request({
|
||||
url: '/material/ma_machine/getHisByCode',
|
||||
method: 'get',
|
||||
data: param,
|
||||
success: response => {
|
||||
response = response.data;
|
||||
if (response.data && response.data.length !== 0) {
|
||||
this.codeData = response.data[0]
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '获取编号信息失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: err => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("获取编号信息失败", error)
|
||||
uni.showToast({
|
||||
title: '获取信息失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page-container {
|
||||
display: flex;
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
background-color: #f7f8fa;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
display: flex;
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
background-color: #f7f8fa;
|
||||
padding: 24rpx;
|
||||
}
|
||||
.table-list-item {
|
||||
background: #fff;
|
||||
padding: 32rpx;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 24rpx;
|
||||
.table-list-item {
|
||||
background: #fff;
|
||||
padding: 32rpx;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.table-list-item :deep(.uni-forms) {
|
||||
.uni-forms-item {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.uni-forms) {
|
||||
.uni-forms-item {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0;
|
||||
.uni-forms-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.uni-forms-item__label {
|
||||
color: #8c8c8c;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
padding: 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.uni-forms-item__label {
|
||||
color: #8c8c8c;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
padding: 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.uni-forms-item__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.uni-forms-item__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
.table-list-item :deep(.uni-row) {
|
||||
.uni-col-6 {
|
||||
color: #8c8c8c;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cont {
|
||||
color: #262626;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.uni-row) {
|
||||
/* margin-bottom: 20rpx; */
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.table-list-item :deep(.uni-easyinput__content) {
|
||||
background-color: #f7f8fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
height: 75rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.uni-col-6 {
|
||||
color: #8c8c8c;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.table-list-item :deep(.uni-easyinput__content:focus-within) {
|
||||
border-color: #3784fb;
|
||||
box-shadow: 0 0 0 2rpx rgba(55, 132, 251, 0.1);
|
||||
}
|
||||
|
||||
.cont {
|
||||
color: #262626;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
.table-list-item :deep(.uni-easyinput__content-input) {
|
||||
font-size: 28rpx;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content) {
|
||||
background-color: #f7f8fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
height: 75rpx;
|
||||
transition: all 0.3s ease;
|
||||
.scan-btn {
|
||||
height: 88rpx;
|
||||
background: #4b8eff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border-color: #3784fb;
|
||||
box-shadow: 0 0 0 2rpx rgba(55, 132, 251, 0.1);
|
||||
}
|
||||
.coding-btn {
|
||||
background: #fff;
|
||||
color: #ff9800;
|
||||
border: 2rpx solid rgba(255, 152, 0, 0.5);
|
||||
background: linear-gradient(to bottom, rgba(255, 152, 0, 0.05), rgba(255, 152, 0, 0.1));
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.uni-easyinput__content-input {
|
||||
font-size: 28rpx;
|
||||
color: #262626;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.scan-btn {
|
||||
/* width: 100%; */
|
||||
height: 88rpx;
|
||||
background: #4b8eff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
|
||||
/* transition: all 0.3s ease; */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.coding-btn {
|
||||
/* padding: 12rpx 0; */
|
||||
background: #fff;
|
||||
color: #ff9800;
|
||||
border: 2rpx solid rgba(255, 152, 0, 0.5);
|
||||
background: linear-gradient(to bottom, rgba(255, 152, 0, 0.05), rgba(255, 152, 0, 0.1));
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1rpx;
|
||||
/* transition: all 0.3s ease; */
|
||||
|
||||
/* &:active {
|
||||
background: rgba(255, 152, 0, 0.15);
|
||||
border-color: rgba(255, 152, 0, 0.6);
|
||||
transform: translateY(1rpx);
|
||||
} */
|
||||
}
|
||||
.outbound-btn {
|
||||
position: fixed;
|
||||
bottom: 40rpx;
|
||||
left: 20%;
|
||||
/* transform: translateX(-50%); */
|
||||
/* width: 90%; */
|
||||
height: 88rpx;
|
||||
width: 620rpx;
|
||||
margin: 0 10px;
|
||||
/* background: linear-gradient(135deg, #4b8eff 0%, #3784fb 100%); */
|
||||
background: #4b8eff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
|
||||
/* transition: all 0.3s ease; */
|
||||
|
||||
/* &:active {
|
||||
transform: translateX(-50%) scale(0.98);
|
||||
opacity: 0.9;
|
||||
} */
|
||||
}
|
||||
|
||||
.camera-background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.camera-options {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.camera-item {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.camera-item .camera-item-image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.camera-options-center .camera-item-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
.select-container {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.data-select {
|
||||
background-color: #f7f8fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
// 图片处理工具类
|
||||
class ImageUtils {
|
||||
/**
|
||||
* 将图片转换为Base64字符串(相当于Android的bitmapToString)
|
||||
* @param {string} imagePath 图片路径
|
||||
* @param {number} quality 压缩质量 0-100
|
||||
* @returns {Promise<string>} Base64字符串
|
||||
*/
|
||||
static async imageToBase64(imagePath, quality = 50) {
|
||||
try {
|
||||
// 如果已经是base64格式,直接返回数据部分
|
||||
if (imagePath.startsWith("data:image/")) {
|
||||
return imagePath.split(",")[1]
|
||||
}
|
||||
|
||||
// 如果是纯base64字符串,直接返回
|
||||
if (!imagePath.includes("/") && !imagePath.includes("\\")) {
|
||||
return imagePath
|
||||
}
|
||||
|
||||
// 压缩图片
|
||||
const compressResult = await new Promise((resolve, reject) => {
|
||||
window.uni.compressImage({
|
||||
src: imagePath,
|
||||
quality: quality,
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
|
||||
// 读取压缩后的文件并转换为base64
|
||||
const fileManager = window.uni.getFileSystemManager()
|
||||
const base64Data = await new Promise((resolve, reject) => {
|
||||
fileManager.readFile({
|
||||
filePath: compressResult.tempFilePath,
|
||||
encoding: "base64",
|
||||
success: (res) => resolve(res.data),
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
|
||||
return base64Data
|
||||
} catch (error) {
|
||||
console.error("Image to base64 conversion failed:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缩放后的图片(相当于Android的getSmallBitmap)
|
||||
* @param {string} imagePath 图片路径
|
||||
* @param {number} maxWidth 最大宽度
|
||||
* @param {number} maxHeight 最大高度
|
||||
* @returns {Promise<string>} 处理后的图片路径
|
||||
*/
|
||||
static async getSmallImage(imagePath, maxWidth = 1024, maxHeight = 1024) {
|
||||
try {
|
||||
return await this.resizeImageWithCanvas(imagePath, maxWidth, maxHeight)
|
||||
} catch (error) {
|
||||
console.error("Get small image failed:", error)
|
||||
// 如果canvas方法失败,尝试使用压缩API
|
||||
return await this.fallbackCompress(imagePath, maxWidth, maxHeight)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Canvas调整图片尺寸
|
||||
* @param {string} imagePath 图片路径
|
||||
* @param {number} maxWidth 最大宽度
|
||||
* @param {number} maxHeight 最大高度
|
||||
* @returns {Promise<string>} 处理后的图片路径
|
||||
*/
|
||||
static async resizeImageWithCanvas(imagePath, maxWidth, maxHeight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取图片信息
|
||||
window.uni.getImageInfo({
|
||||
src: imagePath,
|
||||
success: (imageInfo) => {
|
||||
const { width, height } = imageInfo
|
||||
|
||||
// 计算缩放比例
|
||||
const scale = this.calculateScale(width, height, maxWidth, maxHeight)
|
||||
const newWidth = Math.floor(width * scale)
|
||||
const newHeight = Math.floor(height * scale)
|
||||
|
||||
// 如果不需要缩放,直接返回原图
|
||||
if (scale >= 1) {
|
||||
resolve(imagePath)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建canvas上下文
|
||||
const canvasId = "imageProcessCanvas_" + Date.now()
|
||||
const ctx = window.uni.createCanvasContext(canvasId)
|
||||
|
||||
// 绘制缩放后的图片
|
||||
ctx.drawImage(imagePath, 0, 0, newWidth, newHeight)
|
||||
ctx.draw(false, () => {
|
||||
// 导出canvas为临时文件
|
||||
window.uni.canvasToTempFilePath({
|
||||
canvasId: canvasId,
|
||||
fileType: "jpg",
|
||||
quality: 0.8,
|
||||
success: (res) => {
|
||||
resolve(res.tempFilePath)
|
||||
},
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
},
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 备用压缩方法
|
||||
* @param {string} imagePath 图片路径
|
||||
* @param {number} maxWidth 最大宽度
|
||||
* @param {number} maxHeight 最大高度
|
||||
* @returns {Promise<string>} 处理后的图片路径
|
||||
*/
|
||||
static async fallbackCompress(imagePath, maxWidth, maxHeight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.uni.compressImage({
|
||||
src: imagePath,
|
||||
quality: 80,
|
||||
compressedWidth: maxWidth,
|
||||
compressedHeight: maxHeight,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算缩放比例(相当于Android的calculateInSampleSize)
|
||||
* @param {number} originalWidth 原始宽度
|
||||
* @param {number} originalHeight 原始高度
|
||||
* @param {number} maxWidth 最大宽度
|
||||
* @param {number} maxHeight 最大高度
|
||||
* @returns {number} 缩放比例
|
||||
*/
|
||||
static calculateScale(originalWidth, originalHeight, maxWidth, maxHeight) {
|
||||
if (originalWidth <= maxWidth && originalHeight <= maxHeight) {
|
||||
return 1 // 不需要缩放
|
||||
}
|
||||
|
||||
const widthScale = maxWidth / originalWidth
|
||||
const heightScale = maxHeight / originalHeight
|
||||
|
||||
// 选择较小的缩放比例,确保图片完全适应限制尺寸
|
||||
return Math.min(widthScale, heightScale)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量处理图片
|
||||
* @param {string} imagePath 图片路径
|
||||
* @param {object} options 处理选项
|
||||
* @returns {Promise<string>} 处理后的Base64字符串
|
||||
*/
|
||||
static async processImage(imagePath, options = {}) {
|
||||
const { maxWidth = 1024, maxHeight = 1024, quality = 50, outputFormat = "base64" } = options
|
||||
|
||||
try {
|
||||
// 1. 先缩放图片
|
||||
const resizedImagePath = await this.getSmallImage(imagePath, maxWidth, maxHeight)
|
||||
|
||||
// 2. 根据输出格式返回结果
|
||||
if (outputFormat === "base64") {
|
||||
return await this.imageToBase64(resizedImagePath, quality)
|
||||
} else {
|
||||
return resizedImagePath
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Process image failed:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片尺寸信息
|
||||
* @param {string} imagePath 图片路径
|
||||
* @returns {Promise<object>} 图片信息
|
||||
*/
|
||||
static async getImageInfo(imagePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.uni.getImageInfo({
|
||||
src: imagePath,
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default ImageUtils
|
||||
Loading…
Reference in New Issue