master
lion 3 years ago
parent 5b08295e2c
commit 28f6a5c350

@ -0,0 +1,3 @@
{
"javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": true
}

@ -0,0 +1,76 @@
# image-tools
图像转换工具可用于如下环境uni-app、微信小程序、5+APP、浏览器需允许跨域
## 使用方式
### NPM
```
npm i image-tools --save
```
```js
import { pathToBase64, base64ToPath } from 'image-tools'
```
### 直接下载
```js
// 以下路径需根据项目实际情况填写
import { pathToBase64, base64ToPath } from '../../js/image-tools/index.js'
```
## API
### pathToBase64
从图像路径转换为base64uni-app、微信小程序和5+APP使用的路径不支持网络路径如果是网络路径需要先使用下载API下载下来。
```js
pathToBase64(path)
.then(base64 => {
console.log(base64)
})
.catch(error => {
console.error(error)
})
```
### base64ToPath
将图像base64保存为文件返回文件路径。
```js
base64ToPath(base64)
.then(path => {
console.log(path)
})
.catch(error => {
console.error(error)
})
```
## 提示
可以利用promise来串行和并行的执行多个任务
```js
// 并行
Promise.all(paths.map(path => pathToBase64(path)))
.then(res => {
console.log(res)
// [base64, base64...]
})
.catch(error => {
console.error(error)
})
// 串行
paths.reduce((promise, path) => promise.then(res => pathToBase64(path).then(base64 => (res.push(base64), res))), Promise.resolve([]))
.then(res => {
console.log(res)
// [base64, base64...]
})
.catch(error => {
console.error(error)
})
```

196
node_modules/image-tools/index.js generated vendored

@ -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'))
})
}

