添加扫描商品

This commit is contained in:
lewis 2025-04-13 17:06:37 +08:00
parent 17b927213a
commit 77aac21317
5 changed files with 191 additions and 0 deletions

7
package-lock.json generated
View File

@ -23,6 +23,7 @@
"moment": "^2.30.1", "moment": "^2.30.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.14", "pinia": "^2.0.14",
"tracking": "^1.1.3",
"vditor": "^3.9.9", "vditor": "^3.9.9",
"vue": "^3.5.12", "vue": "^3.5.12",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
@ -8028,6 +8029,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/tracking": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/tracking/-/tracking-1.1.3.tgz",
"integrity": "sha512-XS9CVMzjp6AHT5e7oRamU8U5Lz3k6BIRTkBVs0XqepwgwXQ4kbbmxrWzTG2v6RMvJC/9MKuSBQiX/5hwsQFcCQ==",
"license": "BSD"
},
"node_modules/traverse": { "node_modules/traverse": {
"version": "0.6.8", "version": "0.6.8",
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz",

View File

@ -25,6 +25,7 @@
"moment": "^2.30.1", "moment": "^2.30.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.14", "pinia": "^2.0.14",
"tracking": "^1.1.3",
"vditor": "^3.9.9", "vditor": "^3.9.9",
"vue": "^3.5.12", "vue": "^3.5.12",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",

View File

@ -63,3 +63,11 @@ export function apiPurchaseProductOffersetStoreroomInfoTwo(params: any) {
export function apiPurchaseProductOfferDetail(params: any) { export function apiPurchaseProductOfferDetail(params: any) {
return request.get({ url: '/purchase_product_offer/purchaseproductoffer/detail', params }) return request.get({ url: '/purchase_product_offer/purchaseproductoffer/detail', params })
} }
// 上传商品图片
export function apiPurchaseProductOfferUpload(params: any) {
return request.post({
url: '/purchase_product_offer/purchaseproductoffer/upload',
params
})
}

View File

@ -17,6 +17,11 @@
</el-form> </el-form>
</el-card> </el-card>
<el-card class="!border-none" v-loading="pager.loading" shadow="never"> <el-card class="!border-none" v-loading="pager.loading" shadow="never">
<router-link v-perms="['order/sort_list/scan']" :to="{
path: 'order_sort_list/scan'
}" class="ml-4 mr-4">
<el-button type="primary"> 扫描商品 </el-button>
</router-link>
<div class="mt-4"> <div class="mt-4">
<el-table :data="pager.lists"> <el-table :data="pager.lists">
<el-table-column label="ID" prop="id" show-overflow-tooltip /> <el-table-column label="ID" prop="id" show-overflow-tooltip />

View File

@ -0,0 +1,170 @@
<template>
<div class="demo-frame">
<div class="demo-container">
<video ref="videoRef" id="video" width="400" height="300" preload autoplay loop muted></video>
<canvas ref="canvasRef" id="canvas" width="400" height="300"></canvas>
</div>
</div>
</template>
<script lang="ts">
import 'tracking';
import { ElMessage, type FormInstance } from 'element-plus'
import { apiPurchaseProductOfferUpload } from '@/api/purchase_product_offer'
export default {
name: 'App',
data() {
return {
canvas: null,
context: null,
video: null,
tracker: null,
needInit: false,
stream: null,
};
},
async mounted() {
this.canvas = this.$refs.canvasRef;
this.context = this.canvas.getContext('2d');
this.video = this.$refs.videoRef;
this.startTrack()
},
methods: {
startTrack() {
let getUserMedia = function(constrains, success, error) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit
navigator.webkitGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.mozGetUserMedia) {
//Firefox
navagator.mozGetUserMedia(constrains).then(success).catch(error);
} else if (navigator.getUserMedia) {
//API
navigator.getUserMedia(constrains).then(success).catch(error);
} else {
ElMessage.error('浏览器不支持getUserMedia')
}
}
window.tracking.initUserMedia_ = function (element, opt_options) {
const options = {
video: true,
audio: !!(opt_options && opt_options.audio)
};
getUserMedia(options, function (stream) {
try {
element.srcObject = stream;
} catch (err) {
element.src = window.URL.createObjectURL(stream);
}
}, function (e) {
ElMessage.error(e.message)
});
};
// stopbug
window.tracking.trackVideo_ = function (element, tracker) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var width;
var height;
var resizeCanvas_ = function () {
width = element.offsetWidth;
height = element.offsetHeight;
canvas.width = width;
canvas.height = height;
};
resizeCanvas_();
element.addEventListener('resize', resizeCanvas_);
var requestId;
var stopped = false;
var requestAnimationFrame_ = function () {
requestId = window.requestAnimationFrame(function () {
if (element.readyState === element.HAVE_ENOUGH_DATA) {
try {
context.drawImage(element, 0, 0, width, height);
} catch (err) { }
tracking.trackCanvasInternal_(canvas, tracker);
}
if (stopped !== true) {
requestAnimationFrame_();
}
});
};
var task = new tracking.TrackerTask(tracker);
task.on('stop', function () {
stopped = true;
window.cancelAnimationFrame(requestId);
});
task.on('run', function () {
stopped = false;
requestAnimationFrame_();
});
return task.run();
};
const FastTracker = function () {
FastTracker.base(this, 'constructor');
};
tracking.inherits(FastTracker, tracking.Tracker);
tracking.Fast.THRESHOLD = 8;
FastTracker.prototype.threshold = tracking.Fast.THRESHOLD;
FastTracker.prototype.track = function (pixels, width, height) {
const gray = tracking.Image.grayscale(pixels, width, height);
const corners = tracking.Fast.findCorners(gray, width, height);
this.emit('track', {
data: corners
});
};
this.tracker = new FastTracker();
this.tracker.on('track', (event) => {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
const corners = event.data;
if (corners.length > 220) {
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
if (!this.needInit) {
this.needInit = true
setTimeout(() => {
this.upload()
}, 1000)
}
} else {
this.needInit = false
}
});
tracking.track('#video', this.tracker, {
camera: true
});
},
upload() {
this.canvas.toBlob(async (blob) => {
if (blob) {
try {
const formData = new FormData();
formData.append('file', blob, 'motion_photo.jpg');
await apiPurchaseProductOfferUpload(formData).then(res => {
})
} catch (error) {
console.error('上传过程出错:', error);
}
}
}, 'image/jpeg');
}
}
};
</script>
<style scoped>
</style>