This commit is contained in:
2026-03-06 16:50:46 +08:00
commit 0704be4c9f
249 changed files with 46365 additions and 0 deletions

283
pagesMine/audit.vue Normal file
View File

@@ -0,0 +1,283 @@
<template>
<view class="container">
<z-paging ref="pagingRef" class="containerBox" v-model="listData" :default-page-size="queryParams.pageSize" @query="queryList">
<template #top>
<!-- 项目号/项目描述/物料号/物料描述/供应商编码/供应商名称/订单编号 -->
<uv-search v-model="queryParams.keyword" placeholder="请输入" :showAction="true" actionText="搜索" @search="searchList" @custom="searchList"></uv-search>
<uv-drop-down ref="dropDown" sign="dropDown_1" text-active-color="#3c9cff"
:extra-icon="{ name: 'arrow-down-fill', color: '#666', size: '26rpx' }"
:extra-active-icon="{ name: 'arrow-up-fill', color: '#3c9cff', size: '26rpx' }" :defaultValue="defaultValue"
:custom-style="{ padding: '0 30rpx' }" @click="selectMenu">
<uv-drop-down-item name="billType" type="2" :label="dropItem('billType').label" :value="dropItem('billType').value">
</uv-drop-down-item>
</uv-drop-down>
<uv-drop-down-popup sign="dropDown_1" :click-overlay-on-close="true" :currentDropItem="currentDropItem"
@clickItem="clickItem" @popupChange="change"></uv-drop-down-popup>
</template>
<view class="box">
<view v-for="item in listData" class="item" @tap="goDetail(item)">
<!-- <uv-icon name="file-text" size="36rpx" style="margin-top: 4rpx;" @tap="itemCopy(item)"></uv-icon> -->
<view class="title">单据号{{ item.billNo }}</view>
<view v-show="item.billType == 0"><text>入库类型</text>{{ item.rkTypeName }}</view>
<view v-show="item.billType == 1"><text>出库类型</text>{{ item.ckTypeName }}</view>
<view><text>所属仓库</text>{{ item.cangkuName }}</view>
<view><text>物资类型</text>{{ item.wlTypeName }}</view>
<view><text>理货员</text>{{ item.lihuoYName }}</view>
<view><text>提交时间</text>{{ parseTime(item.createTime) }}</view>
<view class="statusBtn">{{ typeItem('billType', parseInt(item.billType)) }}</view>
</view>
</view>
</z-paging>
</view>
</template>
<script setup>
import { ref, computed } from "vue";
import { onLoad, onShow, onPageScroll } from "@dcloudio/uni-app";
import { auditList } from "@/api/mine"
// *****下拉筛选
// 默认值
const defaultValue = ref([]);
// 选择结果
const result = ref([{ name: 'billType', label: '全部', value: '' }]);
const activeName = ref('');
// 筛选条件配置
const billType = ref({
label: '文档格式',
value: '',
activeIndex: 0,
color: '#333',
activeColor: '#2878ff',
child: [{
label: '全部',
value: ''
}, {
label: '入库',
value: 0
}, {
label: '出库',
value: 1
}]
});
// 计算属性
const dropItem = computed(() => {
return (name) => {
const resultObj = {};
const find = result.value.find(item => item.name === name);
if (find) {
resultObj.label = find.label;
resultObj.value = find.value;
} else {
resultObj.label = getPropertyByName(name).label;
resultObj.value = getPropertyByName(name).value;
}
return resultObj;
}
});
const currentDropItem = computed(() => {
return getPropertyByName(activeName.value);
});
// 工具函数:根据名称获取对应的响应式对象
function getPropertyByName(name) {
switch (name) {
case 'billType': return billType.value;
default: return {};
}
}
// 生命周期钩子
onPageScroll(() => {
// 滚动后及时更新位置
dropDown.value?.init();
});
const selectMenu = (e) => {
const { name, active, type } = e;
activeName.value = name;
// type 等于1 的需要特殊处理type不等于1可以忽略
if (type == 1) {
clickItem({
name: 'vip_type',
label: 'VIP文档',
value: e.active ? 1 : 0
});
} else {
const find = result.value.find(item => item.name == activeName.value);
if (find) {
const findIndex = getPropertyByName(activeName.value).child.findIndex(item =>
item.label == find.label && item.value == find.value
);
getPropertyByName(activeName.value).activeIndex = findIndex;
} else {
getPropertyByName(activeName.value).activeIndex = 0;
}
}
};
const clickItem = (e) => {
// 下面有重新赋值所以用let
let { label, value } = e;
const findIndex = result.value.findIndex(item => item.name == activeName.value);
if (defaultValue.value.indexOf(value) > -1 && getPropertyByName(activeName.value).label) {
label = getPropertyByName(activeName.value).label;
}
// 已经存在筛选项
if (findIndex > -1) {
result.value[findIndex] = {
name: activeName.value,
label,
value
}
} else {
result.value.push({
name: activeName.value,
label,
value
});
}
result.value = result.value.filter(item => defaultValue.value.indexOf(item.value) == -1);
result.value.forEach(item=> {
queryParams.value[item.name] = item.value
})
pagingRef.value.reload()
console.log("筛选的值:", queryParams.value)
};
const change = (e) => {
console.log('弹窗打开状态:', e);
}
// 获取到对应的入/出库
const typeItem = (name, value) => {
const scanTypeObj = getPropertyByName(name);
if (scanTypeObj && scanTypeObj.child) {
const item = scanTypeObj.child.find(item => item.value === value);
return item ? item.label : null;
}
return null;
}
const queryParams = ref({
keyword: "",
approverId: uni.getStorageSync('user').userId,
auditResult: 2,
billType: "",
pageNum: 1,
pageSize: 10
})
const pagingRef = ref(null)
const listData = ref([])
// 获取列表
const queryList = (pageNo, pageSize) => {
queryParams.value.pageNum = pageNo
console.log(pageNo, pageSize, queryParams.value)
auditList(queryParams.value).then(res => {
console.log(res.rows)
pagingRef.value.complete(res.rows)
}).catch(res => {
pagingRef.value.complete(false)
})
}
const searchList = () => {
pagingRef.value.reload()
}
onLoad(() => {
})
onShow(() => {
pagingRef.value?.reload()
})
const goDetail = (item) => {
uni.navigateTo({
url: "/pagesMine/auditDetail",
success: res => {
res.eventChannel.emit('auditInfo',{info:item})
},
})
}
</script>
<style scoped lang="scss">
.container {
position: relative;
.containerBox{
padding: 32rpx;
.dropDown{
padding: 0 30rpx;
border-bottom: 1px solid #dadbde;
.item_dropDown{
padding: 20rpx;
display: flex;
align-items: center;
::v-deep .uv-text{
width: unset !important;
flex: unset !important;
}
::v-deep .item_text{
max-width: 200rpx;
margin-right: 10rpx !important;
}
}
}
::v-deep .uv-search {
flex: unset;
}
.box{
// background-color: #ebebeb;
// background-color: #f5f5f5;
// padding-top: 20rpx;
.item {
background: #efefef;
padding: 20rpx;
margin-top: 20rpx;
line-height: 50rpx;
font-size: 28rpx;
border-radius: 8rpx;
color: #333;
position: relative;
.title {
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
}
text{
color: #666;
}
.statusBtn {
position: absolute;
top: 10rpx;
right: 10rpx;
background-color: #199793;
color: white;
padding: 0 10rpx;
border-radius: 4rpx;
font-size: 24rpx;
line-height: 40rpx;
}
}
}
}
::v-deep .uv-input__content{
flex-direction: column;
align-items: flex-start;
}
}
</style>

