修改
@ -1,245 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="signature-wrap">
|
|
||||||
<canvas :canvas-id="cid" :id="cid" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd"
|
|
||||||
style="height:600rpx" :style="[{ width: formatSize(width)}, customStyle]"></canvas>
|
|
||||||
<slot />
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/**
|
|
||||||
* sign canvas 手写签名
|
|
||||||
* @description 设置线条宽度、颜色,撤回,清空
|
|
||||||
* @tutorial
|
|
||||||
* @property {String} cid canvas id 不设置则默认为 v-sign-时间戳
|
|
||||||
* @property {String, Number} width canvas 宽度
|
|
||||||
* @property {String, Number} height canvas 高度
|
|
||||||
* @property {Object} customStyle 自定义样式
|
|
||||||
* @property {String} lineColor 画笔颜色
|
|
||||||
* @property {Number} lineWidth 画笔大小,权重大于 v-sign-pen 组件设置的画笔大小
|
|
||||||
* @event {Function} init 当创建完 canvas 实例后触发,向外提供 canvas实例,撤回,清空方法
|
|
||||||
* @example <v-sign @init="signInit"></v-sign>
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
formatSize
|
|
||||||
} from './utils'
|
|
||||||
// convas 实例
|
|
||||||
let canvasCtx
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'v-sign',
|
|
||||||
props: {
|
|
||||||
// canvas id
|
|
||||||
cid: {
|
|
||||||
type: String,
|
|
||||||
default: `v-sign-${Date.now()}`
|
|
||||||
// required: true
|
|
||||||
},
|
|
||||||
// canvas 宽度
|
|
||||||
width: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: '100%'
|
|
||||||
},
|
|
||||||
// canvas 高度
|
|
||||||
height: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 300
|
|
||||||
},
|
|
||||||
// 画笔大小,权重大于 v-sign-pen 组件设置的画笔大小
|
|
||||||
lineWidth: {
|
|
||||||
type: Number
|
|
||||||
},
|
|
||||||
// 线颜色
|
|
||||||
lineColor: {
|
|
||||||
type: String,
|
|
||||||
default: '#000'
|
|
||||||
},
|
|
||||||
// canvas自定义样式
|
|
||||||
customStyle: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
provide() {
|
|
||||||
return {
|
|
||||||
getInterface: this.provideInterface
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
formatSize,
|
|
||||||
lineData: [],
|
|
||||||
winWidth: 0,
|
|
||||||
winHeight: 0,
|
|
||||||
penLineWidth: null, // v-sign-pen 组件设置的画笔大小
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
canvasCtx = uni.createCanvasContext(this.cid, this)
|
|
||||||
// 初始化完成,向外暴露接口
|
|
||||||
this.$emit('init', this.provideInterface())
|
|
||||||
// 获取窗口宽高
|
|
||||||
uni.getSystemInfo({
|
|
||||||
success: res => {
|
|
||||||
this.winWidth = res.windowWidth
|
|
||||||
this.winHeight = res.windowHeight
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onTouchStart(e) {
|
|
||||||
const pos = e.touches[0]
|
|
||||||
this.lineData.push({
|
|
||||||
style: {
|
|
||||||
color: this.lineColor,
|
|
||||||
width: this.lineWidth || this.penLineWidth || 4
|
|
||||||
},
|
|
||||||
// 屏幕坐标
|
|
||||||
coordinates: [{
|
|
||||||
type: e.type,
|
|
||||||
x: pos.x,
|
|
||||||
y: pos.y
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
this.drawLine()
|
|
||||||
},
|
|
||||||
onTouchMove(e) {
|
|
||||||
const pos = e.touches[0]
|
|
||||||
this.lineData[this.lineData.length - 1].coordinates.push({
|
|
||||||
type: e.type,
|
|
||||||
x: pos.x,
|
|
||||||
y: pos.y
|
|
||||||
})
|
|
||||||
this.drawLine()
|
|
||||||
},
|
|
||||||
onTouchEnd(e) {
|
|
||||||
// console.log(e.type, e)
|
|
||||||
},
|
|
||||||
// 清空画布
|
|
||||||
clear() {
|
|
||||||
this.lineData = []
|
|
||||||
canvasCtx.clearRect(0, 0, this.winWidth, this.winHeight)
|
|
||||||
canvasCtx.draw()
|
|
||||||
},
|
|
||||||
// 撤销
|
|
||||||
revoke() {
|
|
||||||
this.lineData.pop()
|
|
||||||
this.lineData.forEach((item, index) => {
|
|
||||||
canvasCtx.beginPath()
|
|
||||||
canvasCtx.setLineCap('round')
|
|
||||||
canvasCtx.setStrokeStyle(item.style.color)
|
|
||||||
canvasCtx.setLineWidth(item.style.width)
|
|
||||||
item.coordinates.forEach(pos => {
|
|
||||||
if (pos.type == 'touchstart') {
|
|
||||||
canvasCtx.moveTo(pos.x, pos.y)
|
|
||||||
} else {
|
|
||||||
canvasCtx.lineTo(pos.x, pos.y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
canvasCtx.stroke()
|
|
||||||
})
|
|
||||||
canvasCtx.draw()
|
|
||||||
},
|
|
||||||
// 绘制线条
|
|
||||||
drawLine() {
|
|
||||||
const lineDataLen = this.lineData.length
|
|
||||||
if (!lineDataLen) return
|
|
||||||
const currentLineData = this.lineData[lineDataLen - 1]
|
|
||||||
const coordinates = currentLineData.coordinates
|
|
||||||
const coordinatesLen = coordinates.length
|
|
||||||
if (!coordinatesLen) return
|
|
||||||
let startPos
|
|
||||||
let endPos
|
|
||||||
if (coordinatesLen < 2) {
|
|
||||||
// only start, no move event
|
|
||||||
startPos = coordinates[coordinatesLen - 1]
|
|
||||||
endPos = {
|
|
||||||
x: startPos.x + 1,
|
|
||||||
y: startPos.y
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
startPos = coordinates[coordinatesLen - 2]
|
|
||||||
endPos = coordinates[coordinatesLen - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
const style = currentLineData.style
|
|
||||||
canvasCtx.beginPath()
|
|
||||||
canvasCtx.setLineCap('round')
|
|
||||||
canvasCtx.setStrokeStyle(style.color)
|
|
||||||
canvasCtx.setLineWidth(style.width)
|
|
||||||
canvasCtx.moveTo(startPos.x, startPos.y)
|
|
||||||
canvasCtx.lineTo(endPos.x, endPos.y)
|
|
||||||
// const P1 = this.caculateBezier(startPos, endPos, centerPos)
|
|
||||||
// console.log(P1.x, P1.y)
|
|
||||||
// canvasCtx.moveTo(startPos.x, startPos.y)
|
|
||||||
// canvasCtx.quadraticCurveTo(P1.x, P1.y, endPos.x, endPos.y)
|
|
||||||
canvasCtx.stroke()
|
|
||||||
canvasCtx.draw(true)
|
|
||||||
},
|
|
||||||
canvasToTempFilePath(conf = {}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
uni.canvasToTempFilePath({
|
|
||||||
canvasId: this.cid,
|
|
||||||
...conf,
|
|
||||||
success: res => {
|
|
||||||
resolve(res.tempFilePath)
|
|
||||||
},
|
|
||||||
fail: err => {
|
|
||||||
console.log('fail', err)
|
|
||||||
reject(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
this
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setLineWidth(numberVal) {
|
|
||||||
this.penLineWidth = numberVal
|
|
||||||
},
|
|
||||||
provideInterface() {
|
|
||||||
return {
|
|
||||||
cid: this.cid,
|
|
||||||
ctx: canvasCtx,
|
|
||||||
clear: this.clear,
|
|
||||||
revoke: this.revoke,
|
|
||||||
canvasToTempFilePath: this.canvasToTempFilePath,
|
|
||||||
setLineWidth: this.setLineWidth
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 计算二次贝塞尔曲线 控制点 P1
|
|
||||||
* 起点 P0(x0,y0)、控制点P1(x1, y1)、P2(x2, y2)、曲线上任意点B(x, y)
|
|
||||||
* 二次贝塞尔公式:B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2
|
|
||||||
* 代入坐标得:
|
|
||||||
* x = (1-t)²*x0 + 2t(1-t)*x1 + t²*x2
|
|
||||||
* y = (1-t)²*y0 + 2t(1-t)*y1 + t²*y2
|
|
||||||
*/
|
|
||||||
caculateBezier(P0, P2, B, t = 0.5) {
|
|
||||||
const {
|
|
||||||
x: x0,
|
|
||||||
y: y0
|
|
||||||
} = P0
|
|
||||||
const {
|
|
||||||
x: x2,
|
|
||||||
y: y2
|
|
||||||
} = P2
|
|
||||||
const {
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
} = B
|
|
||||||
let x1 = (x - (1 - t) * (1 - t) * x0 - t * t * x2) / (2 * t * (1 - t))
|
|
||||||
let y1 = (y - (1 - t) * (1 - t) * y0 - t * t * y2) / (2 * t * (1 - t))
|
|
||||||
return {
|
|
||||||
x: x1,
|
|
||||||
y: y1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.signature-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
15
pages.json
@ -9,7 +9,13 @@
|
|||||||
"navigationBarTitleText": "",
|
"navigationBarTitleText": "",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
}, {
|
},{
|
||||||
|
"path": "pages/confirmCompletion/confirmCompletion",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "当前工单",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
"path": "pages/MyBenefits/MyBenefits",
|
"path": "pages/MyBenefits/MyBenefits",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "我的收益",
|
"navigationBarTitleText": "我的收益",
|
||||||
@ -48,13 +54,6 @@
|
|||||||
"navigationBarTitleText": "",
|
"navigationBarTitleText": "",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
"path": "pages/confirmCompletion/confirmCompletion",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "当前工单",
|
|
||||||
"enablePullDownRefresh": false,
|
|
||||||
"disableScroll": true
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
"path": "pages/taskReturn/taskReturn",
|
"path": "pages/taskReturn/taskReturn",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@ -101,15 +101,11 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</u-popup>
|
</u-popup>
|
||||||
<!-- <u-popup v-model="show" mode='bottom' :closeable='true' :safe-area-inset-bottom='true' @touchmove.stop.prevent
|
<u-mask :show="show" @click="show = false">
|
||||||
height='900'>
|
<view style="position:absolute;bottom:0;height:800rpx;width:100%;background-color: #fff;" v-if='show'>
|
||||||
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop
|
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop
|
||||||
style='background-color: #F4F5F7;width: 100%;height: 800rpx;'>
|
style='background-color: #F4F5F7;width: 100%;height: 800rpx;'></signature>
|
||||||
</signature>
|
</view>
|
||||||
</u-popup> -->
|
|
||||||
<u-mask :show="show" @click="show = false" @touchmove.stop.prevent>
|
|
||||||
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop v-if='show'
|
|
||||||
style='background-color: #F4F5F7;position:absolute;bottom:0%;width: 100%;height: 800rpx;'></signature>
|
|
||||||
</u-mask>
|
</u-mask>
|
||||||
<u-toast ref="uToast" />
|
<u-toast ref="uToast" />
|
||||||
</view>
|
</view>
|
||||||
@ -292,8 +288,10 @@
|
|||||||
.app {
|
.app {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: scroll;
|
|
||||||
height: 100vh;
|
::v-deep .u-mask {
|
||||||
|
height: 100vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
.finishmask {
|
.finishmask {
|
||||||
height: 300rpx;
|
height: 300rpx;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="" style="background-color: #F4F5F7;">
|
<view class="signature" style="background-color: #F4F5F7;height: 400rpx;">
|
||||||
<Signature @init="onSignInit" style='background-color: #fff;'></Signature>
|
<Signature @init="onSignInit" style='background-color: #fff;height: 700rpx;width: 100%;'></Signature>
|
||||||
<view class="btns">
|
<view class="btns">
|
||||||
<button @click="clear">清空</button>
|
<button @click="clear">清空</button>
|
||||||
<button @click="revoke">撤回</button>
|
<button @click="revoke">撤回</button>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Signature from '@/components/v-sign/v-sign.vue'
|
import Signature from '../v-sign/components/v-sign/v-sign.vue'
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Signature
|
Signature
|
||||||
@ -37,6 +37,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang='scss'>
|
||||||
|
.signature {
|
||||||
|
height: 800rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.btns {
|
.btns {
|
||||||
margin-top: 50rpx;
|
margin-top: 50rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
48
pages/v-sign/changelog.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
## 0.5.2(2022-06-10)
|
||||||
|
- canvas 默认设置 disable-scroll 属性为true, 当在 canvas 中移动时且有绑定手势事件时,禁止屏幕滚动以及下拉刷新
|
||||||
|
## 0.5.1(2022-05-27)
|
||||||
|
- 取消属性 width, height 默认值
|
||||||
|
## 0.5.0(2022-05-27)
|
||||||
|
- 新增 v-sgin 组件获取坐标信息数组方法 `getLineData`
|
||||||
|
- 新增 v-sign 组件事件触发 @clear, @revoke, @end
|
||||||
|
- 修复撤销操作重设背景色导致线条消失问题
|
||||||
|
## 0.4.0(2022-05-15)
|
||||||
|
- 新增属性 bgColor 设置画布背景色,修复导出图片无背景色问题
|
||||||
|
## 0.3.4(2022-03-30)
|
||||||
|
修复 v-sign-action 子组件 svg 加载报错
|
||||||
|
## 0.3.3(2022-03-30)
|
||||||
|
修复 v-sign-action 子组件 svg 加载报错
|
||||||
|
## 0.3.2(2022-01-12)
|
||||||
|
- 修复多组件共存绘制问题
|
||||||
|
## 0.3.1(2021-12-14)
|
||||||
|
- 【新增】保存 png 图片方法 saveImage;
|
||||||
|
- 【修改】 控件子组件保存按钮动作由保存为临时文件路径改为保存图片
|
||||||
|
## 0.3.0(2021-12-11)
|
||||||
|
- 添加颜色选择器组件
|
||||||
|
## 0.2.0(2021-12-10)
|
||||||
|
- 修改为 uni_modules 规范
|
||||||
|
## 0.1.7 (2021-12-09)
|
||||||
|
|
||||||
|
- 画笔组件优化
|
||||||
|
|
||||||
|
## 0.1.6 (2021-12-08)
|
||||||
|
|
||||||
|
- 画笔样式优化
|
||||||
|
|
||||||
|
## 0.1.5 (2021-12-08)
|
||||||
|
|
||||||
|
- 画笔子组件样式修改,支持circle、line 两种样式
|
||||||
|
|
||||||
|
## 0.1.4 (2021-12-08)
|
||||||
|
|
||||||
|
- 添加画笔子组件
|
||||||
|
|
||||||
|
## 0.1.3 (2021-12-02)
|
||||||
|
|
||||||
|
- 添加按钮控件子组件
|
||||||
|
|
||||||
|
## 0.1.0 (2021-11-28)
|
||||||
|
|
||||||
|
- 支持线宽、颜色自定义,自定义画布样式
|
||||||
|
- 支持画布清空、撤回、保存图片
|
||||||
|
- 事件 init 暴露清空、撤回、保存图片方法
|
||||||
@ -7,7 +7,7 @@
|
|||||||
:style="[{ 'margin-right': formatSize(space) }]"
|
:style="[{ 'margin-right': formatSize(space) }]"
|
||||||
@click="onBtnClick(item)"
|
@click="onBtnClick(item)"
|
||||||
>
|
>
|
||||||
<image :class="['icon', 'icon-' + item.action]" :src="item.icon"></image>
|
<image v-if="item.icon" :class="['icon', 'icon-' + item.action]" :src="item.icon"></image>
|
||||||
<text class="text">{{ item.label }}</text>
|
<text class="text">{{ item.label }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
* @event {Function} 点击对应类型按钮触发对应事件, 例如点击 clear 则触发 clear 事件
|
* @event {Function} 点击对应类型按钮触发对应事件, 例如点击 clear 则触发 clear 事件
|
||||||
* @example 示例
|
* @example 示例
|
||||||
**/
|
**/
|
||||||
import { formatSize } from './utils'
|
import { formatSize } from '../../utils'
|
||||||
|
|
||||||
// v-sign 父组件提供的接口
|
// v-sign 父组件提供的接口
|
||||||
let vSignInterface
|
let vSignInterface
|
||||||
@ -41,22 +41,22 @@ const btnsConf = [
|
|||||||
{
|
{
|
||||||
label: '清空',
|
label: '清空',
|
||||||
action: btn_type.CLEAR,
|
action: btn_type.CLEAR,
|
||||||
icon: '/static/v-sign/clear.png'
|
icon: require('../../static/image/clear.svg')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '撤回',
|
label: '撤回',
|
||||||
action: btn_type.PREV,
|
action: btn_type.PREV,
|
||||||
icon: '/static/v-sign/prev.png'
|
icon: require('../../static/image/prev.svg')
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// label: '取消撤回',
|
// label: '取消撤回',
|
||||||
// action: btn_type.NEXT,
|
// action: btn_type.NEXT,
|
||||||
// icon: '/static/v-sign/next.png'
|
// icon: '/uni_modules/v-sign/static/image/next.svg'
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
label: '保存',
|
label: '保存',
|
||||||
action: btn_type.SAVE,
|
action: btn_type.SAVE,
|
||||||
icon: '/static/v-sign/save.png'
|
icon: require('../../static/image/save.svg')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
export default {
|
export default {
|
||||||
@ -83,7 +83,7 @@ export default {
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['getInterface'],
|
inject: ['getSignInterface'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
formatSize
|
formatSize
|
||||||
@ -95,11 +95,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
vSignInterface = this.getInterface()
|
vSignInterface = this.getSignInterface()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async onBtnClick(btn) {
|
async onBtnClick(btn) {
|
||||||
// console.log(btn, btn.action)
|
|
||||||
let emit_result
|
let emit_result
|
||||||
switch (btn.action) {
|
switch (btn.action) {
|
||||||
case btn_type.CLEAR:
|
case btn_type.CLEAR:
|
||||||
@ -112,7 +111,7 @@ export default {
|
|||||||
// console.log('next')
|
// console.log('next')
|
||||||
// break
|
// break
|
||||||
case btn_type.SAVE:
|
case btn_type.SAVE:
|
||||||
emit_result = await vSignInterface.canvasToTempFilePath()
|
emit_result = await vSignInterface.saveImage()
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -138,8 +137,12 @@ export default {
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
transition: transform 0.3s;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
&.border {
|
&.border {
|
||||||
border: 2rpx solid #666;
|
border: 2rpx solid #333;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
@ -154,7 +157,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.text {
|
.text {
|
||||||
color: #666;
|
color: #333;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
212
pages/v-sign/components/v-sign-color/v-sign-color.vue
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<view class="v-sign-color">
|
||||||
|
<view class="options">
|
||||||
|
<view
|
||||||
|
class="option-item"
|
||||||
|
v-for="(color, index) in options"
|
||||||
|
:key="color"
|
||||||
|
:style="[optionStyle(color, index)]"
|
||||||
|
@click="onSelect(color, index)"
|
||||||
|
>
|
||||||
|
<view class="option-inner" :style="[optionInnerStyle(color, index)]">
|
||||||
|
{{ tick && currentIndex === index ? '✓' : '' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* v-sign-color 颜色选择器(v-sign 子组件)
|
||||||
|
* @description 设置 v-sign 画笔的颜色
|
||||||
|
* @tutorial
|
||||||
|
* @property {String} type 选项样式,可选 SQUARE \ CIRCLE
|
||||||
|
* @property {String} color 默认颜色
|
||||||
|
* @property {Array} options 备选色
|
||||||
|
* @property {Number/String} size 圆/方形大小
|
||||||
|
* @property {Boolean} tick 是否选中打勾
|
||||||
|
* @property {Number/String} tickSize 勾大小
|
||||||
|
* @property {String} borderColor 边框颜色
|
||||||
|
* @property {Boolean} border 是否有边框
|
||||||
|
* @property {Number/String} space 选项间隙
|
||||||
|
* @event {Function} change 改变颜色时触发
|
||||||
|
* @example 示例
|
||||||
|
**/
|
||||||
|
import { formatSize } from '../../utils'
|
||||||
|
// 选项样式
|
||||||
|
const type_style = {
|
||||||
|
CIRCLE: 'circle',
|
||||||
|
SQUARE: 'square'
|
||||||
|
}
|
||||||
|
// v-sign 父组件提供的接口
|
||||||
|
let vSignInterface
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'v-sign-color',
|
||||||
|
props: {
|
||||||
|
// 选项样式
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: type_style.SQUARE
|
||||||
|
},
|
||||||
|
// 默认色,需在备选中
|
||||||
|
color: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
// 备选色
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => ['#333', '#f44236', '#3f51b5', '#2195f3', '#ffeb3b', '#ff9900']
|
||||||
|
},
|
||||||
|
// 圆/方形大小
|
||||||
|
size: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 44
|
||||||
|
},
|
||||||
|
// 是否选中打勾
|
||||||
|
tick: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 勾大小
|
||||||
|
tickSize: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 24
|
||||||
|
},
|
||||||
|
// 边框颜色
|
||||||
|
borderColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#fff'
|
||||||
|
},
|
||||||
|
// 是否有边框
|
||||||
|
border: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 选项间隙
|
||||||
|
space: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['getSignInterface'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formatSize,
|
||||||
|
currentIndex: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
color: {
|
||||||
|
handler(val) {
|
||||||
|
if (!val) {
|
||||||
|
this.currentIndex = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const index = this.options.findIndex(item => item === this.color)
|
||||||
|
if (index > 0) {
|
||||||
|
this.currentIndex = index
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentColor() {
|
||||||
|
return this.options[this.currentIndex]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
vSignInterface = this.getSignInterface()
|
||||||
|
this.setLineColor()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSelect(color, index) {
|
||||||
|
this.currentIndex = index
|
||||||
|
this.$emit('change', color)
|
||||||
|
this.setLineColor()
|
||||||
|
},
|
||||||
|
setLineColor() {
|
||||||
|
vSignInterface.setLineColor(this.currentColor)
|
||||||
|
},
|
||||||
|
optionStyle(color, index) {
|
||||||
|
const size = formatSize(this.size)
|
||||||
|
let style = {
|
||||||
|
marginRight: formatSize(this.space),
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
lineHeight: size,
|
||||||
|
border:
|
||||||
|
this.border && this.currentIndex === index
|
||||||
|
? `4rpx solid ${this.borderColor}`
|
||||||
|
: '',
|
||||||
|
padding: this.currentIndex === index ? 0 : this.border ? '4rpx' : ''
|
||||||
|
}
|
||||||
|
switch (this.type) {
|
||||||
|
case type_style.SQUARE:
|
||||||
|
style = {
|
||||||
|
...style,
|
||||||
|
borderRadius: this.currentIndex === index ? '8rpx' : 0
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case type_style.CIRCLE:
|
||||||
|
style = {
|
||||||
|
...style,
|
||||||
|
borderRadius: '50%'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
},
|
||||||
|
optionInnerStyle(color, index) {
|
||||||
|
let style = {
|
||||||
|
background: color,
|
||||||
|
fontSize: formatSize(this.tickSize)
|
||||||
|
}
|
||||||
|
switch (this.type) {
|
||||||
|
case type_style.SQUARE:
|
||||||
|
style = {
|
||||||
|
...style,
|
||||||
|
borderRadius: this.currentIndex === index ? 0 : '8rpx'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case type_style.CIRCLE:
|
||||||
|
style = {
|
||||||
|
...style,
|
||||||
|
borderRadius: '50%'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.options {
|
||||||
|
padding: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item {
|
||||||
|
overflow: hidden;
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
transition: transform 0.3s;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-inner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<script>
|
<script>
|
||||||
/**
|
/**
|
||||||
* v-sign-pen 画笔(v-sign 子组件)
|
* v-sign-pen 画笔(v-sign 子组件)
|
||||||
* @description 控制 v-sign 画笔的线宽
|
* @description 设置 v-sign 画笔的线宽
|
||||||
* @tutorial
|
* @tutorial
|
||||||
* @property {String} type 选项样式 line / circle
|
* @property {String} type 选项样式 line / circle
|
||||||
* @property {String} label 标签
|
* @property {String} label 标签
|
||||||
@ -105,7 +105,7 @@ export default {
|
|||||||
default: 4
|
default: 4
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['getInterface'],
|
inject: ['getSignInterface'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
type_style,
|
type_style,
|
||||||
@ -142,7 +142,7 @@ export default {
|
|||||||
this.currentSelect = this.csizes[0]
|
this.currentSelect = this.csizes[0]
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
vSignInterface = this.getInterface()
|
vSignInterface = this.getSignInterface()
|
||||||
this.setLineWidth()
|
this.setLineWidth()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -195,6 +195,10 @@ export default {
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
&:active {
|
||||||
|
transition: transform 0.3s;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
.circle {
|
.circle {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 4rpx;
|
padding: 4rpx;
|
||||||
314
pages/v-sign/components/v-sign/v-sign.vue
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
<template>
|
||||||
|
<view class="signature-wrap">
|
||||||
|
<canvas
|
||||||
|
:canvas-id="cid"
|
||||||
|
:id="cid"
|
||||||
|
@touchstart="onTouchStart"
|
||||||
|
@touchmove="onTouchMove"
|
||||||
|
@touchend="onTouchEnd"
|
||||||
|
disable-scroll
|
||||||
|
style="width: 100%;height: 600rpx;"
|
||||||
|
></canvas>
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* sign canvas 手写签名
|
||||||
|
* @description 设置线条宽度、颜色,撤回,清空
|
||||||
|
* @tutorial
|
||||||
|
* @property {String} cid canvas id 不设置则默认为 v-sign-时间戳
|
||||||
|
* @property {String, Number} width canvas 宽度
|
||||||
|
* @property {String, Number} height canvas 高度
|
||||||
|
* @property {bgColor} bgColor 画布背景颜色
|
||||||
|
* @property {Object} customStyle canvas 自定义样式
|
||||||
|
* @property {String} lineWidth 画笔大小,权重小于 v-sign-pen 组件设置的画笔大小
|
||||||
|
* @property {Number} lineColor 画笔颜色,权重小于 v-sign-pen 组件设置的画笔大小
|
||||||
|
* @event {Function} init 当创建完 canvas 实例后触发,向外提供 canvas实例,撤回,清空方法
|
||||||
|
* @example <v-sign @init="signInit"></v-sign>
|
||||||
|
*/
|
||||||
|
import { formatSize } from '../../utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'v-sign',
|
||||||
|
props: {
|
||||||
|
// canvas id
|
||||||
|
cid: {
|
||||||
|
type: String,
|
||||||
|
default: `v-sign-${Date.now()}`
|
||||||
|
// required: true
|
||||||
|
},
|
||||||
|
// canvas 宽度
|
||||||
|
width: {
|
||||||
|
type: [String, Number]
|
||||||
|
},
|
||||||
|
// canvas 高度
|
||||||
|
height: {
|
||||||
|
type: [String, Number]
|
||||||
|
},
|
||||||
|
// 画笔大小,权重小于 v-sign-pen 组件设置的画笔大小 penLineWidth
|
||||||
|
lineWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 4
|
||||||
|
},
|
||||||
|
// 线颜色,权重小于 v-sign-color 组件设置的画笔颜色 penLineColor
|
||||||
|
lineColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#333'
|
||||||
|
},
|
||||||
|
// 画布背景颜色
|
||||||
|
bgColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#fff'
|
||||||
|
},
|
||||||
|
// canvas自定义样式
|
||||||
|
customStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
getSignInterface: this.provideSignInterface
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formatSize,
|
||||||
|
lineData: [],
|
||||||
|
winWidth: 0,
|
||||||
|
winHeight: 0,
|
||||||
|
penLineWidth: null, // v-sign-pen 组件设置的画笔大小
|
||||||
|
penLineColor: null // v-sign-color 组件设置的颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 获取窗口宽高
|
||||||
|
const { windowWidth, windowHeight } = uni.getSystemInfoSync()
|
||||||
|
this.winWidth = windowWidth
|
||||||
|
this.winHeight = windowHeight
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.canvasCtx = uni.createCanvasContext(this.cid, this)
|
||||||
|
// h5 需延迟绘制,否则绘制失败
|
||||||
|
// #ifdef H5
|
||||||
|
setTimeout(() => {
|
||||||
|
// #endif
|
||||||
|
this.setBackgroundColor(this.bgColor)
|
||||||
|
// #ifdef H5
|
||||||
|
}, 10)
|
||||||
|
// #endif
|
||||||
|
// 初始化完成,触发 init 事件
|
||||||
|
this.$emit('init', this.provideSignInterface())
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onTouchStart(e) {
|
||||||
|
const pos = e.touches[0]
|
||||||
|
this.lineData.push({
|
||||||
|
style: {
|
||||||
|
color: this.penLineColor || this.lineColor,
|
||||||
|
width: this.penLineWidth || this.lineWidth
|
||||||
|
},
|
||||||
|
// 屏幕坐标
|
||||||
|
coordinates: [
|
||||||
|
{
|
||||||
|
type: e.type,
|
||||||
|
x: pos.x,
|
||||||
|
y: pos.y
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
this.drawLine()
|
||||||
|
},
|
||||||
|
onTouchMove(e) {
|
||||||
|
const pos = e.touches[0]
|
||||||
|
this.lineData[this.lineData.length - 1].coordinates.push({
|
||||||
|
type: e.type,
|
||||||
|
x: pos.x,
|
||||||
|
y: pos.y
|
||||||
|
})
|
||||||
|
this.drawLine()
|
||||||
|
},
|
||||||
|
onTouchEnd(e) {
|
||||||
|
this.$emit('end', this.lineData)
|
||||||
|
},
|
||||||
|
// 清空画布
|
||||||
|
clear() {
|
||||||
|
this.lineData = []
|
||||||
|
this.canvasCtx.clearRect(0, 0, this.winWidth, this.winHeight)
|
||||||
|
this.canvasCtx.draw()
|
||||||
|
this.setBackgroundColor(this.bgColor)
|
||||||
|
this.$emit('clear')
|
||||||
|
},
|
||||||
|
// 撤销
|
||||||
|
revoke() {
|
||||||
|
this.setBackgroundColor(this.bgColor)
|
||||||
|
this.lineData.pop()
|
||||||
|
this.lineData.forEach((item, index) => {
|
||||||
|
this.canvasCtx.beginPath()
|
||||||
|
this.canvasCtx.setLineCap('round')
|
||||||
|
this.canvasCtx.setStrokeStyle(item.style.color)
|
||||||
|
this.canvasCtx.setLineWidth(item.style.width)
|
||||||
|
if (item.coordinates.length < 2) {
|
||||||
|
const pos = item.coordinates[0]
|
||||||
|
this.canvasCtx.moveTo(pos.x, pos.y)
|
||||||
|
this.canvasCtx.lineTo(pos.x + 1, pos.y)
|
||||||
|
} else {
|
||||||
|
item.coordinates.forEach(pos => {
|
||||||
|
if (pos.type == 'touchstart') {
|
||||||
|
this.canvasCtx.moveTo(pos.x, pos.y)
|
||||||
|
} else {
|
||||||
|
this.canvasCtx.lineTo(pos.x, pos.y)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.canvasCtx.stroke()
|
||||||
|
})
|
||||||
|
this.canvasCtx.draw(true)
|
||||||
|
this.$emit('revoke', this.lineData)
|
||||||
|
},
|
||||||
|
// 绘制线条
|
||||||
|
drawLine() {
|
||||||
|
const lineDataLen = this.lineData.length
|
||||||
|
if (!lineDataLen) return
|
||||||
|
const currentLineData = this.lineData[lineDataLen - 1]
|
||||||
|
const coordinates = currentLineData.coordinates
|
||||||
|
const coordinatesLen = coordinates.length
|
||||||
|
if (!coordinatesLen) return
|
||||||
|
let startPos
|
||||||
|
let endPos
|
||||||
|
if (coordinatesLen < 2) {
|
||||||
|
// only start, no move event
|
||||||
|
startPos = coordinates[coordinatesLen - 1]
|
||||||
|
endPos = {
|
||||||
|
x: startPos.x + 1,
|
||||||
|
y: startPos.y
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startPos = coordinates[coordinatesLen - 2]
|
||||||
|
endPos = coordinates[coordinatesLen - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = currentLineData.style
|
||||||
|
this.canvasCtx.beginPath()
|
||||||
|
this.canvasCtx.setLineCap('round')
|
||||||
|
this.canvasCtx.setStrokeStyle(style.color)
|
||||||
|
this.canvasCtx.setLineWidth(style.width)
|
||||||
|
this.canvasCtx.moveTo(startPos.x, startPos.y)
|
||||||
|
this.canvasCtx.lineTo(endPos.x, endPos.y)
|
||||||
|
// const P1 = this.caculateBezier(startPos, endPos, centerPos)
|
||||||
|
// console.log(P1.x, P1.y)
|
||||||
|
// this.canvasCtx.moveTo(startPos.x, startPos.y)
|
||||||
|
// this.canvasCtx.quadraticCurveTo(P1.x, P1.y, endPos.x, endPos.y)
|
||||||
|
this.canvasCtx.stroke()
|
||||||
|
this.canvasCtx.draw(true)
|
||||||
|
},
|
||||||
|
// 保存png图片,文件名配置 filename 仅支持 h5
|
||||||
|
async saveImage(filename = '签名') {
|
||||||
|
const tempFilePath = await this.canvasToTempFilePath()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef H5
|
||||||
|
try {
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = tempFilePath
|
||||||
|
a.download = filename
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
a.remove()
|
||||||
|
resolve({
|
||||||
|
errMsg: 'saveImageH5:ok'
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef H5
|
||||||
|
uni.saveImageToPhotosAlbum({
|
||||||
|
filePath: tempFilePath,
|
||||||
|
success(resObj) {
|
||||||
|
resolve(resObj)
|
||||||
|
},
|
||||||
|
fail(err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// canvas 保存为临时图片路径,h5返回 base64
|
||||||
|
canvasToTempFilePath(conf = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.canvasToTempFilePath(
|
||||||
|
{
|
||||||
|
canvasId: this.cid,
|
||||||
|
...conf,
|
||||||
|
success: res => {
|
||||||
|
resolve(res.tempFilePath)
|
||||||
|
},
|
||||||
|
fail: err => {
|
||||||
|
console.log('fail', err)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setBackgroundColor(color = '#fff') {
|
||||||
|
this.canvasCtx.beginPath()
|
||||||
|
this.canvasCtx.setFillStyle(color)
|
||||||
|
this.canvasCtx.fillRect(0, 0, this.winWidth, this.winHeight)
|
||||||
|
this.canvasCtx.fill()
|
||||||
|
this.canvasCtx.draw(true)
|
||||||
|
},
|
||||||
|
setLineWidth(numberVal) {
|
||||||
|
this.penLineWidth = numberVal
|
||||||
|
},
|
||||||
|
setLineColor(strValue) {
|
||||||
|
this.penLineColor = strValue
|
||||||
|
},
|
||||||
|
// 向外暴露内部方法
|
||||||
|
provideSignInterface() {
|
||||||
|
return {
|
||||||
|
cid: this.cid,
|
||||||
|
ctx: this.canvasCtx,
|
||||||
|
clear: this.clear,
|
||||||
|
revoke: this.revoke,
|
||||||
|
saveImage: this.saveImage,
|
||||||
|
canvasToTempFilePath: this.canvasToTempFilePath,
|
||||||
|
setLineWidth: this.setLineWidth,
|
||||||
|
setLineColor: this.setLineColor,
|
||||||
|
setBackgroundColor: this.setBackgroundColor,
|
||||||
|
getLineData: () => this.lineData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 计算二次贝塞尔曲线 控制点 P1
|
||||||
|
* 起点 P0(x0,y0)、控制点P1(x1, y1)、P2(x2, y2)、曲线上任意点B(x, y)
|
||||||
|
* 二次贝塞尔公式:B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2
|
||||||
|
* 代入坐标得:
|
||||||
|
* x = (1-t)²*x0 + 2t(1-t)*x1 + t²*x2
|
||||||
|
* y = (1-t)²*y0 + 2t(1-t)*y1 + t²*y2
|
||||||
|
*/
|
||||||
|
caculateBezier(P0, P2, B, t = 0.5) {
|
||||||
|
const { x: x0, y: y0 } = P0
|
||||||
|
const { x: x2, y: y2 } = P2
|
||||||
|
const { x, y } = B
|
||||||
|
let x1 = (x - (1 - t) * (1 - t) * x0 - t * t * x2) / (2 * t * (1 - t))
|
||||||
|
let y1 = (y - (1 - t) * (1 - t) * y0 - t * t * y2) / (2 * t * (1 - t))
|
||||||
|
return {
|
||||||
|
x: x1,
|
||||||
|
y: y1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.signature-wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
83
pages/v-sign/package.json
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"id": "v-sign",
|
||||||
|
"displayName": "canvas 手写 签名 签字 画板组件",
|
||||||
|
"version": "0.5.2",
|
||||||
|
"description": "基于 canvas 实现;1. 支持线条、背景色自定义样式;2. 支持撤回、清空、导出图片等功能;3. 内置按钮、画笔等子组件。",
|
||||||
|
"keywords": [
|
||||||
|
"canvas",
|
||||||
|
"签名",
|
||||||
|
"签字",
|
||||||
|
"电子签名",
|
||||||
|
"signature"
|
||||||
|
],
|
||||||
|
"repository": "https://github.com/jizai1125/v-sign",
|
||||||
|
"engines": {
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"category": [
|
||||||
|
"前端组件",
|
||||||
|
"通用组件"
|
||||||
|
],
|
||||||
|
"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": "n"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "u",
|
||||||
|
"app-nvue": "u"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
245
pages/v-sign/readme.md
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
# v-sign 手写签名
|
||||||
|
|
||||||
|
**如有问题或者建议,欢迎留言或加群联系我(群号:736123963)!!!将保持维护!!!**
|
||||||
|
|
||||||
|
<img src="https://i.loli.net/2021/12/02/bgsfnDmCzXGq8ct.png" alt="uniapp 交流群群聊二维码" style="zoom: 80%;" />
|
||||||
|
|
||||||
|
## 快速使用
|
||||||
|
|
||||||
|
基础示例,具体说明见下方 API,**若需要使用内置子组件,见下方子组件说明。**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<v-sign :width="winWidth + 'px'" @init="onSignInit"></v-sign>
|
||||||
|
<button @click="clear">清空<button>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
winWidth: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 宽度设为可使用窗口宽度
|
||||||
|
const { windowWidth } = uni.getSystemInfoSync()
|
||||||
|
this.winWidth = windowWidth
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSignInit(signCtx) {
|
||||||
|
this.signCtx = signCtx
|
||||||
|
},
|
||||||
|
// 清空
|
||||||
|
clear() {
|
||||||
|
this.signCtx.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### 属性 (Props)
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| :---------: | :-----------: | :-----------: | :--------------------------: |
|
||||||
|
| cid | String | v-sign-时间戳 | canvas id |
|
||||||
|
| width | String/Number | - | canvas 宽度,Number 单位 rpx |
|
||||||
|
| height | String/Number | - | canvas 高度,Number 单位 rpx |
|
||||||
|
| customStyle | Object | - | canvas 自定义样式 |
|
||||||
|
| lineWidth | Number | 4 | 线宽,单位 px |
|
||||||
|
| lineColor | String | #333 | 线颜色 |
|
||||||
|
| bgColor | String | #fff | 画布背景颜色 |
|
||||||
|
|
||||||
|
### 事件(Events)
|
||||||
|
|
||||||
|
| 事件称名 | 说明 | 返回值 |
|
||||||
|
| :------: | :------------------------------------------------------------: | :--------------------------------: |
|
||||||
|
| @init | 创建完 canvas 实例后触发,向外提供 canvas 实例,撤回,清空方法 | Object:具体见下方事件回调参数说明 |
|
||||||
|
| @clear | 清空画布后触发 | - |
|
||||||
|
| @revoke | 撤销操作后触发 | 坐标信息数组 |
|
||||||
|
| @end | 每次绘制结束后触发 | 坐标信息数组 |
|
||||||
|
|
||||||
|
### 事件回调参数说明
|
||||||
|
|
||||||
|
#### **`init(ctx: SignContext)`**
|
||||||
|
|
||||||
|
可以通过该事件回调暴露的 clear、revoke 等方法操作画布。
|
||||||
|
|
||||||
|
```java
|
||||||
|
interface SignContext {
|
||||||
|
// canvas 实例
|
||||||
|
ctx: object;
|
||||||
|
// 清空画布
|
||||||
|
clear(): void;
|
||||||
|
// 撤回
|
||||||
|
revoke(): void;
|
||||||
|
// 保存 png 图片,文件名 filename 配置仅支持 h5
|
||||||
|
saveImage(filename: string): Promise<object>;
|
||||||
|
// 返回图片临时文件路径,config 参数同 uni.canvasToTempFilePath方法,内部只是做了 Promise 化处理而已
|
||||||
|
canvasToTempFilePath(config: object): Promise<object>;
|
||||||
|
// 设置画布背景色
|
||||||
|
setBackgroundColor(color: string): void;
|
||||||
|
setLineWidth(value: number): void;
|
||||||
|
setLineColor(value: string): void;
|
||||||
|
// 获取坐标信息数组
|
||||||
|
getLineData(): Array<object>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<v-sign @init="onSignInit"></v-sign>
|
||||||
|
<button @click="clear">清空<button>
|
||||||
|
<button @click="revoke">撤回<button>
|
||||||
|
<button @click="saveTempFilePath">保存临时图片路径<button>
|
||||||
|
<button @click="saveImage">保存图片<button>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
onSignInit(signCtx) {
|
||||||
|
this.signCtx = signCtx
|
||||||
|
},
|
||||||
|
// 清空
|
||||||
|
clear() {
|
||||||
|
this.signCtx.clear()
|
||||||
|
},
|
||||||
|
// 撤回
|
||||||
|
revoke() {
|
||||||
|
this.signCtx.revoke()
|
||||||
|
},
|
||||||
|
// 保存为临时图片路径,h5返回 base64
|
||||||
|
saveTempFilePath() {
|
||||||
|
this.signCtx.canvasToTempFilePath()
|
||||||
|
},
|
||||||
|
// 保存 png 图片
|
||||||
|
saveImage() {
|
||||||
|
this.signCtx.saveImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
# 子组件
|
||||||
|
|
||||||
|
子组件需要包裹在 `v-sign` 组件内使用!!!
|
||||||
|
|
||||||
|
## 按钮控件(v-sign-action)
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<v-sign>
|
||||||
|
<v-sign-action @save="save" @clear="clear" @prev="revoke"></v-sign-action>
|
||||||
|
</v-sign>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
save(tempFilePath) {
|
||||||
|
console.log(tempFilePath);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
### 属性 (Props)
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| :---------: | :-----------: | :-----------------------: | :----------------------------------------------------: |
|
||||||
|
| actions | Array | ["clear", "prev", "save"] | 按钮配置;清空(clear), 撤回(prev) 保存图片(save) |
|
||||||
|
| border | Boolean | true | 按钮是否有边框 |
|
||||||
|
| space | String/Number | 12 | 按钮间隔,Number 单位 rpx |
|
||||||
|
| customStyle | Object | - | 根元素自定义样式 |
|
||||||
|
|
||||||
|
### 事件(Events)
|
||||||
|
|
||||||
|
点击对应类型按钮触发对应事件, 例如:配置了清空(clear)按钮,点击则触发 clear 事件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 画笔组件(v-sign-pen)
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<v-sign>
|
||||||
|
<v-sign-pen></v-sign-pen>
|
||||||
|
</v-sign>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
### 属性 (Props)
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| :---------: | :-----: | :--------------: | :--------------------------: |
|
||||||
|
| type | String | circle | 选项样式,可选 circle \ line |
|
||||||
|
| label | String | - | 标签 |
|
||||||
|
| sizes | Array | [2, 4, 6, 8, 10] | 画笔尺寸数组,单位 px |
|
||||||
|
| color | String | #333 | 选项颜色 |
|
||||||
|
| activeColor | String | #333 | 选中项颜色 |
|
||||||
|
| border | Boolean | true | 选中项是否有边框 |
|
||||||
|
| borderWidth | Number | 4 | 边框大小,单位 rpx |
|
||||||
|
| space | Number | 20 | 选项间隙,单位 rpx |
|
||||||
|
| bigger | Number | 2 | 圆点变大变粗倍数 |
|
||||||
|
| minSize | Number | 4 | 圆点最小尺寸,单位 px |
|
||||||
|
|
||||||
|
### 事件(Events)
|
||||||
|
|
||||||
|
| 事件称名 | 说明 | 返回值 |
|
||||||
|
| :------: | :----------------: | :----------------: |
|
||||||
|
| @change | 选择画笔大小时触发 | size:画笔尺寸大小 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 颜色选择器组件(v-sign-color)
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<v-sign>
|
||||||
|
<v-sign-color></v-sign-color>
|
||||||
|
</v-sign>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
### 属性 (Props)
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| :---------: | :-----------: | :-------------------------------------------------------------: | :-------------------: |
|
||||||
|
| type | String | square | 选项样式,可选 circle |
|
||||||
|
| color | String | - | 默认颜色 |
|
||||||
|
| options | Array | ['#333', '#f44236', '#3f51b5', '#2195f3', '#ffeb3b', '#ff9900'] | 备选色 |
|
||||||
|
| size | Number/String | 44 | 圆/方形大小,单位 rpx |
|
||||||
|
| tick | Boolean | true | 是否选中打勾 |
|
||||||
|
| tickSize | Number/String | 24 | 勾大小 |
|
||||||
|
| borderColor | String | #fff | 边框颜色 |
|
||||||
|
| border | Boolean | false | 是否有边框 |
|
||||||
|
| space | Number/String | 16 | 选项间隙 |
|
||||||
|
|
||||||
|
### 事件(Events)
|
||||||
|
|
||||||
|
| 事件称名 | 说明 | 返回值 |
|
||||||
|
| :------: | :------------: | :----: |
|
||||||
|
| @change | 选择颜色时触发 | color |
|
||||||
BIN
pages/v-sign/static/image/clear.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
1
pages/v-sign/static/image/clear.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214313546" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="827" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M274.56 798.997333l19.434667-25.130666-33.792 68.565333a18.133333 18.133333 0 0 0 11.562666 25.536l59.733334 16a18.133333 18.133333 0 0 0 17.28-4.48c20.522667-19.818667 35.626667-35.989333 45.290666-48.469333l19.456-25.130667-33.813333 68.565333a18.133333 18.133333 0 0 0 11.562667 25.536l84.48 22.634667a18.133333 18.133333 0 0 0 17.28-4.48c20.522667-19.84 35.626667-35.989333 45.269333-48.469333l19.456-25.130667-33.813333 68.565333A18.133333 18.133333 0 0 0 535.530667 938.666667l72.106666 19.328a18.133333 18.133333 0 0 0 17.28-4.48c20.522667-19.84 35.626667-36.010667 45.269334-48.490667l19.456-25.130667-33.813334 68.586667a18.133333 18.133333 0 0 0 11.584 25.514667l86.421334 23.338666 3.84-0.213333c13.269333-0.704 29.056-5.034667 43.84-12.8 29.781333-15.701333 48.170667-43.2 52.181333-78.250667 2.133333-18.517333 4.778667-38.549333 8.405333-63.530666 1.642667-11.221333 2.944-20.010667 6.229334-41.834667 11.050667-73.322667 14.634667-101.034667 17.130666-133.674667l0.938667-12.373333 2.837333-2.922667 12.330667-1.344a41.813333 41.813333 0 0 0 24.810667-11.221333c10.730667-10.24 14.805333-25.386667 11.093333-42.197333l-37.546667-171.584c-3.029333-13.696-11.264-27.946667-23.146666-39.829334-11.648-11.626667-25.92-20.138667-39.893334-23.893333L723.626667 331.306667l-2.261334-3.925334L774.250667 130.133333c8.32-31.061333-11.754667-63.744-44.970667-72.64l-79.509333-21.312c-33.194667-8.896-66.922667 9.365333-75.264 40.426667l-52.842667 197.269333-3.925333 2.261334-118.101334-31.637334c-13.994667-3.754667-30.634667-3.498667-46.506666 0.746667-16.256 4.352-30.506667 12.586667-39.957334 22.933333l-118.314666 129.792c-11.605333 12.714667-15.658667 27.84-11.52 42.090667 4.16 14.229333 15.850667 25.194667 32.896 30.528l13.610666 4.266667 2.133334 3.882666-3.626667 13.802667c-21.12 79.850667-52.885333 136.917333-85.717333 150.890667-47.530667 20.202667-72.938667 49.429333-78.421334 85.034666-5.034667 32.682667 9.28 67.114667 37.589334 91.541334l22.037333 8.341333 74.666667 20.010667a42.666667 42.666667 0 0 0 41.216-11.050667c15.274667-15.274667 26.88-28.032 34.837333-38.293333z m551.381333-396.565333c14.144 3.797333 29.952 19.2 32.768 32l34.56 157.781333a10.666667 10.666667 0 0 1-13.184 12.586667L240.64 433.493333a10.666667 10.666667 0 0 1-5.12-17.493333l108.8-119.36c8.832-9.685333 30.229333-15.146667 44.373333-11.349333l141.333334 37.866666a21.333333 21.333333 0 0 0 26.133333-15.082666l58.304-217.642667a21.333333 21.333333 0 0 1 26.133333-15.082667l77.056 20.650667a21.333333 21.333333 0 0 1 15.082667 26.133333l-58.325333 217.642667a21.333333 21.333333 0 0 0 15.082666 26.112l136.448 36.565333zM315.456 701.568c-33.664 45.141333-64.597333 79.082667-92.8 101.802667l-5.909333 4.778666-2.837334 0.597334-88.106666-24.106667-2.922667-3.2c-13.034667-14.165333-19.370667-31.04-16.981333-46.592 3.285333-21.333333 22.058667-39.338667 53.205333-52.586667 31.722667-13.482667 59.818667-47.104 82.922667-99.904 10.026667-22.954667 18.88-48.725333 26.389333-76.586666l3.882667-14.4 3.904-2.261334 566.165333 151.701334 2.346667 3.306666-0.789334 12.224c-1.984 30.592-30.336 229.397333-32.128 244.906667-2.346667 20.416-11.306667 34.986667-27.605333 44.394667a73.237333 73.237333 0 0 1-21.397333 8.106666l-5.013334 0.725334-60.373333-16.170667 11.242667-20.288c8.277333-14.976 22.656-43.84 43.093333-86.613333a21.12 21.12 0 0 0-9.962667-28.16l-3.136-1.493334a21.333333 21.333333 0 0 0-26.261333 6.485334c-33.642667 45.056-64.533333 78.912-92.672 101.546666l-5.909333 4.757334-2.837334 0.597333-52.544-14.08 11.114667-20.266667c3.562667-6.485333 7.04-13.013333 10.453333-19.626666 7.04-13.504 17.898667-35.797333 32.597334-66.816a21.290667 21.290667 0 0 0-9.984-28.309334l-3.029334-1.450666a21.333333 21.333333 0 0 0-26.368 6.442666c-33.6 45.013333-64.469333 78.826667-92.608 101.482667l-5.909333 4.757333-2.837333 0.597334-52.138667-13.973334 11.114667-20.266666c3.242667-5.888 6.72-12.416 10.453333-19.626667 6.997333-13.461333 17.962667-35.946667 32.896-67.434667a20.970667 20.970667 0 0 0-10.112-28.010666l-3.328-1.536a21.333333 21.333333 0 0 0-26.069333 6.613333c-33.642667 45.056-64.554667 78.976-92.778667 101.696l-5.909333 4.757333-2.837334 0.597334-32.64-8.746667 11.093334-20.245333c3.541333-6.506667 7.04-13.034667 10.453333-19.626667 6.976-13.482667 17.941333-35.968 32.874667-67.456a21.056 21.056 0 0 0-10.069334-28.074667l-3.242666-1.514666a21.333333 21.333333 0 0 0-26.154667 6.549333z" fill="#333333" p-id="828"></path></svg>
|
||||||
|
After Width: | Height: | Size: 4.7 KiB |
BIN
pages/v-sign/static/image/next.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
1
pages/v-sign/static/image/next.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214327673" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1119" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M588.8 934.4c-17.066667-4.266667-25.6-21.333333-25.6-38.4v-204.8c-68.266667-8.533333-128-4.266667-192 12.8-76.8 21.333333-170.666667 93.866667-273.066667 213.333333-12.8 17.066667-34.133333 21.333333-51.2 12.8-17.066667-8.533333-29.866667-25.6-25.6-42.666666 17.066667-162.133333 76.8-298.666667 187.733334-405.333334 98.133333-98.133333 213.333333-153.6 349.866666-166.4V128c0-17.066667 8.533333-34.133333 25.6-38.4 17.066667-8.533333 34.133333-4.266667 46.933334 8.533333l358.4 375.466667c17.066667 17.066667 17.066667 42.666667 0 59.733333l-358.4 392.533334c-8.533333 8.533333-21.333333 12.8-29.866667 12.8-4.266667 0-8.533333 0-12.8-4.266667z m55.466667-699.733333v123.733333c0 21.333333-17.066667 42.666667-42.666667 42.666667-132.266667 0-243.2 46.933333-337.066667 140.8-59.733333 59.733333-102.4 128-128 204.8 72.533333-68.266667 145.066667-110.933333 209.066667-128 85.333333-21.333333 166.4-25.6 260.266667-8.533334 21.333333 4.266667 34.133333 21.333333 34.133333 42.666667v132.266667l256-281.6-251.733333-268.8z" fill="#333333" p-id="1120"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
pages/v-sign/static/image/prev.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
1
pages/v-sign/static/image/prev.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214320974" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="973" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M422.4 938.666667c-12.8 0-21.333333-4.266667-29.866667-12.8L34.133333 533.333333c-17.066667-17.066667-12.8-42.666667 0-59.733333l358.4-375.466667c8.533333-12.8 25.6-17.066667 42.666667-8.533333s25.6 21.333333 25.6 38.4v187.733333c136.533333 8.533333 256 64 354.133333 166.4 106.666667 106.666667 170.666667 243.2 187.733334 405.333334 0 17.066667-8.533333 38.4-25.6 42.666666-17.066667 8.533333-38.4 4.266667-51.2-12.8-98.133333-119.466667-192-196.266667-273.066667-213.333333-64-17.066667-123.733333-21.333333-192-12.8V896c0 17.066667-12.8 34.133333-25.6 38.4-4.266667 4.266667-8.533333 4.266667-12.8 4.266667z m-298.666667-435.2l256 281.6v-132.266667c0-21.333333 17.066667-38.4 34.133334-42.666667 93.866667-17.066667 174.933333-12.8 260.266666 8.533334 64 17.066667 136.533333 59.733333 209.066667 128-25.6-76.8-72.533333-149.333333-128-204.8-93.866667-93.866667-204.8-140.8-337.066667-140.8-21.333333 0-42.666667-21.333333-42.666666-42.666667V234.666667l-251.733334 268.8z" fill="#333333" p-id="974"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
pages/v-sign/static/image/save.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
1
pages/v-sign/static/image/save.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214153477" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="861" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M62.848 60.992h704v64h-704zM62.848 640.832h543.936v64H62.848z" fill="#333333" p-id="862"></path><path d="M702.848 60.992h64v320h-64zM62.848 113.536h64v527.296h-64z" fill="#333333" p-id="863"></path><path d="M70.528 498.304l218.24-117.312 30.4 56.32-218.304 117.376z" fill="#333333" p-id="864"></path><path d="M298.432 375.04l144.896 274.176-56.576 29.888L241.92 404.928zM512 194.176h126.592v127.168H512zM663.296 448.448h128v320h-128z" fill="#333333" p-id="865"></path><path d="M599.36 764.992l127.936 128 128.064-128z" fill="#333333" p-id="866"></path></svg>
|
||||||
|
After Width: | Height: | Size: 934 B |
@ -10,6 +10,6 @@ export function isNumber(val) {
|
|||||||
* 处理大小单位
|
* 处理大小单位
|
||||||
* @param {Object} val
|
* @param {Object} val
|
||||||
*/
|
*/
|
||||||
export function formatSize(val) {
|
export function formatSize(val, unit = 'rpx') {
|
||||||
return isNumber(val) ? `${val}rpx` : val
|
return isNumber(val) ? `${val}${unit}` : val
|
||||||
}
|
}
|
||||||