@ -0,0 +1,53 @@
{
"_from": "image-tools",
"_id": "image-tools@1.4.0",
"_inBundle": false,
"_integrity": "sha512-TKtvJ6iUwM0mfaD4keMnk1ENHFC470QEjBfA3IlvKdEOufzvWbjbaoNcoyYq6HlViF8+d5tOS1ooE6j7CHf1lQ==",
"_location": "/image-tools",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "image-tools",
"name": "image-tools",
"escapedName": "image-tools",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/image-tools/-/image-tools-1.4.0.tgz",
"_shasum": "66aacbafad677af7f3fd7f32f8fa1e0881b83783",
"_spec": "image-tools",
"_where": "/Users/mac/Documents/朗业/2023/b-bd智能访客系统/szbd-vistor-wx",
"author": {
"name": "Shengqiang Guo"
},
"bugs": {
"url": "https://github.com/zhetengbiji/image-tools/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "图像转换工具可用于如下环境uni-app、微信小程序、5+APP、浏览器",
"devDependencies": {
"@types/html5plus": "^1.0.0"
},
"homepage": "https://github.com/zhetengbiji/image-tools#readme",
"keywords": [
"base64"
],
"license": "ISC",
"main": "index.js",
"name": "image-tools",
"repository": {
"type": "git",
"url": "git+https://github.com/zhetengbiji/image-tools.git"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"version": "1.4.0"
}

257
node_modules/moment/package.json generated vendored

@ -1,112 +1,163 @@
{
"_args": [
[
"moment@2.29.3",
"/Users/mac/Documents/朗业/2023/b-bd智能访客系统/szbd-vistor-wx"
]
],
"_from": "moment@2.29.3",
"_id": "moment@2.29.3",
"_inBundle": false,
"_integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
"_location": "/moment",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "moment@2.29.3",
"name": "moment",
"version": "2.29.3",
"description": "Parse, validate, manipulate, and display dates",
"homepage": "https://momentjs.com",
"author": "Iskren Ivov Chernev <iskren.chernev@gmail.com> (https://github.com/ichernev)",
"contributors": [
"Tim Wood <washwithcare@gmail.com> (http://timwoodcreates.com/)",
"Rocky Meza (http://rockymeza.com)",
"Matt Johnson <mj1856@hotmail.com> (http://codeofmatt.com)",
"Isaac Cambron <isaac@isaaccambron.com> (http://isaaccambron.com)",
"Andre Polykanine <andre@oire.org> (https://github.com/oire)"
],
"keywords": [
"moment",
"date",
"time",
"parse",
"format",
"validate",
"i18n",
"l10n",
"ender"
],
"main": "./moment.js",
"jsnext:main": "./dist/moment.js",
"typings": "./moment.d.ts",
"typesVersions": {
">=3.1": {
"*": [
"ts3.1-typings/*"
]
}
},
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/moment/moment.git"
"escapedName": "moment",
"rawSpec": "2.29.3",
"saveSpec": null,
"fetchSpec": "2.29.3"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
"_spec": "2.29.3",
"_where": "/Users/mac/Documents/朗业/2023/b-bd智能访客系统/szbd-vistor-wx",
"author": {
"name": "Iskren Ivov Chernev",
"email": "iskren.chernev@gmail.com",
"url": "https://github.com/ichernev"
},
"bugs": {
"url": "https://github.com/moment/moment/issues"
},
"contributors": [
{
"name": "Tim Wood",
"email": "washwithcare@gmail.com",
"url": "http://timwoodcreates.com/"
},
"bugs": {
"url": "https://github.com/moment/moment/issues"
{
"name": "Rocky Meza",
"url": "http://rockymeza.com"
},
"license": "MIT",
"devDependencies": {
"benchmark": "latest",
"coveralls": "latest",
"cross-env": "^6.0.3",
"es6-promise": "latest",
"eslint": "~6",
"grunt": "latest",
"grunt-benchmark": "latest",
"grunt-cli": "latest",
"grunt-contrib-clean": "latest",
"grunt-contrib-concat": "latest",
"grunt-contrib-copy": "latest",
"grunt-contrib-uglify": "latest",
"grunt-contrib-watch": "latest",
"grunt-env": "latest",
"grunt-exec": "latest",
"grunt-karma": "latest",
"grunt-nuget": "latest",
"grunt-string-replace": "latest",
"karma": "latest",
"karma-chrome-launcher": "latest",
"karma-firefox-launcher": "latest",
"karma-qunit": "latest",
"karma-sauce-launcher": "4.1.4",
"load-grunt-tasks": "latest",
"lodash": ">=4.17.19",
"node-qunit": "latest",
"nyc": "latest",
"prettier": "latest",
"qunit": "^2.10.0",
"rollup": "2.17.1",
"typescript": "^1.8.10",
"typescript3": "npm:typescript@^3.1.6",
"uglify-js": "latest"
{
"name": "Matt Johnson",
"email": "mj1856@hotmail.com",
"url": "http://codeofmatt.com"
},
"ender": "./ender.js",
"dojoBuild": "package.js",
"jspm": {
"files": [
"moment.js",
"moment.d.ts",
"locale"
],
"map": {
"moment": "./moment"
},
"buildConfig": {
"uglify": true
}
{
"name": "Isaac Cambron",
"email": "isaac@isaaccambron.com",
"url": "http://isaaccambron.com"
},
"scripts": {
"ts3.1-typescript-test": "cross-env node_modules/typescript3/bin/tsc --project ts3.1-typing-tests",
"typescript-test": "cross-env node_modules/typescript/bin/tsc --project typing-tests",
"test": "grunt test",
"eslint": "eslint Gruntfile.js tasks src",
"prettier-check": "prettier --check Gruntfile.js tasks src",
"prettier-fmt": "prettier --write Gruntfile.js tasks src",
"coverage": "nyc npm test && nyc report",
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls"
{
"name": "Andre Polykanine",
"email": "andre@oire.org",
"url": "https://github.com/oire"
}
],
"description": "Parse, validate, manipulate, and display dates",
"devDependencies": {
"benchmark": "latest",
"coveralls": "latest",
"cross-env": "^6.0.3",
"es6-promise": "latest",
"eslint": "~6",
"grunt": "latest",
"grunt-benchmark": "latest",
"grunt-cli": "latest",
"grunt-contrib-clean": "latest",
"grunt-contrib-concat": "latest",
"grunt-contrib-copy": "latest",
"grunt-contrib-uglify": "latest",
"grunt-contrib-watch": "latest",
"grunt-env": "latest",
"grunt-exec": "latest",
"grunt-karma": "latest",
"grunt-nuget": "latest",
"grunt-string-replace": "latest",
"karma": "latest",
"karma-chrome-launcher": "latest",
"karma-firefox-launcher": "latest",
"karma-qunit": "latest",
"karma-sauce-launcher": "4.1.4",
"load-grunt-tasks": "latest",
"lodash": ">=4.17.19",
"node-qunit": "latest",
"nyc": "latest",
"prettier": "latest",
"qunit": "^2.10.0",
"rollup": "2.17.1",
"typescript": "^1.8.10",
"typescript3": "npm:typescript@^3.1.6",
"uglify-js": "latest"
},
"dojoBuild": "package.js",
"ender": "./ender.js",
"engines": {
"node": "*"
},
"homepage": "https://momentjs.com",
"jsnext:main": "./dist/moment.js",
"jspm": {
"files": [
"moment.js",
"moment.d.ts",
"locale"
],
"map": {
"moment": "./moment"
},
"spm": {
"main": "moment.js",
"output": [
"locale/*.js"
]
"buildConfig": {
"uglify": true
}
},
"keywords": [
"moment",
"date",
"time",
"parse",
"format",
"validate",
"i18n",
"l10n",
"ender"
],
"license": "MIT",
"main": "./moment.js",
"name": "moment",
"repository": {
"type": "git",
"url": "git+https://github.com/moment/moment.git"
},
"scripts": {
"coverage": "nyc npm test && nyc report",
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"eslint": "eslint Gruntfile.js tasks src",
"prettier-check": "prettier --check Gruntfile.js tasks src",
"prettier-fmt": "prettier --write Gruntfile.js tasks src",
"test": "grunt test",
"ts3.1-typescript-test": "cross-env node_modules/typescript3/bin/tsc --project ts3.1-typing-tests",
"typescript-test": "cross-env node_modules/typescript/bin/tsc --project typing-tests"
},
"spm": {
"main": "moment.js",
"output": [
"locale/*.js"
]
},
"typesVersions": {
">=3.1": {
"*": [
"ts3.1-typings/*"
]
}
},
"typings": "./moment.d.ts",
"version": "2.29.3"
}

@ -281,7 +281,7 @@
methods: {
init() {
//
//this.$emit('monthSelected', this.selected)
this.$emit('monthSelected', this.selected)
this.$nextTick(() => {
//
// nvue$nextTick100%

@ -1,35 +1,34 @@
// 此版本发布于2022-04-19
const version = '2.0.31'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c uView V${version} %c https://www.uviewui.com/ \n\n`,
'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;');
}
export default {
v: version,
version,
// 主题名称
type: [
'primary',
'success',
'info',
'error',
'warning'
],
// 颜色部分本来可以通过scss的:export导出供js使用但是奈何nvue不支持
color: {
'u-primary': '#2979ff',
'u-warning': '#ff9900',
'u-success': '#19be6b',
'u-error': '#fa3534',
'u-info': '#909399',
'u-main-color': '#303133',
'u-content-color': '#606266',
'u-tips-color': '#909399',
'u-light-color': '#c0c4cc'
},
// 默认单位可以通过配置为rpx那么在用于传入组件大小参数为数值时就默认为rpx
unit: 'px'
// 此版本发布于2022-04-19
const version = '2.0.31'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c uView V${version} %c https://www.uviewui.com/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;');
}
export default {
v: version,
version,
// 主题名称
type: [
'primary',
'success',
'info',
'error',
'warning'
],
// 颜色部分本来可以通过scss的:export导出供js使用但是奈何nvue不支持
color: {
'u-primary': '#2979ff',
'u-warning': '#ff9900',
'u-success': '#19be6b',
'u-error': '#fa3534',
'u-info': '#909399',
'u-main-color': '#303133',
'u-content-color': '#606266',
'u-tips-color': '#909399',
'u-light-color': '#c0c4cc'
},
// 默认单位可以通过配置为rpx那么在用于传入组件大小参数为数值时就默认为rpx
unit: 'px'
}

203
node_modules/uview-ui/package.json generated vendored

@ -1,87 +1,122 @@
{
"id": "uview-ui",
"name": "uview-ui",
"displayName": "uView2.0重磅发布,利剑出鞘,一统江湖",
"version": "2.0.31",
"description": "uView UI已完美兼容nvue全面的组件和便捷的工具会让您信手拈来如鱼得水",
"keywords": [
"uview",
"uview",
"ui",
"ui",
"uni-app",
"uni-app",
"ui"
"_args": [
[
"uview-ui@2.0.31",
"/Users/mac/Documents/朗业/2023/b-bd智能访客系统/szbd-vistor-wx"
]
],
"_from": "uview-ui@2.0.31",
"_id": "uview-ui@2.0.31",
"_inBundle": false,
"_integrity": "sha512-I/0fGuvtiKHH/mBb864SGYk+SJ7WaF32tsBgYgeBOsxlUp+Th+Ac2tgz2cTvsQJl6eZYWsKZ3ixiSXCAcxZ8Sw==",
"_location": "/uview-ui",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "uview-ui@2.0.31",
"name": "uview-ui",
"escapedName": "uview-ui",
"rawSpec": "2.0.31",
"saveSpec": null,
"fetchSpec": "2.0.31"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-2.0.31.tgz",
"_spec": "2.0.31",
"_where": "/Users/mac/Documents/朗业/2023/b-bd智能访客系统/szbd-vistor-wx",
"bugs": {
"url": "https://github.com/umicro/uView2.0/issues"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"repository": "https://github.com/umicro/uView2.0",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "1416956117"
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/uview-ui"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "n"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "1416956117"
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/uview-ui"
},
"description": "uView UI已完美兼容nvue全面的组件和便捷的工具会让您信手拈来如鱼得水",
"displayName": "uView2.0重磅发布,利剑出鞘,一统江湖",
"engines": {
"HBuilderX": "^3.1.0"
},
"homepage": "https://github.com/umicro/uView2.0#readme",
"id": "uview-ui",
"keywords": [
"uview",
"uview",
"ui",
"ui",
"uni-app",
"uni-app",
"ui"
],
"name": "uview-ui",
"repository": {
"type": "git",
"url": "git+https://github.com/umicro/uView2.0.git"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "n"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
},
"version": "2.0.31"
}

2
node_modules/uview-ui/theme.scss generated vendored

@ -10,7 +10,7 @@ $u-border-color: #dadbde;
$u-bg-color: #f3f4f6;
$u-disabled-color: #c8c9cc;
$u-primary: #044ed7;
$u-primary: #3c9cff;
$u-primary-dark: #398ade;
$u-primary-disabled: #9acafc;
$u-primary-light: #ecf5ff;

32
package-lock.json generated

@ -1,32 +1,12 @@
{
"name": "wx-dangyuanjiaoyujidi",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"moment": "^2.29.3",
"uview-ui": "^2.0.31"
}
},
"node_modules/moment": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
"engines": {
"node": "*"
}
},
"node_modules/uview-ui": {
"version": "2.0.31",
"resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-2.0.31.tgz",
"integrity": "sha512-I/0fGuvtiKHH/mBb864SGYk+SJ7WaF32tsBgYgeBOsxlUp+Th+Ac2tgz2cTvsQJl6eZYWsKZ3ixiSXCAcxZ8Sw==",
"engines": {
"HBuilderX": "^3.1.0"
}
}
},
"lockfileVersion": 1,
"dependencies": {
"image-tools": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/image-tools/-/image-tools-1.4.0.tgz",
"integrity": "sha512-TKtvJ6iUwM0mfaD4keMnk1ENHFC470QEjBfA3IlvKdEOufzvWbjbaoNcoyYq6HlViF8+d5tOS1ooE6j7CHf1lQ=="
},
"moment": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",

@ -1,5 +1,6 @@
{
"dependencies": {
"image-tools": "^1.4.0",
"moment": "^2.29.3",
"uview-ui": "^2.0.31"
}

@ -26,6 +26,10 @@
{
"path": "pages/bd/bddetail"
},
//
{
"path": "pages/bd/signpic"
},
// 访
{
"path": "pages/visit/addrecord"

@ -36,7 +36,7 @@
<template v-if="info.type!='3'">
<view class="dbtext">随访人员</view>
<view class="dbitem sfyritem">
<template v-if="info.follw_people">
<template v-if="info.follw_people.length>0">
<view class="dbinfo justify-left" v-for="people in info.follw_people">
<view>
<view>
@ -95,16 +95,11 @@
</view>
<!-- 审核 -->
<view class="checkforms dbitem" v-if="info.audit_status==0" style="padding-top:30rpx">
<view class="checkforms dbitem" v-if="info.audit_status==0&&isCheck" style="padding-top:30rpx">
<uni-forms ref="formdata" :model="checkForm" :rules="rules" labelWidth="100px">
<uni-forms-item label="状态" required name="status">
<uni-data-select v-model="checkForm.status" :localdata="statusList">
</uni-data-select>
</uni-forms-item>
<uni-forms-item label="审核" required name="level">
<uni-data-select v-model="checkForm.level" :localdata="levelList" @change="setLevel">
</uni-data-select>
<view>{{check_admin_name}}</view>
</uni-forms-item>
<uni-forms-item label="备注">
<uni-easyinput type="textarea" v-model="checkForm.reason" placeholder="请输入备注" />
@ -142,13 +137,7 @@
required: true,
errorMessage: '请选择状态'
}]
},
level: {
rules: [{
required: true,
errorMessage: '请选择审核级别'
}]
},
}
},
statusList: [{
value: 0,
@ -163,33 +152,25 @@
text: "驳回"
},
],
levelList: [],
check_admin_name: ""
check_admin_name: "",
userId:'',
isCheck:false,
}
},
onReady() {
},
onLoad(options) {
this.id = options.id
this.id = options.id
this.userId = uni.getStorageSync('userInfo_Bd').id
this.checkForm.visit_id = parseInt(options.id)
this.loadDetail()
},
methods: {
setLevel(val) {
let that = this
console.log(val)
this.levelList.map(item => {
if (item.value == val) {
that.checkForm.level = item.value
that.checkForm.audit_admin_id = item.admin_id
that.check_admin_name = item.admin_name ? item.admin_name : ''
}
})
},
checkSubmit() {
let that = this
console.log(that.checkForm)
// return
this.util.request({
api: '/api/admin/visit_audit/save',
method: "POST",
@ -225,16 +206,16 @@
id: that.id
},
utilSuccess: function(res) {
for (let k of res.visit_area.audit_admin) {
that.levelList.push({
text: k.type_name,
value: k.level,
admin_id: k.admin_id,
admin_name: k.admin_name
})
that.info = res
for (let item of res.audit) {
if(that.userId==item.audit_admin_id){
that.checkForm.level = item.level
that.checkForm.id = item.id
that.checkForm.audit_admin_id = item.audit_admin_id
that.isCheck = true
return
}
}
that.info = res
},
utilFail: function(res) {
uni.showToast({

@ -181,30 +181,13 @@
})
},
getBdInfo() {
let that = this
this.util.request({
api: '/api/admin/auth/me',
method: "POST",
requestType: 'bd',
utilSuccess: function(res) {
that.userName = res.name
that.userEmail = res.email
uni.showToast({
title: res.errmsg,
duration: 2000,
icon: 'none'
})
},
utilFail: function(res) {
console.log("resss",res)
uni.showToast({
title: res,
duration: 2000,
icon: 'none'
})
}
})
let that = this
let userInfo_bd = uni.getStorageSync('userInfo_Bd')
if(userInfo_bd){
that.userName = userInfo_bd.name
that.userEmail = userInfo_bd.email?userInfo_bd.email:''
return
}
},
loadList() {
let that = this

@ -14,7 +14,10 @@
<view class="dbitem" @click="toDetail(item.id)">
<text class="dbtype">{{item.type_text}}</text>
<view class="dbstatus bm">
<view>{{item.audit_status_text}}</view>
<view class="justify-start">
{{item.audit_status_text}}
<button class="signBtn" @click.stop='signPic(item.id)' type="primary" v-if="item.audit_status==3&&!item.accept_admin_sign"></button>
</view>
<view>
<text style="margin-right:10px">访问时间{{item.date}}</text>
<text>{{item.visit_time.start_time}}{{item.visit_time.end_time}}</text>
@ -114,7 +117,12 @@
this.loadList()
}
},
methods: {
methods: {
signPic(id){
uni.navigateTo({
url:'/pages/bd/signpic?id='+id
})
},
toDetail(id) {
uni.navigateTo({
url: '/pages/bd/bddetail?id=' + id
@ -173,5 +181,12 @@
}
/deep/ .u-empty{
min-height: 60vh;
}
.signBtn{
background-color: #044ed7;
padding: 0 30rpx;
margin-left: 40rpx;
font-size: 32rpx;
line-height: 2.3;
}
</style>

@ -0,0 +1,159 @@
<template>
<view class="containers">
<view class="signwrap">
<l-signature disableScroll backgroundColor="#ddd" ref="signatureRef" :penColor="penColor"
:penSize="penSize"></l-signature>
</view>
<view class="signbtns justify-between">
<button type="primary" @click="onClick('clear')"></button>
<button type="primary" @click="onClick('undo')"></button>
<button type="primary" @click="onClick('save')"></button>
<!-- <button @click="onClick('openSmooth')">{{openSmooth?'':''}}</button> -->
</view>
</view>
</template>
<script>
import {
base64ToPath
} from 'image-tools'
export default {
data() {
return {
id:"",
title: 'Hello',
penColor: 'black',
penSize: 5,
urls: '',
accept_admin_sign:"",
picForm:{},
host:''
}
},
onLoad(options){
this.id = options.id
this.host = this.util.HOST
this.loadDetail()
},
methods: {
onClick(type) {
if (type == 'save') {
this.$refs.signatureRef.canvasToTempFilePath({
success: (res) => {
//
if (res.isEmpty) {
return
}
//
// app | H5 | base64
this.urls = res.tempFilePath
base64ToPath(this.urls)
.then(path => {
this.uploadImg(path)
})
.catch(error => {
console.error(error)
})
}
})
return
}
if (this.$refs.signatureRef)
this.$refs.signatureRef[type]()
},
uploadImg(url) {
let that = this
uni.uploadFile({
url: this.host+'/api/admin/upload-file',
filePath: url,
name: 'file',
header:{
token:uni.getStorageSync('userInfo_BD_token').token
},
success: (res) => {
console.log("respic",res.data)
let data = JSON.parse(res.data)
that.picForm.accept_admin_sign = data.response?data.response.id:data.id
console.log("that.accept_admin_sign",that.picForm)
that.picSubmit()
}
});
},
loadDetail() {
let that = this
console.log("that.id",that.id)
this.util.request({
api: '/api/admin/visit/show',
method: "get",
requestType: 'bd',
data: {
id: that.id
},
utilSuccess: function(res) {
that.picForm = res
},
utilFail: function(res) {
uni.showToast({
title: res.errmsg,
duration: 2000,
icon: 'none'
})
}
})
},
picSubmit() {
let that = this
// return
this.util.request({
api: '/api/admin/visit/save',
method: "POST",
requestType:'bd',
data: that.picForm,
utilSuccess: function(res) {
uni.showToast({
title: res.msg,
duration: 2000,
icon: 'none'
})
uni.navigateTo({
url:'/pages/bd/record?type=myrecord'
})
},
utilFail: function(res) {
uni.showToast({
title: res.errmsg,
duration: 2000,
icon: 'none'
})
}
})
},
}
}
</script>
<style scoped>
.signwrap {
width: 100%;
height: 100vh;
position: relative;
}
.signbtns {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.signbtns button {
background: #fff;
border: none;
width: 25%;
margin-top: 20rpx;
/* padding: 40rpx; */
color: #333;
}
</style>

@ -69,27 +69,47 @@
uni.setStorageSync("userInfo_BD_token", {
token:res.access_token
})
uni.showToast({
title:"登录成功",
success() {
uni.navigateTo({
url:"/pages/bd/mine"
})
}
})
that.getBdInfo();
},
utilFail: function(res) {}
})
}).catch(err => {
console.log('err', err);
})
// uni.navigateTo({
// url:"/pages/bd/mine"
// })
}
},
getBdInfo() {
let that = this
this.util.request({
api: '/api/admin/auth/me',
method: "POST",
requestType: 'bd',
utilSuccess: function(res) {
// that.userName = res.name
// that.userEmail = res.email?res.email:''
uni.setStorageSync('userInfo_Bd',res)
uni.showToast({
title:"登录成功",
success() {
uni.navigateTo({
url:"/pages/bd/mine"
})
}
})
},
utilFail: function(res) {
console.log("resss",res)
uni.showToast({
title: res,
duration: 2000,
icon: 'none'
})
}
})
},
}
}
</script>

@ -120,7 +120,7 @@
<uni-forms-item label="姓名">
<uni-easyinput v-model="follw_people_obj.name" placeholder="请输入姓名" />
</uni-forms-item>
<uni-forms-item label="联系电话" required>
<uni-forms-item label="联系电话">
<uni-easyinput v-model="follw_people_obj.mobile" placeholder="请输入联系电话" />
</uni-forms-item>
<uni-forms-item label="证件类型">

@ -6,7 +6,7 @@
<view class="study">
<view class="service-show" v-if="studyInfo.file_detail.length>0">
<view v-if="picList.length>0&&showSwiperFlag==0">
<u-swiper :list="picList" @change="e => imgcurrent = e.current" keyName="url"
<u-swiper :list="picList" @change="changeImg" keyName="url"
:height="'360rpx'" :interval='4000' :duration='500' :autoplay='false' circular>
<view slot="indicator" class="indicator">
<view class="indicator__dot" v-for="(item, index) in picList" :key="index"
@ -16,7 +16,7 @@
</u-swiper>
</view>
<view v-if="videoList.length>0&&showSwiperFlag==1">
<u-swiper :list="videoList" @change="e => videocurrent = e.current" keyName="url"
<u-swiper :list="videoList" @change="changeVideo" keyName="url"
:height="'360rpx'" :interval='4000' :duration='500' :autoplay='false' circular>
<view slot="indicator" class="indicator">
@ -28,11 +28,11 @@
</view>
<view class="changePicVideo">
<view @click='showSwiperFlag=0,imgcurrent=0' v-if="picList.length>0">
<view @click='changeSwiperImg' v-if="picList.length>0">
<image src="../../static/img/pic.png">
</image>{{picList.length}}
</view>
<view @click='showSwiperFlag=1,videocurrent=0' v-if="videoList.length>0">
<view @click='changeSwiperVideo' v-if="videoList.length>0">
<image src="../../static/img/video.png">
</image>{{videoList.length}}
</view>
@ -131,7 +131,22 @@
uni.navigateTo({
url: '/pages/visit/testStudy?type=' + this.type
})
}
},
changeImg(e){
this.imgcurrent = e.current
},
changeImg(e){
this.videocurrent = e.current
},
changeSwiperImg(){
this.showSwiperFlag=0
this.imgcurrent=0
},
changeSwiperVideo(){
this.showSwiperFlag=1
this.videocurrent=0
}
}
}

