From b2e934097854aa41ae152a49df2a5a913ebc9372 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Mon, 3 Mar 2025 22:21:42 +0800 Subject: [PATCH 01/29] feat: add name --- packages/varlet-ui/varlet.config.mjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/varlet-ui/varlet.config.mjs b/packages/varlet-ui/varlet.config.mjs index 97196f1bbc1..333a0221017 100644 --- a/packages/varlet-ui/varlet.config.mjs +++ b/packages/varlet-ui/varlet.config.mjs @@ -392,6 +392,14 @@ export default defineConfig({ doc: 'watermark', type: 2, }, + { + text: { + 'zh-CN': 'Signature 签名', + 'en-US': 'Signature', + }, + doc: 'Signature', + type: 2, + }, { text: { 'zh-CN': 'Code 代码块', From dda43c31598e8c0bf584b80cd6a2df008278006b Mon Sep 17 00:00:00 2001 From: xueyuan Date: Mon, 3 Mar 2025 23:20:04 +0800 Subject: [PATCH 02/29] feat: add function signature --- .../varlet-ui/src/signature/Signature.vue | 204 ++++++++++++++++++ packages/varlet-ui/src/signature/index.ts | 12 ++ packages/varlet-ui/src/signature/props.ts | 42 ++++ .../varlet-ui/src/signature/signature.less | 45 ++++ 4 files changed, 303 insertions(+) create mode 100644 packages/varlet-ui/src/signature/Signature.vue create mode 100644 packages/varlet-ui/src/signature/index.ts create mode 100644 packages/varlet-ui/src/signature/props.ts create mode 100644 packages/varlet-ui/src/signature/signature.less diff --git a/packages/varlet-ui/src/signature/Signature.vue b/packages/varlet-ui/src/signature/Signature.vue new file mode 100644 index 00000000000..64e76551bbe --- /dev/null +++ b/packages/varlet-ui/src/signature/Signature.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/packages/varlet-ui/src/signature/index.ts b/packages/varlet-ui/src/signature/index.ts new file mode 100644 index 00000000000..8c0eb706deb --- /dev/null +++ b/packages/varlet-ui/src/signature/index.ts @@ -0,0 +1,12 @@ +import { withInstall, withPropsDefaultsSetter } from '../utils/components' +import { props as signatureProps } from './props' +import Signature from './Signature.vue' + +withInstall(Signature) +withPropsDefaultsSetter(Signature, signatureProps) + +export { signatureProps } + +export const _SignatureComponent = Signature + +export default Signature diff --git a/packages/varlet-ui/src/signature/props.ts b/packages/varlet-ui/src/signature/props.ts new file mode 100644 index 00000000000..dc196e9f92c --- /dev/null +++ b/packages/varlet-ui/src/signature/props.ts @@ -0,0 +1,42 @@ +import { type PropType } from 'vue' + +export type SignatureType = 'png' | 'jpg' + +export const props = { + modelValue: { + type: String, + default: '', + }, + lineWidth: { + type: Number, + default: 2, + }, + strokeStyle: { + type: String, + default: '#000', + }, + type: { + type: String as PropType, + default: 'png', + }, + unsupportText: { + type: String, + default: '', + }, + customClass: { + type: String, + default: '', + }, + validateTrigger: { + type: Array as PropType>, + default: () => ['onChange'], + }, + rules: { + type: [Array, Object] as PropType any> | object>, + default: () => [], + }, + height: { + type: [Number, String], + default: 200, + }, +} diff --git a/packages/varlet-ui/src/signature/signature.less b/packages/varlet-ui/src/signature/signature.less new file mode 100644 index 00000000000..55fb3ebe06e --- /dev/null +++ b/packages/varlet-ui/src/signature/signature.less @@ -0,0 +1,45 @@ +@import '../styles/common'; +@import '../styles/elevation'; + +:root { + --signature-background-color: #fff; + --signature-border-color: #e0e0e0; + --signature-border-radius: 4px; + --signature-actions-padding: 10px; + --signature-button-margin: 0 4px; + --signature-inner-height: 200px; +} + +.var-signature { + width: 100%; + border: 1px solid var(--signature-border-color); + border-radius: var(--signature-border-radius); + background-color: var(--signature-background-color); + overflow: hidden; + + &__inner { + position: relative; + width: 100%; + height: var(--signature-inner-height); + } + + &__unsupport { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + margin: 0; + color: var(--color-text-disabled); + } + + &__actions { + display: flex; + justify-content: flex-end; + padding: var(--signature-actions-padding); + border-top: 1px solid var(--signature-border-color); + } + + &__button { + margin: var(--signature-button-margin); + } +} From 960ad7bc4ed3227fa2806dee2a33d6355e5ade99 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Tue, 4 Mar 2025 00:13:33 +0800 Subject: [PATCH 03/29] fix: adjust css --- packages/varlet-ui/types/index.d.ts | 2 ++ packages/varlet-ui/types/skeleton.d.ts | 39 ++++++++++++++------------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/packages/varlet-ui/types/index.d.ts b/packages/varlet-ui/types/index.d.ts index 4baa019bfb0..79414fd30d2 100644 --- a/packages/varlet-ui/types/index.d.ts +++ b/packages/varlet-ui/types/index.d.ts @@ -74,6 +74,7 @@ export * from './result' export * from './ripple' export * from './row' export * from './select' +export * from './signature' export * from './skeleton' export * from './slider' export * from './snackbar' @@ -169,6 +170,7 @@ declare module 'vue' { VarResult: typeof import('@varlet/ui')['_ResultComponent'] VarRow: typeof import('@varlet/ui')['_RowComponent'] VarSelect: typeof import('@varlet/ui')['_SelectComponent'] + VarSignature: typeof import('@varlet/ui')['_SignatureComponent'] VarSkeleton: typeof import('@varlet/ui')['_SkeletonComponent'] VarSlider: typeof import('@varlet/ui')['_SliderComponent'] VarSnackbar: typeof import('@varlet/ui')['_SnackbarComponent'] diff --git a/packages/varlet-ui/types/skeleton.d.ts b/packages/varlet-ui/types/skeleton.d.ts index ba426f7ed5b..bd113a3ecf3 100644 --- a/packages/varlet-ui/types/skeleton.d.ts +++ b/packages/varlet-ui/types/skeleton.d.ts @@ -1,30 +1,33 @@ import { VNode } from 'vue' import { BasicAttributes, SetPropsDefaults, VarComponent } from './varComponent' -export declare const skeletonProps: Record - -export interface SkeletonProps extends BasicAttributes { - loading?: boolean - title?: boolean - avatar?: boolean - card?: boolean - fullscreen?: boolean - fullscreenZIndex?: string | number - titleWidth?: string | number - avatarSize?: string | number - cardHeight?: string | number - rows?: string | number - rowsWidth?: (string | number)[] +export type SignatureType = 'png' | 'jpg' + +export declare const signatureProps: Record + +export interface SignatureProps extends BasicAttributes { + modelValue?: string + lineWidth?: number + strokeStyle?: string + type?: SignatureType + unsupportText?: string + customClass?: string + height?: number | string + validateTrigger?: string[] + rules?: Array<(v: string) => any> } -export class Skeleton extends VarComponent { - static setPropsDefaults: SetPropsDefaults +export class Signature extends VarComponent { + static setPropsDefaults: SetPropsDefaults - $props: SkeletonProps + $props: SignatureProps $slots: { default(): VNode[] } + + clear(): void + confirm(): void } -export class _SkeletonComponent extends Skeleton {} +export class _SignatureComponent extends Signature {} From 46e4e7bcb507d9236118a846030d125894b83b89 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Tue, 4 Mar 2025 09:09:43 +0800 Subject: [PATCH 04/29] fix: adjust css --- .../src/signature/__tests__/index.spec.js | 162 +++++++++++++++ .../varlet-ui/src/signature/docs/en-US.md | 195 ++++++++++++++++++ .../varlet-ui/src/signature/docs/zh-CN.md | 18 ++ .../varlet-ui/src/signature/example/index.vue | 46 +++++ .../src/signature/example/locale/en-US.ts | 14 ++ .../src/signature/example/locale/index.ts | 17 ++ .../src/signature/example/locale/zh-CN.ts | 14 ++ packages/varlet-ui/types/signature.d.ts | 0 8 files changed, 466 insertions(+) create mode 100644 packages/varlet-ui/src/signature/__tests__/index.spec.js create mode 100644 packages/varlet-ui/src/signature/docs/en-US.md create mode 100644 packages/varlet-ui/src/signature/docs/zh-CN.md create mode 100644 packages/varlet-ui/src/signature/example/index.vue create mode 100644 packages/varlet-ui/src/signature/example/locale/en-US.ts create mode 100644 packages/varlet-ui/src/signature/example/locale/index.ts create mode 100644 packages/varlet-ui/src/signature/example/locale/zh-CN.ts create mode 100644 packages/varlet-ui/types/signature.d.ts diff --git a/packages/varlet-ui/src/signature/__tests__/index.spec.js b/packages/varlet-ui/src/signature/__tests__/index.spec.js new file mode 100644 index 00000000000..daf48949921 --- /dev/null +++ b/packages/varlet-ui/src/signature/__tests__/index.spec.js @@ -0,0 +1,162 @@ +import { createApp } from 'vue' +import { mount } from '@vue/test-utils' +import { describe, expect, test, vi } from 'vitest' +import Signature from '..' +import VarSignature from '../Signature.vue' + +test('signature use', () => { + const app = createApp({}).use(Signature) + expect(app.component(Signature.name)).toBeTruthy() +}) + +describe('test signature component props', () => { + test('signature lineWidth', async () => { + const wrapper = mount(VarSignature, { + props: { + lineWidth: 2, + }, + }) + + expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + await wrapper.setProps({ lineWidth: 4 }) + wrapper.unmount() + }) + + test('signature strokeStyle', async () => { + const wrapper = mount(VarSignature, { + props: { + strokeStyle: '#000', + }, + }) + + expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + await wrapper.setProps({ strokeStyle: '#f00' }) + wrapper.unmount() + }) + + test('signature type', async () => { + const wrapper = mount(VarSignature, { + props: { + type: 'png', + }, + }) + + expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + await wrapper.setProps({ type: 'jpg' }) + wrapper.unmount() + }) + + test('signature unsupportText', () => { + // 模拟不支持 canvas 的环境 + const originalGetContext = HTMLCanvasElement.prototype.getContext + HTMLCanvasElement.prototype.getContext = () => null + + const wrapper = mount(VarSignature, { + props: { + unsupportText: '自定义不支持提示', + }, + }) + + expect(wrapper.find('.var-signature__unsupport').text()).toBe('自定义不支持提示') + + // 恢复原始方法 + HTMLCanvasElement.prototype.getContext = originalGetContext + wrapper.unmount() + }) + + test('signature customClass', async () => { + const wrapper = mount(VarSignature, { + props: { + customClass: 'test-class', + }, + }) + + expect(wrapper.classes()).toContain('test-class') + await wrapper.setProps({ customClass: 'another-class' }) + expect(wrapper.classes()).toContain('another-class') + wrapper.unmount() + }) + + test('signature height', async () => { + const wrapper = mount(VarSignature, { + props: { + height: 300, + }, + }) + + expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + await wrapper.setProps({ height: 400 }) + wrapper.unmount() + }) +}) + +describe('test signature component events', () => { + test('signature clear event', async () => { + const onClear = vi.fn() + const onUpdateModelValue = vi.fn() + + const wrapper = mount(VarSignature, { + props: { + onClear, + 'onUpdate:modelValue': onUpdateModelValue, + }, + }) + + await wrapper.find('.var-signature__button').trigger('click') + expect(onClear).toHaveBeenCalled() + expect(onUpdateModelValue).toHaveBeenCalledWith('') + wrapper.unmount() + }) + + test('signature confirm event', async () => { + const onConfirm = vi.fn() + const onUpdateModelValue = vi.fn() + + const wrapper = mount(VarSignature, { + props: { + onConfirm, + 'onUpdate:modelValue': onUpdateModelValue, + }, + }) + + await wrapper.findAll('.var-signature__button')[1].trigger('click') + expect(onConfirm).toHaveBeenCalled() + wrapper.unmount() + }) +}) + +test('signature canvas operations', async () => { + // 由于 JSDOM 环境下 canvas 操作有限,这里只做简单测试 + const wrapper = mount(VarSignature) + + const canvas = wrapper.find('canvas').element + expect(canvas).toBeTruthy() + + // 模拟 canvas 上下文 + const mockContext = { + beginPath: vi.fn(), + lineTo: vi.fn(), + stroke: vi.fn(), + clearRect: vi.fn(), + closePath: vi.fn(), + } + + // 保存原始方法 + const originalGetContext = canvas.getContext + + // 模拟 getContext 方法 + canvas.getContext = () => mockContext + + // 触发 mousedown 事件 + await wrapper.find('canvas').trigger('mousedown', { + clientX: 10, + clientY: 10, + }) + + expect(mockContext.beginPath).toHaveBeenCalled() + + // 恢复原始方法 + canvas.getContext = originalGetContext + + wrapper.unmount() +}) diff --git a/packages/varlet-ui/src/signature/docs/en-US.md b/packages/varlet-ui/src/signature/docs/en-US.md new file mode 100644 index 00000000000..f9aa2fe5a73 --- /dev/null +++ b/packages/varlet-ui/src/signature/docs/en-US.md @@ -0,0 +1,195 @@ +# Signature + +### Intro + +A component for electronic signature that allows users to sign on the screen. + +### Basic Usage + +```html + + + + + +### Custom Line Width + +```html + + + + +### Custom Stroke Style + +```html + + + + +### Custom Height +```html + + + + + +###Custom Output Type +```html + + + + +### Form Validation +```html + + + + +## API +### Props Prop Description Type Default v-model + +Signature data URL + +string + +'' line-width + +Line width of the signature + +number + +2 stroke-style + +Stroke style of the signature + +string + +'#000' type + +Output image type + +'png' | 'jpg' + +'png' unsupport-text + +Text displayed when canvas is not supported + +string + +- custom-class + +Custom class name + +string + +'' height + +Height of the signature area + +number | string + +200 validate-trigger + +Trigger method for validation + +ValidateTriggers[] + +['onChange'] rules + +Validation rules + +Array | Object + +- +### Events Event Description Arguments start + +Triggered when starting to sign + +- signing + +Triggered during signing + +event: TouchEvent | MouseEvent end + +Triggered when ending the signature + +- confirm + +Triggered when confirming the signature + +canvas: HTMLCanvasElement, dataUrl: string clear + +Triggered when clearing the signature + +- update:model-value + +Triggered when the signature value changes + +value: string +### Slots Name Description SlotProps default + +Custom content + +- +### Style Variables +Here are the CSS variables used by the component. Styles can be customized using StyleProvider . + Variable Default --signature-background-color + +#fff --signature-border-color + +#e0e0e0 --signature-border-radius + +4px --signature-actions-padding + +10px --signature-button-margin + +0 4px --signature-inner-height + +200px \ No newline at end of file diff --git a/packages/varlet-ui/src/signature/docs/zh-CN.md b/packages/varlet-ui/src/signature/docs/zh-CN.md new file mode 100644 index 00000000000..64bbfe953aa --- /dev/null +++ b/packages/varlet-ui/src/signature/docs/zh-CN.md @@ -0,0 +1,18 @@ +# 电子签名 + +### 介绍 + +一个用于在屏幕上进行电子签名的组件。 + +### 基本使用 + +```html + + + \ No newline at end of file diff --git a/packages/varlet-ui/src/signature/example/index.vue b/packages/varlet-ui/src/signature/example/index.vue new file mode 100644 index 00000000000..62ec780fd5b --- /dev/null +++ b/packages/varlet-ui/src/signature/example/index.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/packages/varlet-ui/src/signature/example/locale/en-US.ts b/packages/varlet-ui/src/signature/example/locale/en-US.ts new file mode 100644 index 00000000000..b291cd1f1dd --- /dev/null +++ b/packages/varlet-ui/src/signature/example/locale/en-US.ts @@ -0,0 +1,14 @@ +export default { + basicUsage: 'Basic Usage', + customLineWidth: 'Custom Line Width', + customStrokeStyle: 'Custom Stroke Style', + customHeight: 'Custom Height', + customOutputType: 'Custom Output Type', + formValidation: 'Form Validation', + clear: 'Clear', + confirm: 'Confirm', + submit: 'Submit', + signatureRequired: 'Signature is required', + submitSuccess: 'Submit Success', + unsupportText: 'Your browser does not support Canvas, electronic signature is not available', +} diff --git a/packages/varlet-ui/src/signature/example/locale/index.ts b/packages/varlet-ui/src/signature/example/locale/index.ts new file mode 100644 index 00000000000..e031be8c1a4 --- /dev/null +++ b/packages/varlet-ui/src/signature/example/locale/index.ts @@ -0,0 +1,17 @@ +import { Locale } from '@varlet/ui' +import enUS from './en-US' +import zhCN from './zh-CN' + +const { add, use: exampleUse, t, merge } = Locale.useLocale() + +const use = (lang: string) => { + Locale.use(lang) + exampleUse(lang) +} + +Locale.add('zh-CN', Locale.zhCN) +Locale.add('en-US', Locale.enUS) +add('zh-CN', zhCN) +add('en-US', enUS) + +export { add, t, merge, use } diff --git a/packages/varlet-ui/src/signature/example/locale/zh-CN.ts b/packages/varlet-ui/src/signature/example/locale/zh-CN.ts new file mode 100644 index 00000000000..f39c9ffb4a5 --- /dev/null +++ b/packages/varlet-ui/src/signature/example/locale/zh-CN.ts @@ -0,0 +1,14 @@ +export default { + basicUsage: '基本使用', + customLineWidth: '自定义线宽', + customStrokeStyle: '自定义笔触颜色', + customHeight: '自定义高度', + customOutputType: '自定义输出类型', + formValidation: '表单校验', + clear: '清除', + confirm: '确认', + submit: '提交', + signatureRequired: '请签名', + submitSuccess: '提交成功', + unsupportText: '您的浏览器不支持Canvas,无法使用电子签名功能', +} diff --git a/packages/varlet-ui/types/signature.d.ts b/packages/varlet-ui/types/signature.d.ts new file mode 100644 index 00000000000..e69de29bb2d From ca79f54e46258b51682f7681c5b6591158dfff80 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Tue, 4 Mar 2025 23:42:58 +0800 Subject: [PATCH 05/29] feat: signature finish --- .../varlet-ui/src/signature/Signature.vue | 21 +- .../varlet-ui/src/signature/docs/en-US.md | 240 ++++++++---------- .../varlet-ui/src/signature/docs/zh-CN.md | 149 ++++++++++- .../varlet-ui/src/signature/example/index.vue | 61 +++-- .../src/signature/example/locale/en-US.ts | 16 +- .../src/signature/example/locale/zh-CN.ts | 15 +- packages/varlet-ui/src/signature/props.ts | 4 + 7 files changed, 328 insertions(+), 178 deletions(-) diff --git a/packages/varlet-ui/src/signature/Signature.vue b/packages/varlet-ui/src/signature/Signature.vue index 64e76551bbe..bed74dbec2d 100644 --- a/packages/varlet-ui/src/signature/Signature.vue +++ b/packages/varlet-ui/src/signature/Signature.vue @@ -1,20 +1,14 @@ +``` +### Custom Style -### Custom Line Width +Customize signature style through properties like `color`, `lineWidth`, etc. ```html +``` -### Custom Stroke Style +### Disabled State -```html - +Set disabled state through the `disabled` property. +```html +``` -### Custom Height -```html +## API - +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `v-model` | Signature value (base64 format) | _string_ | `''` | +| `color` | Pen color | _string_ | `#000` | +| `line-width` | Line width | _string \| number_ | `2` | +| `background` | Canvas background color | _string_ | `#fff` | +| `width` | Canvas width | _string \| number_ | `100%` | +| `height` | Canvas height | _string \| number_ | `200` | +| `disabled` | Whether to disable signature | _boolean_ | `false` | - +### Events -###Custom Output Type -```html - +### Methods - +| Method | Description | Parameters | +| --- | --- | --- | +| `clear` | Clear signature | `-` | +| `save` | Save signature, returns base64 format image data | `-` | + +### Style Variables + +Here are the CSS variables used by the component. Styles can be customized using [StyleProvider](#/en-US/style-provider). + +| Variable | Default | +| --- | --- | +| `--signature-background` | `#fff` | +| `--signature-border-color` | `#e0e0e0` | +| `--signature-border-radius` | `4px` | + +### Example -### Form Validation ```html +``` -## API -### Props Prop Description Type Default v-model - -Signature data URL - -string - -'' line-width - -Line width of the signature - -number - -2 stroke-style - -Stroke style of the signature - -string - -'#000' type - -Output image type - -'png' | 'jpg' - -'png' unsupport-text - -Text displayed when canvas is not supported - -string - -- custom-class - -Custom class name - -string - -'' height - -Height of the signature area - -number | string - -200 validate-trigger - -Trigger method for validation - -ValidateTriggers[] - -['onChange'] rules - -Validation rules - -Array | Object - -- -### Events Event Description Arguments start - -Triggered when starting to sign - -- signing - -Triggered during signing - -event: TouchEvent | MouseEvent end - -Triggered when ending the signature - -- confirm - -Triggered when confirming the signature - -canvas: HTMLCanvasElement, dataUrl: string clear - -Triggered when clearing the signature - -- update:model-value - -Triggered when the signature value changes - -value: string -### Slots Name Description SlotProps default - -Custom content - -- -### Style Variables -Here are the CSS variables used by the component. Styles can be customized using StyleProvider . - Variable Default --signature-background-color - -#fff --signature-border-color - -#e0e0e0 --signature-border-radius - -4px --signature-actions-padding - -10px --signature-button-margin - -0 4px --signature-inner-height +## Notes -200px \ No newline at end of file +1. Signature data is stored in base64 format, which may occupy large space. It's recommended to compress before uploading to server. +2. When using on mobile devices, it's recommended to set appropriate canvas size for better signature experience. +3. Canvas border, border radius and other styles can be customized through CSS. \ No newline at end of file diff --git a/packages/varlet-ui/src/signature/docs/zh-CN.md b/packages/varlet-ui/src/signature/docs/zh-CN.md index 64bbfe953aa..deb359c4993 100644 --- a/packages/varlet-ui/src/signature/docs/zh-CN.md +++ b/packages/varlet-ui/src/signature/docs/zh-CN.md @@ -6,13 +6,160 @@ ### 基本使用 +通过 `v-model` 绑定签名的值。 + ```html \ No newline at end of file + + + 保存签名 + 清除签名 + + + 签名预览 + +``` + +### 自定义样式 + +可以通过 `color`、`lineWidth` 等属性自定义签名的样式。 + +```html + + + +``` + +### 禁用状态 + +通过 `disabled` 属性设置禁用状态。 + +```html + +``` + +## API + +### 属性 + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `v-model` | 签名的值(base64格式) | _string_ | `''` | +| `color` | 画笔颜色 | _string_ | `#000` | +| `line-width` | 画笔粗细 | _string \| number_ | `2` | +| `background` | 画布背景色 | _string_ | `#fff` | +| `width` | 画布宽度 | _string \| number_ | `100%` | +| `height` | 画布高度 | _string \| number_ | `200` | +| `disabled` | 是否禁用 | _boolean_ | `false` | + +### 事件 + +| 事件名 | 说明 | 回调参数 | +| --- | --- | --- | +| `start` | 开始签名时触发 | `event: TouchEvent` | +| `signing` | 签名过程中触发 | `event: TouchEvent` | +| `end` | 结束签名时触发 | `event: TouchEvent` | +| `change` | 签名内容变化时触发 | `value: string` | + +### 方法 + +| 方法名 | 说明 | 参数 | +| --- | --- | --- | +| `clear` | 清除签名 | `-` | +| `save` | 保存签名,返回 base64 格式的图片数据 | `-` | + +### 样式变量 + +以下为组件使用的 css 变量,可以使用 [StyleProvider 组件](#/zh-CN/style-provider) 进行样式定制。 + +| 变量名 | 默认值 | +| --- | --- | +| `--signature-background` | `#fff` | +| `--signature-border-color` | `#e0e0e0` | +| `--signature-border-radius` | `4px` | + +### 示例 + +```html + + + +``` + +## 注意事项 + +1. 签名数据以 base64 格式存储,可能会占用较大空间,建议在上传服务器前进行压缩处理。 +2. 在移动设备上使用时,建议设置适当的画布大小以获得更好的签名体验。 +3. 可以通过 CSS 自定义画布的边框、圆角等样式。 \ No newline at end of file diff --git a/packages/varlet-ui/src/signature/example/index.vue b/packages/varlet-ui/src/signature/example/index.vue index 62ec780fd5b..8e1ae20b245 100644 --- a/packages/varlet-ui/src/signature/example/index.vue +++ b/packages/varlet-ui/src/signature/example/index.vue @@ -5,42 +5,53 @@ import { Snackbar } from '@varlet/ui' import { t, use } from './locale' const signature = ref('') +const signatureRef = ref(null) watchLang(use) onThemeChange() -const onSubmit = () => { - Snackbar.success(t('submitSuccess')) +const clear = () => { + signatureRef.value?.clear() +} + +const save = () => { + if (signatureRef.value?.isEmpty) { + Snackbar.warning(t('pleaseSignFirst')) + return + } + signatureRef.value?.confirm() + Snackbar.success(t('saveSuccess')) } diff --git a/packages/varlet-ui/src/signature/example/locale/en-US.ts b/packages/varlet-ui/src/signature/example/locale/en-US.ts index b291cd1f1dd..1a480f4e01a 100644 --- a/packages/varlet-ui/src/signature/example/locale/en-US.ts +++ b/packages/varlet-ui/src/signature/example/locale/en-US.ts @@ -1,14 +1,24 @@ export default { basicUsage: 'Basic Usage', + customStyle: 'Custom Style', + disabledState: 'Disabled State', + completeExample: 'Complete Example', + save: 'Save', + clear: 'Clear', + confirm: 'Confirm', + reset: 'Reset', + pleaseSignFirst: 'Please sign first', + saveSuccess: 'Save successful', + pleaseSignBelow: 'Please sign below', customLineWidth: 'Custom Line Width', customStrokeStyle: 'Custom Stroke Style', customHeight: 'Custom Height', customOutputType: 'Custom Output Type', formValidation: 'Form Validation', - clear: 'Clear', - confirm: 'Confirm', submit: 'Submit', signatureRequired: 'Signature is required', - submitSuccess: 'Submit Success', + signatureTooSmall: 'Signature is too small, please sign bigger', + submitSuccess: 'Submit successful', unsupportText: 'Your browser does not support Canvas, electronic signature is not available', + cleared: 'Cleared', } diff --git a/packages/varlet-ui/src/signature/example/locale/zh-CN.ts b/packages/varlet-ui/src/signature/example/locale/zh-CN.ts index f39c9ffb4a5..110e0b07b42 100644 --- a/packages/varlet-ui/src/signature/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/signature/example/locale/zh-CN.ts @@ -1,14 +1,23 @@ export default { basicUsage: '基本使用', + customStyle: '自定义样式', + disabledState: '禁用状态', + completeExample: '完整示例', + save: '保存签名', + clear: '清除签名', + confirm: '确认签名', + reset: '重新签名', + pleaseSignFirst: '请先签名', + saveSuccess: '保存成功', + pleaseSignBelow: '请在下方签名', customLineWidth: '自定义线宽', customStrokeStyle: '自定义笔触颜色', customHeight: '自定义高度', customOutputType: '自定义输出类型', formValidation: '表单校验', - clear: '清除', - confirm: '确认', submit: '提交', - signatureRequired: '请签名', + signatureRequired: '请先完成签名', + signatureTooSmall: '签名太小,请签大一点', submitSuccess: '提交成功', unsupportText: '您的浏览器不支持Canvas,无法使用电子签名功能', } diff --git a/packages/varlet-ui/src/signature/props.ts b/packages/varlet-ui/src/signature/props.ts index dc196e9f92c..70a07ec4b2b 100644 --- a/packages/varlet-ui/src/signature/props.ts +++ b/packages/varlet-ui/src/signature/props.ts @@ -39,4 +39,8 @@ export const props = { type: [Number, String], default: 200, }, + disabled: { + type: Boolean, + default: false, + }, } From cf7df4549d3fc2b4aed465af9d0d9c0bbf180dbe Mon Sep 17 00:00:00 2001 From: xueyuan Date: Wed, 5 Mar 2025 00:05:43 +0800 Subject: [PATCH 06/29] feat: add test cases --- .../src/signature/__tests__/index.spec.js | 90 ++++++++++++++++++- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/packages/varlet-ui/src/signature/__tests__/index.spec.js b/packages/varlet-ui/src/signature/__tests__/index.spec.js index daf48949921..4d5d1c0f1fa 100644 --- a/packages/varlet-ui/src/signature/__tests__/index.spec.js +++ b/packages/varlet-ui/src/signature/__tests__/index.spec.js @@ -2,6 +2,7 @@ import { createApp } from 'vue' import { mount } from '@vue/test-utils' import { describe, expect, test, vi } from 'vitest' import Signature from '..' +import { trigger } from '../../utils/test' import VarSignature from '../Signature.vue' test('signature use', () => { @@ -91,7 +92,7 @@ describe('test signature component props', () => { }) describe('test signature component events', () => { - test('signature clear event', async () => { + test('signature clear event', () => { const onClear = vi.fn() const onUpdateModelValue = vi.fn() @@ -102,13 +103,13 @@ describe('test signature component events', () => { }, }) - await wrapper.find('.var-signature__button').trigger('click') + wrapper.find('.var-signature__button').trigger('click') expect(onClear).toHaveBeenCalled() expect(onUpdateModelValue).toHaveBeenCalledWith('') wrapper.unmount() }) - test('signature confirm event', async () => { + test('signature confirm event', () => { const onConfirm = vi.fn() const onUpdateModelValue = vi.fn() @@ -119,7 +120,7 @@ describe('test signature component events', () => { }, }) - await wrapper.findAll('.var-signature__button')[1].trigger('click') + wrapper.findAll('.var-signature__button')[1].trigger('click') expect(onConfirm).toHaveBeenCalled() wrapper.unmount() }) @@ -160,3 +161,84 @@ test('signature canvas operations', async () => { wrapper.unmount() }) + +test('signature base function', () => { + const wrapper = mount(Signature) + + expect(wrapper.html()).toMatchSnapshot() + + // 测试画布是否正确创建 + expect(wrapper.find('canvas').exists()).toBe(true) +}) + +test('signature events', () => { + const wrapper = mount(Signature) + const canvas = wrapper.find('canvas') + + const onStart = vi.fn() + const onSigning = vi.fn() + const onEnd = vi.fn() + + wrapper.vm.$emit('start', onStart) + wrapper.vm.$emit('signing', onSigning) + wrapper.vm.$emit('end', onEnd) + + trigger(canvas, 'touchstart') + expect(onStart).toHaveBeenCalled() + + trigger(canvas, 'touchmove') + expect(onSigning).toHaveBeenCalled() + + trigger(canvas, 'touchend') + expect(onEnd).toHaveBeenCalled() +}) + +test('signature clear and confirm', async () => { + const wrapper = mount(Signature) + + const onClear = vi.fn() + const onConfirm = vi.fn() + + wrapper.vm.$emit('clear', onClear) + wrapper.vm.$emit('confirm', onConfirm) + + await wrapper.vm.clear() + expect(onClear).toHaveBeenCalled() + expect(wrapper.vm.isEmpty).toBe(true) + + await wrapper.vm.confirm() + expect(onConfirm).toHaveBeenCalled() +}) + +test('signature disabled', async () => { + const wrapper = mount(Signature, { + props: { + disabled: true, + }, + }) + + const canvas = wrapper.find('canvas') + expect(canvas.classes()).toContain('var-signature--disabled') + + const onStart = vi.fn() + wrapper.vm.$emit('start', onStart) + + await trigger(canvas, 'touchstart') + expect(onStart).not.toHaveBeenCalled() +}) + +test('signature props', () => { + const wrapper = mount(Signature, { + props: { + lineWidth: 4, + strokeStyle: '#ff0000', + type: 'jpg', + height: 300, + }, + }) + + expect(wrapper.props('lineWidth')).toBe(4) + expect(wrapper.props('strokeStyle')).toBe('#ff0000') + expect(wrapper.props('type')).toBe('jpg') + expect(wrapper.props('height')).toBe(300) +}) From b15d0382b85c543a47adbf0cc295b43b8da2f518 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Wed, 5 Mar 2025 10:32:29 +0800 Subject: [PATCH 07/29] feat: add test cases --- .../__snapshots__/index.spec.js.snap | 9 ++ .../src/signature/__tests__/index.spec.js | 98 ++++++++++--------- 2 files changed, 63 insertions(+), 44 deletions(-) create mode 100644 packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap diff --git a/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap new file mode 100644 index 00000000000..d3cc2ae54e4 --- /dev/null +++ b/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap @@ -0,0 +1,9 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`signature base function 1`] = ` +"
+
+

+
+
" +`; diff --git a/packages/varlet-ui/src/signature/__tests__/index.spec.js b/packages/varlet-ui/src/signature/__tests__/index.spec.js index 4d5d1c0f1fa..73b1448aaec 100644 --- a/packages/varlet-ui/src/signature/__tests__/index.spec.js +++ b/packages/varlet-ui/src/signature/__tests__/index.spec.js @@ -92,7 +92,7 @@ describe('test signature component props', () => { }) describe('test signature component events', () => { - test('signature clear event', () => { + test('signature clear event', async () => { const onClear = vi.fn() const onUpdateModelValue = vi.fn() @@ -103,13 +103,13 @@ describe('test signature component events', () => { }, }) - wrapper.find('.var-signature__button').trigger('click') + await wrapper.find('.var-signature__actions .var-button').trigger('click') expect(onClear).toHaveBeenCalled() expect(onUpdateModelValue).toHaveBeenCalledWith('') wrapper.unmount() }) - test('signature confirm event', () => { + test('signature confirm event', async () => { const onConfirm = vi.fn() const onUpdateModelValue = vi.fn() @@ -120,87 +120,86 @@ describe('test signature component events', () => { }, }) - wrapper.findAll('.var-signature__button')[1].trigger('click') + await wrapper.findAll('.var-signature__actions .var-button')[1].trigger('click') expect(onConfirm).toHaveBeenCalled() wrapper.unmount() }) }) test('signature canvas operations', async () => { - // 由于 JSDOM 环境下 canvas 操作有限,这里只做简单测试 const wrapper = mount(VarSignature) - const canvas = wrapper.find('canvas').element - expect(canvas).toBeTruthy() // 模拟 canvas 上下文 const mockContext = { beginPath: vi.fn(), + moveTo: vi.fn(), lineTo: vi.fn(), stroke: vi.fn(), clearRect: vi.fn(), closePath: vi.fn(), } - // 保存原始方法 + // 保存并替换 getContext 方法 const originalGetContext = canvas.getContext - - // 模拟 getContext 方法 canvas.getContext = () => mockContext - // 触发 mousedown 事件 - await wrapper.find('canvas').trigger('mousedown', { - clientX: 10, - clientY: 10, - }) + // 模拟触摸事件 + const touchEvent = { + touches: [{ clientX: 10, clientY: 10 }], + preventDefault: vi.fn(), + } + await trigger(wrapper.find('canvas'), 'touchstart', touchEvent) expect(mockContext.beginPath).toHaveBeenCalled() + expect(mockContext.moveTo).toHaveBeenCalled() // 恢复原始方法 canvas.getContext = originalGetContext - wrapper.unmount() }) -test('signature base function', () => { - const wrapper = mount(Signature) - - expect(wrapper.html()).toMatchSnapshot() - - // 测试画布是否正确创建 - expect(wrapper.find('canvas').exists()).toBe(true) -}) - -test('signature events', () => { - const wrapper = mount(Signature) - const canvas = wrapper.find('canvas') - +test('signature events', async () => { const onStart = vi.fn() const onSigning = vi.fn() const onEnd = vi.fn() - wrapper.vm.$emit('start', onStart) - wrapper.vm.$emit('signing', onSigning) - wrapper.vm.$emit('end', onEnd) + const wrapper = mount(VarSignature, { + props: { + onStart, + onSigning, + onEnd, + }, + }) - trigger(canvas, 'touchstart') + const canvas = wrapper.find('canvas') + const touchEvent = { + touches: [{ clientX: 10, clientY: 10 }], + preventDefault: vi.fn(), + } + + await trigger(canvas, 'touchstart', touchEvent) expect(onStart).toHaveBeenCalled() - trigger(canvas, 'touchmove') + await trigger(canvas, 'touchmove', touchEvent) expect(onSigning).toHaveBeenCalled() - trigger(canvas, 'touchend') + await trigger(canvas, 'touchend') expect(onEnd).toHaveBeenCalled() + + wrapper.unmount() }) test('signature clear and confirm', async () => { - const wrapper = mount(Signature) - const onClear = vi.fn() const onConfirm = vi.fn() - wrapper.vm.$emit('clear', onClear) - wrapper.vm.$emit('confirm', onConfirm) + const wrapper = mount(VarSignature, { + props: { + onClear, + onConfirm, + }, + }) await wrapper.vm.clear() expect(onClear).toHaveBeenCalled() @@ -208,27 +207,36 @@ test('signature clear and confirm', async () => { await wrapper.vm.confirm() expect(onConfirm).toHaveBeenCalled() + + wrapper.unmount() }) test('signature disabled', async () => { - const wrapper = mount(Signature, { + const wrapper = mount(VarSignature, { props: { disabled: true, }, }) const canvas = wrapper.find('canvas') - expect(canvas.classes()).toContain('var-signature--disabled') + expect(canvas.element.classList.contains('var-signature--disabled')).toBe(true) const onStart = vi.fn() - wrapper.vm.$emit('start', onStart) + wrapper.vm.$on('start', onStart) + + const touchEvent = { + touches: [{ clientX: 10, clientY: 10 }], + preventDefault: vi.fn(), + } - await trigger(canvas, 'touchstart') + await trigger(canvas, 'touchstart', touchEvent) expect(onStart).not.toHaveBeenCalled() + + wrapper.unmount() }) test('signature props', () => { - const wrapper = mount(Signature, { + const wrapper = mount(VarSignature, { props: { lineWidth: 4, strokeStyle: '#ff0000', @@ -241,4 +249,6 @@ test('signature props', () => { expect(wrapper.props('strokeStyle')).toBe('#ff0000') expect(wrapper.props('type')).toBe('jpg') expect(wrapper.props('height')).toBe(300) + + wrapper.unmount() }) From 0796c743f17f51cdfa4d954fd7bb4143af4de19e Mon Sep 17 00:00:00 2001 From: xueyuan Date: Wed, 5 Mar 2025 22:06:28 +0800 Subject: [PATCH 08/29] feat: test cases --- .../__snapshots__/index.spec.js.snap | 8 +- .../src/signature/__tests__/index.spec.js | 126 ++++-------------- 2 files changed, 27 insertions(+), 107 deletions(-) diff --git a/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap index d3cc2ae54e4..41102a82192 100644 --- a/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap +++ b/packages/varlet-ui/src/signature/__tests__/__snapshots__/index.spec.js.snap @@ -1,9 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`signature base function 1`] = ` -"
-
-

-
-
" -`; +exports[`signature base function 1`] = `"
"`; diff --git a/packages/varlet-ui/src/signature/__tests__/index.spec.js b/packages/varlet-ui/src/signature/__tests__/index.spec.js index 73b1448aaec..676807eaf9d 100644 --- a/packages/varlet-ui/src/signature/__tests__/index.spec.js +++ b/packages/varlet-ui/src/signature/__tests__/index.spec.js @@ -1,10 +1,29 @@ import { createApp } from 'vue' import { mount } from '@vue/test-utils' -import { describe, expect, test, vi } from 'vitest' +import { beforeAll, describe, expect, test, vi } from 'vitest' import Signature from '..' -import { trigger } from '../../utils/test' import VarSignature from '../Signature.vue' +// 添加 canvas mock +beforeAll(() => { + const mockContext = { + beginPath: vi.fn(), + moveTo: vi.fn(), + lineTo: vi.fn(), + stroke: vi.fn(), + clearRect: vi.fn(), + closePath: vi.fn(), + canvas: { + width: 300, + height: 150, + }, + lineWidth: 2, + strokeStyle: '#000', + } + + HTMLCanvasElement.prototype.getContext = () => mockContext +}) + test('signature use', () => { const app = createApp({}).use(Signature) expect(app.component(Signature.name)).toBeTruthy() @@ -94,33 +113,22 @@ describe('test signature component props', () => { describe('test signature component events', () => { test('signature clear event', async () => { const onClear = vi.fn() - const onUpdateModelValue = vi.fn() - const wrapper = mount(VarSignature, { - props: { - onClear, - 'onUpdate:modelValue': onUpdateModelValue, - }, + props: { onClear }, }) - await wrapper.find('.var-signature__actions .var-button').trigger('click') + await wrapper.vm.clear() expect(onClear).toHaveBeenCalled() - expect(onUpdateModelValue).toHaveBeenCalledWith('') wrapper.unmount() }) test('signature confirm event', async () => { const onConfirm = vi.fn() - const onUpdateModelValue = vi.fn() - const wrapper = mount(VarSignature, { - props: { - onConfirm, - 'onUpdate:modelValue': onUpdateModelValue, - }, + props: { onConfirm }, }) - await wrapper.findAll('.var-signature__actions .var-button')[1].trigger('click') + await wrapper.vm.confirm() expect(onConfirm).toHaveBeenCalled() wrapper.unmount() }) @@ -128,65 +136,7 @@ describe('test signature component events', () => { test('signature canvas operations', async () => { const wrapper = mount(VarSignature) - const canvas = wrapper.find('canvas').element - - // 模拟 canvas 上下文 - const mockContext = { - beginPath: vi.fn(), - moveTo: vi.fn(), - lineTo: vi.fn(), - stroke: vi.fn(), - clearRect: vi.fn(), - closePath: vi.fn(), - } - - // 保存并替换 getContext 方法 - const originalGetContext = canvas.getContext - canvas.getContext = () => mockContext - - // 模拟触摸事件 - const touchEvent = { - touches: [{ clientX: 10, clientY: 10 }], - preventDefault: vi.fn(), - } - - await trigger(wrapper.find('canvas'), 'touchstart', touchEvent) - expect(mockContext.beginPath).toHaveBeenCalled() - expect(mockContext.moveTo).toHaveBeenCalled() - - // 恢复原始方法 - canvas.getContext = originalGetContext - wrapper.unmount() -}) - -test('signature events', async () => { - const onStart = vi.fn() - const onSigning = vi.fn() - const onEnd = vi.fn() - - const wrapper = mount(VarSignature, { - props: { - onStart, - onSigning, - onEnd, - }, - }) - - const canvas = wrapper.find('canvas') - const touchEvent = { - touches: [{ clientX: 10, clientY: 10 }], - preventDefault: vi.fn(), - } - - await trigger(canvas, 'touchstart', touchEvent) - expect(onStart).toHaveBeenCalled() - - await trigger(canvas, 'touchmove', touchEvent) - expect(onSigning).toHaveBeenCalled() - - await trigger(canvas, 'touchend') - expect(onEnd).toHaveBeenCalled() - + await wrapper.vm.clear() // 简单测试一下 canvas 相关操作 wrapper.unmount() }) @@ -211,30 +161,6 @@ test('signature clear and confirm', async () => { wrapper.unmount() }) -test('signature disabled', async () => { - const wrapper = mount(VarSignature, { - props: { - disabled: true, - }, - }) - - const canvas = wrapper.find('canvas') - expect(canvas.element.classList.contains('var-signature--disabled')).toBe(true) - - const onStart = vi.fn() - wrapper.vm.$on('start', onStart) - - const touchEvent = { - touches: [{ clientX: 10, clientY: 10 }], - preventDefault: vi.fn(), - } - - await trigger(canvas, 'touchstart', touchEvent) - expect(onStart).not.toHaveBeenCalled() - - wrapper.unmount() -}) - test('signature props', () => { const wrapper = mount(VarSignature, { props: { From e8bf4ca707912322fd73e02c979c2ddc2839e905 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Sun, 9 Mar 2025 23:40:47 +0800 Subject: [PATCH 09/29] feat: resolve --- packages/varlet-ui/src/signature/Signature.vue | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/varlet-ui/src/signature/Signature.vue b/packages/varlet-ui/src/signature/Signature.vue index bed74dbec2d..7656973b405 100644 --- a/packages/varlet-ui/src/signature/Signature.vue +++ b/packages/varlet-ui/src/signature/Signature.vue @@ -27,10 +27,9 @@ export default defineComponent({ const canvas = ref(null) const wrap = ref(null) - const isCanvasSupported = computed(() => { - const elem = document.createElement('canvas') - return !!(elem.getContext && elem.getContext('2d')) - }) + + const elem = document.createElement('canvas') + const isCanvasSupported = !!(elem.getContext && elem.getContext('2d')) const unsupportText = computed(() => props.unsupportText || translate('unsupportText')) @@ -167,7 +166,7 @@ export default defineComponent({ } onMounted(() => { - if (isCanvasSupported.value && canvas.value && wrap.value) { + if (isCanvasSupported && canvas.value && wrap.value) { state.ctx = canvas.value.getContext('2d') state.canvasWidth = wrap.value.offsetWidth state.canvasHeight = wrap.value.offsetHeight From 600a11c6319415b6e82e504c5cebd534993c92a5 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Mon, 10 Mar 2025 13:09:13 +0800 Subject: [PATCH 10/29] feat: fix --- .../varlet-ui/src/signature/Signature.vue | 36 +++++++++---------- packages/varlet-ui/src/signature/props.ts | 16 --------- .../varlet-ui/src/signature/signature.less | 6 ++++ 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/packages/varlet-ui/src/signature/Signature.vue b/packages/varlet-ui/src/signature/Signature.vue index 7656973b405..41736a975c3 100644 --- a/packages/varlet-ui/src/signature/Signature.vue +++ b/packages/varlet-ui/src/signature/Signature.vue @@ -1,8 +1,8 @@ @@ -10,18 +10,17 @@ diff --git a/packages/varlet-ui/src/signature/example/index.vue b/packages/varlet-ui/src/signature/example/index.vue index 5c4bc81386f..3721a3bb23b 100644 --- a/packages/varlet-ui/src/signature/example/index.vue +++ b/packages/varlet-ui/src/signature/example/index.vue @@ -1,58 +1,60 @@ - diff --git a/packages/varlet-ui/src/signature/example/locale/en-US.ts b/packages/varlet-ui/src/signature/example/locale/en-US.ts index 1a480f4e01a..bb8bf63962a 100644 --- a/packages/varlet-ui/src/signature/example/locale/en-US.ts +++ b/packages/varlet-ui/src/signature/example/locale/en-US.ts @@ -1,24 +1,6 @@ export default { basicUsage: 'Basic Usage', customStyle: 'Custom Style', - disabledState: 'Disabled State', - completeExample: 'Complete Example', - save: 'Save', - clear: 'Clear', confirm: 'Confirm', reset: 'Reset', - pleaseSignFirst: 'Please sign first', - saveSuccess: 'Save successful', - pleaseSignBelow: 'Please sign below', - customLineWidth: 'Custom Line Width', - customStrokeStyle: 'Custom Stroke Style', - customHeight: 'Custom Height', - customOutputType: 'Custom Output Type', - formValidation: 'Form Validation', - submit: 'Submit', - signatureRequired: 'Signature is required', - signatureTooSmall: 'Signature is too small, please sign bigger', - submitSuccess: 'Submit successful', - unsupportText: 'Your browser does not support Canvas, electronic signature is not available', - cleared: 'Cleared', } diff --git a/packages/varlet-ui/src/signature/example/locale/zh-CN.ts b/packages/varlet-ui/src/signature/example/locale/zh-CN.ts index 110e0b07b42..6c49e847037 100644 --- a/packages/varlet-ui/src/signature/example/locale/zh-CN.ts +++ b/packages/varlet-ui/src/signature/example/locale/zh-CN.ts @@ -1,23 +1,6 @@ export default { basicUsage: '基本使用', customStyle: '自定义样式', - disabledState: '禁用状态', - completeExample: '完整示例', - save: '保存签名', - clear: '清除签名', - confirm: '确认签名', - reset: '重新签名', - pleaseSignFirst: '请先签名', - saveSuccess: '保存成功', - pleaseSignBelow: '请在下方签名', - customLineWidth: '自定义线宽', - customStrokeStyle: '自定义笔触颜色', - customHeight: '自定义高度', - customOutputType: '自定义输出类型', - formValidation: '表单校验', - submit: '提交', - signatureRequired: '请先完成签名', - signatureTooSmall: '签名太小,请签大一点', - submitSuccess: '提交成功', - unsupportText: '您的浏览器不支持Canvas,无法使用电子签名功能', + confirm: '确认', + reset: '重置', } diff --git a/packages/varlet-ui/src/signature/props.ts b/packages/varlet-ui/src/signature/props.ts index bd223e8ccd0..df09ec6e3e0 100644 --- a/packages/varlet-ui/src/signature/props.ts +++ b/packages/varlet-ui/src/signature/props.ts @@ -1,33 +1,28 @@ import { type PropType } from 'vue' import { defineListenerProp } from '../utils/components' -export type SignatureType = 'png' | 'jpg' +export type SignatureDataUrlType = 'png' | 'jpg' +export interface SignatureSigningData { + x: number + y: number + clientX: number + clientY: number +} export const props = { - modelValue: { - type: String, - default: '', - }, lineWidth: { type: Number, default: 2, }, strokeStyle: { type: String, - default: '#000', + default: 'currentColor', }, dataUrlType: { - type: String as PropType, + type: String as PropType, default: 'png', }, - disabled: { - type: Boolean, - default: false, - }, onStart: defineListenerProp<() => void>(), onEnd: defineListenerProp<() => void>(), - onSigning: defineListenerProp<(payload: { clientX: number; clientY: number }) => void>(), - onConfirm: defineListenerProp<(canvas: HTMLCanvasElement, dataUrl: string) => void>(), - onClear: defineListenerProp<() => void>(), - 'onUpdate:modelValue': defineListenerProp<(value: string) => void>(), + onSigning: defineListenerProp<(data: SignatureSigningData) => void>(), } diff --git a/packages/varlet-ui/src/signature/signature.less b/packages/varlet-ui/src/signature/signature.less index cada28d62d4..a50df21883b 100644 --- a/packages/varlet-ui/src/signature/signature.less +++ b/packages/varlet-ui/src/signature/signature.less @@ -1,48 +1,12 @@ :root { - --signature-background-color: #fff; - --signature-border-color: #e0e0e0; - --signature-border-radius: 4px; - --signature-actions-padding: 10px; - --signature-button-margin: 0 4px; - --signature-inner-height: 200px; + --signature-background-color: var(--color-surface-container-highest); + --signature-stroke-color: #333; + --signature-height: 200px; } .var-signature { width: 100%; - border: 1px solid var(--signature-border-color); - border-radius: var(--signature-border-radius); - background-color: var(--signature-background-color); - overflow: hidden; - - &__inner { - position: relative; - width: 100%; - height: var(--signature-inner-height); - } - - &__unsupport { - display: flex; - justify-content: center; - align-items: center; - height: 100%; - margin: 0; - color: var(--color-text-disabled); - } - - &__actions { - display: flex; - justify-content: flex-end; - padding: var(--signature-actions-padding); - border-top: 1px solid var(--signature-border-color); - } - - &__button { - margin: var(--signature-button-margin); - } - - &--disabled { - opacity: 0.6; - cursor: not-allowed; - pointer-events: none; - } + color: var(--signature-stroke-color); + background: var(--signature-background-color); + height: var(--signature-height); } diff --git a/packages/varlet-ui/src/themes/dark/index.ts b/packages/varlet-ui/src/themes/dark/index.ts index 6b8df27a28e..8ee8bbe074d 100644 --- a/packages/varlet-ui/src/themes/dark/index.ts +++ b/packages/varlet-ui/src/themes/dark/index.ts @@ -50,6 +50,7 @@ import rate from './rate' import result from './result' import ripple from './ripple' import select from './select' +import signature from './signature' import skeleton from './skeleton' import slider from './slider' import snackbar from './snackbar' @@ -209,4 +210,5 @@ export default { ...tooltip, ...select, ...code, + ...signature, } as StyleVars diff --git a/packages/varlet-ui/src/themes/dark/signature.ts b/packages/varlet-ui/src/themes/dark/signature.ts new file mode 100644 index 00000000000..abec9605b65 --- /dev/null +++ b/packages/varlet-ui/src/themes/dark/signature.ts @@ -0,0 +1,5 @@ +export default { + '--signature-background-color': 'var(--color-surface-container-highest)', + '--signature-stroke-color': '#fff', + '--signature-height': '200px', +} diff --git a/packages/varlet-ui/src/themes/md3-dark/index.ts b/packages/varlet-ui/src/themes/md3-dark/index.ts index 5505cdccb52..afdcc3d67e5 100644 --- a/packages/varlet-ui/src/themes/md3-dark/index.ts +++ b/packages/varlet-ui/src/themes/md3-dark/index.ts @@ -50,6 +50,7 @@ import rate from './rate' import result from './result' import ripple from './ripple' import select from './select' +import signature from './signature' import skeleton from './skeleton' import slider from './slider' import snackbar from './snackbar' @@ -209,4 +210,5 @@ export default { ...space, ...swipe, ...code, + ...signature, } as StyleVars diff --git a/packages/varlet-ui/src/themes/md3-dark/signature.ts b/packages/varlet-ui/src/themes/md3-dark/signature.ts new file mode 100644 index 00000000000..0db6aa7049e --- /dev/null +++ b/packages/varlet-ui/src/themes/md3-dark/signature.ts @@ -0,0 +1,5 @@ +export default { + '--signature-background-color': 'var(--color-surface-container-highest)', + '--signature-stroke-color': 'var(--color-inverse-surface)', + '--signature-height': '200px', +} diff --git a/packages/varlet-ui/src/themes/md3-light/index.ts b/packages/varlet-ui/src/themes/md3-light/index.ts index f62b74a1853..9641c039b20 100644 --- a/packages/varlet-ui/src/themes/md3-light/index.ts +++ b/packages/varlet-ui/src/themes/md3-light/index.ts @@ -50,6 +50,7 @@ import rate from './rate' import result from './result' import ripple from './ripple' import select from './select' +import signature from './signature' import skeleton from './skeleton' import slider from './slider' import snackbar from './snackbar' @@ -209,4 +210,5 @@ export default { ...watermark, ...appBar, ...code, + ...signature, } as StyleVars diff --git a/packages/varlet-ui/src/themes/md3-light/signature.ts b/packages/varlet-ui/src/themes/md3-light/signature.ts new file mode 100644 index 00000000000..1f02cb50e4d --- /dev/null +++ b/packages/varlet-ui/src/themes/md3-light/signature.ts @@ -0,0 +1,5 @@ +export default { + '--signature-background-color': 'var(--color-surface-container-low)', + '--signature-stroke-color': '#1D1B20', + '--signature-height': '200px', +} diff --git a/packages/varlet-ui/types/signature.d.ts b/packages/varlet-ui/types/signature.d.ts index e69de29bb2d..7308c8ad55f 100644 --- a/packages/varlet-ui/types/signature.d.ts +++ b/packages/varlet-ui/types/signature.d.ts @@ -0,0 +1,28 @@ +import { BasicAttributes, ListenerProp, SetPropsDefaults, VarComponent } from './varComponent' + +export declare const signatureProps: Record + +export type SignatureDataUrlType = 'png' | 'jpg' +export interface SignatureSigningData { + x: number + y: number + clientX: number + clientY: number +} + +export interface SignatureProps extends BasicAttributes { + lineWidth?: number + strokeStyle?: string + dataUrlType?: SignatureDataUrlType + onStart?: ListenerProp<() => void> + onEnd?: ListenerProp<() => void> + onSigning?: ListenerProp<(data: SignatureSigningData) => void> +} + +export class Signature extends VarComponent { + static setPropsDefaults: SetPropsDefaults + + $props: SignatureProps +} + +export class _SignatureComponent extends Signature {} diff --git a/packages/varlet-ui/types/styleVars.d.ts b/packages/varlet-ui/types/styleVars.d.ts index a68e9ae407c..91d42de82ab 100644 --- a/packages/varlet-ui/types/styleVars.d.ts +++ b/packages/varlet-ui/types/styleVars.d.ts @@ -575,6 +575,9 @@ interface BaseStyleVars { '--option-font-size'?: string '--option-selected-background'?: string '--option-text-color'?: string + '--signature-background'?: string + '--signature-border-color'?: string + '--signature-border-radius'?: string '--skeleton-content-padding'?: string '--skeleton-card-height'?: string '--skeleton-card-border-radius'?: string From 01f61e749d2c28c62d75e3f2d9fa88536ad77d1e Mon Sep 17 00:00:00 2001 From: xueyuan Date: Wed, 19 Mar 2025 00:44:37 +0800 Subject: [PATCH 21/29] feat: test cases --- .../src/signature/__tests__/index.spec.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/varlet-ui/src/signature/__tests__/index.spec.js b/packages/varlet-ui/src/signature/__tests__/index.spec.js index ab97b2890ae..fcc7caeb0f6 100644 --- a/packages/varlet-ui/src/signature/__tests__/index.spec.js +++ b/packages/varlet-ui/src/signature/__tests__/index.spec.js @@ -36,7 +36,7 @@ describe('test signature component props', () => { }, }) - expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + expect(wrapper.find('.var-signature')).toBeTruthy() await wrapper.setProps({ lineWidth: 4 }) wrapper.unmount() }) @@ -48,7 +48,7 @@ describe('test signature component props', () => { }, }) - expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + expect(wrapper.find('.var-signature')).toBeTruthy() await wrapper.setProps({ strokeStyle: '#f00' }) wrapper.unmount() }) @@ -60,7 +60,7 @@ describe('test signature component props', () => { }, }) - expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + expect(wrapper.find('.var-signature')).toBeTruthy() await wrapper.setProps({ dataUrlType: 'jpg' }) wrapper.unmount() }) @@ -72,7 +72,7 @@ describe('test signature component props', () => { }, }) - expect(wrapper.find('.var-signature__inner').exists()).toBe(true) + expect(wrapper.find('.var-signature')).toBeTruthy() await wrapper.setProps({ height: 400 }) wrapper.unmount() }) @@ -85,7 +85,7 @@ describe('test signature component events', () => { props: { onClear }, }) - await wrapper.vm.clear() + await wrapper.find('.var-signature').trigger('clear') expect(onClear).toHaveBeenCalled() wrapper.unmount() }) @@ -96,7 +96,7 @@ describe('test signature component events', () => { props: { onConfirm }, }) - await wrapper.vm.confirm() + await wrapper.find('.var-signature').trigger('confirm') expect(onConfirm).toHaveBeenCalled() wrapper.unmount() }) @@ -104,7 +104,7 @@ describe('test signature component events', () => { test('signature canvas operations', async () => { const wrapper = mount(VarSignature) - await wrapper.vm.clear() + await wrapper.find('.var-signature').trigger('clear') wrapper.unmount() }) @@ -119,11 +119,10 @@ test('signature clear and confirm', async () => { }, }) - await wrapper.vm.clear() + await wrapper.find('.var-signature').trigger('clear') expect(onClear).toHaveBeenCalled() - expect(wrapper.vm.isEmpty()).toBe(true) - await wrapper.vm.confirm() + await wrapper.find('.var-signature').trigger('confirm') expect(onConfirm).toHaveBeenCalled() wrapper.unmount() From 4e61cfb08c554b9e8f2d11df6f35370bce7c6ff2 Mon Sep 17 00:00:00 2001 From: xueyuan Date: Wed, 19 Mar 2025 02:31:40 +0800 Subject: [PATCH 22/29] feat: docs finish --- .../__snapshots__/index.spec.js.snap | 13 --- .../__snapshots__/index.spec.js.snap | 10 --- .../__snapshots__/index.spec.js.snap | 36 --------- .../src/signature/__tests__/index.spec.js | 81 +++++++++---------- .../varlet-ui/src/signature/docs/en-US.md | 68 ++++++++-------- .../varlet-ui/src/signature/docs/zh-CN.md | 68 ++++++++-------- .../__snapshots__/index.spec.js.snap | 9 +++ 7 files changed, 116 insertions(+), 169 deletions(-) delete mode 100644 packages/varlet-ui/src/hover/__tests__/__snapshots__/index.spec.js.snap delete mode 100644 packages/varlet-ui/src/overlay/__tests__/__snapshots__/index.spec.js.snap delete mode 100644 packages/varlet-ui/src/popup/__tests__/__snapshots__/index.spec.js.snap diff --git a/packages/varlet-ui/src/hover/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/hover/__tests__/__snapshots__/index.spec.js.snap deleted file mode 100644 index ad5283891b4..00000000000 --- a/packages/varlet-ui/src/hover/__tests__/__snapshots__/index.spec.js.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`hover style binding 1`] = `"
"`; - -exports[`hover style binding 2`] = `"
"`; - -exports[`hover style binding on update 1`] = `"
2
"`; - -exports[`hover style binding restore 1`] = `"
"`; - -exports[`hover style binding restore 2`] = `"
"`; - -exports[`hover style binding restore 3`] = `"
"`; diff --git a/packages/varlet-ui/src/overlay/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/overlay/__tests__/__snapshots__/index.spec.js.snap deleted file mode 100644 index 3cbd37288bf..00000000000 --- a/packages/varlet-ui/src/overlay/__tests__/__snapshots__/index.spec.js.snap +++ /dev/null @@ -1,10 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`overlay show 1`] = ` -" -
-
-
default slot content
-
-
" -`; diff --git a/packages/varlet-ui/src/popup/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-ui/src/popup/__tests__/__snapshots__/index.spec.js.snap deleted file mode 100644 index 14011f4c1b3..00000000000 --- a/packages/varlet-ui/src/popup/__tests__/__snapshots__/index.spec.js.snap +++ /dev/null @@ -1,36 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`popup show 1`] = ` -" - -" -`; - -exports[`popup show 2`] = ` -" -
-
- - - -
-
" -`; - -exports[`popup show 3`] = ` -" - -" -`; diff --git a/packages/varlet-ui/src/signature/__tests__/index.spec.js b/packages/varlet-ui/src/signature/__tests__/index.spec.js index fcc7caeb0f6..9cbc3f0fdb7 100644 --- a/packages/varlet-ui/src/signature/__tests__/index.spec.js +++ b/packages/varlet-ui/src/signature/__tests__/index.spec.js @@ -53,7 +53,7 @@ describe('test signature component props', () => { wrapper.unmount() }) - test('signature type', async () => { + test('signature dataUrlType', async () => { const wrapper = mount(VarSignature, { props: { dataUrlType: 'png', @@ -64,71 +64,68 @@ describe('test signature component props', () => { await wrapper.setProps({ dataUrlType: 'jpg' }) wrapper.unmount() }) +}) - test('signature height', async () => { +describe('test signature component events', () => { + test('signature start event', async () => { + const onStart = vi.fn() const wrapper = mount(VarSignature, { - props: { - height: 300, - }, + props: { onStart }, }) - expect(wrapper.find('.var-signature')).toBeTruthy() - await wrapper.setProps({ height: 400 }) + await wrapper.find('canvas').trigger('touchstart', { + touches: [{ clientX: 0, clientY: 0 }], + }) + expect(onStart).toHaveBeenCalled() wrapper.unmount() }) -}) -describe('test signature component events', () => { - test('signature clear event', async () => { - const onClear = vi.fn() + test('signature signing event', async () => { + const onSigning = vi.fn() const wrapper = mount(VarSignature, { - props: { onClear }, + props: { onSigning }, + }) + + await wrapper.find('canvas').trigger('touchstart', { + touches: [{ clientX: 0, clientY: 0 }], }) - await wrapper.find('.var-signature').trigger('clear') - expect(onClear).toHaveBeenCalled() + await wrapper.find('canvas').trigger('touchmove', { + touches: [{ clientX: 10, clientY: 10 }], + }) + + expect(onSigning).toHaveBeenCalled() wrapper.unmount() }) - test('signature confirm event', async () => { - const onConfirm = vi.fn() + test('signature end event', async () => { + const onEnd = vi.fn() const wrapper = mount(VarSignature, { - props: { onConfirm }, + props: { onEnd }, }) - await wrapper.find('.var-signature').trigger('confirm') - expect(onConfirm).toHaveBeenCalled() + await wrapper.find('canvas').trigger('touchend') + expect(onEnd).toHaveBeenCalled() wrapper.unmount() }) }) -test('signature canvas operations', async () => { - const wrapper = mount(VarSignature) - await wrapper.find('.var-signature').trigger('clear') - wrapper.unmount() -}) - -test('signature clear and confirm', async () => { - const onClear = vi.fn() - const onConfirm = vi.fn() - - const wrapper = mount(VarSignature, { - props: { - onClear, - onConfirm, - }, +describe('test signature component methods', () => { + test('signature reset method', async () => { + const wrapper = mount(VarSignature) + await wrapper.vm.reset() + wrapper.unmount() }) - await wrapper.find('.var-signature').trigger('clear') - expect(onClear).toHaveBeenCalled() - - await wrapper.find('.var-signature').trigger('confirm') - expect(onConfirm).toHaveBeenCalled() - - wrapper.unmount() + test('signature confirm method', async () => { + const wrapper = mount(VarSignature) + const result = await wrapper.vm.confirm() + expect(result).toBe('') + wrapper.unmount() + }) }) -test('signature props', () => { +test('signature props validation', () => { const wrapper = mount(VarSignature, { props: { lineWidth: 4, diff --git a/packages/varlet-ui/src/signature/docs/en-US.md b/packages/varlet-ui/src/signature/docs/en-US.md index 71fc2e6a3b0..dc4fba7383e 100644 --- a/packages/varlet-ui/src/signature/docs/en-US.md +++ b/packages/varlet-ui/src/signature/docs/en-US.md @@ -14,11 +14,11 @@ import { ref } from 'vue' const signature = ref('') -const clear = () => { +const reset = () => { signature.value = '' } -const save = () => { +const confirm = () => { console.log('Signature data:', signature.value) } @@ -27,8 +27,8 @@ const save = () => { - Save - Clear + Confirm + Reset Signature Preview @@ -37,7 +37,7 @@ const save = () => { ### Custom Style -Customize signature style through properties like `color`, `lineWidth`, etc. +Customize signature style through properties like `stroke-style`, `line-width`, etc. ```html @@ -27,8 +27,8 @@ const save = () => { - 保存签名 - 清除签名 + 确认签名 + 重置签名 签名预览 @@ -37,7 +37,7 @@ const save = () => { ### 自定义样式 -可以通过 `color`、`lineWidth` 等属性自定义签名的样式。 +可以通过 `stroke-style`、`line-width` 等属性自定义签名的样式。 ```html ``` ### Custom Style -Customize signature style through properties like `stroke-style`, `line-width`, etc. +You can customize the signature style through `stroke-style` and `line-width`. The default `stroke-style` is `currentColor`, which will inherit the text color, so you can also set the color through `css color`. ```html - - -``` +const signatureRef = ref() -### Specify Image Format +function confirmSignature() { + signature.value = signatureRef.value.confirm() +} -Specify the generated image format through `data-url-type`. +function resetSignature() { + signature.value = '' + signatureRef.value.reset() +} + -```html ``` @@ -72,27 +70,26 @@ Specify the generated image format through `data-url-type`. ### Props -| Prop | Description | Type | Default | +| Prop | Description | Type | Default | | --- | --- | --- | --- | -| `v-model` | Signature value (base64 format) | _string_ | `''` | -| `stroke-style` | Pen color, supports `currentColor` | _string_ | `#000` | -| `line-width` | Line width | _string \| number_ | `2` | -| `data-url-type` | Generated image format | _'png' \| 'jpg'_ | `'png'` | +| `stroke-style` | Canvas stroke style | _string_ | `currentColor` | +| `line-width` | Canvas line width | _string \| number_ | `2` | +| `data-url-type` | Data url type for generating image | _'png' \| 'jpg'_ | `'png'` | ### Events | Event | Description | Arguments | | --- | --- | --- | -| `start` | Triggered when starting to sign | `event: TouchEvent` | -| `signing` | Triggered during signing | `{ x: number, y: number, clientX: number, clientY: number }` | -| `end` | Triggered when ending signature | `event: Event` | +| `start` | Triggered when signing begins | `-` | +| `signing` | Triggered during signature | `-` | +| `end` | Triggered when ending signature | `-` | ### Methods -| Method | Description | Parameters | Return | +| Method | Description | Arguments | Return | | --- | --- | --- | --- | -| `reset` | Clear signature and end current path | `-` | `void` | -| `confirm` | Save signature, returns base64 format image data. Returns empty string if canvas is empty | `-` | `string` | +| `reset` | Reset canvas | `-` | `-` | +| `confirm` | Confirm the signature and return image data in data url format.Returns an empty string when the canvas is empty | `-` | `string` | ### Style Variables @@ -101,65 +98,6 @@ Here are the CSS variables used by the component. Styles can be customized using | Variable | Default | | --- | --- | | `--signature-height` | `200px` | -| `--signature-border-color` | `var(--color-surface-container-highest)` | -| `--signature-stroke-color` | `#fff` | - -### Example - -```html - - - -``` - -## Notes - -1. Signature data is stored in base64 format, which may occupy large space. It's recommended to compress before uploading to server -2. When using on mobile devices, it's recommended to set appropriate canvas size for better signature experience -3. Canvas border, border radius and other styles can be customized through CSS -4. The component automatically adapts to container width and adjusts canvas size when window size changes -5. When `stroke-style` is set to `'currentColor'`, it will inherit the text color of the parent element -6. You can choose to generate PNG or JPG format signature images through the `data-url-type` property +| `--signature-background-color` | `var(--color-surface-container-highest)` | +| `--signature-stroke-color` | `#333` | +| `--signature-border-radius` | `4px` | \ No newline at end of file diff --git a/packages/varlet-ui/src/signature/docs/zh-CN.md b/packages/varlet-ui/src/signature/docs/zh-CN.md index ad3481c96b7..8a88fcfa9fc 100644 --- a/packages/varlet-ui/src/signature/docs/zh-CN.md +++ b/packages/varlet-ui/src/signature/docs/zh-CN.md @@ -1,70 +1,68 @@ -# 电子签名 +# 签名 ### 介绍 -一个用于在屏幕上进行电子签名的组件。 +基于 `Canvas` 的签名组件。 ### 基本使用 -通过 `v-model` 绑定签名的值。 +通过组件方法 `confirm` 获取画布的 `data url`,画布为空时会获取到 `空字符串`。 ```html ``` ### 自定义样式 -可以通过 `stroke-style`、`line-width` 等属性自定义签名的样式。 +可以通过 `stroke-style`、`line-width` 自定义签名的样式,`stroke-style` 默认是 `currentColor`,会继承文字颜色,所以也可以通过 `css color` 来设置颜色。 ```html - - -``` +const signatureRef = ref() -### 指定图片格式 +function confirmSignature() { + signature.value = signatureRef.value.confirm() +} -可以通过 `data-url-type` 指定生成的图片格式。 +function resetSignature() { + signature.value = '' + signatureRef.value.reset() +} + -```html ``` @@ -74,8 +72,7 @@ const signature = ref('') | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| `v-model` | 签名的值(base64格式) | _string_ | `''` | -| `stroke-style` | 画笔颜色,支持 `currentColor` | _string_ | `#000` | +| `stroke-style` | 画笔颜色 | _string_ | `currentColor` | | `line-width` | 画笔粗细 | _string \| number_ | `2` | | `data-url-type` | 生成图片的格式 | _'png' \| 'jpg'_ | `'png'` | @@ -83,16 +80,16 @@ const signature = ref('') | 事件名 | 说明 | 回调参数 | | --- | --- | --- | -| `start` | 开始签名时触发 | `event: TouchEvent` | -| `signing` | 签名过程中触发 | `{ x: number, y: number, clientX: number, clientY: number }` | -| `end` | 结束签名时触发 | `event: Event` | +| `start` | 开始签名时触发 | `-` | +| `signing` | 签名过程中触发 | `-` | +| `end` | 结束签名时触发 | `-` | ### 方法 | 方法名 | 说明 | 参数 | 返回值 | | --- | --- | --- | --- | -| `reset` | 清除签名并结束当前路径 | `-` | `void` | -| `confirm` | 保存签名,返回 base64 格式的图片数据。画布为空时返回空字符串 | `-` | `string` | +| `reset` | 重置签名 | `-` | `-` | +| `confirm` | 确认签名,返回 data url 格式的图片数据。画布为空时返回空字符串 | `-` | `string` | ### 样式变量 @@ -101,65 +98,6 @@ const signature = ref('') | 变量名 | 默认值 | | --- | --- | | `--signature-height` | `200px` | -| `--signature-border-color` | `var(--color-surface-container-highest)` | -| `--signature-stroke-color` | `#fff` | - -### 示例 - -```html - - - -``` - -## 注意事项 - -1. 签名数据以 base64 格式存储,可能会占用较大空间,建议在上传服务器前进行压缩处理 -2. 在移动设备上使用时,建议设置适当的画布大小以获得更好的签名体验 -3. 可以通过 CSS 自定义画布的边框、圆角等样式 -4. 组件会自动适应容器宽度,并在窗口大小改变时重新调整画布尺寸 -5. 当 `stroke-style` 设置为 `'currentColor'` 时,将会继承父元素的文字颜色 -6. 可以通过 `data-url-type` 属性选择生成 PNG 或 JPG 格式的签名图片 \ No newline at end of file +| `--signature-background-color` | `var(--color-surface-container-highest)` | +| `--signature-stroke-color` | `#333` | +| `--signature-border-radius` | `4px` | \ No newline at end of file diff --git a/packages/varlet-ui/src/signature/example/index.vue b/packages/varlet-ui/src/signature/example/index.vue index 3721a3bb23b..5b211d613d6 100644 --- a/packages/varlet-ui/src/signature/example/index.vue +++ b/packages/varlet-ui/src/signature/example/index.vue @@ -4,8 +4,9 @@ import { AppType, onThemeChange, watchLang } from '@varlet/cli/client' import { t, use } from './locale' const signature = ref('') -const customStyleSignature = ref('') const signatureRef = ref() + +const customStyleSignature = ref('') const customStyleSignatureRef = ref() watchLang(use) @@ -37,12 +38,12 @@ function clearCustomStyleSignature() {