mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-07 09:38:44 +08:00
parent
981c8b4ea9
commit
0ad8fcce28
@ -337,7 +337,12 @@ export default defineConfig({
|
|||||||
}, {
|
}, {
|
||||||
link: "/component/password-input",
|
link: "/component/password-input",
|
||||||
text: "PasswordInput 密码输入框"
|
text: "PasswordInput 密码输入框"
|
||||||
}]
|
},
|
||||||
|
, {
|
||||||
|
link: "/component/signature",
|
||||||
|
text: "Signature 签名"
|
||||||
|
}
|
||||||
|
]
|
||||||
}, {
|
}, {
|
||||||
text: "反馈",
|
text: "反馈",
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
|||||||
87
docs/component/signature.md
Normal file
87
docs/component/signature.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# Signature 组件
|
||||||
|
`Signature`组件是一个用于生成手写签名的 Vue 组件。它提供了多种自定义选项,包括签名笔的颜色、宽度以及自定义操作按钮
|
||||||
|
## 基础用法
|
||||||
|
```html
|
||||||
|
<wd-signature @confirm="confirm" />
|
||||||
|
<wd-img :height="img.height" :width="img.width" :src="img.src" v-if="img.src" />
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const img = ref({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
src: ''
|
||||||
|
})
|
||||||
|
function confirm(result: FileType) {
|
||||||
|
img.value.src = result.tempFilePath
|
||||||
|
img.value.height = result.height
|
||||||
|
img.value.width = result.width
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 自定义颜色
|
||||||
|
`pen-color`设置签名笔的颜色,默认为`黑色`
|
||||||
|
```html
|
||||||
|
<wd-signature pen-color="red" />
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 自定义宽度
|
||||||
|
`line-width`设置签名笔的宽度,默认为`2`。
|
||||||
|
```html
|
||||||
|
<wd-signature :line-width="6" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自定义按钮
|
||||||
|
通过`footer`插槽可以自定义按钮
|
||||||
|
```html
|
||||||
|
<wd-signature :disabled="disabled">
|
||||||
|
<template #footer="{ clear, confirm }">
|
||||||
|
<wd-button block @click="changeDisabled" v-if="disabled">开始签名</wd-button>
|
||||||
|
<wd-button v-if="!disabled" size="small" plain @click="clear">清除</wd-button>
|
||||||
|
<wd-button v-if="!disabled" size="small" style="margin-left: 4px" @click="confirm">确认</wd-button>
|
||||||
|
</template>
|
||||||
|
</wd-signature>
|
||||||
|
```
|
||||||
|
```typescript
|
||||||
|
const disabled = ref(true)
|
||||||
|
|
||||||
|
function changeDisabled() {
|
||||||
|
disabled.value = false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Attributes
|
||||||
|
| 参数 | 说明 | 类型 | 可选值 | 默认值| 最低版本 |
|
||||||
|
|-----|------|-----|-------|-------|--------|
|
||||||
|
| penColor | 签名笔颜色 | String | -- | #000000 | -- |
|
||||||
|
| lineWidth | 签名笔宽度 | Number | -- | 2 | -- |
|
||||||
|
| height | 画布的高度 | Number | -- | 200 | -- |
|
||||||
|
| width | 画布的宽度 | Number | -- | 300 | -- |
|
||||||
|
| clearText | 清空按钮的文本 | String |-- | 清空 | -- |
|
||||||
|
| confirmText | 确认按钮的文本 | String | -- | 确认 | -- |
|
||||||
|
| fileType | 目标文件的类型,[wx.canvasToTempFilePath属性介绍](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.canvasToTempFilePath.html#%E5%8F%82%E6%95%B0) | String | -- | png | -- |
|
||||||
|
| quality | 目标文件的类型,[wx.canvasToTempFilePath属性介绍](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.canvasToTempFilePath.html#%E5%8F%82%E6%95%B0) | Number | -- | 1 |-- |
|
||||||
|
| exportScale | 导出图片的缩放比例 | Number | -- | 1 |-- |
|
||||||
|
| disabled | 是否禁用签名板 | Boolean | -- | false | -- |
|
||||||
|
|
||||||
|
## Slot
|
||||||
|
|
||||||
|
| name | 说明 |参数 | 最低版本 |
|
||||||
|
| ------- | ------------------------ |--- | -------- |
|
||||||
|
| footer | 自定义footer | `{ clear, confirm }` |- |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 参数 | 最低版本 |
|
||||||
|
|---------|-----|-----|---------|
|
||||||
|
| confirm | 点击确认按钮时触发 | `{tempFilePath, width, height}` 分别为生成文件的临时路径 (本地路径)、生成图片宽、生成图片高| - |
|
||||||
|
| clear | 点击清空按钮时触发 | - | - |
|
||||||
|
| touchstart | 按下时触发 | `event`| - |
|
||||||
|
| touchend | 按下结束时触发 | `event` | - |
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
对外暴露函数
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 参数 | 最低版本 |
|
||||||
|
|--------|------|-----|---------|
|
||||||
|
| confirm | 点击确认按钮时触发 | `{tempFilePath, width, height}` 分别为生成文件的临时路径 (本地路径)、生成图片宽、生成图片高| - |
|
||||||
|
| clear | 点击清空按钮时触发 | - | - |
|
||||||
@ -27,7 +27,6 @@
|
|||||||
"mp-alipay": {
|
"mp-alipay": {
|
||||||
"allowsBounceVertical": "NO"
|
"allowsBounceVertical": "NO"
|
||||||
},
|
},
|
||||||
|
|
||||||
"navigationBarTitleText": "Icon 图标"
|
"navigationBarTitleText": "Icon 图标"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -802,6 +801,16 @@
|
|||||||
"navigationBarTitleText": "PasswordInput 密码输入框"
|
"navigationBarTitleText": "PasswordInput 密码输入框"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/signature/Index",
|
||||||
|
"name": "signature",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "Signature 签名"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/backtop/Index",
|
"path": "pages/backtop/Index",
|
||||||
"name": "backtop",
|
"name": "backtop",
|
||||||
@ -858,7 +867,6 @@
|
|||||||
"backgroundColorTop": "@bgColorTop",
|
"backgroundColorTop": "@bgColorTop",
|
||||||
"backgroundColorBottom": "@bgColorBottom",
|
"backgroundColorBottom": "@bgColorBottom",
|
||||||
"navigationStyle": "default"
|
"navigationStyle": "default"
|
||||||
|
|
||||||
// "navigationBarTextStyle": "black",
|
// "navigationBarTextStyle": "black",
|
||||||
// "navigationBarBackgroundColor": "#FFF",
|
// "navigationBarBackgroundColor": "#FFF",
|
||||||
// "backgroundColor": "#F8F8F8"
|
// "backgroundColor": "#F8F8F8"
|
||||||
|
|||||||
@ -218,6 +218,10 @@ const list = ref([
|
|||||||
{
|
{
|
||||||
id: 'passwordInput',
|
id: 'passwordInput',
|
||||||
name: 'PasswordInput 密码输入框'
|
name: 'PasswordInput 密码输入框'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'signature',
|
||||||
|
name: 'Signature 签名'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -427,37 +431,45 @@ onShareTimeline(() => {
|
|||||||
.kind-list__item {
|
.kind-list__item {
|
||||||
background: $-dark-background2;
|
background: $-dark-background2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.wd-cell__label) {
|
:deep(.wd-cell__label) {
|
||||||
color: $-dark-color3 !important;
|
color: $-dark-color3 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kind-list__img {
|
.kind-list__img {
|
||||||
filter: invert(100%);
|
filter: invert(100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page__hd {
|
.page__hd {
|
||||||
padding: 40px 40px 30px;
|
padding: 40px 40px 30px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page__title {
|
.page__title {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #0083ff;
|
color: #0083ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page__desc {
|
.page__desc {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
color: #999;
|
color: #999;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page__bd {
|
.page__bd {
|
||||||
padding: 0 15px 30px 20px;
|
padding: 0 15px 30px 20px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 14px;
|
margin-right: 14px;
|
||||||
@ -468,10 +480,12 @@ onShareTimeline(() => {
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline {
|
.inline {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
.version {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@ -479,20 +493,24 @@ onShareTimeline(() => {
|
|||||||
.wd-cell_access {
|
.wd-cell_access {
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-cell__ft {
|
.wd-cell__ft {
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-cells {
|
.wd-cells {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
|
|
||||||
:deep(.wd-cell__label) {
|
:deep(.wd-cell__label) {
|
||||||
color: rgba(0, 0, 0, 0.65);
|
color: rgba(0, 0, 0, 0.65);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-cells_show {
|
.wd-cells_show {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
@ -502,6 +520,7 @@ onShareTimeline(() => {
|
|||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
@ -521,6 +540,7 @@ onShareTimeline(() => {
|
|||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kind-list__item-bd_show {
|
.kind-list__item-bd_show {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
@ -528,13 +548,16 @@ onShareTimeline(() => {
|
|||||||
.wd-flex {
|
.wd-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-flex__item {
|
.wd-flex__item {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: rgba(0, 0, 0, 0.85);
|
color: rgba(0, 0, 0, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-name {
|
.page-name {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: rgba(0, 0, 0, 0.65);
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
|||||||
49
src/pages/signature/Index.vue
Normal file
49
src/pages/signature/Index.vue
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: 810505339
|
||||||
|
* @Date: 2025-01-10 15:49:26
|
||||||
|
* @LastEditors: 810505339
|
||||||
|
* @LastEditTime: 2025-01-11 22:10:22
|
||||||
|
* @FilePath: \wot-design-uni\src\pages\signature\Index.vue
|
||||||
|
* 记得注释
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<page-wraper>
|
||||||
|
<demo-block title="基础用法">
|
||||||
|
<wd-signature @confirm="confirm" />
|
||||||
|
<wd-img :height="img.height" :width="img.width" :src="img.src" v-if="img.src" />
|
||||||
|
</demo-block>
|
||||||
|
<demo-block title="自定义颜色">
|
||||||
|
<wd-signature pen-color="red" />
|
||||||
|
</demo-block>
|
||||||
|
<demo-block title="自定义宽度">
|
||||||
|
<wd-signature :line-width="6" />
|
||||||
|
</demo-block>
|
||||||
|
<demo-block title="自定义按钮">
|
||||||
|
<wd-signature :disabled="disabled">
|
||||||
|
<template #footer="{ clear, confirm }">
|
||||||
|
<wd-button block @click="changeDisabled" v-if="disabled">开始签名</wd-button>
|
||||||
|
<wd-button v-if="!disabled" size="small" plain @click="clear">清除</wd-button>
|
||||||
|
<wd-button v-if="!disabled" size="small" style="margin-left: 4px" @click="confirm">确认</wd-button>
|
||||||
|
</template>
|
||||||
|
</wd-signature>
|
||||||
|
</demo-block>
|
||||||
|
</page-wraper>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { FileType } from '@/uni_modules/wot-design-uni/components/wd-signature/types'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
const img = ref({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
src: ''
|
||||||
|
})
|
||||||
|
const disabled = ref(true)
|
||||||
|
function confirm(result: FileType) {
|
||||||
|
img.value.src = result.tempFilePath
|
||||||
|
img.value.height = result.height
|
||||||
|
img.value.width = result.width
|
||||||
|
}
|
||||||
|
function changeDisabled() {
|
||||||
|
disabled.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -953,3 +953,8 @@ $-floating-panel-bar-height: var(--wot-floating-panel-bar-height, 3px) !default;
|
|||||||
$-floating-panel-bar-bg: var(--wot-floating-panel-bar-bg, $-color-gray-5) !default; // bar 背景色
|
$-floating-panel-bar-bg: var(--wot-floating-panel-bar-bg, $-color-gray-5) !default; // bar 背景色
|
||||||
$-floating-panel-bar-radius: var(--wot-floating-panel-bar-radius, 4px) !default; // bar 圆角
|
$-floating-panel-bar-radius: var(--wot-floating-panel-bar-radius, 4px) !default; // bar 圆角
|
||||||
$-floating-panel-content-bg: var(--wot-floating-panel-content-bg, $-color-white) !default; // 内容背景色
|
$-floating-panel-content-bg: var(--wot-floating-panel-content-bg, $-color-white) !default; // 内容背景色
|
||||||
|
|
||||||
|
/* signature */
|
||||||
|
$-signature-bg: var(--wot-signature-bg, $-color-white) !default; // 背景色
|
||||||
|
$-signature-radius: var(--wot-signature-radius, 4px) !default; // 圆角
|
||||||
|
$-signature-border: var(--wot-signature-border, 1px solid $-color-gray-5) !default; // 边框圆角
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
@import '../common/abstracts/variable';
|
||||||
|
@import '../common/abstracts/mixin';
|
||||||
|
|
||||||
|
@include b(signature) {
|
||||||
|
@include e(context){
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
background: $-signature-bg;
|
||||||
|
border-radius:$-signature-radius;
|
||||||
|
border:$-signature-border;
|
||||||
|
}
|
||||||
|
@include e(footer)
|
||||||
|
{
|
||||||
|
margin-top: 4px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
& .wd-button + .wd-button{
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* @Author: 810505339
|
||||||
|
* @Date: 2025-01-10 20:03:57
|
||||||
|
* @LastEditors: 810505339
|
||||||
|
* @LastEditTime: 2025-01-11 23:03:44
|
||||||
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-signature\types.ts
|
||||||
|
* 记得注释
|
||||||
|
*/
|
||||||
|
import type { ExtractPropTypes, Prop, PropType } from 'vue'
|
||||||
|
import { baseProps, makeBooleanProp, makeNumberProp, makeRequiredProp } from '../common/props'
|
||||||
|
|
||||||
|
export const signatureProps = {
|
||||||
|
...baseProps,
|
||||||
|
penColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#000'
|
||||||
|
},
|
||||||
|
lineWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 300
|
||||||
|
},
|
||||||
|
clearText: {
|
||||||
|
type: String,
|
||||||
|
default: '清空'
|
||||||
|
},
|
||||||
|
confirmText: {
|
||||||
|
type: String,
|
||||||
|
default: '确认'
|
||||||
|
},
|
||||||
|
fileType: {
|
||||||
|
type: String,
|
||||||
|
default: 'png'
|
||||||
|
},
|
||||||
|
quality: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
exportScale: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export type FileType = {
|
||||||
|
tempFilePath: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
export type SignatureExpose = {
|
||||||
|
/** 点击清除按钮清除签名 */
|
||||||
|
clear: () => void
|
||||||
|
/** 点击确定按钮 */
|
||||||
|
confirm: (result: FileType) => void
|
||||||
|
}
|
||||||
@ -0,0 +1,239 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: 810505339
|
||||||
|
* @Date: 2025-01-10 15:41:12
|
||||||
|
* @LastEditors: 810505339
|
||||||
|
* @LastEditTime: 2025-01-11 22:53:54
|
||||||
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-signature\wd-signature.vue
|
||||||
|
* 记得注释
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<view class="wd-signature">
|
||||||
|
<view class="wd-signature__context">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<canvas
|
||||||
|
:style="canvasStyle"
|
||||||
|
:canvas-id="canvasId"
|
||||||
|
:id="canvasId"
|
||||||
|
@touchstart="startDrawing"
|
||||||
|
@touchend="stopDrawing"
|
||||||
|
@touchmove="draw"
|
||||||
|
type="2d"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifndef MP-WEIXIN -->
|
||||||
|
<canvas
|
||||||
|
:height="height"
|
||||||
|
:width="width"
|
||||||
|
:style="canvasStyle"
|
||||||
|
:canvas-id="canvasId"
|
||||||
|
:id="canvasId"
|
||||||
|
@touchstart="startDrawing"
|
||||||
|
@touchend="stopDrawing"
|
||||||
|
@touchmove="draw"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
<view class="wd-signature__footer">
|
||||||
|
<slot name="footer" :clear="clear" :confirm="confirmSignature">
|
||||||
|
<wd-button size="small" plain @click="clear">{{ clearText }}</wd-button>
|
||||||
|
<wd-button size="small" @click="confirmSignature">{{ confirmText }}</wd-button>
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'wd-signature',
|
||||||
|
options: {
|
||||||
|
addGlobalClass: true,
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, getCurrentInstance, onBeforeMount, onMounted, ref, watch, defineExpose } from 'vue'
|
||||||
|
import { addUnit, objToStyle, uuid } from '../common/util'
|
||||||
|
import { signatureProps, type SignatureExpose } from './types'
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
import { canvas2dAdapter } from '../common/canvasHelper'
|
||||||
|
// #endif
|
||||||
|
const props = defineProps(signatureProps)
|
||||||
|
const emit = defineEmits(['clear', 'confirm', 'touchstart', 'touchend'])
|
||||||
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
const canvasId = ref<string>(`signature${uuid()}`) // canvas 组件的唯一标识符
|
||||||
|
let canvas: null = null //canvas对象 微信小程序生成图片必须传入
|
||||||
|
let ctx: UniApp.CanvasContext | null = null // canvas上下文
|
||||||
|
const drawing = ref<boolean>(false) // 是否正在绘制
|
||||||
|
const pixelRatio = ref<number>(1) // 像素比
|
||||||
|
const canvasDom = ref({
|
||||||
|
w: 0,
|
||||||
|
h: 0
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
() => props.penColor,
|
||||||
|
() => {
|
||||||
|
setLine()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.lineWidth,
|
||||||
|
() => {
|
||||||
|
setLine()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const canvasStyle = computed(() => {
|
||||||
|
const style = {
|
||||||
|
height: addUnit(props.height),
|
||||||
|
width: addUnit(props.width)
|
||||||
|
}
|
||||||
|
return `${objToStyle(style)};`
|
||||||
|
})
|
||||||
|
|
||||||
|
/* 开始画线 */
|
||||||
|
const startDrawing = (e: TouchEvent) => {
|
||||||
|
drawing.value = true
|
||||||
|
setLine()
|
||||||
|
emit('touchstart', e)
|
||||||
|
draw(e)
|
||||||
|
}
|
||||||
|
/* 结束画线 */
|
||||||
|
const stopDrawing = (e: TouchEvent) => {
|
||||||
|
drawing.value = false
|
||||||
|
if (ctx) ctx.beginPath()
|
||||||
|
emit('touchend', e)
|
||||||
|
}
|
||||||
|
// 初始化 canvas
|
||||||
|
const initCanvas = () => {
|
||||||
|
getContext()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空 canvas
|
||||||
|
const clear = () => {
|
||||||
|
const { w, h } = canvasDom.value
|
||||||
|
if (ctx) {
|
||||||
|
ctx.clearRect(0, 0, w, h)
|
||||||
|
ctx.draw()
|
||||||
|
}
|
||||||
|
emit('clear')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认签名
|
||||||
|
const confirmSignature = () => {
|
||||||
|
canvasToImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
//canvas划线
|
||||||
|
const draw = (e: any) => {
|
||||||
|
if (!drawing.value) return
|
||||||
|
if (props.disabled) return
|
||||||
|
if (!ctx) return
|
||||||
|
const { x, y } = e.touches[0]
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.draw(true) //是否记住上一次画线
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initCanvas()
|
||||||
|
})
|
||||||
|
onBeforeMount(() => {
|
||||||
|
pixelRatio.value = uni.getSystemInfoSync().pixelRatio
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* 获取canvas上下文
|
||||||
|
*/
|
||||||
|
function getContext() {
|
||||||
|
return new Promise<UniApp.CanvasContext>((resolve) => {
|
||||||
|
if (ctx) {
|
||||||
|
return resolve(ctx)
|
||||||
|
}
|
||||||
|
uni
|
||||||
|
.createSelectorQuery()
|
||||||
|
.in(proxy)
|
||||||
|
.select(`#${canvasId.value}`)
|
||||||
|
.node((res) => {
|
||||||
|
if (res && res.node) {
|
||||||
|
const canvasInstance = res.node
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
ctx = uni.createCanvasContext(canvasId.value, proxy)
|
||||||
|
// #endif
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
ctx = canvas2dAdapter(canvasInstance.getContext('2d') as CanvasRenderingContext2D)
|
||||||
|
canvasInstance.width = props.width * pixelRatio.value
|
||||||
|
canvasInstance.height = props.height * pixelRatio.value
|
||||||
|
ctx.scale(pixelRatio.value, pixelRatio.value)
|
||||||
|
// #endif
|
||||||
|
canvas = canvasInstance
|
||||||
|
canvasDom.value = {
|
||||||
|
w: canvasInstance.width,
|
||||||
|
h: canvasInstance.height
|
||||||
|
}
|
||||||
|
resolve(ctx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/* 设置线段 */
|
||||||
|
function setLine() {
|
||||||
|
if (ctx) {
|
||||||
|
ctx.setLineWidth(props.lineWidth)
|
||||||
|
ctx.setStrokeStyle(props.penColor)
|
||||||
|
ctx.setLineJoin('round')
|
||||||
|
ctx.setLineCap('round')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* canvas 绘制图片输出成文件类型
|
||||||
|
*/
|
||||||
|
function canvasToImage() {
|
||||||
|
const { fileType, quality, exportScale } = props
|
||||||
|
const { w, h } = canvasDom.value
|
||||||
|
try {
|
||||||
|
uni.canvasToTempFilePath(
|
||||||
|
{
|
||||||
|
width: w * exportScale,
|
||||||
|
height: h * exportScale,
|
||||||
|
destWidth: w * exportScale,
|
||||||
|
destHeight: h * exportScale,
|
||||||
|
fileType,
|
||||||
|
quality,
|
||||||
|
canvasId: canvasId.value,
|
||||||
|
canvas: canvas,
|
||||||
|
success: (res: any) => {
|
||||||
|
const result = { tempFilePath: res.tempFilePath, width: (w * exportScale) / pixelRatio.value, height: (h * exportScale) / pixelRatio.value }
|
||||||
|
// #ifdef MP-DINGTALK
|
||||||
|
result.tempFilePath = res.filePath
|
||||||
|
// #endif
|
||||||
|
emit('confirm', result)
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
proxy
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose<SignatureExpose>({
|
||||||
|
clear,
|
||||||
|
confirm: confirmSignature
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import './index.scss';
|
||||||
|
</style>
|
||||||
9
src/uni_modules/wot-design-uni/global.d.ts
vendored
9
src/uni_modules/wot-design-uni/global.d.ts
vendored
@ -1,3 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* @Author: 810505339
|
||||||
|
* @Date: 2025-01-11 23:03:12
|
||||||
|
* @LastEditors: 810505339
|
||||||
|
* @LastEditTime: 2025-01-11 23:03:24
|
||||||
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\global.d.ts
|
||||||
|
* 记得注释
|
||||||
|
*/
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
// Helper for Volar
|
// Helper for Volar
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
@ -92,6 +100,7 @@ declare module 'vue' {
|
|||||||
WdText: typeof import('./components/wd-text/wd-text.vue')['default']
|
WdText: typeof import('./components/wd-text/wd-text.vue')['default']
|
||||||
WdCountTo: typeof import('./components/wd-count-to/wd-count-to.vue')['default']
|
WdCountTo: typeof import('./components/wd-count-to/wd-count-to.vue')['default']
|
||||||
WdFloatingPanel: typeof import('./components/wd-floating-panel/wd-floating-panel.vue')['default']
|
WdFloatingPanel: typeof import('./components/wd-floating-panel/wd-floating-panel.vue')['default']
|
||||||
|
WdSignature: typeof import('./components/wd-signature/wd-signature.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user