@ -142,7 +142,7 @@
duration: 2000,
icon: 'none'
})
uni.removeStorageSync('formData')
uni.removeStorageSync('formdata')
uni.navigateTo({
url:'/pages/visit/successform'
})

@ -0,0 +1,22 @@
## 1.0.02022-10-27
- feat: 增加背景色
- feat: 修复 app canvasToTempFilePath 无操作只能执行一次的问题
## 0.8.02022-08-22
- feat: 增加beforeDelay 延时初始化,可用于手写板在弹窗里时
## 0.7.02022-08-16
- fix: 修复缺少 canvasWidth
## 0.6.02022-07-16
- fix: 修复 success is no defined
## 0.5.02022-07-09
- feat: canvasToTempFilePath success 增加返回 isEmpty
- fix: 修复 微信小程序 canvasToTempFilePath 无效问题
## 0.4.02022-07-04
- fix: 生成图片缺少最后一笔
## 0.3.02022-05-24
- chore: 支持多端 H5 小程序 APP APP-NVUE
## 0.2.02021-07-09
- chore: 统一命名规范,无须主动引入组件
- fix: 修复错位问题
## 0.1.02021-03-07
- 首次上传
- 撤消、清空、保存、模拟压感等功能

@ -0,0 +1,66 @@
export const uniContext = (ctx) => {
const ALIAS_ATTRS_MAP = [
'lineCap',
'strokeStyle',
'lineWidth',
'fillStyle',
]
ALIAS_ATTRS_MAP.forEach(style => {
Object.defineProperty(ctx, style, {
set: value => {
if(value)
ctx[`set${style.charAt(0).toUpperCase()}${style.slice(1)}`](value)
}
})
})
ctx.uniDrawImage = ctx.drawImage
ctx.drawImage = (image,...agrs) => {
ctx.uniDrawImage(image.src, ...agrs)
}
return ctx
}
class Image {
constructor() {
this.currentSrc = null
this.naturalHeight = 0
this.naturalWidth = 0
this.width = 0
this.height = 0
this.tagName = 'IMG'
}
set src(src) {
this.currentSrc = src
uni.getImageInfo({
src,
success: (res) => {
this.naturalWidth = this.width = res.width
this.naturalHeight = this.height = res.height
this.onload()
},
fail: () => {
this.onerror()
}
})
}
get src() {
return this.currentSrc
}
}
export const createImage = () => {
return new Image()
}
export const toDataURL = (canvasId, com) => {
return new Promise((resolve, reject) => {
uni.canvasToTempFilePath({
canvasId,
success: (res) => {
resolve(res.tempFilePath)
},
fail: reject
}, com)
})
}