208
pagesMine/auditDetail.vue Normal file
View File

@@ -0,0 +1,208 @@
<template>
<view class="container">
<view v-show="detailData.billType == 0"><text>入库类型</text>{{ detailData.rkTypeName }}</view>
<view v-show="detailData.billType == 1"><text>出库类型</text>{{ detailData.ckTypeName }}</view>
<view><text>所属仓库</text>{{ detailData.cangkuName }}</view>
<view><text>物资类型</text>{{ detailData.wlTypeName }}</view>
<view><text>理货员</text>{{ detailData.lihuoYName }}</view>
<view><text>入库时间</text>{{ parseTime(detailData.createTime) }}</view>
<view class="box">
<view v-for="item in detailData.scenePhotos" class="item">
<view><text>项目描述</text>{{ item.xmMs }}</view>
<view><text>供应商</text>{{ item.gysMc }}</view>
<view><text>物料号</text>{{ item.wlNo }}</view>
<view><text>物料描述</text>{{ item.wlMs }}</view>
<view><text>数量</text>{{ item.realQty }}{{ item.dw }}</view>
<view><text>项目定义号</text>{{ item.xmNo }}</view>
<view><text>存放位置</text>{{ item.pcode || "-" }}</view>
<!-- <view><text>身份码</text>{{ item.entityId || "-" }}</view> -->
<view><text>备注</text>{{ item.rkRemark || "-" }}</view>
<view><text>现场照片</text><uv-image height="80" mode="heightFix" @tap="previewImage(item.signUrl)" :src="item.signUrl"></uv-image></view>
</view>
</view>
<view class="btn">
<uv-button type="error" text="确定" size="large" style="width: 48%;" @tap="openModal(0)"> </uv-button>
<uv-button type="primary" text="确定" size="large" class="mainBtn" style="width: 48%;" @tap="openModal(1)"> </uv-button>
</view>
<uv-modal ref="auditModalRef" title="审批信息" @confirm="auditConfirm" :showCancelButton="true" :closeOnClickOverlay="false" asyncClose>
<view class="slot-content" style="width: 100%;">
<uv-form labelPosition="top" :model="auditForm" labelWidth="80" :rules="auditRules" ref="auditFormRef">
<uv-form-item label="签字" prop="approverSignUrl" borderBottom v-if="auditStatus == 1">
<uv-button text="去签名" v-if="!auditForm.approverSignUrl" @tap="goSign"></uv-button>
<uv-image height="80" mode="heightFix" v-if="auditForm.approverSignUrl" @tap="previewImage(auditForm.approverSignUrl)" :src="auditForm.approverSignUrl"></uv-image>
</uv-form-item>
<uv-form-item label="备注" prop="remark" borderBottom>
<uv-textarea v-model="auditForm.remark" placeholder="备注" />
</uv-form-item>
</uv-form>
</view>
</uv-modal>
</view>
</template>
<script setup>
import { ref, computed, getCurrentInstance, onUnmounted } from "vue";
import { onLoad, onShow, onPageScroll } from "@dcloudio/uni-app";
import { passedAudit } from "@/api/mine"
const instance = getCurrentInstance().proxy
const detailData = ref({})
onLoad(() => {
const eventChannel = instance.getOpenerEventChannel();
// 监听redactInfo事件获取上一页面通过eventChannel传送到当前页面的数据
eventChannel.on('auditInfo', function(data) {
detailData.value = data.info
console.log("获取到信息", data.info)
});
})
// 预览
const previewImage = (img) => {
let arr = []
arr.push(img)
uni.previewImage({
urls: arr,
})
}
const auditModalRef = ref(null)
const auditFormRef = ref(null)
const auditStatus = ref(0)
const openModal = (status) => {
auditForm.value.approverSignUrl = ""
auditForm.value.remark = ""
auditStatus.value = status
auditModalRef.value.open();
}
const auditForm = ref({
approverSignUrl: "",
remark: '',
})
const auditRules = computed(() => ({
'approverSignUrl': {
type: 'string',
required: auditStatus.value == 1,
message: '请手写签字',
trigger: ['blur', 'change']
},
'remark': {
type: 'string',
required: auditStatus.value == 0,
message: '请填写备注',
trigger: ['blur', 'change']
},
}));
// 去签名页面
const goSign = () => {
// 移除之前的事件监听器避免重复
uni.$off('getSignImg')
uni.navigateTo({
url: '/pagesMine/signature',
success: () => {
// 监听签名返回事件
const eventHandler = (e) => {
console.log('getSignImg', e)
// signBase64.value = e.base64
// signTempimg.value = e.path
auditForm.value.approverSignUrl = e.url
}
// 监听一次自定义事件
uni.$once('getSignImg', eventHandler)
// 页面卸载时移除监听(避免内存泄漏)
// uni.$off('getSignImg', eventHandler)
}
})
}
const auditConfirm = () => {
auditFormRef.value.validate().then(res => {
let obj = {
billNo: detailData.value.billNo,
id: detailData.value.id,
rkId: detailData.value.rkId,
remark: auditForm.value.remark,
auditResult: auditStatus.value,
}
if (auditStatus.value == 1) {
obj.approverSignUrl = auditForm.value.approverSignUrl
}
passedAudit(obj).then(res => {
auditModalRef.value.close()
uni.showToast({
title: res.msg,
icon: 'none',
mask: true,
dution: 2000
})
setTimeout(() => {
uni.navigateBack({})
}, 2000)
})
}).catch(errors => {
auditModalRef.value.closeLoading()
})
}
// 组件卸载时移除事件监听器
onUnmounted(() => {
console.log("协助")
uni.$off('getSignImg')
})
</script>
<style scoped lang="scss">
.container {
position: relative;
padding: 32rpx;
padding-bottom: 120rpx;
line-height: 50rpx;
font-size: 28rpx;
border-radius: 8rpx;
color: #333;
text{
color: #666;
}
.box{
.item {
background: #efefef;
padding: 20rpx;
margin-top: 20rpx;
.title {
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
}
}
}
.btn{
position: fixed;
width: calc(100vw - 64rpx);
bottom: 0;
left: 32rpx;
z-index: 99;
display: flex;
align-items: center;
justify-content: space-between;
}
::v-deep .uv-input__content{
flex-direction: column;
align-items: flex-start;
}
}
</style>

