Browse Source

打印

hella_vue3
zhang_li 5 months ago
parent
commit
f6f44c02bb
  1. 196
      src/api/img-to-base64.js
  2. 133
      src/hybrid/html/point.html
  3. 16
      src/pages.json
  4. 214
      src/pages/point/index.vue
  5. 31
      src/uni_modules/lime-qrcode/changelog.md
  6. 234
      src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue
  7. 223
      src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue
  8. 36
      src/uni_modules/lime-qrcode/components/l-qrcode/props.ts
  9. 6
      src/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js
  10. 47
      src/uni_modules/lime-qrcode/components/l-qrcode/type.ts
  11. 78
      src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts
  12. 35
      src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts
  13. 140
      src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue
  14. 79
      src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue
  15. 77
      src/uni_modules/lime-qrcode/hybrid/html/index.html
  16. 6
      src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js
  17. 1
      src/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js
  18. 91
      src/uni_modules/lime-qrcode/package.json
  19. 152
      src/uni_modules/lime-qrcode/readme.md
  20. 25
      src/uni_modules/lime-shared/addUnit/index.ts
  21. 63
      src/uni_modules/lime-shared/arrayBufferToFile/index.ts
  22. 13
      src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts
  23. 76
      src/uni_modules/lime-shared/base64ToPath/index.ts
  24. 21
      src/uni_modules/lime-shared/camelCase/index.ts
  25. 58
      src/uni_modules/lime-shared/canIUseCanvas2d/index.ts
  26. 30
      src/uni_modules/lime-shared/changelog.md
  27. 16
      src/uni_modules/lime-shared/clamp/index.ts
  28. 103
      src/uni_modules/lime-shared/cloneDeep/index.ts
  29. 22
      src/uni_modules/lime-shared/closest/index.ts
  30. 149
      src/uni_modules/lime-shared/createAnimation/index.ts
  31. 61
      src/uni_modules/lime-shared/createImage/index.ts
  32. 38
      src/uni_modules/lime-shared/debounce/index.ts
  33. 1056
      src/uni_modules/lime-shared/exif/index.ts
  34. 11
      src/uni_modules/lime-shared/fillZero/index.ts
  35. 36
      src/uni_modules/lime-shared/floatAdd/index.ts
  36. 27
      src/uni_modules/lime-shared/getClassStr/index.ts
  37. 6
      src/uni_modules/lime-shared/getCurrentPage/index.ts
  38. 14
      src/uni_modules/lime-shared/getLocalFilePath/index.ts
  39. 86
      src/uni_modules/lime-shared/getRect/index.ts
  40. 30
      src/uni_modules/lime-shared/getStyleStr/index.ts
  41. 30
      src/uni_modules/lime-shared/hasOwn/index.ts
  42. 43
      src/uni_modules/lime-shared/index.ts
  43. 9
      src/uni_modules/lime-shared/isBase64/index.ts
  44. 2
      src/uni_modules/lime-shared/isBrowser/index.ts
  45. 9
      src/uni_modules/lime-shared/isDef/index.ts
  46. 8
      src/uni_modules/lime-shared/isFunction/index.ts
  47. 9
      src/uni_modules/lime-shared/isNumber/index.ts
  48. 9
      src/uni_modules/lime-shared/isNumeric/index.ts
  49. 8
      src/uni_modules/lime-shared/isObject/index.ts
  50. 13
      src/uni_modules/lime-shared/isPromise/index.ts
  51. 7
      src/uni_modules/lime-shared/isString/index.ts
  52. 17
      src/uni_modules/lime-shared/kebabCase/index.ts
  53. 83
      src/uni_modules/lime-shared/package.json
  54. 121
      src/uni_modules/lime-shared/pathToBase64/index.ts
  55. 2320
      src/uni_modules/lime-shared/piexif/index.ts
  56. 27
      src/uni_modules/lime-shared/platform/index.ts
  57. 30
      src/uni_modules/lime-shared/raf/index.ts
  58. 22
      src/uni_modules/lime-shared/random/index.ts
  59. 31
      src/uni_modules/lime-shared/range/index.ts
  60. 251
      src/uni_modules/lime-shared/readme.md
  61. 152
      src/uni_modules/lime-shared/selectComponent/index.ts
  62. 30
      src/uni_modules/lime-shared/sleep/index.ts
  63. 41
      src/uni_modules/lime-shared/throttle/index.ts
  64. 13
      src/uni_modules/lime-shared/toArray/index.ts
  65. 15
      src/uni_modules/lime-shared/toNumber/index.ts
  66. 39
      src/uni_modules/lime-shared/unitConvert/index.ts
  67. 81
      src/uni_modules/lime-shared/useIntersectionObserver/index.ts
  68. 8
      src/uni_modules/lime-shared/vue/index.ts

196
src/api/img-to-base64.js