@ -0,0 +1,558 @@
<template>
<view class="lime-signature" :style="[canvasStyle, styles]" ref="limeSignature">
<!-- #ifndef APP-VUE || APP-NVUE -->
<canvas
v-if="useCanvas2d"
class="lime-signature__canvas"
:id="canvasId"
type="2d"
:disableScroll="disableScroll"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
></canvas>
<canvas
v-else
:disableScroll="disableScroll"
class="lime-signature__canvas"
:canvas-id="canvasId"
:id="canvasId"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="touchEnd"
></canvas>
<!-- #endif -->
<!-- #ifdef APP-VUE -->
<view
:id="canvasId"
:disableScroll="disableScroll"
:rparam="param"
:change:rparam="sign.update"
:rclear="rclear"
:change:rclear="sign.clear"
:rundo="rundo"
:change:rundo="sign.undo"
:rsave="rsave"
:change:rsave="sign.save"
:rempty="rempty"
:change:rempty="sign.isEmpty"
></view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<web-view
src="/uni_modules/lime-signature/static/index.html"
class="lime-signature__canvas"
ref="webview"
@pagefinish="onPageFinish"
@error="onError"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
</view>
</template>
<!-- #ifdef APP-VUE -->
<script module="sign" lang="renderjs">
// #ifdef APP-VUE
// import { Signature } from '@signature'
import { Signature } from './signature'
// import {base64ToPath} from './utils'
export default {
data() {
return {
canvasid: null,
signature: null,
observer: null,
options: {},
saveCount: 0,
}
},
mounted() {
this.$nextTick(this.init)
},
methods: {
init() {
const el = this.$refs.limeSignature;
const canvas = document.createElement('canvas')
canvas.style = 'width:100%; height: 100%;'
el.appendChild(canvas)
this.signature = new Signature({el: canvas})
this.signature.pen.setOption(this.options)
const width = this.signature.canvas.get('width')
const height = this.signature.canvas.get('height')
this.emit({
changeSize: {width, height}
})
},
undo(v) {
if(v && this.signature) {
this.signature.undo()
}
},
clear(v) {
if(v && this.signature) {
this.signature.clear()
}
},
save(v) {
if(v !== this.saveCount) {
this.saveCount = v;
const image = this.signature.canvas.get('el').toDataURL()
const {backgroundColor } = this.options
if(backgroundColor) {
const canvas = document.createElement('canvas')
const width = this.signature.canvas.get('width')
const height = this.signature.canvas.get('height')
const pixelRatio = this.signature.canvas.get('pixelRatio')
canvas.width = width * pixelRatio
canvas.height = height * pixelRatio
const context = canvas.getContext('2d')
context.scale(pixelRatio, pixelRatio)
context.fillStyle = backgroundColor
context.fillRect(0,0, width, height)
context.drawImage(this.signature.canvas.get('el'), 0, 0, width, height)
this.emit({save: canvas.toDataURL()})
canvas.remove()
} else {
this.emit({save: image})
}
// base64ToPath(image).then((res) => {
// this.emit({save: res})
// })
}
},
isEmpty(v) {
if(v && this.signature) {
const isEmpty = this.signature.isEmpty()
this.emit({isEmpty})
}
},
emit(event) {
this.$ownerInstance.callMethod('onMessage', {
detail: {
data: [
{
event
}
]
}
})
},
update(v) {
if(v) {
if(this.signature) {
this.options = v
this.signature.pen.setOption(v)
} else {
this.options = v
}
}
}
}
}
// #endif
</script>
<!-- #endif -->
<script>
// #ifndef APP-NVUE
import {getCanvas2d, wrapEvent, requestAnimationFrame, sleep} from './utils'
import {Signature} from './signature'
// import {Signature} from '@signature';
import {uniContext, createImage, toDataURL} from './context'
// #endif
import {base64ToPath} from './utils'
export default {
props: {
styles: String,
disableScroll: Boolean,
type: {
type: String,
default: '2d'
},
//
penColor: {
type: String,
default: 'black'
},
penSize: {
type: Number,
default: 2
},
//
backgroundColor: String,
//
openSmooth: Boolean,
//
minLineWidth: {
type: Number,
default: 2
},
//
maxLineWidth: {
type: Number,
default: 6
},
// (px/ms)1.0-10.0
minSpeed: {
type: Number,
default: 1.5
},
// 线()1-100线使maxWidthDiffRate
maxWidthDiffRate: {
type: Number,
default: 20
},
// 0
maxHistoryLength: {
type: Number,
default: 20
},
beforeDelay: {
type: Number,
default: 0
}
},
data() {
return {
canvasWidth: null,
canvasHeight: null,
useCanvas2d: true,
// #ifdef APP-PLUS
rclear: 0,
rundo: 0,
rsave: 0,
rempty: 0,
risEmpty: true,
toDataURL: null,
tempFilePath: [],
// #endif
}
},
computed: {
canvasId() {
return `lime-signature${this._uid||this._.uid}`
},
canvasStyle() {
const {canvasWidth, canvasHeight, backgroundColor} = this
return {
width: canvasWidth && (canvasWidth + 'px'),
height: canvasHeight && (canvasHeight + 'px'),
background: backgroundColor
}
},
param() {
const {penColor, penSize, backgroundColor, openSmooth, minLineWidth, maxLineWidth, minSpeed, maxWidthDiffRate, maxHistoryLength, disableScroll} = this
return JSON.parse(JSON.stringify({penColor, penSize, backgroundColor, openSmooth, minLineWidth, maxLineWidth, minSpeed, maxWidthDiffRate, maxHistoryLength, disableScroll}))
}
},
// #ifdef APP-NVUE
watch: {
param(v) {
this.$refs.webview.evalJS(`update(${JSON.stringify(v)})`)
}
},
// #endif
// #ifndef APP-PLUS
created() {
this.useCanvas2d = this.type=== '2d' && getCanvas2d()
},
// #endif
// #ifndef APP-PLUS
async mounted() {
if(this.beforeDelay) {
await sleep(this.beforeDelay)
}
const config = await this.getContext()
this.signature = new Signature(config)
this.canvasEl = this.signature.canvas.get('el')
this.canvasWidth = this.signature.canvas.get('width')
this.canvasHeight = this.signature.canvas.get('height')
this.stopWatch = this.$watch('param' , (v) => {
this.signature.pen.setOption(v)
}, {immediate: true})
},
// #endif
// #ifndef APP-PLUS
// #ifdef VUE3
beforeUnmount() {
this.stopWatch()
this.signature.destroy()
},
// #endif
// #ifdef VUE2
beforeDestroy() {
this.stopWatch()
this.signature.destroy()
},
// #endif
// #endif
methods: {
// #ifdef APP-PLUS
onPageFinish() {
this.$refs.webview.evalJS(`update(${JSON.stringify(this.param)})`)
},
onMessage(e = {}) {
const {detail: {data: [res]}} = e
if(res.event?.save) {
this.toDataURL = res.event.save
}
if(res.event?.changeSize) {
const {width, height} = res.event.changeSize
}
if(res.event.hasOwnProperty('isEmpty')) {
this.risEmpty = res.event.isEmpty
}
if (res.event?.file) {
this.tempFilePath.push(res.event.file)
if (this.tempFilePath.length > 7) {
this.tempFilePath.shift()
}
return
}
if (res.event?.success) {
if (res.event.success) {
this.tempFilePath.push(res.event.success)
if (this.tempFilePath.length > 8) {
this.tempFilePath.shift()
}
this.toDataURL = this.tempFilePath.join('')
this.tempFilePath = []
// base64ToPath(this.tempFilePath.join('')).then(res => {
// })
} else {
this.$emit('fail', 'canvas no data')
}
return
}
},
// #endif
undo() {
// #ifdef APP-VUE || APP-NVUE
this.rundo += 1
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJS(`undo()`)
// #endif
// #ifndef APP-VUE
if(this.signature)
this.signature.undo()
// #endif
},
clear() {
// #ifdef APP-VUE || APP-NVUE
this.rclear += 1
// #endif
// #ifdef APP-NVUE
this.$refs.webview.evalJS(`clear()`)
// #endif
// #ifndef APP-VUE
if(this.signature)
this.signature.clear()
// #endif
},
isEmpty() {
// #ifdef APP-NVUE
this.$refs.webview.evalJS(`isEmpty()`)
// #endif
// #ifdef APP-VUE || APP-NVUE
this.rempty += 1
// #endif
// #ifndef APP-VUE || APP-NVUE
return this.signature.isEmpty()
// #endif
},
canvasToTempFilePath(param) {
const isEmpty = this.isEmpty()
// #ifdef APP-NVUE
this.$refs.webview.evalJS(`save()`)
// #endif
// #ifdef APP-VUE || APP-NVUE
const stopURLWatch = this.$watch('toDataURL', (v, n) => {
if(v && v !== n) {
if(param.pathType == 'url') {
base64ToPath(v).then(res => {
param.success({tempFilePath: res,isEmpty: this.risEmpty })
})
} else {
param.success({tempFilePath: v,isEmpty: this.risEmpty })
}
this.toDataURL = ''
}
stopURLWatch && stopURLWatch()
})
this.rsave += 1
// #endif
// #ifndef APP-VUE || APP-NVUE
const success = (success) => param.success && param.success(success)
const fail = (fail) => param.fail && param.fail(err)
const {canvas} = this.signature.canvas.get('el')
const context = this.signature.canvas.get('context')
const {backgroundColor} = this
const width = this.signature.canvas.get('width')
const height = this.signature.canvas.get('height')
if(this.useCanvas2d) {
try{
// #ifndef MP-ALIPAY
const tempFilePath = canvas.toDataURL()
if(backgroundColor) {
const image = canvas.createImage()
image.src = tempFilePath
image.onload = () => {
context.fillStyle = backgroundColor
context.fillRect(0, 0, width, height)
context.drawImage(image, 0, 0, width, height);
const tempFilePath = canvas.toDataURL()
success({tempFilePath, isEmpty})
context.clearRect(0,0, width, height)
context.drawImage(image, 0, 0, width, height);
}
} else {
success({tempFilePath, isEmpty})
}
// #endif
// #ifdef MP-ALIPAY
canvas.toTempFilePath({
canvasid: this.canvasid,
success(res){
if(backgroundColor) {
const image = canvas.createImage()
image.src = tempFilePath
image.onload = () => {
canvas.toTempFilePath({
canvasid: this.canvasid,
success(res) {
context.fillStyle = backgroundColor
context.fillRect(0, 0, width, height)
context.drawImage(image, 0, 0, width, height);
success({tempFilePath, isEmpty})
context.clearRect(0,0, width, height)
context.drawImage(image, 0, 0, width, height);
}
})
}
} else {
success({tempFilePath: res, isEmpty})
}
},
fail
})
// #endif
}catch(err){
console.warn(err)
fail(err)
}
} else {
toDataURL(this.canvasId, this).then(res => {
if(backgroundColor) {
const image = createImage()
image.src = res
image.onload = () => {
context.fillStyle = backgroundColor
context.fillRect(0, 0, width, height)
context.drawImage(image, 0, 0, width, height);
context.draw && context.draw(true, () => {
toDataURL(this.canvasId, this).then(res => {
success({tempFilePath: res, isEmpty})
context.clearRect(0,0, width, height)
context.drawImage(image, 0, 0, width, height);
context.draw && context.draw(true)
})
});
}
} else {
success({tempFilePath: res, isEmpty})
}
}).catch(err => {
console.warn(err)
fail(err)
})
}
// #endif
},
// #ifndef APP-PLUS
getContext() {
const {pixelRatio} = uni.getSystemInfoSync()
return new Promise(resolve => {
if(this.useCanvas2d) {
uni.createSelectorQuery().in(this)
.select(`#${this.canvasId}`)
.fields({
node: true,
size: true,
rect: true,
})
.exec(res => {
if(res) {
const {width, height, node, left, top, right} = res[0]
const context = node.getContext('2d')
node.width = width * pixelRatio;
node.height = height * pixelRatio;
resolve({ left, top, right, width, height, context, canvas: node, pixelRatio})
}
})
} else {
uni.createSelectorQuery().in(this)
.select(`#${this.canvasId}`)
.boundingClientRect()
.exec(res => {
if(res) {
const {width, height, left, top, right} = res[0]
const context = uniContext(uni.createCanvasContext(this.canvasId, this))
const canvas = {
createImage,
toDataURL: () => toDataURL(this.canvasId, this),
requestAnimationFrame
}
resolve({ left, top, right, width, height, context, pixelRatio:1, canvas})
}
})
}
})
},
touchStart(e) {
if(!this.canvasEl) return
this.isStart = true
this.canvasEl.dispatchEvent('touchstart', wrapEvent(e))
},
touchMove(e) {
if(!this.canvasEl || !this.isStart && this.canvasEl) return
this.canvasEl.dispatchEvent('touchmove', wrapEvent(e))
},
touchEnd(e) {
if(!this.canvasEl) return
this.isStart = false
this.canvasEl.dispatchEvent('touchend', wrapEvent(e))
},
// #endif
}
}
</script>
<style lang="stylus">
.lime-signature,.lime-signature__canvas
// #ifndef APP-NVUE
width: 100%;
height: 100%
// #endif
// #ifdef APP-NVUE
flex: 1;
// #endif
</style>