196
pagesMine/index.js Normal file
View File

@@ -0,0 +1,196 @@
function getLocalFilePath(path) {
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
return path
}
if (path.indexOf('file://') === 0) {
return path
}
if (path.indexOf('/storage/emulated/0/') === 0) {
return path
}
if (path.indexOf('/') === 0) {
var localFilePath = plus.io.convertAbsoluteFileSystem(path)
if (localFilePath !== path) {
return localFilePath
} else {
path = path.substr(1)
}
}
return '_www/' + path
}
function dataUrlToBase64(str) {
var array = str.split(',')
return array[array.length - 1]
}
var index = 0
function getNewFileId() {
return Date.now() + String(index++)
}
function biggerThan(v1, v2) {
var v1Array = v1.split('.')
var v2Array = v2.split('.')
var update = false
for (var index = 0; index < v2Array.length; index++) {
var diff = v1Array[index] - v2Array[index]
if (diff !== 0) {
update = diff > 0
break
}
}
return update
}
export function pathToBase64(path) {
return new Promise(function(resolve, reject) {
if (typeof window === 'object' && 'document' in window) {
if (typeof FileReader === 'function') {
var xhr = new XMLHttpRequest()
xhr.open('GET', path, true)
xhr.responseType = 'blob'
xhr.onload = function() {
if (this.status === 200) {
let fileReader = new FileReader()
fileReader.onload = function(e) {
resolve(e.target.result)
}
fileReader.onerror = reject
fileReader.readAsDataURL(this.response)
}
}
xhr.onerror = reject
xhr.send()
return
}
var canvas = document.createElement('canvas')
var c2x = canvas.getContext('2d')
var img = new Image
img.onload = function() {
canvas.width = img.width
canvas.height = img.height
c2x.drawImage(img, 0, 0)
resolve(canvas.toDataURL())
canvas.height = canvas.width = 0
}
img.onerror = reject
img.src = path
return
}
if (typeof plus === 'object') {
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
entry.file(function(file) {
var fileReader = new plus.io.FileReader()
fileReader.onload = function(data) {
resolve(data.target.result)
}
fileReader.onerror = function(error) {
reject(error)
}
fileReader.readAsDataURL(file)
}, function(error) {
reject(error)
})
}, function(error) {
reject(error)
})
return
}
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
wx.getFileSystemManager().readFile({
filePath: path,
encoding: 'base64',
success: function(res) {
resolve('data:image/png;base64,' + res.data)
},
fail: function(error) {
reject(error)
}
})
return
}
reject(new Error('not support'))
})
}
export function base64ToPath(base64) {
return new Promise(function(resolve, reject) {
if (typeof window === 'object' && 'document' in window) {
base64 = base64.split(',')
var type = base64[0].match(/:(.*?);/)[1]
var str = atob(base64[1])
var n = str.length
var array = new Uint8Array(n)
while (n--) {
array[n] = str.charCodeAt(n)
}
return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
}
var extName = base64.split(',')[0].match(/data\:\S+\/(\S+);/)
if (extName) {
extName = extName[1]
} else {
reject(new Error('base64 error'))
}
var fileName = getNewFileId() + '.' + extName
if (typeof plus === 'object') {
var basePath = '_doc'
var dirPath = 'uniapp_temp'
var filePath = basePath + '/' + dirPath + '/' + fileName
if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
plus.io.resolveLocalFileSystemURL(basePath, function(entry) {
entry.getDirectory(dirPath, {
create: true,
exclusive: false,
}, function(entry) {
entry.getFile(fileName, {
create: true,
exclusive: false,
}, function(entry) {
entry.createWriter(function(writer) {
writer.onwrite = function() {
resolve(filePath)
}
writer.onerror = reject
writer.seek(0)
writer.writeAsBinary(dataUrlToBase64(base64))
}, reject)
}, reject)
}, reject)
}, reject)
return
}
var bitmap = new plus.nativeObj.Bitmap(fileName)
bitmap.loadBase64Data(base64, function() {
bitmap.save(filePath, {}, function() {
bitmap.clear()
resolve(filePath)
}, function(error) {
bitmap.clear()
reject(error)
})
}, function(error) {
bitmap.clear()
reject(error)
})
return
}
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
var filePath = wx.env.USER_DATA_PATH + '/' + fileName
wx.getFileSystemManager().writeFile({
filePath: filePath,
data: dataUrlToBase64(base64),
encoding: 'base64',
success: function() {
resolve(filePath)
},
fail: function(error) {
reject(error)
}
})
return
}
reject(new Error('not support'))
})
}