@ -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 async function pathToBase64(path) {
return await new Promise(async function(resolve, reject) {
if (typeof window === 'object' && 'document' in window) {
if (typeof FileReader === 'function') {
var xhr =await 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'))
})
}

133
src/hybrid/html/point.html

@ -0,0 +1,133 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title></title>
<script src="../../api/img-to-base64.js"></script>
<script type="text/javascript">
document.addEventListener('plusready', function(){
//console.log("所有plus api都应该在此事件发生后调用,否则会出现plus is undefined。")
});
// 解析URL参数
function getQueryParam(param) {
var searchParams = new URLSearchParams(window.location.search);
return searchParams.get(param);
}
// 使用函数获取参数
document.addEventListener('DOMContentLoaded', function() {
// var itemNumber = getQueryParam('itemNumber');
// var itemName = getQueryParam('itemName');
// var tempFilePath = getQueryParam('tempFilePath')
// document.getElementById('name1').innerText = itemNumber;
// document.getElementById('name2').innerText = itemName;
// // document.getElementById('image1').src = barcodeImage;
// console.log(99,tempFilePath)
// pathToBase64(tempFilePath){
// }
// pathToBase64(tempFilePath).then(base64 => {
// document.getElementById('image1').src = base64;
// }).catch(err => {
// console.log(err);
// })
// console.log(document.getElementById('name1'))
});
</script>
<style>
.box{
font-size: 14px;
display: flex;
}
.left{
border-top: 1px solid #b1b1b1;
border-left: 1px solid #b1b1b1;
flex: 1;
}
.left-item{
display: flex;
}
.label{
border-bottom: 1px solid #b1b1b1;
border-right: 1px solid #b1b1b1;
width: 60px;
padding: 0px 5px;
height: 67px;
line-height: 67px;
}
.value{
border-bottom: 1px solid #b1b1b1;
border-right: 1px solid #b1b1b1;
padding: 0px 5px;
height: 67px;
flex: 1;
width: 0px;
display: flex;
align-items: center;
word-wrap: break-word;
}
.right{
width: 200px;
}
.image{
width: 100%;
height: 202px;
border-bottom: 1px solid #b1b1b1;
border-top: 1px solid #b1b1b1;
border-right: 1px solid #b1b1b1;
}
.image img{
width: calc(100% - 4px);
height: calc(100% - 4px);
margin: 2px;
}
</style>
</head>
<body>
<div>
<div class="box">
<div class="left">
<div class="left-item">
<div class="label">物品代码</div>
<div class="value" id="name1">replaceItemNumber</div>
</div>
<div class="left-item">
<div class="label">物品名称</div>
<div class="value" id="name2">replaceItemName</div>
</div>
<div class="left-item">
<div class="label">供应商</div>
<div class="value">replaceSupplierCode</div>
</div>
<div class="left-item">
<div class="label">包装号</div>
<div class="value">replacePackageCode</div>
</div>
<div class="left-item">
<div class="label">订单行</div>
<div class="value">replaceReferenceOrderRow</div>
</div>
</div>
<div class="right">
<div class="image">
<img id='image1' src="replaceBase" alt="" />
</div>
<div class="left-item">
<div class="label">订单号</div>
<div class="value">replaceReferenceOrderCode</div>
</div>
<div class="left-item">
<div class="label">数量</div>
<div class="value">replaceAmount</div>
</div>
</div>
</div>
</div>
</body>
</html>

16
src/pages.json

@ -2021,25 +2021,13 @@
"navigationBarTitleText": "翻包任务详情",
"enablePullDownRefresh": false
}
},
// ,{
// "path": "pages/point/index",
// "style": {
// "navigationBarTitleText": "打印",
// "enablePullDownRefresh": false
// }
// },
{
"path": "pages/print/index",
},{
"path": "pages/point/index",
"style": {
"navigationBarTitleText": "打印",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "white",

214
src/pages/point/index.vue

@ -1,34 +1,77 @@
<template>
<view class="content">
<view class="" style="position: absolute; bottom:80px ;left: 30px;z-index: 99999;">
打印
<view class="content" style="padding:30rpx 20rpx;min-height: calc(100vh - 40rpx);">
<!-- 只用于展示页面 -->
<view class="box">
<view class="left">
<view class="left-item">
<view class="label">物品代码</view>
<view class="value" id="name1">{{data.itemNumber}}</view>
</view>
<view class="left-item">
<view class="label">物品名称</view>
<view class="value" id="name2">{{data.itemName}}</view>
</view>
<view class="left-item">
<view class="label">供应商</view>
<view class="value">{{data.supplierCode}}</view>
</view>
<view class="left-item">
<view class="label">包装号</view>
<view class="value">{{data.packageCode}}</view>
</view>
<view class="left-item">
<view class="label">订单行</view>
<view class="value">{{data.referenceOrderRow}}</view>
</view>
</view>
<view class="right">
<view class="image">
<l-qrcode ref="qrcodeRef" :value="data.barcodeString" size="300rpx"></l-qrcode>
</view>
<view class="left-item">
<view class="label">订单号</view>
<view class="value">{{data.referenceOrderCode}}</view>
</view>
<view class="left-item">
<view class="label">数量</view>
<view class="value">{{data.amount}}</view>
</view>
</view>
</view>
<view class="">
<button @click="printImage"
style='background:rgb(60, 156, 255) !important ;color: white;margin-top: 80rpx;'> 打印</button>
</view>
<!-- #ifdef APP -->
<web-view src="/hybrid/html/point.html" :fullscreen='false' :webview-styles="webviewStyles"></web-view>
<!-- #endif -->
</view>
</template>
<script>
//
const htmlFileUrl = '/hybrid/html/point.html';
import {
getList
} from '@/api/request2.js';
pathToBase64
} from "../../api/img-to-base64.js" //js
// #ifdef APP
var testModule = uni.requireNativePlugin("TestModule")
// #endif
export default {
data() {
return {
webviewStyles: {
progress: {
color: '#FF3333'
},
width: '100%',
height: '500px',
}
data: {},
newHtmlContent: '' //html
}
},
methods: {
// #ifdef APP
// #ifdef APP
//
printImage() {
testModule.doHTMLPrint(this.newHtmlContent)
},
// html
readFile(path, callback) {
plus.io.resolveLocalFileSystemURL(path, function(entry) {
entry.file(function(file) {
@ -44,45 +87,120 @@
console.log("获取图片资源失败:" + e.message);
});
}
// #endif
// #endif
},
onShow() {
// this.readFile(htmlFileUrl, function(htmlContent) {
// console.log(htmlContent); // HTML
// console.log(typeof htmlContent); // HTML
// });
// #ifdef APP
console.log(11)
var filters = []
if (this.checkedToday) {
filters.push({
column: "request_time",
action: "betweeen",
value: this.todayTime
})
async onShow() {
//
this.data = {
itemNumber: '555555',
itemName: 'hahah',
referenceOrderRow: '60',
referenceOrderCode: '6000',
supplierCode: '888',
packageCode: '9999-8888',
amount: '6000',
barcodeString: 'HMQ;V1.0;Dwork1-line1;F;R2024-04-03T08:28:41;B20240403;PPN0403-000005;I399.960-12;Q5.000000;UEA;'
}
console.log(22)
var params = {
filters: filters,
pageNo: this.pageNo,
pageSize: this.pageSize,
}
console.log(33)
getList(params).then(res => {
console.log(44)
this.data = res.data.list[7]
console.log(this.data)
}).catch(error => {
console.log(55)
this.showMessage(error)
this.$nextTick(() => {
//
const el = this.$refs['qrcodeRef']
el.canvasToTempFilePath({
success: async (res) => {
// base64
await pathToBase64(res.tempFilePath).then(base64 => {
// html
this.readFile(htmlFileUrl, (htmlContent) => {
this.newHtmlContent = htmlContent
this.newHtmlContent = this.newHtmlContent.replace(
"replaceItemNumber", this.data
.itemNumber); //
this.newHtmlContent = this.newHtmlContent.replace(
"replaceItemName", this.data.itemName
); //
this.newHtmlContent = this.newHtmlContent.replace(
"replaceSupplierCode", this.data
.supplierCode); //
this.newHtmlContent = this.newHtmlContent.replace(
"replacePackageCode", this.data
.packageCode); //
this.newHtmlContent = this.newHtmlContent.replace(
"replaceReferenceOrderRow", this.data
.referenceOrderRow); //
this.newHtmlContent = this.newHtmlContent.replace(
"replaceReferenceOrderCode", this.data
.referenceOrderCode); //
this.newHtmlContent = this.newHtmlContent.replace(
"amount", this.data.amount); //
this.newHtmlContent = this.newHtmlContent.replace(
"replaceBase", base64); //
});
}).catch(err => {
console.log(err);
})
},
fail(err) {
console.log('err:::', err)
}
})
})
// #endif
},
},
}
</script>
<style>
.box {
font-size: 24rpx;
display: flex;
}
.left {
border-top: 1px solid #b1b1b1;
border-left: 1px solid #b1b1b1;
flex: 1;
}
.left-item {
display: flex;
}
.label {
border-bottom: 1px solid #b1b1b1;
border-right: 1px solid #b1b1b1;
width: 140rpx;
padding: 0px 10rpx;
height: 100rpx;
line-height: 100rpx;
}
.value {
border-bottom: 1px solid #b1b1b1;
border-right: 1px solid #b1b1b1;
padding: 0px 5px;
height: 100rpx;
flex: 1;
width: 0px;
display: flex;
align-items: center;
word-wrap: break-word;
}
.right {
width: 300rpx;
}
.image {
width: 100%;
height: 301rpx;
border-bottom: 1px solid #b1b1b1;
border-top: 1px solid #b1b1b1;
border-right: 1px solid #b1b1b1;
}
.image img {
width: calc(100% - 4px);
height: calc(100% - 4px);
margin: 4rpx;
}
</style>

31
src/uni_modules/lime-qrcode/changelog.md

@ -0,0 +1,31 @@
## 0.1.5(2024-04-14)
- fix: 修复缺少依赖
## 0.1.4(2024-04-10)
- chore: 更新文档
## 0.1.3(2024-04-01)
- chore: 兼容uniapp x ios(app-js)
## 0.1.2(2023-12-14)
- fix: uvue 引入 API 自定义包出错
## 0.1.1(2023-12-11)
- chore: uvue的二维码API独立,需要单独下载
## 0.1.0(2023-12-07)
- fix: 修复因utssdk目录导致无法运行
## 0.0.9(2023-12-06)
- feat: 支持uvue
## 0.0.8(2023-12-06)
- feat: 支持uvue
## 0.0.7(2023-12-06)
- feat: 支持uvue
## 0.0.6(2023-12-06)
- feat: 支持uvue
## 0.0.5(2023-07-30)
- fix: 修复再次生成前没有清空,导致图形叠加
## 0.0.4(2023-07-27)
- fix: 修复相同尺寸无法再次生成
## 0.0.3(2023-06-09)
- feat: 支持通过`@vue/composition-api`在`vue2`上使用
- chore: 更新文档
## 0.0.2(2023-06-08)
- chore: 更新文档
## 0.0.1(2023-06-08)
- 首次

234
src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue

@ -0,0 +1,234 @@
<template>
<!-- #ifdef MP -->
<canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas>
<!-- #endif -->
<!-- #ifdef APP -->
<view class="l-qrcode" ref="drawableRef" :style="[styles]">
<image class="l-qrcode__icon" v-if="icon" :src="icon" :style="[iconStyle]"></image>
</view>
<!-- #endif -->
<!-- #ifdef WEB -->
<view class="l-qrcode" ref="drawableRef" :style="[styles]">
</view>
<!-- #endif -->
</template>
<script lang="uts" setup>
import { type PropType, nextTick } from 'vue'
// #ifndef APP
import { QRCodeCanvas } from './qrcode.js';
import { QRCodePropsTypes , ImageSettings } from './type'
// #endif
// #ifdef APP
import { QRCodeCanvas, QRCodePropsTypes , ImageSettings } from '@/uni_modules/lime-qrcodegen'
// #endif
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
// import { addUnit, unitConvert } from './utils'
import { LQrcodeFailCallback, LQrcodeCompleteCallback, LQrcodeSuccessCallback} from './type'
const name = 'l-qrcode'
const props = defineProps({
value: {
type: String
},
icon: {
type: String
},
// #ifdef APP
size: {
type: Object,
default: 160
},
iconSize: {
type: Object,
default: 40
},
// #endif
// #ifndef APP
size: {
type: [Number, String],
default: 160
},
iconSize: {
type: [Number, String],
default: 40
},
// #endif
marginSize: {
type: Number,
default: 0
},
color: {
type: String,
default: '#000'
},
bgColor: {
type: String,
default: 'transparent'
},
bordered: {
type: Boolean,
default: true
},
errorLevel: {
type: String as PropType<'L' | 'M' | 'Q' | 'H'>,
default: 'M' // 'L' | 'M' | 'Q' | 'H'
},
useCanvasToTempFilePath: {
type: Boolean,
default: false
}
// status: {
// type: String as PropType<'active'|'expired'|'loading'>,
// default: 'active' // active | expired | loading
// }
})
const emits = defineEmits(['success'])
const context = getCurrentInstance();
const canvasId = `l-qrcode${context!.uid}`
const styles = computed<Map<string, any>>(():Map<string, any>=>{
const style = new Map<string, any>()
const size = addUnit(props.size);
if(size!=null){
style.set('width', size)
style.set('height', size)
}
style.set('background', props.bgColor)
return style
})
// #ifdef APP
const iconStyle = computed<Map<string, any>>(():Map<string, any>=>{
const style = new Map<string, any>()
const size = addUnit(props.iconSize);
if(size!=null){
style.set('width', size)
style.set('height', size)
}
return style
})
// #endif
const drawableRef = ref<UniElement|null>(null);
// #ifdef WEB
let canvas:HTMLCanvasElement|null = null
// #endif
let qrcode:QRCodeCanvas|null = null
const canvasToTempFilePath = (options: UTSJSONObject)=>{
const format = options.getString('format') ?? 'png';
const fail = options.get('fail') as LQrcodeFailCallback | null;
const complete = options.get('complete') as LQrcodeCompleteCallback | null;
const success = options.get('success') as LQrcodeSuccessCallback | null;
// #ifdef APP
const newOptions = {
format,
fail,
complete,
success,
} as TakeSnapshotOptions
drawableRef.value!.takeSnapshot(newOptions)
// #endif
// #ifdef WEB
success?.({
tempFilePath: canvas?.toDataURL('image/'+format)
})
// #endif
}
const render = ()=>{
const param:QRCodePropsTypes = {
value: props.value,
size: unitConvert(props.size),
fgColor: props.color,
level: ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M',
marginSize: props.marginSize,
includeMargin: props.bordered,
imageSettings: null,
} as QRCodePropsTypes
if(toBoolean(props.iconSize) && toBoolean(props.icon)){
const size = unitConvert(props.iconSize)
param.imageSettings = {
src: props.icon,
width: size,
height: size,
excavate: true
} as ImageSettings
}
qrcode?.render(param)
if(props.useCanvasToTempFilePath){
setTimeout(()=>{
canvasToTempFilePath({
success: (res: TakeSnapshotSuccess)=>{
emits('success', res.tempFilePath)
}
})
},100)
}
}
defineExpose({
canvasToTempFilePath
})
onMounted(()=>{
nextTick(()=>{
// #ifdef APP
const ctx = drawableRef.value!.getDrawableContext();
qrcode = new QRCodeCanvas(ctx!)
// #endif
// #ifdef WEB
canvas = document.createElement('canvas')
canvas.style.width = '100%'
canvas.style.height = '100%'
drawableRef.value!.appendChild(canvas)
qrcode = new QRCodeCanvas(canvas, {
pixelRatio: uni.getSystemInfoSync().pixelRatio,
createImage: () => {
const image = new Image();
image.crossOrigin = 'anonymous';
return image;
}
})
// #endif
watchEffect(()=>{
render()
})
})
})
onUnmounted(()=>{
// #ifdef WEB
canvas?.remove();
// #endif
qrcode = null;
})
</script>
<style lang="scss">
.l-qrcode {
position: relative;
background-color: aqua;
justify-content: center;
align-items: center;
&-mask {
position: absolute;
// inset: 0;
// inset-block-start: 0;
// inset-inline-start: 0;
z-index: 10;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// width: 100%;
// height: 100%;
color: rgba(0, 0, 0, 0.88);
line-height: 1.5714285714285714;
background: rgba(255, 255, 255, 0.96);
text-align: center;
}
}
</style>

223
src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue

@ -0,0 +1,223 @@
<template>
<view class="l-qrcode" :style="[styles]">
<!-- #ifndef APP-NVUE -->
<canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<web-view
ref="qrcodeRef"
@pagefinish="onFinished"
@error="onError"
@onPostMessage="onMessage"
:style="styles" src="/uni_modules/lime-qrcode/hybrid/html/index.html?v=1"></web-view>
<!-- #endif -->
<!-- <view class="l-qrcode-mask" v-if="['loading', 'expired'].includes(props.status)">
<l-loading v-if="props.status == 'loading'"></l-loading>
<view class="l-qrcode-expired" v-if="props.status == 'expired'">
<slot></slot>
</view>
</view> -->
</view>
</template>
<script lang="ts">
// @ts-nocheck
import { computed, defineComponent, getCurrentInstance, watch, onUnmounted, onMounted } from '@/uni_modules/lime-shared/vue';
import QRCodeProps from './props'
// #ifndef APP-NVUE
import { getCanvas, isCanvas2d } from './useCanvas'
import { QRCodeCanvas } from './qrcode.js';
// #endif
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
import { createImage } from '@/uni_modules/lime-shared/createImage'
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
import { pathToBase64 } from '@/uni_modules/lime-shared/pathToBase64'
import { debounce } from '@/uni_modules/lime-shared/debounce'
const name = 'l-qrcode'
export default defineComponent({
name,
props: QRCodeProps,
emits: ['success'],
setup(props, {emit}) {
const context = getCurrentInstance();
const canvasId = `l-qrcode${context.uid}`
const styles = computed(() => `width: ${addUnit(props.size)}; height: ${addUnit(props.size)};`)
let qrcode = null
let canvas = null
const qrCodeProps = computed(() => {
const { value, icon, size, color, bgColor, bordered, iconSize, errorLevel, marginSize } = props
const imageSettings = {
src: icon,
x: undefined,
y: undefined,
height: unitConvert(iconSize),
width: unitConvert(iconSize),
excavate: true,
}
return {
value,
size: unitConvert(size),
level: errorLevel,
bgColor,
fgColor: color,
imageSettings: icon ? imageSettings : undefined,
includeMargin: bordered,
marginSize: marginSize ?? 0
}
})
// #ifdef APP-NVUE
const stacks = new Map()
// #endif
const canvasToTempFilePath = debounce((args: UniNamespace.CanvasToTempFilePathRes) => {
if(!canvas) return
// #ifndef APP-NVUE
const copyArgs = Object.assign({
canvasId,
canvas: null
}, args)
if (isCanvas2d) {
copyArgs.canvas = canvas
}
if ('toTempFilePath' in canvas) {
canvas.toTempFilePath(copyArgs)
} else {
uni.canvasToTempFilePath(copyArgs, context);
}
// #endif
// #ifdef APP-NVUE
if(!stacks.size) {
const flie = 'file-' + Math.random();
const stack = {args, time: +new Date()}
stacks.set(`${flie}`, stack)
canvas.toDataURL(flie)
setTimeout(() => {
const stack = stacks.get(flie)
if(stack && 'fail' in stack.args) {
stack.args.fail({
error: '超时'
})
stacks.delete(flie)
}
},5000)
}
// #endif
})
const useCanvasToTempFilePath = () => {
if(props.useCanvasToTempFilePath) {
canvasToTempFilePath({
success(res: UniNamespace.CanvasToTempFilePathRes) {
emit('success', res.tempFilePath)
}
})
}
}
// #ifdef APP-NVUE
const onFinished = () => {
const { pixelRatio } = uni.getSystemInfoSync()
canvas = {
toDataURL(flie: string) {
const ref: any = context.refs['qrcodeRef'];
if(ref) {
ref?.evalJS(`toDataURL('${flie}')`)
}
}
};
qrcode = {
async render(props: any) {
const ref: any = context.refs['qrcodeRef'];
const { src } = props.imageSettings || { };
if(!ref) return
if(src && !isBase64(src) && !/^http/.test(src) && /^\/static/.test(src)) {
props.imageSettings.src = await pathToBase64(src)
}
const _props = JSON.stringify(Object.assign({}, props, {pixelRatio}));
ref?.evalJS(`render(${_props})`);
}
}
qrcode.render(qrCodeProps.value)
useCanvasToTempFilePath()
}
const onError = () => {
console.warn('lime-qrcode 加载失败')
}
const onMessage = (e: any) => {
const {detail:{data: [res]}} = e
if(res.event == 'toDataURL') {
const {file, image, msg} = res.data;
const stack = stacks.get(file)
if(stack && image && 'success' in stack.args) {
stack.args.success({tempFilePath: image})
stacks.delete(file)
} else if(stack && 'fails' in stack.args) {
stack.args.fail({error: msg})
stacks.delete(file)
}
}
}
// #endif
const propsWatch = watch(props, () => {
if (qrcode) {
qrcode.render(qrCodeProps.value)
useCanvasToTempFilePath()
}
})
onMounted(() => {
// #ifndef APP-NVUE
getCanvas(canvasId, { context }).then(res => {
canvas = res
qrcode = new QRCodeCanvas(res, {
// #ifdef H5
path2D: false,
// #endif
pixelRatio: isCanvas2d ? uni.getSystemInfoSync().pixelRatio : 1,
createImage
})
qrcode.render(qrCodeProps.value)
useCanvasToTempFilePath()
})
// #endif
})
onUnmounted(() => {
propsWatch && propsWatch()
})
return {
canvasId,
styles,
props,
canvasToTempFilePath,
// #ifdef APP-NVUE
onFinished,
onError,
onMessage
// #endif
}
}
})
</script>
<style lang="scss">
.l-qrcode {
position: relative;
&-mask {
position: absolute;
inset: 0;
// inset-block-start: 0;
// inset-inline-start: 0;
z-index: 10;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// width: 100%;
// height: 100%;
color: rgba(0, 0, 0, 0.88);
line-height: 1.5714285714285714;
background: rgba(255, 255, 255, 0.96);
text-align: center;
}
}
</style>

36
src/uni_modules/lime-qrcode/components/l-qrcode/props.ts

@ -0,0 +1,36 @@
// @ts-nocheck
// import type { PropType } from './vue'
export default {
value: String,
icon: String,
size: {
type: [Number, String],
default: 160
},
iconSize: {
type: [Number, String],
default: 40
},
marginSize: Number,
color: {
type: String,
default: '#000'
},
bgColor: {
type: String,
default: 'transparent'
},
bordered: {
type: Boolean,
default: true
},
errorLevel: {
type: String as PropType<'L'|'M'|'Q'|'H'>,
default: 'M' // 'L' | 'M' | 'Q' | 'H'
},
useCanvasToTempFilePath: Boolean
// status: {
// type: String as PropType<'active'|'expired'|'loading'>,
// default: 'active' // active | expired | loading
// }
}

6
src/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js

File diff suppressed because one or more lines are too long

47
src/uni_modules/lime-qrcode/components/l-qrcode/type.ts

@ -0,0 +1,47 @@
export type ImageSettings = {
width: number
height: number
x?: number
y?: number
excavate: boolean
}
export type QRCodePropsTypes = {
value?: string
size?: number
fgColor?: string
level?: string
marginSize: number
includeMargin: boolean
imageSettings?: ImageSettings
}
export type QRCodeCallback = (cells : boolean[][]) => void
export type Excavation = {
x: number
y: number
h: number
w: number
}
/**
*
*/
export type TakeSnapshotSuccessCallback = (res: TakeSnapshotSuccess) => void
/**
*
*/
export type TakeSnapshotFailCallback = (res: TakeSnapshotFail) => void
/**
*
*/
export type TakeSnapshotCompleteCallback = (res: any) => void
export type LQrcodeFailCallback = TakeSnapshotFailCallback
export type LQrcodeCompleteCallback = TakeSnapshotCompleteCallback
export type LQrcodeSuccessCallback = TakeSnapshotSuccessCallback

78
src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts

@ -0,0 +1,78 @@
// @ts-nocheck
import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue'
import { getRect } from '@/uni_modules/lime-shared/getRect'
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
export const isCanvas2d = canIUseCanvas2d()
export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) {
let { context } = options
// #ifdef MP || VUE2
if (context.proxy) context = context.proxy
// #endif
return getRect('#' + canvasId, {context, type: isCanvas2d ? 'fields': 'boundingClientRect'}).then(res => {
if(res.node){
return res.node
} else {
const ctx = uni.createCanvasContext(canvasId, context)
return {
getContext(type: string) {
if(type == '2d') {
return ctx
}
},
width: res.width,
height: res.height,
}
// #ifdef H5
// canvas.value = context.proxy.$el.querySelector('#'+ canvasId)
// #endif
}
})
}
// #ifndef H5 || APP-NVUE
class Image {
currentSrc: string | null = null
naturalHeight: number = 0
naturalWidth: number = 0
width: number = 0
height: number = 0
tagName: string = 'IMG'
path: any = ''
crossOrigin: any = ''
referrerPolicy: any = ''
onload: () => void
onerror: () => void
constructor() {}
set src(src) {
this.currentSrc = src
uni.getImageInfo({
src,
success: (res) => {
this.path = res.path
this.naturalWidth = this.width = res.width
this.naturalHeight = this.height = res.height
this.onload()
},
fail: () => {
this.onerror()
}
})
}
get src() {
return this.currentSrc
}
}
// #endif
export function createImage(canvas: WechatMiniprogram.Canvas) {
if(canvas && canvas.createImage) {
return canvas.createImage()
} else if(typeof window != 'undefined' && window.Image) {
return new window.Image()
}
// #ifndef H5 || APP-NVUE
return new Image()
// #endif
}

35
src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts

@ -0,0 +1,35 @@
export function addUnit(value: any|null):string{
if(value == null){
return ''
}
value = `${value}`
return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value
}
export function unitConvert(value: any|null): number{
if(typeof value == 'number'){
return value as number
}
if(typeof value == 'string'){
value = `${value}`
if(/^(-)?\d+(\\.\d+)?$/.test(value)){
return parseFloat(value);
}
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g;
const results = reg.exec(value);
if (results == null) {
return 0;
}
const unit = results[3];
const v = parseFloat(value);
if (unit == 'rpx') {
const { windowWidth } = uni.getWindowInfo()
return windowWidth / 750 * v;
}
if (unit == 'px') {
return v;
}
}
return 0;
}

140
src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue

@ -0,0 +1,140 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">QRCode</text>
<text class="demo-block__desc-text">能够将文本转换生成二维码的组件,支持自定义配色和 Logo 配置</text>
<view class="demo-block__body">
<view class="demo-block card">
<text class="demo-block__title-text large">基础</text>
<view class="demo-block__body">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">icon</text>
<view class="demo-block__body">
<image v-if="image !=''" :src="image" style="width: 300rpx;" mode="widthFix"></image>
<view style="flex-direction: row; justify-content: space-between">
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
<l-qrcode :useCanvasToTempFilePath="true" @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
</view>
<button type="primary" style="margin-top: 20rpx;" @click="onClick">生成图片</button>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">颜色</text>
<view class="demo-block__body">
<view style="flex-direction: row; justify-content: space-between">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">纠错比例</text>
<view class="demo-block__body">
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
<button type="primary" style="margin-top: 20rpx;" @click="onToggle">切换纠错等级:{{levels[index]}}</button>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">动态</text>
<view class="demo-block__body">
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
<button type="primary" style="margin-top: 20rpx;" @click="update">更新</button>
</view>
</view>
</view>
</view>
</template>
<script>
// import {ComponentPublicInstance} from 'vue'
export default {
name: 'lime-qrcode',
data() {
return {
text: 'qcoon.com.cn',
image: '',
index: 0,
levels: ['L', 'M', 'Q', 'H']
}
},
methods:{
success(src: string) {
console.log(`src`, src)
},
update() {
this.text = `qcoon.cn?v=${Math.random()}`
},
onToggle() {
this.index++
this.index = this.index % this.levels.length
},
onClick() {
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
el.canvasToTempFilePath({
success:(res: TakeSnapshotSuccess)=>{
this.image = res.tempFilePath
}
})
}
},
mounted() {
}
}
</script>
<style lang="scss">
.demo-block {
margin: 32px 10px 0;
overflow: visible;
&.card{
background-color: white;
padding: 30rpx;
margin-bottom: 20rpx;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>

79
src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue

@ -0,0 +1,79 @@
<template>
<demo-block title="QRCode" type="ultra">
<demo-block title="基础">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode>
</demo-block>
<demo-block title="icon">
<view style="display: flex; gap: 10px">
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
</view>
<button @click="onClick">生成图片</button>
</demo-block>
<demo-block title="颜色">
<view style="display: flex; gap: 10px">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
</view>
</demo-block>
<demo-block title="纠错比例">
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
<button @click="onToggle">切换纠错等级{{levels[index]}}</button>
</demo-block>
<demo-block title="动态">
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
<button @click="update">更新</button>
</demo-block>
</demo-block>
</template>
<script>
import {ref, defineComponent} from '@/uni_modules/lime-shared/vue'
export default defineComponent({
setup() {
const qrcodeRef = ref(null)
const image = ref(null)
const text = ref('qcoon.com.cn')
const levels = ['L', 'M', 'Q', 'H']
let index = ref(0)
const onToggle = () => {
index.value++
index.value = index.value % levels.length
}
const onClick = () => {
if(qrcodeRef.value) {
qrcodeRef.value.canvasToTempFilePath({
success(res) {
image.value = res.tempFilePath
console.log('success:::', res)
},
fail(err) {
console.log('err:::', err)
}
})
}
}
const success = (res) => {
console.log('res', res)
}
const update = () =>{
text.value = `qcoon.cn?v=${Math.random()}`
}
return {
levels,
index,
image,
text,
qrcodeRef,
onClick,
update,
success,
onToggle,
}
}
})
</script>
<style>
</style>

77
src/uni_modules/lime-qrcode/hybrid/html/index.html

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lime-qrcode</title>
<style>
html,body,canvas {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
pointer-events: none;
/* background-color: rgba(255,0,0,0.1) */
}
</style>
</head>
<body>
<canvas id="lime-qrcode"></canvas>
<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
<script type="text/javascript" src="./qrcode.min.js"></script>
<script>
var canvas = document.querySelector('#lime-qrcode')
var pixelRatio = window.devicePixelRatio || 1
function appendWatermark(image) {
emit('append', mark.toDataURL())
}
var qrcode = new lime.QRCodeCanvas(canvas, {
pixelRatio,
})
function render(props) {
if(props.pixelRatio) {
pixelRatio = props.pixelRatio
}
if(qrcode) {
qrcode.render(props)
}
}
function toDataURL(file) {
if(qrcode && canvas) {
try{
const image = canvas.toDataURL()
emit('toDataURL', {
file,
image
})
}catch(e){
emit('toDataURL', {
file,
msg: e
})
}
}
}
function emit(event, data) {
postMessage({
event,
data
});
};
function postMessage(data) {
uni.postMessage({
data
});
};
// render({
// content: ['Lime UI'],
// // rotate: -22,
// // baseSize: 2,
// // fontGap: 3
// })
</script>
</body>
</html>

6
src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js

File diff suppressed because one or more lines are too long

1
src/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js

File diff suppressed because one or more lines are too long

91
src/uni_modules/lime-qrcode/package.json

@ -0,0 +1,91 @@
{
"id": "lime-qrcode",
"displayName": "qrcode 二维码生成",
"version": "0.1.5",
"description": "一款全平台通用的二维码生成插件,支持uniapp/uniappx(web,ios,安卓)",
"keywords": [
"qrcode",
"qr",
"uvue",
"生成图片",
"二维码"
],
"repository": "",
"engines": {
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "305716444"
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"lime-shared"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-uvue": "y",
"app-android": {
"minVersion": "19"
}
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

152
src/uni_modules/lime-qrcode/readme.md

@ -0,0 +1,152 @@
# lime-qrcode 二维码
- 一款全平台通用的二维码生成插件,支持uniapp/uniappx(web,ios,安卓)
- uvue 需要导入[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)
## 使用
- 导入插件后直接使用
- uvue 需要导入**[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)**
#### 基础使用
```html
<l-qrcode value="http://lime.qcoon.cn" />
```
#### ICON
- 带 Icon 的二维码
```html
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" icon="/static/logo.png" iconSize="70rpx"></l-qrcode>
```
#### 颜色
- 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。
```html
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
```
#### 纠错比例
- 通过设置 `errorLevel` 调整不同的容错等级。
```html
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" errorLevel="H"></l-qrcode>
```
#### 生成图片
- 1、通过调用插件的`canvasToTempFilePath`方法生成图片。
```html
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
<button @click="onClick">生成图片</button>
```
```js
// vue3
const qrcodeRef = ref(null)
const onClick = () => {
if(!qrcodeRef.value) return
qrcodeRef.value.canvasToTempFilePath({
success(res) {
image.value = res.tempFilePath
console.log('success:::', res)
},
fail(err) {
console.log('err:::', err)
}
})
}
// vue2
const el = this.$refs['qrcodeRef']
el.canvasToTempFilePath({
success:(res)=>{
this.image = res.tempFilePath
},
fail(err) {
console.log('err:::', err)
}
})
// uvue
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
el.canvasToTempFilePath({
success:(res: TakeSnapshotSuccess)=>{
this.image = res.tempFilePath
},
fail(err: TakeSnapshotFail) {
console.log('err:::', err)
}
})
```
- 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址
```html
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn"></l-qrcode>
```
```js
const image = ref(null)
const success = (img) => {
image.value = img
}
```
### 关于vue2的使用方式
- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
- 关键代码是: 在main.js中 在vue2部分加上这一段即可
```js
// main.js vue2
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
```
另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录
```cmd
// \HBuilderX\plugins\compile-typescript
yarn add typescript -D
- or -
npm install typescript -D
```
### 查看示例
- 导入后直接使用这个标签查看演示效果
```html
// 代码位于 uni_modules/lime-qrcode/compoents/lime-qrcode
<lime-qrcode />
```
### 插件标签
- 默认 l-qrcode 为 component
- 默认 lime-qrcode 为 demo
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ |
| value | 扫描后的文本 | <em>string</em> | `-` |
| icon | 二维码中图片的地址 | <em>string</em> | `-` |
| size | 二维码大小 | <em>number,string</em> | `160` |
| iconSize | 二维码中图片的大小 | <em>number,string</em> | `40` |
| color | 二维码颜色 | <em>string</em> | `-` |
| bgColor | 二维码背景颜色 | <em>string</em> | `-` |
| errorLevel | 二维码纠错等级 | `'L' | 'M' | 'Q' | 'H' ` | `M` |
| marginSize | 边距码大小,默认为0码点 | <em>number</em> | `0` |
### 常见问题
- icon 是网络地址时,H5和Nvue需要解决跨域问题,小程序需要配置download
## 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

25
src/uni_modules/lime-shared/addUnit/index.ts

@ -0,0 +1,25 @@
// @ts-nocheck
import {isNumeric} from '../isNumeric'
import {isDef} from '../isDef'
/**
* px
* @param value
* @returns undefined undefined
*/
export function addUnit(value?: string | number): string | undefined {
if (!isDef(value)) {
return undefined;
}
value = String(value); // 将值转换为字符串
// 如果值是数字,则在后面添加单位 "px",否则保持原始值
return isNumeric(value) ? `${value}px` : value;
}
// console.log(addUnit(100)); // 输出: "100px"
// console.log(addUnit("200")); // 输出: "200px"
// console.log(addUnit("300px")); // 输出: "300px"(已经包含单位)
// console.log(addUnit()); // 输出: undefined(值为 undefined)
// console.log(addUnit(null)); // 输出: undefined(值为 null)

63
src/uni_modules/lime-shared/arrayBufferToFile/index.ts

@ -0,0 +1,63 @@
// @ts-nocheck
import {platform} from '../platform'
/**
* buffer转路径
* @param {Object} buffer
*/
// @ts-nocheck
export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> {
return new Promise((resolve, reject) => {
// #ifdef MP
const fs = uni.getFileSystemManager()
//自定义文件名
if (!name && !format) {
reject(new Error('ERROR_NAME_PARSE'))
}
const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
let pre = platform()
const filePath = `${pre.env.USER_DATA_PATH}/${fileName}`
fs.writeFile({
filePath,
data: buffer,
success() {
resolve(filePath)
},
fail(err) {
console.error(err)
reject(err)
}
})
// #endif
// #ifdef H5
const file = new File([buffer], name, {
type: format,
});
resolve(file)
// #endif
// #ifdef APP-PLUS
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
const base64 = uni.arrayBufferToBase64(buffer)
bitmap.loadBase64Data(base64, () => {
if (!name && !format) {
reject(new Error('ERROR_NAME_PARSE'))
}
const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
const filePath = `_doc/uniapp_temp/${fileNmae}`
bitmap.save(filePath, {},
() => {
bitmap.clear()
resolve(filePath)
},
(error) => {
bitmap.clear()
reject(error)
})
}, (error) => {
bitmap.clear()
reject(error)
})
// #endif
})
}

13
src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts

@ -0,0 +1,13 @@
// @ts-nocheck
// 未完成
export function base64ToArrayBuffer(base64 : string) {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
if (!format) {
new Error('ERROR_BASE64SRC_PARSE')
}
if(uni.base64ToArrayBuffer) {
return uni.base64ToArrayBuffer(bodyData)
} else {
}
}

76
src/uni_modules/lime-shared/base64ToPath/index.ts

@ -0,0 +1,76 @@
// @ts-nocheck
import {platform} from '../platform'
/**
* base64转路径
* @param {Object} base64
*/
export function base64ToPath(base64: string, filename?: string):Promise<string> {
const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || [];
console.log('format', format)
return new Promise((resolve, reject) => {
// #ifdef MP
const fs = uni.getFileSystemManager()
//自定义文件名
if (!filename && !format) {
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
// const time = new Date().getTime();
const name = filename || `${new Date().getTime()}.${format}`;
let pre = platform()
const filePath = `${pre.env.USER_DATA_PATH}/${name}`
fs.writeFile({
filePath,
data: base64.split(',')[1],
encoding: 'base64',
success() {
resolve(filePath)
},
fail(err) {
console.error(err)
reject(err)
}
})
// #endif
// #ifdef H5
// mime类型
let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
//base64 解码
let byteString = atob(base64.split(',')[1]);
//创建缓冲数组
let arrayBuffer = new ArrayBuffer(byteString.length);
//创建视图
let intArray = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
resolve(URL.createObjectURL(new Blob([intArray], {
type: mimeString
})))
// #endif
// #ifdef APP-PLUS
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
bitmap.loadBase64Data(base64, () => {
if (!filename && !format) {
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
// const time = new Date().getTime();
const name = filename || `${new Date().getTime()}.${format}`;
const filePath = `_doc/uniapp_temp/${name}`
bitmap.save(filePath, {},
() => {
bitmap.clear()
resolve(filePath)
},
(error) => {
bitmap.clear()
reject(error)
})
}, (error) => {
bitmap.clear()
reject(error)
})
// #endif
})
}

21
src/uni_modules/lime-shared/camelCase/index.ts

@ -0,0 +1,21 @@
/**
* camelCase PascalCase
* @param str
* @param isPascalCase PascalCase false
* @returns
*/
export function camelCase(str: string, isPascalCase: boolean = false): string {
// 将字符串分割成单词数组
let words: string[] = str.split(/[\s_-]+/);
// 将数组中的每个单词首字母大写(除了第一个单词)
let camelCased: string[] = words.map((word, index) => {
if (index === 0 && !isPascalCase) {
return word.toLowerCase(); // 第一个单词全小写
}
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
});
// 将数组中的单词拼接成一个字符串
return camelCased.join('');
};

58
src/uni_modules/lime-shared/canIUseCanvas2d/index.ts

@ -0,0 +1,58 @@
// @ts-nocheck
// #ifdef MP-ALIPAY
interface My {
SDKVersion: string
}
declare var my: My
// #endif
function compareVersion(v1:string, v2:string) {
let a1 = v1.split('.');
let a2 = v2.split('.');
const len = Math.max(a1.length, a2.length);
while (a1.length < len) {
a1.push('0');
}
while (a2.length < len) {
a2.push('0');
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(a1[i], 10);
const num2 = parseInt(a2[i], 10);
if (num1 > num2) {
return 1;
}
if (num1 < num2) {
return -1;
}
}
return 0;
}
function gte(version: string) {
let {SDKVersion} = uni.getSystemInfoSync();
// #ifdef MP-ALIPAY
SDKVersion = my.SDKVersion
// #endif
return compareVersion(SDKVersion, version) >= 0;
}
/** 环境是否支持canvas 2d */
export function canIUseCanvas2d() {
// #ifdef MP-WEIXIN
return gte('2.9.0');
// #endif
// #ifdef MP-ALIPAY
return gte('2.7.0');
// #endif
// #ifdef MP-TOUTIAO
return gte('1.78.0');
// #endif
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO
return false
// #endif
}

30
src/uni_modules/lime-shared/changelog.md

@ -0,0 +1,30 @@
## 0.1.4(2023-09-05)
- feat: 增加 Hooks `useIntersectionObserver`
- feat: 增加 `floatAdd`
- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译
## 0.1.3(2023-08-13)
- feat: 增加 `camelCase`
## 0.1.2(2023-07-17)
- feat: 增加 `getClassStr`
## 0.1.1(2023-07-06)
- feat: 增加 `isNumeric`, 区别于 `isNumber`
## 0.1.0(2023-06-30)
- fix: `clamp`忘记导出了
## 0.0.9(2023-06-27)
- feat: 增加`arrayBufferToFile`
## 0.0.8(2023-06-19)
- feat: 增加`createAnimation`、`clamp`
## 0.0.7(2023-06-08)
- chore: 更新注释
## 0.0.6(2023-06-08)
- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖
## 0.0.5(2023-06-03)
- chore: 更新注释
## 0.0.4(2023-05-22)
- feat: 增加`range`,`exif`,`selectComponent`
## 0.0.3(2023-05-08)
- feat: 增加`fillZero`,`debounce`,`throttle`,`random`
## 0.0.2(2023-05-05)
- chore: 更新文档
## 0.0.1(2023-05-05)
- 无

16
src/uni_modules/lime-shared/clamp/index.ts

@ -0,0 +1,16 @@
// @ts-nocheck
/**
*
* @param min
* @param max
* @param val
* @returns
*/
export function clamp(min: number, max: number, val: number): number {
return Math.max(min, Math.min(max, val));
}
// console.log(clamp(0, 10, 5)); // 输出: 5(在范围内,不做更改)
// console.log(clamp(0, 10, -5)); // 输出: 0(小于最小值,被限制为最小值)
// console.log(clamp(0, 10, 15)); // 输出: 10(大于最大值,被限制为最大值)

103
src/uni_modules/lime-shared/cloneDeep/index.ts

@ -0,0 +1,103 @@
// @ts-nocheck
/**
*
* @param obj
* @returns
*/
export function cloneDeep<T>(obj: any): T {
// 如果传入的对象为空,返回空
if (obj === null) {
return null as unknown as T;
}
// 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象
if (obj instanceof Set) {
return new Set([...obj]) as unknown as T;
}
// 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象
if (obj instanceof Map) {
return new Map([...obj]) as unknown as T;
}
// 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值
if (obj instanceof WeakMap) {
let weakMap = new WeakMap();
weakMap = obj;
return weakMap as unknown as T;
}
// 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值
if (obj instanceof WeakSet) {
let weakSet = new WeakSet();
weakSet = obj;
return weakSet as unknown as T;
}
// 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象
if (obj instanceof RegExp) {
return new RegExp(obj) as unknown as T;
}
// 如果传入的对象是 undefined 类型,则返回 undefined
if (typeof obj === 'undefined') {
return undefined as unknown as T;
}
// 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆
if (Array.isArray(obj)) {
return obj.map(cloneDeep) as unknown as T;
}
// 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象
if (obj instanceof Date) {
return new Date(obj.getTime()) as unknown as T;
}
// 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆
if (typeof obj === 'object') {
const newObj: any = {};
for (const [key, value] of Object.entries(obj)) {
newObj[key] = cloneDeep(value);
}
const symbolKeys = Object.getOwnPropertySymbols(obj);
for (const key of symbolKeys) {
newObj[key] = cloneDeep(obj[key]);
}
return newObj;
}
// 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回
return obj;
}
// 示例使用
// // 克隆一个对象
// const obj = { name: 'John', age: 30 };
// const clonedObj = cloneDeep(obj);
// console.log(clonedObj); // 输出: { name: 'John', age: 30 }
// console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的)
// // 克隆一个数组
// const arr = [1, 2, 3];
// const clonedArr = cloneDeep(arr);
// console.log(clonedArr); // 输出: [1, 2, 3]
// console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的)
// // 克隆一个包含嵌套对象的对象
// const person = {
// name: 'Alice',
// age: 25,
// address: {
// city: 'New York',
// country: 'USA',
// },
// };
// const clonedPerson = cloneDeep(person);
// console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } }
// console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的)
// console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的)

22
src/uni_modules/lime-shared/closest/index.ts

@ -0,0 +1,22 @@
// @ts-nocheck
/**
*
* @param arr
* @param target
* @returns
*/
export function closest(arr: number[], target: number) {
return arr.reduce((pre, cur) =>
Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
);
}
// 示例
// // 定义一个数字数组
// const numbers = [1, 3, 5, 7, 9];
// // 在数组中找到最接近目标数字 6 的元素
// const closestNumber = closest(numbers, 6);
// console.log(closestNumber); // 输出结果: 5

149
src/uni_modules/lime-shared/createAnimation/index.ts

@ -0,0 +1,149 @@
// @ts-nocheck
// nvue 需要在节点上设置ref或在export里传入
// const animation = createAnimation({
// ref: this.$refs['xxx'],
// duration: 0,
// timingFunction: 'linear'
// })
// animation.opacity(1).translate(x, y).step({duration})
// animation.export(ref)
// 抹平nvue 与 uni.createAnimation的使用差距
// 但是nvue动画太慢~~~无语
// #ifdef APP-NVUE
const nvueAnimation = uni.requireNativePlugin('animation')
type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ'
| 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom'
interface Styles {
[key : string] : any
}
interface StepConfig {
duration?: number
timingFunction?: string
delay?: number
needLayout?: boolean
transformOrigin?: string
}
interface StepAnimate {
styles?: Styles
config?: StepConfig
}
interface StepAnimates {
[key: number]: StepAnimate
}
interface CreateAnimationOptions extends UniApp.CreateAnimationOptions {
ref?: string
}
type Callback = (time: number) => void
const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
'translateZ'
]
const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor']
const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom']
class LimeAnimation {
ref : any
context : any
options : UniApp.CreateAnimationOptions
// stack : any[] = []
next : number = 0
currentStepAnimates : StepAnimates = {}
duration : number = 0
constructor(options : CreateAnimationOptions) {
const {ref} = options
this.ref = ref
this.options = options
}
addAnimate(type : AnimationTypes, args: (string | number)[]) {
let aniObj = this.currentStepAnimates[this.next]
let stepAnimate:StepAnimate = {}
if (!aniObj) {
stepAnimate = {styles: {}, config: {}}
} else {
stepAnimate = aniObj
}
if (animateTypes1.includes(type)) {
if (!stepAnimate.styles.transform) {
stepAnimate.styles.transform = ''
}
let unit = ''
if (type === 'rotate') {
unit = 'deg'
}
stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) `
} else {
stepAnimate.styles[type] = `${args.join(',')}`
}
this.currentStepAnimates[this.next] = stepAnimate
}
animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) {
const el = ref || this.ref
if (!el) return
return new Promise((resolve) => {
const time = +new Date()
nvueAnimation.transition(el, {
styles,
...config
}, () => {
resolve(+new Date() - time)
})
})
}
nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) {
let obj = animates[step]
if (obj) {
let { styles, config } = obj
// this.duration += config.duration
this.animateRun(styles, config, ref).then((time: number) => {
step += 1
this.duration += time
this.nextAnimate(animates, step, ref, cb)
})
} else {
this.currentStepAnimates = {}
cb && cb(this.duration)
}
}
step(config:StepConfig = {}) {
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
this.next++
return this
}
export(ref: any, cb?: Callback) {
ref = ref || this.ref
if(!ref) return
this.duration = 0
this.next = 0
this.nextAnimate(this.currentStepAnimates, 0, ref, cb)
return null
}
}
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
LimeAnimation.prototype[type] = function(...args: (string | number)[]) {
this.addAnimate(type, args)
return this
}
})
// #endif
export function createAnimation(options : CreateAnimationOptions) {
// #ifndef APP-NVUE
// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
return uni.createAnimation({ ...options })
// #endif
// #ifdef APP-NVUE
return new LimeAnimation(options)
// #endif
}

61
src/uni_modules/lime-shared/createImage/index.ts

@ -0,0 +1,61 @@
// @ts-nocheck
import {isBrowser} from '../isBrowser'
class Image {
currentSrc: string | null = null
naturalHeight: number = 0
naturalWidth: number = 0
width: number = 0
height: number = 0
tagName: string = 'IMG'
path: string = ''
crossOrigin: string = ''
referrerPolicy: string = ''
onload: () => void = () => {}
onerror: () => void = () => {}
complete: boolean = false
constructor() {}
set src(src: string) {
console.log('src', src)
if(!src) {
return this.onerror()
}
src = src.replace(/^@\//,'/')
this.currentSrc = src
uni.getImageInfo({
src,
success: (res) => {
const localReg = /^\.|^\/(?=[^\/])/;
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
res.path = localReg.test(src) ? `/${res.path}` : res.path;
// #endif
this.complete = true
this.path = res.path
this.naturalWidth = this.width = res.width
this.naturalHeight = this.height = res.height
this.onload()
},
fail: () => {
this.onerror()
}
})
}
get src() {
return this.currentSrc
}
}
interface UniImage extends WechatMiniprogram.Image {
complete?: boolean
naturalHeight?: number
naturalWidth?: number
}
/** 创建用于 canvas 的 img */
export function createImage(canvas?: any): HTMLImageElement | UniImage {
if(canvas && canvas.createImage) {
return (canvas as WechatMiniprogram.Canvas).createImage()
} else if(this.tagName == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){
return new Image()
} else if(isBrowser) {
return new window.Image()
}
return new Image()
}

38
src/uni_modules/lime-shared/debounce/index.ts

@ -0,0 +1,38 @@
// @ts-nocheck
type Timeout = ReturnType<typeof setTimeout> | null;
/**
*
* @param fn
* @param wait
* @returns
*/
export function debounce(fn: (...args: any[]) => void, wait = 300) {
let timer: Timeout = null; // 用于存储 setTimeout 的标识符的变量
return function (this: any, ...args: any[]) {
if (timer) clearTimeout(timer); // 如果上一个 setTimeout 存在,则清除它
// 设置一个新的 setTimeout,在指定的等待时间后调用防抖函数
timer = setTimeout(() => {
fn.apply(this, args); // 使用提供的参数调用原始函数
}, wait);
};
};
// 示例
// 定义一个函数
// function saveData(data: string) {
// // 模拟保存数据的操作
// console.log(`Saving data: ${data}`);
// }
// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数
// const debouncedSaveData = debounce(saveData, 500);
// // 连续调用防抖函数
// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数
// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数
// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2"

1056
src/uni_modules/lime-shared/exif/index.ts

File diff suppressed because it is too large

11
src/uni_modules/lime-shared/fillZero/index.ts

@ -0,0 +1,11 @@
// @ts-nocheck
/**
*
* @param number
* @param length 2
* @returns
*/
export function fillZero(number: number, length: number = 2): string {
// 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度
return `${number}`.padStart(length, '0');
}

36
src/uni_modules/lime-shared/floatAdd/index.ts

@ -0,0 +1,36 @@
import {isNumber} from '../isNumber'
/**
*
* @param num1
* @param num2
* @returns
*/
export function floatAdd(num1: number, num2: number): number {
// 检查 num1 和 num2 是否为数字类型
if (!(isNumber(num1) || isNumber(num2))) {
console.warn('Please pass in the number type');
return NaN;
}
let r1: number, r2: number, m: number;
try {
// 获取 num1 小数点后的位数
r1 = num1.toString().split('.')[1].length;
} catch (error) {
r1 = 0;
}
try {
// 获取 num2 小数点后的位数
r2 = num2.toString().split('.')[1].length;
} catch (error) {
r2 = 0;
}
// 计算需要扩大的倍数
m = Math.pow(10, Math.max(r1, r2));
// 返回相加结果
return (num1 * m + num2 * m) / m;
}

27
src/uni_modules/lime-shared/getClassStr/index.ts

@ -0,0 +1,27 @@
// @ts-nocheck
/**
*
* @param obj -
* @returns
*/
export function getClassStr<T>(obj: T): string {
let classNames: string[] = [];
// 遍历对象的属性
for (let key in obj) {
// 检查属性确实属于对象自身且其值为true
if ((obj as any).hasOwnProperty(key) && obj[key]) {
// 将属性名添加到类名数组中
classNames.push(key);
}
}
// 将类名数组用空格连接成字符串并返回
return classNames.join(' ');
}
// 示例
// const obj = { foo: true, bar: false, baz: true };
// const classNameStr = getClassStr(obj);
// console.log(classNameStr); // 输出: "foo baz"

6
src/uni_modules/lime-shared/getCurrentPage/index.ts

@ -0,0 +1,6 @@
// @ts-nocheck
/** 获取当前页 */
export const getCurrentPage = () => {
const pages = getCurrentPages();
return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
};

14
src/uni_modules/lime-shared/getLocalFilePath/index.ts

@ -0,0 +1,14 @@
// @ts-nocheck
export const getLocalFilePath = (path: string) => {
if(typeof plus == 'undefined') return path
if(/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path
if (/^\//.test(path)) {
const localFilePath = plus.io.convertAbsoluteFileSystem(path)
if (localFilePath !== path) {
return localFilePath
} else {
path = path.slice(1)
}
}
return '_www/' + path
}

86
src/uni_modules/lime-shared/getRect/index.ts

@ -0,0 +1,86 @@
// @ts-nocheck
// #ifdef APP-NVUE
// 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知
const dom = uni.requireNativePlugin('dom')
// #endif
interface RectOptions {
/**
*
*/
context ?: any // ComponentInternalInstance 类型,用于指定上下文
/**
* nvue
*/
needAll ?: boolean,
/**
* UniNamespace.NodesRef
*/
nodes ?: UniNamespace.NodesRef
/**
* UniNamespace.NodesRef
*/
type ?: keyof UniNamespace.NodesRef
}
/**
*
* @param selector
* @param options RectOptions
* @returns Promise
*/
export function getRect(selector : string, options : RectOptions = {}) {
// #ifndef APP-NVUE
const typeDefault = 'boundingClientRect'
let { context, needAll, type = typeDefault } = options
// #endif
// #ifdef MP || VUE2
if (context.proxy) context = context.proxy
// #endif
return new Promise<UniNamespace.NodeInfo>((resolve, reject) => {
// #ifndef APP-NVUE
const dom = uni.createSelectorQuery().in(context)[needAll ? 'selectAll' : 'select'](selector);
const result = (rect: UniNamespace.NodeInfo) => {
if (rect) {
resolve(rect)
} else {
reject('no rect')
}
}
if (type == typeDefault) {
dom[type](result).exec()
} else {
dom[type]({
node: true,
size: true,
rect: true
}, result).exec()
}
// #endif
// #ifdef APP-NVUE
let { context } = options
if (/#|\./.test(selector) && context.refs) {
selector = selector.replace(/#|\./, '')
if (context.refs[selector]) {
selector = context.refs[selector]
if(Array.isArray(selector)) {
selector = selector[0]
}
}
}
dom.getComponentRect(selector, (res) => {
if (res.size) {
resolve(res.size)
} else {
reject('no rect')
}
})
// #endif
});
};

30
src/uni_modules/lime-shared/getStyleStr/index.ts

@ -0,0 +1,30 @@
// @ts-nocheck
interface CSSProperties {
[key: string]: string | number
}
/**
*
* @param key -
* @returns
*/
export function toLowercaseSeparator(key: string) {
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
}
/**
*
* @param style - CSS样式对象
* @returns
*/
export function getStyleStr(style: CSSProperties): string {
return Object.keys(style)
.filter(key => style[key] !== undefined && style[key] !== null && style[key] !== '')
.map((key: string) => `${toLowercaseSeparator(key)}: ${style[key]};`)
.join(' ');
}
// 示例
// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null };
// const styleStr = getStyleStr(style);
// console.log(styleStr);
// 输出: "color: red; font-size: 16px;"

30
src/uni_modules/lime-shared/hasOwn/index.ts

@ -0,0 +1,30 @@
// @ts-nocheck
const hasOwnProperty = Object.prototype.hasOwnProperty
/**
*
* @param obj
* @param key
* @returns truefalse
*/
export function hasOwn(obj: Object | Array<any>, key: string): boolean {
return hasOwnProperty.call(obj, key);
}
// 示例
// const obj = { name: 'John', age: 30 };
// if (hasOwn(obj, 'name')) {
// console.log("对象具有 'name' 属性");
// } else {
// console.log("对象不具有 'name' 属性");
// }
// // 输出: 对象具有 'name' 属性
// const arr = [1, 2, 3];
// if (hasOwn(arr, 'length')) {
// console.log("数组具有 'length' 属性");
// } else {
// console.log("数组不具有 'length' 属性");
// }
// 输出: 数组具有 'length' 属性

43
src/uni_modules/lime-shared/index.ts

@ -0,0 +1,43 @@
// @ts-nocheck
// validator
export * from './isString'
export * from './isNumber'
export * from './isNumeric'
export * from './isDef'
export * from './isFunction'
export * from './isObject'
export * from './isPromise'
export * from './isBase64'
export * from './hasOwn'
// 单位转换
export * from './addUnit'
export * from './unitConvert'
export * from './toNumber'
export * from './random'
export * from './range'
export * from './fillZero'
// image
export * from './base64ToPath'
export * from './pathToBase64'
export * from './exif'
// canvas
export * from './canIUseCanvas2d'
// page
export * from './getCurrentPage'
// dom
export * from './getRect'
export * from './selectComponent'
export * from './createAnimation'
// delay
export * from './sleep'
export * from './debounce'
export * from './throttle'

9
src/uni_modules/lime-shared/isBase64/index.ts

@ -0,0 +1,9 @@
// @ts-nocheck
/**
* Base64编码的图像路径
* @param path
* @returns Base64编码truefalse
*/
export const isBase64 = (path: string): boolean => {
return /^data:image\/(\w+);base64/.test(path);
};

2
src/uni_modules/lime-shared/isBrowser/index.ts

@ -0,0 +1,2 @@
// @ts-nocheck
export const isBrowser = typeof window !== 'undefined';

9
src/uni_modules/lime-shared/isDef/index.ts

@ -0,0 +1,9 @@
// @ts-nocheck
/**
* undefined null
* @param value
* @returns null true false
*/
export function isDef(value: unknown): boolean {
return value !== undefined && value !== null;
}

8
src/uni_modules/lime-shared/isFunction/index.ts

@ -0,0 +1,8 @@
// @ts-nocheck
/**
*
* @param val
* @returns true false
*/
export const isFunction = (val: unknown): val is Function =>
typeof val === 'function';

9
src/uni_modules/lime-shared/isNumber/index.ts

@ -0,0 +1,9 @@
// @ts-nocheck
/**
*
* @param value number string
* @returns NaN true false
*/
export function isNumber(value: number | string): boolean {
return typeof value === 'number' && !isNaN(value);
}

9
src/uni_modules/lime-shared/isNumeric/index.ts

@ -0,0 +1,9 @@
// @ts-nocheck
/**
*
* @param value string number
* @returns true false
*/
export function isNumeric(value: string | number): boolean {
return /^(-)?\d+(\.\d+)?$/.test(value);
}

8
src/uni_modules/lime-shared/isObject/index.ts

@ -0,0 +1,8 @@
// @ts-nocheck
/**
*
* @param val
* @returns true false
*/
export const isObject = (val : unknown) : val is Record<any, any> =>
val !== null && typeof val === 'object';

13
src/uni_modules/lime-shared/isPromise/index.ts

@ -0,0 +1,13 @@
// @ts-nocheck
import {isFunction} from '../isFunction'
import {isObject} from '../isObject'
/**
* Promise
* @param val
* @returns Promise true false
*/
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
// 使用 isObject 函数判断值是否为对象类型
// 使用 isFunction 函数判断值是否具有 then 方法和 catch 方法
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
};

7
src/uni_modules/lime-shared/isString/index.ts

@ -0,0 +1,7 @@
// @ts-nocheck
/**
*
* @param str
* @returns true false
*/
export const isString = (str: unknown): str is string => typeof str === 'string';

17
src/uni_modules/lime-shared/kebabCase/index.ts

@ -0,0 +1,17 @@
// export function toLowercaseSeparator(key: string) {
// return key.replace(/([A-Z])/g, '-$1').toLowerCase();
// }
/**
*
* @param str
* @param separator "-"
* @returns
*/
export function kebabCase(str: string, separator: string = "-"): string {
return str
.replace(/[A-Z]/g, match => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母
.replace(/[\s_-]+/g, separator) // 将空格、下划线和短横线替换为指定连接符
.replace(new RegExp(`^${separator}|${separator}$`, "g"), "") // 删除开头和结尾的连接符
.toLowerCase(); // 将结果转换为全小写
}

83
src/uni_modules/lime-shared/package.json

@ -0,0 +1,83 @@
{
"id": "lime-shared",
"displayName": "lime-shared",
"version": "0.1.4",
"description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等",
"keywords": [
"lime-shared",
"exif",
"selectComponent"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "sdk-js",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

121
src/uni_modules/lime-shared/pathToBase64/index.ts

@ -0,0 +1,121 @@
// @ts-nocheck
// #ifdef APP-PLUS
import { getLocalFilePath } from '../getLocalFilePath'
// #endif
function isImage(extension : string) {
const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "svg"];
return imageExtensions.includes(extension.toLowerCase());
}
// #ifdef H5
function getSVGFromURL(url: string) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'text';
xhr.onload = function () {
if (xhr.status === 200) {
const svg = xhr.responseText;
resolve(svg);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = function () {
reject(new Error('Network error'));
};
xhr.send();
});
}
// #endif
/**
* base64
* @param {Object} string
*/
export function pathToBase64(path : string) : Promise<string> {
if (/^data:/.test(path)) return path
let extension = path.substring(path.lastIndexOf('.') + 1);
const isImageFile = isImage(extension)
let prefix = ''
if (isImageFile) {
prefix = 'image/';
if(extension == 'svg') {
extension += '+xml'
}
} else if (extension === 'pdf') {
prefix = 'application/pdf';
} else if (extension === 'txt') {
prefix = 'text/plain';
} else {
// 添加更多文件类型的判断
// 如果不是图片、PDF、文本等类型,可以设定默认的前缀或采取其他处理
prefix = 'application/octet-stream';
}
return new Promise((resolve, reject) => {
// #ifdef H5
if (isImageFile) {
if(extension == 'svg') {
getSVGFromURL(path).then(svg => {
const base64 = btoa(svg);
resolve(`data:image/svg+xml;base64,${base64}`);
})
} else {
let image = new Image();
image.setAttribute("crossOrigin", 'Anonymous');
image.onload = function () {
let canvas = document.createElement('canvas');
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
canvas.getContext('2d').drawImage(image, 0, 0);
let result = canvas.toDataURL(`${prefix}${extension}`)
resolve(result);
canvas.height = canvas.width = 0
}
image.src = path + '?v=' + Math.random()
image.onerror = (error) => {
reject(error);
};
}
} else {
reject('not image');
}
// #endif
// #ifdef MP
if (uni.canIUse('getFileSystemManager')) {
uni.getFileSystemManager().readFile({
filePath: path,
encoding: 'base64',
success: (res) => {
resolve(`data:${prefix}${extension};base64,${res.data}`)
},
fail: (error) => {
console.error({ error, path })
reject(error)
}
})
}
// #endif
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
entry.file((file : any) => {
const fileReader = new plus.io.FileReader()
fileReader.onload = (data) => {
resolve(data.target.result)
}
fileReader.onerror = (error) => {
console.error({ error, path })
reject(error)
}
fileReader.readAsDataURL(file)
}, reject)
}, reject)
// #endif
})
}

2320
src/uni_modules/lime-shared/piexif/index.ts

File diff suppressed because it is too large

27
src/uni_modules/lime-shared/platform/index.ts

@ -0,0 +1,27 @@
// @ts-nocheck
declare var tt: Uni
declare var swan: Uni
declare var my: Uni
declare var dd: Uni
declare var ks: Uni
declare var jd: Uni
declare var qa: Uni
declare var qq: Uni
declare var qh: Uni
declare var qq: Uni
export function platform(): Uni | WechatMiniprogram.Wx {
const UNDEFINED = 'undefined'
if(typeof wx !== UNDEFINED) return wx // 微信
if(typeof tt !== UNDEFINED) return tt // 字节 飞书
if(typeof swan !== UNDEFINED) return swan // 百度
if(typeof my !== UNDEFINED) return my // 支付宝
if(typeof dd !== UNDEFINED) return dd // 钉钉
if(typeof ks !== UNDEFINED) return ks // 快手
if(typeof jd !== UNDEFINED) return jd // 京东
if(typeof qa !== UNDEFINED) return qa // 快应用
if(typeof qq !== UNDEFINED) return qq // qq
if(typeof qh !== UNDEFINED) return qh // 360
if(typeof uni !== UNDEFINED) return uni
return null
}

30
src/uni_modules/lime-shared/raf/index.ts

@ -0,0 +1,30 @@
// @ts-nocheck
import {isBrowser} from '../isBrowser'
// 是否支持被动事件监听
export const supportsPassive = true;
// 请求动画帧
export function raf(fn: FrameRequestCallback): number {
// 如果是在浏览器环境下,使用 requestAnimationFrame 方法
if (isBrowser) {
return requestAnimationFrame(fn); // 请求动画帧
} else { // 在非浏览器环境下,使用 setTimeout 模拟
return setTimeout(fn, 1000 / 30); // 使用 setTimeout 模拟动画帧,每秒钟执行 30 次
}
}
// 取消动画帧
export function cancelRaf(id: number) {
// 如果是在浏览器环境下,使用 cancelAnimationFrame 方法
if (isBrowser) {
cancelAnimationFrame(id); // 取消动画帧
} else { // 在非浏览器环境下,使用 clearTimeout 模拟
clearTimeout(id); // 使用 clearTimeout 模拟取消动画帧
}
}
// 双倍动画帧
export function doubleRaf(fn: FrameRequestCallback): void {
raf(() => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果
}

22
src/uni_modules/lime-shared/random/index.ts

@ -0,0 +1,22 @@
// @ts-nocheck
/**
*
* @param min
* @param max
* @param fixed 0
* @returns
*/
export function random(min: number, max: number, fixed: number = 0) {
// 将 min 和 max 转换为数字类型
min = +min || 0;
max = +max || 0;
// 计算随机数范围内的一个随机数
const num = Math.random() * (max - min) + min;
// 如果 fixed 参数为 0,则返回四舍五入的整数随机数;否则保留固定小数位数
return fixed == 0 ? Math.round(num) : Number(num.toFixed(fixed));
}
// 示例
// console.log(random(0, 10)); // 输出:在 0 和 10 之间的一个整数随机数
// console.log(random(0, 1, 2)); // 输出:在 0 和 1 之间的一个保留两位小数的随机数
// console.log(random(1, 100, 3)); // 输出:在 1 和 100 之间的一个保留三位小数的随机数

31
src/uni_modules/lime-shared/range/index.ts

@ -0,0 +1,31 @@
// @ts-nocheck
/**
*
* @param start
* @param end
* @param step 1
* @param fromRight false
* @returns
*/
export function range(start: number, end: number, step: number = 1, fromRight: boolean = false): number[] {
let index = -1;
// 计算范围的长度
let length = Math.max(Math.ceil((end - start) / (step || 1)), 0);
// 创建一个长度为 length 的数组
const result = new Array(length);
// 使用循环生成数字范围数组
while (length--) {
// 根据 fromRight 参数决定从左侧还是右侧开始填充数组
result[fromRight ? length : ++index] = start;
start += step;
}
return result;
}
// 示例
// console.log(range(0, 5)); // 输出: [0, 1, 2, 3, 4]
// console.log(range(1, 10, 2, true)); // 输出: [9, 7, 5, 3, 1]
// console.log(range(5, 0, -1)); // 输出: [5, 4, 3, 2, 1]

251
src/uni_modules/lime-shared/readme.md

@ -0,0 +1,251 @@
# lime-shared 工具库
- 本人插件的几个公共函数
## 引入
```js
// 按需引入
// 这种只会引入相关的方法
import {getRect} from '@/uni_modules/lime-shared/getRect'
// 全量引入
// 这种引入方式,会全量打包
import {getRect} from '@/uni_modules/lime-shared'
```
## Utils
#### getRect
- 返回节点尺寸信息
```js
// 组件内需要传入上下文
// 如果是nvue 则需要在节点上加与id或class同名的ref
getRect('#id',{context: this}).then(res => {})
```
#### addUnit
- 将未带单位的数值添加px,如果有单位则返回原值
```js
addUnit(10)
// 10px
```
#### unitConvert
- 将带有rpx|px的字符转成number,若本身是number则直接返回
```js
unitConvert('10rpx')
// 5 设备不同 返回的值也不同
unitConvert('10px')
// 10
unitConvert(10)
// 10
```
#### canIUseCanvas2d
- 环境是否支持使用 canvas 2d
```js
canIUseCanvas2d()
// 若支持返回 true 否则 false
```
#### getCurrentPage
- 获取当前页
```js
const page = getCurrentPage()
```
#### base64ToPath
- 把base64的图片转成临时路径
```js
base64ToPath(`xxxxx`).then(res => {})
```
#### pathToBase64
- 把图片的临时路径转成base64
```js
pathToBase64(`xxxxx/xxx.png`).then(res => {})
```
#### sleep
- 睡眠,让 async 内部程序等待一定时间后再执行
```js
async next () => {
await sleep(300)
console.log('limeui');
}
```
#### isBase64
- 判断字符串是否为base64
```js
isBase64('xxxxx')
```
#### throttle
- 节流
```js
throttle((nama) => {console.log(nama)}, 200)('limeui');
```
#### debounce
- 防抖
```js
debounce((nama) => {console.log(nama)}, 200)('limeui');
```
#### random
- 返回指定范围的随机数
```js
random(1, 5);
```
#### range
- 生成区间数组
```js
range(0, 5)
// [0,1,2,3,4,5]
```
#### clamp
- 夹在min和max之间的数值,如小于min,返回min, 如大于max,返回max,否侧原值返回
```js
clamp(0, 10, -1)
// 0
clamp(0, 10, 11)
// 10
clamp(0, 10, 9)
// 9
```
#### floatAdd
- 返回两个浮点数相加的结果
```js
floatAdd(0.1, 0.2) // 0.3
```
#### fillZero
- 补零,如果传入的是`个位数`则在前面补0
```js
fillZero(9);
// 09
```
#### exif
- 获取图片exif
- 支持临时路径、base64
```js
uni.chooseImage({
count: 1, //最多可以选择的图片张数
sizeType: "original",
success: (res) => {
exif.getData(res.tempFiles[0], function() {
let tagj = exif.getTag(this, "GPSLongitude");
let Orientation = exif.getTag(this, 'Orientation');
console.log(tagj, Orientation)
})
}
})
```
#### selectComponent
- 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件)
- 仅vue3,vue2没有测试过
```js
// 当前页面
const page = getCurrentPage()
selectComponent('.custom', {context: page}).then(res => {
})
```
#### createAnimation
- 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue
```html
<view ref="ball" :animation="animationData"></view>
```
```js
const ball = ref(null)
const animation = createAnimation({
transformOrigin: "50% 50%",
duration: 1000,
timingFunction: "ease",
delay: 0
})
animation.scale(2,2).rotate(45).step()
// nvue 无导出数据,这样写只为了平台一致,
// nvue 需要把 ref 传入,其它平台不需要
const animationData = animation.export(ball.value)
```
## composition-api
- 因本人插件需要兼容vue2/vue3,故增加一个vue文件,代替条件编译
- vue2需要在main.js加上这一段
```js
// vue2
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
```
```js
//使用
import {computed, onMounted, watch, reactive} from '@/uni_modules/lime-shared/vue'
```
## Hooks
#### useIntersectionObserver
- 使用 Intersection Observer 观察元素可见性的钩子函数
```html
<div class="target">
<h1>Hello world</h1>
</div>
```
```js
// options 接口可传的参数,若在插件里context为必传
interface UseIntersectionObserverOptions {
root ?: string; // 观察器的根元素选择器字符串
rootMargin ?: {
top ?: number; // 根元素顶部边距
bottom ?: number; // 根元素底部边距
left ?: number; // 根元素左侧边距
right ?: number; // 根元素右侧边距
}; // 根元素的边距
thresholds ?: any[]; // 交叉比例数组,用于指定何时触发回调函数
context ?: any; // 上下文对象,用于指定观察器的上下文
initialRatio ?: number; // 初始的交叉比例
observeAll ?: boolean; // 是否同时观察所有交叉对象
}
const options: UseIntersectionObserverOptions = {
rootMargin: {top: 44},
context: this
}
const {stop} = useIntersectionObserver('.target', (result) => {
}, options)
```

152
src/uni_modules/lime-shared/selectComponent/index.ts

@ -0,0 +1,152 @@
// @ts-nocheck
interface SelectOptions {
context?: any
needAll?: boolean
node?: boolean
}
// #ifdef MP
function selectMPComponent(key: string, name: string, context: any, needAll: boolean) {
const {proxy, $vm} = context
context = $vm || proxy
if(!['ref','component'].includes(key)) {
const queue = [context]
let result = null
const selector = (key == 'id' ? '#': '.') + name;
while(queue.length > 0) {
const child = queue.shift();
const flag = child?.selectComponent(selector)
if(flag) {
if(!needAll) {return result = flag.$vm}
return result = child.selectAllComponents(selector).map(item => item.$vm)
} else {
child.$children && (queue.push(...child.$children));
}
}
return result
} else {
const {$templateRefs} = context.$
const nameMap = {}
for (var i = 0; i < $templateRefs.length; i++) {
const item = $templateRefs[i]
nameMap[item.i] = item.r
}
let result = []
if(context.$children.length) {
const queue = [...context.$children]
while(queue.length > 0) {
const child = queue.shift();
if(key == 'component' && (child.type?.name === name || child.$?.type?.name === name)) {
result.push(child)
} else if(child.$refs && child.$refs[name]) {
result = child.$refs[name]
} else if(nameMap[child.id] === name){
result.push(child)
} else {
child.$children && (queue.push(...child.$children));
}
if(result.length && !needAll) {
return;
}
}
}
return needAll ? result : result[0]
}
}
// #endif
// #ifdef H5
function selectH5Component(key: string, name: string, context: any, needAll: boolean) {
const {_, component } = context
const child = {component: _ || component || context, children: null , subTree: null, props: null}
let result = []
let queue = [child]
while(queue.length > 0 ) {
const child = queue.shift()
const {component, children , props, subTree} = child
if(key === 'component' && component?.type?.name == name) {
result.push(component)
} else if(key === 'ref' && component && (props?.ref == name || component[key][name])) {
if(props?.ref == name) {
//exposed
result.push(component)
} else if(component[key][name]) {
result.push(component[key][name])
}
} else if(key !== 'ref' && component?.exposed && new RegExp(`\\b${name}\\b`).test(component.attrs[key])) {
// exposed
result.push(component)
} else if(children && Array.isArray(children)) {
queue.push(...children)
} else if(!component && subTree) {
queue.push(subTree)
} else if(component?.subTree) {
queue.push(component.subTree)
}
if(result.length && !needAll) {
break
}
}
return needAll ? result : result[0]
}
// #endif
// #ifdef APP
function selectAPPComponent(key: string, name: string, context: any, needAll: boolean, node: boolean) {
let result = []
// const {_, component} = context
// const child = {component: _ || component || context, children: null, props: null, subTree: null}
const queue = [context]
while(queue.length > 0) {
const child = queue.shift()
const {component, children, props, subTree} = child
const isComp = component && props && component.exposed && !node
if(key == 'component' && child.type && child.type.name === name) {
result.push(component)
} else if(props?.[key] === name && node) {
result.push(child)
} else if(key === 'ref' && isComp && (props.ref === name || props.ref_key === name)) {
// exposed
result.push(component)
} else if(key !== 'ref' && isComp && new RegExp(`\\b${name}\\b`).test(props[key])) {
// exposed
result.push(component)
}
// else if(component && component.subTree && Array.isArray(component.subTree.children)){
// queue.push(...component.subTree.children)
// }
else if(subTree) {
queue.push(subTree)
} else if(component && component.subTree){
queue.push(component.subTree)
}
else if(children && Array.isArray(children)) {
queue.push(...children)
}
if(result.length && !needAll) {
break;
}
}
return needAll ? result : result[0]
}
// #endif
export function selectComponent(selector: string, options: SelectOptions = {}) {
// . class
// # id
// $ ref
// @ component name
const reg = /^(\.|#|@|\$)([a-zA-Z_0-9\-]+)$/;
if(!reg.test(selector)) return null
let { context, needAll, node} = options
const [,prefix, name] = selector.match(reg)
const symbolMappings = {'.': 'class', '#': 'id', '$':'ref', '@':'component'}
const key = symbolMappings [prefix] //prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref';
console.log('key', key)
// #ifdef MP
return selectMPComponent(key, name, context, needAll)
// #endif
// #ifdef H5
return selectH5Component(key, name, context, needAll)
// #endif
// #ifdef APP
return selectAPPComponent(key, name, context, needAll, node)
// #endif
}

30
src/uni_modules/lime-shared/sleep/index.ts

@ -0,0 +1,30 @@
// @ts-nocheck
/**
* Promise
* @param delay 300
* @returns Promise
*/
export const sleep = (delay: number = 300) =>
new Promise(resolve => setTimeout(resolve, delay));
// 示例
// async function example() {
// console.log("Start");
// // 延迟 1 秒后执行
// await sleep(1000);
// console.log("1 second later");
// // 延迟 500 毫秒后执行
// await sleep(500);
// console.log("500 milliseconds later");
// // 延迟 2 秒后执行
// await sleep(2000);
// console.log("2 seconds later");
// console.log("End");
// }
// example();

41
src/uni_modules/lime-shared/throttle/index.ts

@ -0,0 +1,41 @@
// @ts-nocheck
/**
*
* @param fn
* @param delay
* @returns
*/
export function throttle(fn: (...args: any[]) => void, delay: number) {
let flag = true; // 标记是否可以执行函数
return (...args: any[]) => {
if (flag) {
flag = false; // 设置为不可执行状态
fn(...args); // 执行传入的函数
setTimeout(() => {
flag = true; // 经过指定时间后,设置为可执行状态
}, delay);
}
};
}
// // 示例
// // 定义一个被节流的函数
// function handleScroll() {
// console.log("Scroll event handled!");
// }
// // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒
// const throttledScroll = throttle(handleScroll, 500);
// // 模拟多次调用 handleScroll
// throttledScroll(); // 输出 "Scroll event handled!"
// throttledScroll(); // 不会输出
// throttledScroll(); // 不会输出
// // 经过 500 毫秒后,再次调用 handleScroll
// setTimeout(() => {
// throttledScroll(); // 输出 "Scroll event handled!"
// }, 500);

13
src/uni_modules/lime-shared/toArray/index.ts

@ -0,0 +1,13 @@
// @ts-nocheck
/**
*
* @param item
* @returns
*/
export const toArray = <T>(item: T | T[]): T[] => Array.isArray(item) ? item : [item];
// 示例
// console.log(toArray(5)); // 输出: [5]
// console.log(toArray("hello")); // 输出: ["hello"]
// console.log(toArray([1, 2, 3])); // 输出: [1, 2, 3]
// console.log(toArray(["apple", "banana"])); // 输出: ["apple", "banana"]

15
src/uni_modules/lime-shared/toNumber/index.ts

@ -0,0 +1,15 @@
// @ts-nocheck
/**
*
* @param val
* @returns
*/
export function toNumber(val: string): number | string {
const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数
return isNaN(n) ? val : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串
}
// 示例
// console.log(toNumber("123")); // 输出: 123
// console.log(toNumber("3.14")); // 输出: 3.14
// console.log(toNumber("hello")); // 输出: "hello"

39
src/uni_modules/lime-shared/unitConvert/index.ts

@ -0,0 +1,39 @@
// @ts-nocheck
import {isString} from '../isString'
import {isNumeric} from '../isNumeric'
/**
*
* @param value
* @returns 0
*/
export function unitConvert(value: string | number): number {
// 如果是字符串数字
if (isNumeric(value)) {
return Number(value);
}
// 如果有单位
if (isString(value)) {
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g;
const results = reg.exec(value);
if (!value || !results) {
return 0;
}
const unit = results[3];
value = parseFloat(value);
if (unit === 'rpx') {
return uni.upx2px(value);
}
if (unit === 'px') {
return value * 1;
}
// 如果是其他单位,可以继续添加对应的转换逻辑
}
return 0;
}
// 示例
// console.log(unitConvert("123")); // 输出: 123 (字符串数字转换为数字)
// console.log(unitConvert("3.14em")); // 输出: 0 (无法识别的单位)
// console.log(unitConvert("20rpx")); // 输出: 根据具体情况而定 (根据单位进行转换)
// console.log(unitConvert(10)); // 输出: 10 (数字不需要转换)

81
src/uni_modules/lime-shared/useIntersectionObserver/index.ts

@ -0,0 +1,81 @@
import { watch, unref, Ref } from "../vue"
// #ifdef APP-NVUE
// const dom = weex.requireModule('dom')
// const dom = uni.requireNativePlugin('dom')
// #endif
interface UseIntersectionObserverOptions {
root ?: string; // 观察器的根元素选择器字符串
rootMargin ?: {
top ?: number; // 根元素顶部边距
bottom ?: number; // 根元素底部边距
left ?: number; // 根元素左侧边距
right ?: number; // 根元素右侧边距
}; // 根元素的边距
thresholds ?: any[]; // 交叉比例数组,用于指定何时触发回调函数
context ?: any; // 上下文对象,用于指定观察器的上下文
initialRatio ?: number; // 初始的交叉比例
observeAll ?: boolean; // 是否同时观察所有交叉对象
}
/**
* 使 Intersection Observer
* @param {Ref<string> | string} target - ref
* @param {(result: UniNamespace.ObserveResult) => void} callback -
* @param {UseIntersectionObserverOptions} options -
* @returns {Object} - stop
*/
export function useIntersectionObserver(
target : Ref<string> | string,
callback : (result : UniNamespace.ObserveResult) => void,
options : UseIntersectionObserverOptions = {}) {
const {
root, // 观察器的根元素选择器
rootMargin = { top: 0, bottom: 0 }, // 根元素的边距,默认为顶部和底部都为0
thresholds = [0], // 交叉比例数组,默认为[0]
initialRatio = 0, // 初始交叉比例,默认为0
observeAll = false, // 是否同时观察所有交叉对象,默认为false
context // 上下文对象,用于指定观察器的上下文
} = options
const noop = () => { }; // 空函数,用于初始化 cleanup
let cleanup = noop; // 清理函数,用于停止 Intersection Observer 的观察
const stopWatch = watch(() => ({ el: unref(target), root: unref(root) }), ({ el, root }) => {
if (!el) {
return
}
// #ifndef APP-NVUE
// 创建 Intersection Observer 实例
const observer = uni.createIntersectionObserver(context, { thresholds, initialRatio, observeAll })
if (root) {
// 相对于根元素设置边界
observer.relativeTo(root, rootMargin)
} else {
// 相对于视口设置边界
observer.relativeToViewport(rootMargin)
}
// 观察目标元素的可见性变化
observer.observe(el, callback)
cleanup = () => {
// 停止观察
observer.disconnect()
// 将 cleanup 函数重置为空函数
cleanup = noop
}
// #endif
// #ifdef APP-NVUE
// dom.getComponentRect(el, (res) => {
// console.log('res', res)
// })
// #endif
}, { immediate: true, flush: 'post' })
const stop = () => {
// 调用 cleanup 函数停止观察
cleanup && cleanup()
// 停止 watch
stopWatch && stopWatch()
}
return { stop }
}

8
src/uni_modules/lime-shared/vue/index.ts

@ -0,0 +1,8 @@
// @ts-nocheck
// #ifdef VUE3
export * from 'vue';
// #endif
// #ifndef VUE3
export * from '@vue/composition-api';
// #endif
Loading…
Cancel
Save