File diff suppressed because one or more lines are too long

@ -0,0 +1,95 @@
export function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i], 10)
const num2 = parseInt(v2[i], 10)
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
export const getCanvas2d = () => {
let {SDKVersion, uniPlatform} = uni.getSystemInfoSync()
if(!uniPlatform) {
// #ifdef MP-WEIXIN
uniPlatform = 'mp-weixin'
// #endif
// #ifdef MP-MP-ALIPAY
SDKVersion = my.SDKVersion
uniPlatform = 'mp-alipay'
// #endif
// #ifdef MP-MP-ALIPAY
uniPlatform = 'mp-toutiao'
// #endif
}
const MAP = {
'mp-weixin': '2.9.7',
'mp-toutiao': '1.78.0',
'mp-alipay': '2.7.0'
}[uniPlatform]
return MAP && SDKVersion && compareVersion(SDKVersion, MAP) >= 1
}
export const wrapEvent = (e) => {
if (!e) return;
if (!e.preventDefault) {
e.preventDefault = function() {};
}
return e;
}
export const requestAnimationFrame = (cb) => {
setTimeout(cb, 30)
}
/**
* base64转路径
* @param {Object} base64
*/
export function base64ToPath(base64) {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
return new Promise((resolve, reject) => {
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
bitmap.loadBase64Data(base64, () => {
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
const time = new Date().getTime();
const filePath = `_doc/uniapp_temp/${time}.${format}`
bitmap.save(filePath, {},
() => {
bitmap.clear()
resolve(filePath)
},
(error) => {
bitmap.clear()
reject(error)
})
}, (error) => {
bitmap.clear()
reject(error)
})
})
}
export function sleep(delay) {
return new Promise(resolve => setTimeout(resolve, delay))
}

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