633
pagesMine/print.vue Normal file
View File

@@ -0,0 +1,633 @@
<template>
<div>
<div style="display: flex;flex-wrap: wrap;justify-content: space-evenly;">
<button type="primary" @click="openDev" style="width:100px;height:50px;" :disabled="isOpen == true">连接打印机</button>
<button type="primary" @click="closeConnect" style="width:100px;height:50px;" :disabled="isOpen == false">关闭连接</button>
<button type="primary" @click="getPrinterStatus" style="width:100px;height:50px;" :disabled="isOpen == false">获取状态</button>
<button type="primary" @click="printText" style="width:100px;height:50px;" :disabled="isOpen == false">文字打印</button>
<button type="primary" @click="printBarCode" style="width:100px;height:50px;" :disabled="isOpen == false">打印条码</button>
<button type="primary" @click="searchGap" style="width:100px;height:50px;" :disabled="isOpen == false">标签对齐</button>
<button type="primary" @click="printTable" style="width:100px;height:50px;" :disabled="isOpen == false">打印表格</button>
<button type="primary" @click="setSensitivity" style="width:100px;height:50px;" :disabled="isOpen == false">灵敏度</button>
<button type="primary" @click="printSelfCheck" style="width:100px;height:50px;" :disabled="isOpen == false">打印自检页</button>
<button type="primary" @click="setPaperFeed" style="width:100px;height:50px;" :disabled="isOpen == false">走纸</button>
<button type="primary" @click="setPaperBack" style="width:100px;height:50px;" :disabled="isOpen == false">退纸</button>
<button type="primary" @click="setDensity" style="width:100px;height:50px;" :disabled="isOpen == false">浓度设置</button>
<button type="primary" @click="setPaperType(0)" style="width:100px;height:50px;" :disabled="isOpen == false">设置热敏纸</button>
<button type="primary" @click="setPaperType(1)" style="width:100px;height:50px;" :disabled="isOpen == false">设置标签纸</button>
<button type="primary" @click="sendBytesData" style="width:100px;height:50px;" :disabled="isOpen == false">发送测试</button>
<button type="primary" @click="read" style="width:100px;height:50px;" :disabled="isOpen == false">读取测试</button>
<button type="primary" @click="setFlowMode" style="width:100px;height:50px;" :disabled="isOpen == false">打开流控开关</button>
<button type="primary" style="width:100px;height:50px;opacity: 0;">占位</button>
</div>
<canvas canvas-id="myCanvas" style="width: 340px;height: 240px;"></canvas>
<div style="height:50px;width:100px;">
<button type="primary" @click="printImg" style="width:300px;height:50px;" :disabled="isOpen == false">打印图片</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { onLoad, onReady } from '@dcloudio/uni-app';
var printSDK = uni.requireNativePlugin("PrintSDK");
import QRCode from '@/static/uqrcode.es.js'; // 确保引入qrcode.js
// 响应式数据
const isOpen = ref(false);
// 画布相关
let canvasContext = null;
// 生命周期钩子 - 组件加载时
onLoad(() => {
// 调用同步方法初始化程序和uhf
/* console.log("开始初始化")
uni.showToast({
title: "开始初始化",
icon: "none",
});
var ret = printSDK.initUHF()
var initResult = ret.code == "0"? '初始化成功':'初始化失败';
uni.showToast({
title: initResult,
icon: "none",
}); */
});
// 生命周期钩子 - 组件DOM已更新
onReady(() => {
drawCanvas();
});
// 生命周期钩子 - 组件挂载后
onMounted(() => {
// 注册全局事件监听
plus.globalEvent.addEventListener('sendMessage', function(e) {
if (e.tag == "getPrinterStatus") {
// 打印机状态返回值
uni.showToast({
title: "sendMessage收到" + JSON.stringify(e.msg),
icon: "none",
});
} else if (e.tag == "initPrint") {
// 连接和关闭打印机时返回的信息
uni.showToast({
title: "sendMessage收到" + JSON.stringify(e.msg),
icon: "none",
});
} else if (e.tag = "read") {
// read函数返回的读取到的打印机信息的函数
uni.showToast({
title: "sendMessage收到" + JSON.stringify(e.msg),
icon: "none",
});
}
});
});
// 绘制画布表格+二维码
const drawCanvas = () => {
canvasContext = uni.createCanvasContext('myCanvas');
console.log(canvasContext)
canvasContext.fillStyle = 'white'; // 设置填充颜色为红色
canvasContext.fillRect(0, 0, 340, 240); // 填充整个画板
drawTable(canvasContext);
drawText(canvasContext);
drawQRCode(canvasContext);
canvasContext.draw(true); // 异步绘制false表示绘制完成后不返回绘图结果
};
// 绘制文本
const drawText = (ctx) => {
ctx.setFontSize(25);
ctx.fillText('绘制测试页XX公司', 70, 40);
};
// 绘制二维码
const drawQRCode = (ctx) => {
// 使用uQRCode.js生成二维码
var qr = new QRCode('myCanvas', {
width: 70,
height: 70,
colorDark: "#000000",
colorLight: "#ffffff"
});
qr.data = "123";
qr.size = 70;
qr.make();
qr.canvasContext = ctx;
ctx.save();
// 平移画布到 (245, 115) 位置
ctx.translate(245, 115);
// 在平移后的位置绘制二维码
qr.drawCanvas();
// 恢复之前保存的画布上下文
ctx.restore();
};
// 绘制表格
const drawTable = (ctx) => {
// 绘制表格数据
ctx.moveTo(10, 1);
ctx.lineTo(320, 1);
ctx.moveTo(10, 70);
ctx.lineTo(320, 70);
ctx.setFontSize(20);
ctx.setFillStyle('#000000');
ctx.fillText('物料编号: 134588788-1', 20, 100);
ctx.moveTo(10, 110)
ctx.lineTo(320, 110);
ctx.fillText('物料名称: 连接杆', 20, 140);
ctx.moveTo(10, 150)
ctx.lineTo(240, 150);
ctx.moveTo(240, 110);
ctx.lineTo(240, 190);
ctx.fillText('批次: H28228618', 20, 180);
ctx.moveTo(10, 190)
ctx.lineTo(320, 190);
ctx.fillText('数量(QTY): 1', 20, 220);
ctx.moveTo(10, 230)
ctx.lineTo(320, 230);
ctx.setFontSize(25);
// ctx.fillText('标记问题件', 100, 275);
// ctx.moveTo(10, 285)
// ctx.lineTo(320, 285);
// 左边外边框
ctx.moveTo(10, 0)
ctx.lineTo(10, 230);
// 右边外边框
ctx.moveTo(320, 0)
ctx.lineTo(320, 230);
ctx.setStrokeStyle('red')
ctx.stroke()
};
// 流控开关
const setFlowMode = () => {
let a = printSDK.sendBytesData("1F111F2C371F1F")
console.log(a)
uni.showToast({
title: "流控开关已打开",
icon: "none",
});
};
// 连接打印机
const openDev = () => {
console.log("ret", printSDK);
var ret = printSDK.connectPrinter()
var openResult = ret.code == "0" ? '连接成功' : '连接失败';
if (ret.code == "0") {
isOpen.value = true
}
uni.showToast({
title: openResult,
icon: "none",
});
var ret = printSDK.initPrint();
if (ret.code != '0') {
uni.showToast({
title: "打印机初始化失败",
icon: "none",
});
}
};
// 下电
const closeConnect = () => {
var ret = printSDK.closeConnect()
var closeResult = ret.code == "0" ? '成功' : '失败';
if (ret.code == "0") {
isOpen.value = false
}
uni.showToast({
title: closeResult,
icon: "none",
});
};
// 打印文字测试
const printText = () => {
var ret = printSDK.initPrint();
if (ret.code != '0') {
uni.showToast({
title: "打印机初始化失败",
icon: "none",
});
} else {
printSDK.setFont(0, 1, 1, 0, 0);
printSDK.setPrinter(2, 1);
var ret = printSDK.printText("打印文字测试\r\n");
if (ret.code != '0') {
uni.showToast({
title: "函数执行失败",
icon: "none",
});
} else {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
printSDK.setFont(0, 0, 0, 0, 0);
printSDK.setPrinter(2, 0);
var ret = printSDK.printText("打印文字测试\r\n");
}
}
};
const printBarCode = () => {
var ret = printSDK.initPrint();
if (ret.code != '0') {
uni.showToast({
title: "打印机初始化失败",
icon: "none",
});
} else {
var type = [73, 4, 6, 5, 72, 3, 102]
for (var i = 0; i < type.length; i++) {
printSDK.setPrinter(1, 2);
if (type[i] == 102) {
var ret = printSDK.printBarCode(type[i], 2, 3, 6, "123456");
} else {
var ret = printSDK.printBarCode(type[i], 2, 162, 2, "0123456789");
}
printSDK.setPrinter(1, 3);
printSDK.setPrinter(2, 0);
if (ret.code == '0') {
uni.showToast({
title: "函数执行失败",
icon: "none",
});
}
}
}
};
// 保存图片并打印
const saveImage = (picData) => {
uni.saveImageToPhotosAlbum({
filePath: picData,
success(result) {
uni.showToast({
title: "开始打印图片",
icon: "none",
});
var destPic = result.file;
var ret = printSDK.initPrint();
if (ret.code != '0') {
uni.showToast({
title: "打印机初始化失败",
icon: "none",
});
} else {
printSDK.printText("\n");
printSDK.setPrinter(1, 1);
var path = result.path;
getImageSize(path);
uni.showToast({
title: "打印路径:" + path,
icon: "none",
});
console.log(path);
ret = printSDK.printImage(result.file, 3, 0, false);
// printSDK.setPaperFeed(50)
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
printSDK.searchGap()
} else {
uni.showToast({
title: "函数执行失败",
icon: "none",
});
}
}
},
fail() {
uni.hideLoading()
uni.showModal({
content: '检测到您没打开获取信息功能权限,是否去设置打开?',
confirmText: "确认",
cancelText: '取消',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (res) => {
console.log(res);
uni.showToast({
title: "请重新打印图片",
icon: "none"
});
}
})
} else {
uni.showToast({
title: "保存失败,请打开权限功能重试",
icon: "none"
});
}
}
})
}
})
};
// 获取图片尺寸
const getImageSize = (filePath) => {
// 使用 uni.getImageInfo 获取图片的元数据,包括尺寸信息
uni.getImageInfo({
src: filePath,
success: (imageInfo) => {
console.log('图片宽度:', imageInfo.width);
console.log('图片高度:', imageInfo.height);
},
fail: (err) => {
console.error('获取图片信息失败', err);
}
});
};
// 打印图片
const printImg = () => {
for (let index = 0; index < 5; index++) {
printSDK.setPaperBack(2)
}
// uni.showToast({
// title: "函数执行成功",
// icon: "none",
// });
// canvas保存成临时图片
uni.canvasToTempFilePath({
width: 350,
height: 600,
canvasId: 'myCanvas',
success: function(res) {
console.log(res.tempFilePath)
var picData = res.tempFilePath
// 后台直接打印图片
saveImage(picData);
}
})
};
// 打印表格
const printTable = () => {
var column = "品名;数量;单价;金额";
var regularExpression = ";";
var columnWidth = "14,6,6,6"; //逗号分隔字符串,不要调整格式,逗号和数字之间不要加空格!!!!!!!!!!!!!!
var rows = ["保鲜袋;10.00;1;10.00", "铁丝挂钩;5.00;2;10.00", "雨伞;5.00;3;15.00"];
var ret = printSDK.printTable(column, regularExpression, columnWidth, rows);
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功!",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
}
};
// 打印自检页
const printSelfCheck = () => {
var ret = printSDK.printSelfCheck();
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
}
};
// 走纸
const setPaperFeed = () => {
var ret = printSDK.setPaperFeed(2);
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
}
};
// 退纸
const setPaperBack = () => {
var ret = printSDK.setPaperBack(2);
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
}
};
// 获取打印机状态
const getPrinterStatus = () => {
var ret = printSDK.getPrinterStatus();
if (ret.code == "0") {
uni.showToast({
title: "正常",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
} else {
uni.showToast({
title: ret.code,
icon: "none",
});
}
};
// 标签对齐
const searchGap = () => {
var ret = printSDK.searchGap();
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
}
};
// 设置灵敏度
const setSensitivity = () => {
var ret = printSDK.setSensitivity(10);
if (ret.code == "0") {
uni.showToast({
title: "函数执行成功",
icon: "none",
});
} else if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
}
};
// 设置浓度
const setDensity = () => {
var ret = printSDK.setDensity(0);
if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
} else {
uni.showToast({
title: ret.code,
icon: "none",
});
}
};
// 设置纸张类型
const setPaperType = (type) => {
var ret = printSDK.setPaperType(type)
if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
} else {
uni.showToast({
title: ret.code,
icon: "none",
});
}
};
// 发送测试
const sendBytesData = () => {
var ret = printSDK.sendBytesData("1b3300")
if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
} else {
uni.showToast({
title: ret.code,
icon: "none",
});
}
};
// 读取测试
const read = () => {
var ret = printSDK.read()
if (ret.code == "-999") {
uni.showToast({
title: "打印机未连接",
icon: "none",
});
} else if (ret.code == "999") {
uni.showToast({
title: "打印机未初始化",
icon: "none",
});
} else {
uni.showToast({
title: ret.code,
icon: "none",
});
}
};
</script>
<style scoped lang="scss">
uni-button{
margin: 0;
margin-bottom: 30rpx;
}
canvas {
/* border: 1px solid #000; */
}
</style>

