wangqing 4 years ago
parent
commit
a5cecd2d20
  1. 26
      src/components/form/DescText/index.vue
  2. 115
      src/components/form/ImageSelect/index.vue
  3. 49
      src/components/form/SignPad/index.css
  4. 125
      src/components/form/SignPad/index.vue
  5. 8
      src/components/form/index.js

26
src/components/form/DescText/index.vue

@ -0,0 +1,26 @@
<template>
<p :style="{'color':color,'text-align':textAlign}">
{{ value }}
</p>
</template>
<script>
export default {
name: 'DescText',
props: {
value: {
type: String,
default: ''
},
color: {
type: String,
default: '#000000'
},
textAlign: {
type: String,
default: 'left'
}
},
methods: {}
}
</script>

115
src/components/form/ImageSelect/index.vue

@ -0,0 +1,115 @@
<template>
<div>
<el-radio-group
v-if="!multiple"
v-model="selectValue"
>
<div v-for="option in options" :key="option.value" class="img-radio-item">
<el-image
:preview-src-list="[option.image]"
:src="option.image"
class="image"
fit="scale-down"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline" />
</div>
</el-image>
<el-radio :label="option.value">{{ option.label }}</el-radio>
</div>
</el-radio-group>
<el-checkbox-group
v-else
v-model="selectValue"
>
<div v-for="option in options" :key="option.value" class="img-radio-item">
<el-image
:preview-src-list="[option.image]"
:src="option.image"
class="image"
fit="scale-down"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline" />
</div>
</el-image>
<el-checkbox :label="option.value">{{ option.label }}</el-checkbox>
</div>
</el-checkbox-group>
</div>
</template>
<script>
export default {
name: 'ImageSelect',
props: {
value: {
type: Number,
default: 1
},
options: {
type: Array,
default: function() {
return []
},
required: true
},
multiple: {
type: Boolean,
default: false
}
},
data() {
return {
selectValue: this.value
}
},
watch: {
selectValue(val) {
this.$emit('input', val)
},
value(val) {
this.selectValue = val
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
::v-deep .el-radio-button__inner,
.el-radio-group,
.el-checkbox-group {
display: flex !important;
flex-wrap: wrap;
}
.img-radio-item {
display: flex;
flex-direction: column !important;
align-content: center !important;
align-items: center !important;
border: 1px solid rgba(0, 0, 0, 0.1) !important;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !important;
margin: 3px !important;
background-color: #fff;
.image {
width: 100px;
height: 100px;
margin: 2px 15px 4px 15px;
}
.el-radio {
margin: 4px;
}
}
::v-deep .image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
//background: #f5f7fa;
color: #909399;
font-size: 30px;
}
</style>

49
src/components/form/SignPad/index.css

@ -0,0 +1,49 @@
*,
*::before,
*::after {
box-sizing: border-box;
}
.signature-pad {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
font-size: 10px;
width: 100%;
height: 100%;
max-width: 900px;
max-height: 460px;
background-color: #fff;
border-radius: 4px;
padding: 16px;
}
.signature-pad::before {
left: 20px;
-webkit-transform: skew(-3deg) rotate(-3deg);
transform: skew(-3deg) rotate(-3deg);
}
.signature-pad::after {
right: 20px;
-webkit-transform: skew(3deg) rotate(3deg);
transform: skew(3deg) rotate(3deg);
}
.signature-pad--body {
position: relative;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
border: 1px solid #f4f4f4;
}
.signature-pad--body canvas {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: 4px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.02) inset;
}

125
src/components/form/SignPad/index.vue

@ -0,0 +1,125 @@
<template>
<div id="signature-pad" class="signature-pad">
<div class="signature-pad--body" style="min-height: 300px; width: 100%;">
<canvas v-show="!signImageUrl" class="canvasId" style="border: 2px dashed #f7f7f7;" />
</div>
<img v-if="signImageUrl" :src="signImageUrl">
<p class="desc-text">请在上面区域完成签名 然后点击确"确认"按钮</p>
<div v-if="!signImageUrl">
<el-button plain size="mini" type="danger" @click="clear">清除</el-button>
<el-button plain size="mini" type="warning" @click="undo">回撤</el-button>
<el-button size="mini" type="success" @click="savePng">确认</el-button>
</div>
<div v-if="signImageUrl">
<el-button size="mini" type="primary" @click="reSign">重签</el-button>
</div>
</div>
</template>
<script>
import SignaturePad from 'signature_pad'
export default {
name: 'SignPad',
components: {},
data() {
return {
signaturePad: null,
canvas: null,
signImageUrl: '',
config: {
minWidth: 1,
maxWidth: 3,
penColor: 'rgb(66, 133, 244)'
}
}
},
mounted() {
this.init()
window.onresize = () => {
return (() => {
this.resizeCanvas()
})()
}
this.resizeCanvas()
},
methods: {
resizeCanvas() {
console.log('resizeCanvas')
let canvas = this.canvas
// When zoomed out to less than 100%, for some very strange reason,
// some browsers report devicePixelRatio as less than 1
// and only part of the canvas is cleared then.
let ratio = Math.max(window.devicePixelRatio || 1, 1)
// This part causes the canvas to be cleared
canvas.width = canvas.offsetWidth * ratio
canvas.height = canvas.offsetHeight * ratio
canvas.getContext('2d').scale(ratio, ratio)
// This library does not listen for canvas changes, so after the canvas is automatically
// cleared by the browser, SignaturePad#isEmpty might still return false, even though the
// canvas looks empty, because the internal data of this library wasn't cleared. To make sure
// that the state of this library is consistent with visual state of the canvas, you
// have to clear it manually.
this.signaturePad.clear()
},
init() {
this.canvas = document.querySelector('.canvasId')
let canvas = this.canvas
this.signaturePad = new SignaturePad(canvas, this.config)
// canvas.height = 300
// canvas.width = 500
},
clear() {
this.signaturePad.clear()
},
undo() {
let data = this.signaturePad.toData()
if (data) {
data.pop() // remove the last dot or line
this.signaturePad.fromData(data)
}
},
savePng() {
if (this.signaturePad.isEmpty()) {
this.msgError('请先填写')
return
}
let data = this.signaturePad.toDataURL('image/png')
let file = this.dataURLtoFile(data, `${new Date().getTime()}.png`)
let param = new FormData() // form
param.append('file', file) // appendform
let config = {
headers: {'Content-Type': 'multipart/form-data'}
}
this.$api.post('/project/file/upload/77f1648542af4caf98deb8345a3d0406', param, config).then(res => {
this.signImageUrl = res.data
})
},
reSign() {
this.signImageUrl = ''
this.signaturePad.clear()
},
dataURLtoFile(dataurl, filename) {
// base64
const arr = dataurl.split(',')
// base64
const bstr = window.atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n) // 0length
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, {
type: 'image/png'
})
}
}
}
</script>
<style scoped>
@import "index.css";
.sign-pad-container {
padding: 1px;
}
</style>

8
src/components/form/index.js

@ -0,0 +1,8 @@
const ImageSelect = require('./ImageSelect/index').default
const DescText = require('./DescText/index').default
export default {
install: Vue => {
Vue.component(ImageSelect.name, ImageSelect)
Vue.component(DescText.name, DescText)
}
}
Loading…
Cancel
Save