@ -0,0 +1,77 @@
{
"id": "lime-signature",
"displayName": "手写板 签名 签字-LimeUI",
"version": "1.0.0",
"description": "手写板签名组件: 一款能跑在uniapp各端中的签名插件支持签字颜色笔画大小等功能",
"keywords": [
"canvas",
"写字",
"签名",
"签字",
"涂鸦"
],
"repository": "",
"engines": {
"HBuilderX": "^3.5.4"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"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": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

@ -0,0 +1,100 @@
# signature 写字板
> uniapp 写字板,可用业务签名等场景
> [查看更多 站点1](https://limeui.qcoon.cn/#/signature) <br>
> [查看更多 站点2](http://liangei.gitee.io/limeui/#/signature)
> Q群1169785031
## 平台兼容
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- |
| √ | √ | 未测 | 未测 | 未测 | 未测 | √ |
## 代码演示
### 基本用法
```html
<view style="width: 750rpx ;height: 750rpx;">
<l-signature disableScroll backgroundColor="#ddd" ref="signatureRef" :penColor="penColor" :penSize="penSize" :openSmooth="openSmooth" ></l-signature>
</view>
<view>
<button @click="onClick('clear')">清空</button>
<button @click="onClick('undo')">撤消</button>
<button @click="onClick('save')">保存</button>
<button @click="onClick('openSmooth')">压感{{openSmooth?'开':'关'}}</button>
</view>
```
```js
export default {
data() {
return {
title: 'Hello',
penColor: 'red',
penSize: 5,
url: '',
openSmooth: true
}
},
methods: {
onClick(type) {
if(type == 'openSmooth') {
this.openSmooth = !this.openSmooth
return
}
if (type == 'save') {
this.$refs.signatureRef.canvasToTempFilePath({
success: (res) => {
// 是否为空画板 无签名
console.log(res.isEmpty)
// 生成图片的临时路径
// app | H5 | 微信小程序 生成的是base64
this.url = res.tempFilePath
}
})
return
}
if (this.$refs.signatureRef)
this.$refs.signatureRef[type]()
}
}
}
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| -------------- | ------------ | ---------------- | ------------ |
| penSize | 画笔大小 | <em>number</em> | `2` |
| minLineWidth | 线条最小宽 | <em>number</em> | `2` |
| maxLineWidth | 线条最大宽 | <em>number</em> | `6` |
| penColor | 画笔颜色 | <em>string</em> | `black` |
| backgroundColor | 背景颜色 | <em>string</em> | `` |
| type | 指定 canvas 类型 | <em>string</em> | `2d` |
| openSmooth | 是否模拟压感 | <em>boolean</em> | `false` |
| beforeDelay | 延时初始化,在放在弹窗里可以使用 (毫秒) | <em>number</em> | `0` |
| maxHistoryLength | 限制历史记录数即最大可撤销数传入0则关闭历史记录功能 | <em>boolean</em> | `20` |
### 事件 Events
| 事件名 | 说明 | 回调 |
| ------- | ------------ | -------------- |
| undo | 撤消,回退到上一步 | |
| clear | 清空,清空画板 | |
| canvasToTempFilePath | 保存生成图片与官方保持一致但不需要传canvasId | |
### 常见问题
- 放在弹窗里时,尺寸不对 可以延时手写板出现时机给手写板加vif或beforeDelay="300"
### 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
![输入图片说明](https://static-6d65bd90-8508-4d6c-abbc-a4ef5c8e49e7.bspapp.com/image/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg")
![输入图片说明](https://static-6d65bd90-8508-4d6c-abbc-a4ef5c8e49e7.bspapp.com/image/wxplay.jpg "wxplay.jpg")

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
<style type="text/css">
html,
body,
canvas {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
overflow-y: hidden;
background-color: transparent;
}
</style>
</head>
<body>
<canvas id="lime-signature"></canvas>
<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
<script type="text/javascript" src="./signature.js"></script>
<script>
var signature = null;
var timer = null;
var isStart = false;
var options = null
console.log = function(...args) {
postMessage(args);
};
// function stringify(key, value) {
// if (typeof value === 'object' && value !== null) {
// if (cache.indexOf(value) !== -1) {
// return;
// }
// cache.push(value);
// }
// return value;
// };
function emit(event, data) {
postMessage({
event,
data: typeof data !== "object" && data !== null ? data : JSON.stringify(data),
});
// cache = [];
}
function postMessage(data) {
uni.postMessage({
data
});
}
function update(v = {}) {
if (signature) {
options = v
signature.pen.setOption(v);
} else {
signature = new Signature.Signature({el: "lime-signature"});
canvasEl = signature.canvas.get("el");
options = v
signature.pen.setOption(v)
const width = signature.canvas.get("width");
const height = signature.canvas.get("height");
emit({changeSize: {width,height}})
}
}
function clear() {
signature.clear()
}
function undo() {
signature.undo()
}
function isEmpty() {
const isEmpty = signature.isEmpty()
emit({isEmpty});
}
function save(args) {
// delete args.success;
// delete args.fail;
clearTimeout(timer);
timer = setTimeout(() => {
let path = canvasEl.toDataURL()
if(options.backgroundColor) {
const canvas = document.createElement('canvas')
const width = signature.canvas.get('width')
const height = signature.canvas.get('height')
const pixelRatio = signature.canvas.get('pixelRatio')
canvas.width = width * pixelRatio
canvas.height = height * pixelRatio
const context = canvas.getContext('2d')
context.scale(pixelRatio, pixelRatio)
context.fillStyle = backgroundColor
context.fillRect(0,0, width, height)
context.drawImage(signature.canvas.get('el'), 0, 0, width, height)
path = canvas.toDataURL()
canvas.remove()
}
if (typeof path == "string") {
const index = Math.ceil(path.length / 8);
for (var i = 0; i < 8; i++) {
if (i == 7) {
emit({"success": path.substr(i * index, index)});
} else {
emit({"file": path.substr(i * index, index)});
}
}
} else {
console.error("canvas no data");
emit({"fail": "canvas no data"});
}
}, 30);
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save