88
pagesMine/signPreview.vue Normal file
View File

@@ -0,0 +1,88 @@
<template>
<view class="exmaple1">
<button @click="onSign">去签名</button>
<view class="sign-preview">
<image
class="sign-image"
v-if="signBase64"
:src="signBase64"
mode="widthFix"
@click="onPreview"
></image>
<view v-else>签名预览</view>
</view>
</view>
</template>
<script setup>
import { onShow } from '@dcloudio/uni-app'
import { ref, onUnmounted } from 'vue'
const signBase64 = ref('')
const signTempimg = ref('')
// 去签名页面
const onSign = () => {
// 移除之前的事件监听器避免重复
uni.$off('getSignImg')
uni.navigateTo({
url: '/pagesMine/signature',
success: () => {
// 监听签名返回事件
const eventHandler = (e) => {
console.log('getSignImg', e)
signBase64.value = e.base64
signTempimg.value = e.path
}
// 监听一次自定义事件
uni.$once('getSignImg', eventHandler)
// 页面卸载时移除监听(避免内存泄漏)
// uni.$off('getSignImg', eventHandler)
}
})
}
// 预览签名图片
const onPreview = () => {
if (signTempimg.value) {
uni.previewImage({
urls: [signTempimg.value]
})
}
}
onShow(()=>{
// plus.screen.lockOrientation('portrait-primary');
console.log("竖屏")
})
// 组件卸载时移除事件监听器
// onUnmounted(() => {
// uni.$off('getSignImg')
// })
</script>
<style>
.exmaple1 {
padding: 24rpx;
box-sizing: border-box;
}
.sign-preview {
min-height: 1000rpx;
margin-top: 24rpx;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #66ccff;
background-color: #f0f0f0;
color: #bbbbbb;
}
.sign-image {
width: 640rpx;
}
</style>

