浏览代码

自定义组件新增上传图片和签名组件

wyn 1 月之前
父节点
当前提交
85d5edad24
共有 5 个文件被更改,包括 181 次插入2 次删除
  1. 2 0
      package.json
  2. 120 0
      src/components/SignaturePad/SignaturePad.vue
  3. 52 0
      src/components/generator/config.js
  4. 4 1
      src/main.js
  5. 3 1
      vue.config.js

+ 2 - 0
package.json

@@ -59,6 +59,7 @@
     "pdfjs-dist": "^2.6.347",
     "quill": "1.3.7",
     "screenfull": "5.0.2",
+    "signature_pad": "^5.1.1",
     "sortablejs": "1.10.2",
     "throttle-debounce": "^2.3.0",
     "vue": "2.6.12",
@@ -74,6 +75,7 @@
     "worker-loader": "^3.0.8"
   },
   "devDependencies": {
+    "@babel/plugin-proposal-class-properties": "^7.18.6",
     "@vue/cli-plugin-babel": "4.4.6",
     "@vue/cli-plugin-eslint": "4.4.6",
     "@vue/cli-service": "4.4.6",

+ 120 - 0
src/components/SignaturePad/SignaturePad.vue

@@ -0,0 +1,120 @@
+<template>
+  <div class="signature-wrapper">
+    <canvas
+      ref="canvas"
+      :width="width"
+      :height="height"
+      class="signature-canvas"
+    ></canvas>
+    <div class="signature-actions">
+      <el-button size="small" type="primary" @click="save">保存签名</el-button>
+      <el-button size="small" @click="clear">{{ clearButtonText }}</el-button>
+    </div>
+    <div v-if="preview" class="signature-preview">
+      <p>签名预览:</p>
+      <img :src="preview" alt="签名" />
+    </div>
+  </div>
+</template>
+
+<script>
+import SignaturePad from 'signature_pad'
+// import SignaturePad from 'signature_pad/dist/signature_pad.min.js'
+
+export default {
+  name: 'SignaturePad',
+  props: {
+    modelValue: {
+      type: String,
+      default: ''
+    },
+    width: {
+      type: Number,
+      default: 500
+    },
+    height: {
+      type: Number,
+      default: 200
+    },
+    penColor: {
+      type: String,
+      default: '#000'
+    },
+    backgroundColor: {
+      type: String,
+      default: '#fff'
+    },
+    clearButtonText: {
+      type: String,
+      default: '清空签名'
+    }
+  },
+  data() {
+    return {
+      signaturePad: null,
+      preview: this.modelValue
+    }
+  },
+  mounted() {
+    const canvas = this.$refs.canvas
+    const ctx = canvas.getContext('2d')
+    ctx.fillStyle = this.backgroundColor
+    ctx.fillRect(0, 0, this.width, this.height)
+
+    this.signaturePad = new SignaturePad(canvas, {
+      penColor: this.penColor,
+      backgroundColor: this.backgroundColor
+    })
+
+    this.signaturePad.onEnd = () => {
+      const dataUrl = this.signaturePad.toDataURL()
+      this.preview = dataUrl
+      this.$emit('update:modelValue', dataUrl)
+    }
+  },
+  methods: {
+    clear() {
+      this.signaturePad.clear()
+      this.preview = ''
+      this.$emit('update:modelValue', '')
+    },
+    save() {
+      if (this.signaturePad.isEmpty()) {
+        this.$message.warning('请先签名')
+        return
+      }
+      const dataUrl = this.signaturePad.toDataURL()
+      this.preview = dataUrl
+      this.$emit('update:modelValue', dataUrl)
+      this.$message.success('签名已保存')
+    }
+  }
+}
+</script>
+
+<style scoped>.signature-wrapper {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+}
+
+.signature-canvas {
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  cursor: crosshair;
+}
+
+.signature-actions {
+  margin-top: 8px;
+}
+
+.signature-preview {
+  margin-top: 10px;
+}
+
+.signature-preview img {
+  border: 1px solid #ddd;
+  max-width: 100%;
+  height: auto;
+}
+</style>

+ 52 - 0
src/components/generator/config.js

@@ -31,6 +31,7 @@ export const inputComponents = [
       // 正则校验规则
       regList: []
     },
+
     // 组件的插槽属性
     __slot__: {
       prepend: '',
@@ -146,6 +147,26 @@ export const inputComponents = [
     placeholder: '请输入',
     height: 300, // 编辑器高度
     branding: false // 隐藏右下角品牌烙印
+  },
+  {
+    __config__: {
+      label: '签名',
+      tag: 'signature-pad',
+      tagIcon: 'edit',
+      layout: 'colFormItem',
+      defaultValue: '', // 存储签名的 base64
+      showLabel: true,
+      labelWidth: null,
+      required: false,
+      span: 24,
+      regList: [],
+      changeTag: true,
+      document: 'https://github.com/szimek/signature_pad'
+    },
+    height: 200,
+    penColor: '#000',
+    backgroundColor: '#fff',
+    clearButtonText: '清空签名'
   }
 ]
 
@@ -506,6 +527,37 @@ export const selectComponents = [
     'auto-upload': true,
     'list-type': 'text',
     multiple: false
+  },
+  {
+    __config__: {
+      label: '图片上传',
+      tag: 'el-upload',
+      tagIcon: 'picture',
+      layout: 'colFormItem',
+      defaultValue: null,
+      showLabel: true,
+      labelWidth: null,
+      required: false,
+      span: 24,
+      showTip: false,
+      buttonText: '点击上传图片',
+      regList: [],
+      changeTag: true,
+      fileSize: 5, // 默认 5MB
+      sizeUnit: 'MB',
+      document: 'https://element-plus.org/#/zh-CN/component/upload'
+    },
+    __slot__: {
+      'list-type': true
+    },
+    action: 'https://jsonplaceholder.typicode.com/posts/', // 测试接口
+    disabled: false,
+    accept: 'image/*', // 只允许图片
+    name: 'file',
+    'auto-upload': true,
+    'list-type': 'picture-card', // ✅ 图片卡片展示
+    multiple: true, // 支持多选
+    limit: 5
   }
 ]
 

+ 4 - 1
src/main.js

@@ -44,6 +44,8 @@ import VueMeta from 'vue-meta'
 import DictData from '@/components/DictData'
 import webSite from '@/config/website'
 // import debounce from '@/components/directives/debounce';
+// 引入签名组件
+import SignaturePad from '@/components/SignaturePad/SignaturePad.vue'
 
 Vue.prototype.website = webSite
 // Vue.prototype.debounce = debounce
@@ -69,7 +71,8 @@ Vue.component('FileUpload', FileUpload)
 Vue.component('ImageUpload', ImageUpload)
 Vue.component('ImageUploadSingle', ImageUploadSingle)
 Vue.component('ImagePreview', ImagePreview)
-
+// 全局注册签名组件
+Vue.component('signature-pad', SignaturePad)
 
 // 解决el-radio报错
 Vue.directive('removeAriaHidden', {

+ 3 - 1
vue.config.js

@@ -147,5 +147,7 @@ module.exports = {
           }
         }
       )
-  }
+  },
+  transpileDependencies: ['signature_pad']
+
 }