278
pagesMine/signature.vue Normal file
View File

@@ -0,0 +1,278 @@
<template>
<view class="box">
<canvas v-if="showCanvas" id="canvas" canvas-id="canvas" style="width: 100vw;background-color: green;" :style="`height:${canvasHeight}px`" @touchstart="startDraw" @touchmove="drawing" @@touchend="endDraw"></canvas>
<cover-view class="tipTxt">请在空白处横屏签名</cover-view>
<view class="btn">
<uv-button size="large" style="width: 50%;" @tap="clearSign"> </uv-button>
<uv-button type="primary" size="large" class="mainBtn" style="width: 50%;" @tap="confirm"> </uv-button>
</view>
</view>
</template>
<script setup>
import { onReady, onUnload } from '@dcloudio/uni-app';
import { ref, nextTick } from 'vue';
import { pathToBase64, base64ToPath } from './index.js'
import { uploadImg } from "@/api/upload"
const btnHeight = ref(null)
const BoxHeight = ref(null)
const canvasWidth = ref(null)
const canvasHeight = ref(null)
const showCanvas = ref(false)
const signFlag = ref(false)
const getBtnHeight = () => {
const query = uni.createSelectorQuery().in(this);
// 选择要查询的元素使用元素ID或类名
query.select('.btn').boundingClientRect(data => {
btnHeight.value = data.height
}).select('.box').boundingClientRect(res => {
// console.log('元素高度:', res);
// BoxHeight.value = res.height
// canvasWidth.value = res.width
}).exec(() => {
// 确保两个查询都完成后再初始化画布
if (btnHeight.value && BoxHeight.value && canvasWidth.value) {
console.log("获取",BoxHeight.value,btnHeight.value);
canvasHeight.value = BoxHeight.value - btnHeight.value
showCanvas.value = true
// 等待DOM更新完成后再初始化Canvas
nextTick(() => {
initCanvas();
});
} else {
console.error('获取元素尺寸失败,无法初始化画布');
}
}); // 执行查询
}
let canvasContext = null
const initCanvas = () => {
console.log(canvasWidth.value, canvasHeight.value)
canvasContext = uni.createCanvasContext('canvas');
canvasContext.setFillStyle('#F5F5F5'); // 设置填充颜色为红色
canvasContext.fillRect(0, 0, canvasWidth.value, canvasHeight.value); // 填充整个画板
canvasContext.draw(true)
}
const startX = ref(null)
const startY = ref(null)
const startDraw = (e) => {
startX.value = e.touches[0].x
startY.value = e.touches[0].y
canvasContext.beginPath(); // 开始创建一个路径
signFlag.value = true
}
const endDraw = (e) => {
startX.value = 0
startY.value = 0
}
const drawing = (e) => {
let temX = e.touches[0].x;
let temY = e.touches[0].y;
canvasContext.moveTo(startX.value, startY.value)
canvasContext.lineTo(temX, temY)
canvasContext.setStrokeStyle('#000') // 文字颜色
canvasContext.setLineWidth(4) // 文字宽度
canvasContext.setLineCap('round') // 文字圆角
canvasContext.stroke() // 文字绘制
startX.value = temX
startY.value = temY
canvasContext.draw(true) // 文字绘制到画布中
}
const clearSign = () => {
canvasContext.setFillStyle('#F5F5F5'); // 设置填充颜色为红色
canvasContext.fillRect(0, 0, canvasWidth.value, canvasHeight.value); // 填充整个画板
canvasContext.draw(true) // 文字绘制到画布中
signFlag.value = false
}
const confirm = () => {
// 确认按钮事件
if (!signFlag.value) {
uni.showToast({
title: '请签名后再点击确定',
icon: 'none',
duration: 2000
})
return
}
uni.showModal({
title: '确认',
content: '确认签名无误吗',
showCancel: true,
success: async ({ confirm }) => {
if (confirm) {
let tempFile
// if (this.horizontal) {
tempFile = await saveHorizontalCanvas()
// } else {
// tempFile = await this.saveCanvas()
// }
console.log(tempFile, "tempFile")
const base64 = await pathToBase64(tempFile)
const path = await base64ToPath(base64)
console.log(path, "path")
const res = await uploadImg(path, {});
uni.navigateBack();
uni.$emit('getSignImg', { base64, path, url: res.url });
// uploadImg(path, {}).then(res=>{
// console.log(base64, path, res.url, "打印")
// let url = res.url
// uni.$emit('getSignImg', { base64, path, url })
// uni.navigateBack()
// })
}
}
})
}
const saveHorizontalCanvas = async () => {
return await new Promise((resolve, reject) => {
uni.canvasToTempFilePath(
{
canvasId: 'canvas',
fileType: "png", // 只支持png和jpg
success: (res) => {
if (!res.tempFilePath) {
uni.showModal({
title: '提示',
content: '保存签名失败',
showCancel: false
})
return
}
// #ifdef APP
uni.compressImage({
src: res.tempFilePath,
quality: 100, // 范围 0 - 100
rotate: 270,
success: (r) => {
console.log('==== compressImage :', r)
resolve(r.tempFilePath)
}
})
// #endif
// #ifndef APP
// uni.getImageInfo({
// src: res.tempFilePath,
// success: (r) => {
// // console.log('==== getImageInfo :', r)
// // 将signCanvas的内容复制到hsignCanvas中
// const hcanvasCtx = uni.createCanvasContext('hsignCanvas', this)
// // 横屏宽高互换
// hcanvasCtx.translate(this.canvasHeight / 2, this.canvasWidth / 2)
// hcanvasCtx.rotate(Math.PI * (-90 / 180))
// hcanvasCtx.drawImage(
// r.path,
// -this.canvasWidth / 2,
// -this.canvasHeight / 2,
// this.canvasWidth,
// this.canvasHeight
// )
// hcanvasCtx.draw(false, async () => {
// const hpathRes = await uni.canvasToTempFilePath(
// {
// canvasId: 'hsignCanvas',
// fileType: this.expFile.fileType, // 只支持png和jpg
// quality: this.expFile.quality // 范围 0 - 1
// },
// this
// )
// let tempFile = ''
// if (Array.isArray(hpathRes)) {
// hpathRes.some((item) => {
// if (item) {
// tempFile = item.tempFilePath
// return
// }
// })
// } else {
// tempFile = hpathRes.tempFilePath
// }
// resolve(tempFile)
// })
// }
// })
// #endif
},
fail: (err) => {
console.log('图片生成失败:' + err)
resolve(false)
}
},
this
)
})
}
onReady(() => {
// uni.showLoading({
// title: '正在加载,请稍后...',
// mask: true
// })
// 页面加载完成后设置横屏
// plus.screen.lockOrientation('landscape-primary')
// nextTick(() => {
// getBtnHeight();
// });
// setTimeout(() => {
// uni.hideLoading();
uni.getSystemInfo({
success: function (res) {
console.log(res)
BoxHeight.value = res.windowHeight
canvasWidth.value = res.windowWidth
}
});
getBtnHeight();
// }, 1500);
// nextTick(() => {
// getBtnHeight();
// });
})
onUnload(() => {
// 页面卸载时恢复竖屏
// plus.screen.lockOrientation('portrait-primary');
})
</script>
<style scoped lang="scss">
.box {
position: relative;
width: 100vw;
height: 100vh;
.tipTxt{
position: absolute;
top: 10rpx;
left: 10rpx;
color: #999;
font-size: 44rpx;
}
.btn {
position: fixed;
width: 100vw;
bottom: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: space-between;
}
}
</style>