Showing
2 changed files
with
129 additions
and
64 deletions
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-06 16:29:31 | 2 | * @Date: 2022-09-06 16:29:31 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2022-09-07 00:46:48 | 4 | + * @LastEditTime: 2022-09-07 15:58:45 |
| 5 | * @FilePath: /data-table/src/components/SignField/index.vue | 5 | * @FilePath: /data-table/src/components/SignField/index.vue |
| 6 | - * @Description: 文件描述 | 6 | + * @Description: 电子签名控件 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <div class="sign-page"> | 9 | <div class="sign-page"> |
| 10 | <div class="label">{{ item.label }}<span v-if="item.required"> *</span></div> | 10 | <div class="label">{{ item.label }}<span v-if="item.required"> *</span></div> |
| 11 | - <div style="padding: 1rem;"> | 11 | + <div style="padding: 1rem; position: relative;"> |
| 12 | - | 12 | + <!-- <div style="padding: 1rem; position: relative; height: 150px; background-color: #FCFCFC;border: 1px solid #EAEAEA; border-radius: 5px;"> --> |
| 13 | - <vue-esign ref="esign" style="border: 1px dashed #c2c1c1;" :height="700" :isCrop="isCrop" :lineWidth="lineWidth" | 13 | + <vue-esign ref="esign" class="sign-wrapper" style="" :isCrop="isCrop" :lineWidth="lineWidth" |
| 14 | :lineColor="lineColor" :bgColor.sync="bgColor" /> | 14 | :lineColor="lineColor" :bgColor.sync="bgColor" /> |
| 15 | + <div v-if="show_sign" class="whiteboard"> | ||
| 16 | + <div class="text" @click="startSign"> | ||
| 17 | + <van-icon name="edit" /> 点击开始签署电子签名 | ||
| 18 | + </div> | ||
| 19 | + </div> | ||
| 20 | + </div> | ||
| 21 | + <div v-if="!show_sign"> | ||
| 22 | + <div v-if="show_control" class="control-sign"> | ||
| 23 | + <van-row gutter="20" style="padding: 0 1rem;"> | ||
| 24 | + <van-col :span="12"> | ||
| 25 | + <van-button type="default" block @click="handleGenerate">确认签名</van-button> | ||
| 26 | + </van-col> | ||
| 27 | + <van-col :span="12"> | ||
| 28 | + <van-button type="default" block @click="cancelSign">取消签名</van-button> | ||
| 29 | + </van-col> | ||
| 30 | + </van-row> | ||
| 31 | + </div> | ||
| 32 | + <div v-else style="padding: 0 1rem;"> | ||
| 33 | + <van-button type="danger" block @click="handleReset">删除签名</van-button> | ||
| 15 | </div> | 34 | </div> |
| 16 | - <div class="control-sign"> | ||
| 17 | - <button @click="handleReset">清空画板</button> | ||
| 18 | - <button @click="handleGenerate">生成图片</button> | ||
| 19 | </div> | 35 | </div> |
| 20 | </div> | 36 | </div> |
| 21 | </template> | 37 | </template> |
| 22 | 38 | ||
| 23 | <script setup> | 39 | <script setup> |
| 40 | +import { v4 as uuidv4 } from 'uuid'; | ||
| 41 | +import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' | ||
| 42 | +import { showSuccessToast, showFailToast } from 'vant'; | ||
| 43 | + | ||
| 24 | const props = defineProps({ | 44 | const props = defineProps({ |
| 25 | item: Object | 45 | item: Object |
| 26 | }); | 46 | }); |
| 27 | -</script> | ||
| 28 | 47 | ||
| 29 | -<script> | 48 | +const emit = defineEmits(['active']); |
| 30 | -import { v4 as uuidv4 } from 'uuid'; | ||
| 31 | -import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common' | ||
| 32 | 49 | ||
| 33 | -export default { | 50 | +const esign = ref(null); |
| 34 | - data() { | 51 | + |
| 35 | - return { | 52 | +const lineWidth = ref(6) |
| 36 | - lineWidth: 6, | 53 | +const lineColor = ref('#000000') |
| 37 | - lineColor: "#000000", | 54 | +const bgColor = ref('#FCFCFC') |
| 38 | - bgColor: "", | 55 | +const isCrop = ref(false) |
| 39 | - resultImg: "", | 56 | +const show_control = ref(true) |
| 40 | - isCrop: false, | 57 | + |
| 41 | - } | 58 | +const handleReset = () => { |
| 42 | - }, | 59 | + // 清空画板 |
| 43 | - methods: { | 60 | + esign.value.reset(); |
| 44 | - //清空画板.. | 61 | + show_control.value = true; |
| 45 | - handleReset() { | 62 | + // 删除可能存在的签名 |
| 46 | - this.$refs.esign.reset(); | 63 | + props.item.value = { key: 'sign', value: '' }; |
| 47 | - this.resultImg = ""; | 64 | + emit('active', props.item.value) |
| 48 | - }, | 65 | +} |
| 49 | - //生成签名图片.. | 66 | + |
| 50 | - handleGenerate () { | 67 | +const handleGenerate = () => { |
| 51 | - this.$refs.esign.generate() | 68 | + esign.value.generate() |
| 52 | .then(async res => { | 69 | .then(async res => { |
| 53 | // let fileName = "img1.png"; | 70 | // let fileName = "img1.png"; |
| 54 | // let file = this.dataURLtoFile(res, fileName); | 71 | // let file = this.dataURLtoFile(res, fileName); |
| ... | @@ -69,13 +86,23 @@ export default { | ... | @@ -69,13 +86,23 @@ export default { |
| 69 | if (filekey) { | 86 | if (filekey) { |
| 70 | // 保存图片 | 87 | // 保存图片 |
| 71 | const { data } = await saveFileAPI({ filekey, hash, format: image_info.format, height: image_info.height, width: image_info.width }); | 88 | const { data } = await saveFileAPI({ filekey, hash, format: image_info.format, height: image_info.height, width: image_info.width }); |
| 72 | - console.warn(data.src); | 89 | + props.item.value = { key: 'sign', value: data.src}; |
| 90 | + show_control.value = false; | ||
| 91 | + emit('active', props.item.value) | ||
| 73 | } | 92 | } |
| 74 | } | 93 | } |
| 75 | - }); | 94 | + }) |
| 76 | - }, | 95 | + .catch(err => { |
| 77 | - //将图片base64转换为文件 | 96 | + // 签名生成失败 |
| 78 | - dataURLtoFile(dataurl, filename) { | 97 | + console.warn(err); |
| 98 | + if (err) { | ||
| 99 | + showFailToast('签名生成失败'); | ||
| 100 | + } | ||
| 101 | + }) | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +//将图片base64转换为文件 | ||
| 105 | +const dataURLtoFile = (dataurl, filename) => { | ||
| 79 | var arr = dataurl.split(","), | 106 | var arr = dataurl.split(","), |
| 80 | mime = arr[0].match(/:(.*?);/)[1], | 107 | mime = arr[0].match(/:(.*?);/)[1], |
| 81 | bstr = atob(arr[1]), | 108 | bstr = atob(arr[1]), |
| ... | @@ -85,13 +112,21 @@ export default { | ... | @@ -85,13 +112,21 @@ export default { |
| 85 | u8arr[n] = bstr.charCodeAt(n); | 112 | u8arr[n] = bstr.charCodeAt(n); |
| 86 | } | 113 | } |
| 87 | return new File([u8arr], filename, { type: mime }); | 114 | return new File([u8arr], filename, { type: mime }); |
| 88 | - } | 115 | +} |
| 89 | - } | 116 | + |
| 117 | +const show_sign = ref(true); | ||
| 118 | +const startSign = () => { | ||
| 119 | + show_sign.value = false; | ||
| 120 | +} | ||
| 121 | +const cancelSign = () => { | ||
| 122 | + show_sign.value = true; | ||
| 123 | + handleReset() | ||
| 90 | } | 124 | } |
| 91 | </script> | 125 | </script> |
| 92 | 126 | ||
| 93 | <style lang="less" scoped> | 127 | <style lang="less" scoped> |
| 94 | .sign-page { | 128 | .sign-page { |
| 129 | + padding-bottom: 1rem; | ||
| 95 | .label { | 130 | .label { |
| 96 | padding: 1rem 1rem 0 1rem; | 131 | padding: 1rem 1rem 0 1rem; |
| 97 | font-size: 0.9rem; | 132 | font-size: 0.9rem; |
| ... | @@ -101,5 +136,25 @@ export default { | ... | @@ -101,5 +136,25 @@ export default { |
| 101 | color: red; | 136 | color: red; |
| 102 | } | 137 | } |
| 103 | } | 138 | } |
| 139 | + .sign-wrapper { | ||
| 140 | + border: 1px solid #EAEAEA; | ||
| 141 | + border-radius: 5px; | ||
| 142 | + } | ||
| 143 | + .whiteboard { | ||
| 144 | + position: absolute; | ||
| 145 | + height: 100%; | ||
| 146 | + width: 100%; | ||
| 147 | + left: 50%; | ||
| 148 | + top: 50%; | ||
| 149 | + transform: translate(-50%, -50%); | ||
| 150 | + text-align: center; | ||
| 151 | + .text { | ||
| 152 | + position: absolute; | ||
| 153 | + width: 100%; | ||
| 154 | + top: 50%; | ||
| 155 | + left: 50%; | ||
| 156 | + transform: translate(-50%, -50%); | ||
| 157 | + } | ||
| 158 | + } | ||
| 104 | } | 159 | } |
| 105 | </style> | 160 | </style> | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-07-18 10:22:22 | 2 | * @Date: 2022-07-18 10:22:22 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2022-09-07 14:12:37 | 4 | + * @LastEditTime: 2022-09-07 16:02:34 |
| 5 | * @FilePath: /data-table/src/views/index.vue | 5 | * @FilePath: /data-table/src/views/index.vue |
| 6 | * @Description: 首页 | 6 | * @Description: 首页 |
| 7 | --> | 7 | --> |
| ... | @@ -11,7 +11,7 @@ | ... | @@ -11,7 +11,7 @@ |
| 11 | <div class="table-box"> | 11 | <div class="table-box"> |
| 12 | <van-form @submit="onSubmit"> | 12 | <van-form @submit="onSubmit"> |
| 13 | <van-cell-group> | 13 | <van-cell-group> |
| 14 | - <component v-for="(item, index) in mockData" :key="index" :is="item.component" :item="item" /> | 14 | + <component v-for="(item, index) in mockData" :key="index" :is="item.component" :item="item" @active="onActive" /> |
| 15 | </van-cell-group> | 15 | </van-cell-group> |
| 16 | <div style="margin: 16px;"> | 16 | <div style="margin: 16px;"> |
| 17 | <van-button round block type="primary" native-type="submit"> | 17 | <van-button round block type="primary" native-type="submit"> |
| ... | @@ -24,37 +24,39 @@ | ... | @@ -24,37 +24,39 @@ |
| 24 | 24 | ||
| 25 | <script setup> | 25 | <script setup> |
| 26 | import { createComponentType } from '@/hooks/useComponentType' | 26 | import { createComponentType } from '@/hooks/useComponentType' |
| 27 | +import _ from 'lodash' | ||
| 27 | 28 | ||
| 28 | const table_cover = ref(''); | 29 | const table_cover = ref(''); |
| 29 | const table_title = ref(''); | 30 | const table_title = ref(''); |
| 30 | const mockData = ref([]); | 31 | const mockData = ref([]); |
| 32 | +const postData = ref({}) | ||
| 31 | 33 | ||
| 32 | onMounted(() => { | 34 | onMounted(() => { |
| 33 | table_cover.value = 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg' | 35 | table_cover.value = 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg' |
| 34 | table_title.value = '这是一个表单的描述' | 36 | table_title.value = '这是一个表单的描述' |
| 35 | mockData.value = [ | 37 | mockData.value = [ |
| 36 | - // { | 38 | + { |
| 37 | - // key: 'phone', | 39 | + key: 'phone', |
| 38 | - // value: '', | 40 | + value: '', |
| 39 | - // label: '手机号', | 41 | + label: '手机号', |
| 40 | - // placeholder: '请输入手机号', | 42 | + placeholder: '请输入手机号', |
| 41 | - // component: '', | 43 | + component: '', |
| 42 | - // component_props: { | 44 | + component_props: { |
| 43 | - // name: 'phone' | 45 | + name: 'phone' |
| 44 | - // }, | 46 | + }, |
| 45 | - // required: true, | 47 | + required: true, |
| 46 | - // }, | 48 | + }, |
| 47 | - // { | 49 | + { |
| 48 | - // key: 'username', | 50 | + key: 'username', |
| 49 | - // value: 'test', | 51 | + value: 'test', |
| 50 | - // label: '用户名', | 52 | + label: '用户名', |
| 51 | - // placeholder: '请输入用户名', | 53 | + placeholder: '请输入用户名', |
| 52 | - // component: '', | 54 | + component: '', |
| 53 | - // component_props: { | 55 | + component_props: { |
| 54 | - // name: 'text' | 56 | + name: 'text' |
| 55 | - // }, | 57 | + }, |
| 56 | - // required: true, | 58 | + required: true, |
| 57 | - // }, | 59 | + }, |
| 58 | // { | 60 | // { |
| 59 | // key: 'email', | 61 | // key: 'email', |
| 60 | // value: '', | 62 | // value: '', |
| ... | @@ -143,7 +145,7 @@ onMounted(() => { | ... | @@ -143,7 +145,7 @@ onMounted(() => { |
| 143 | placeholder: '', | 145 | placeholder: '', |
| 144 | component: '', | 146 | component: '', |
| 145 | component_props: { | 147 | component_props: { |
| 146 | - name: 'sign' | 148 | + name: 'sign', |
| 147 | }, | 149 | }, |
| 148 | }, | 150 | }, |
| 149 | // { | 151 | // { |
| ... | @@ -185,9 +187,17 @@ onMounted(() => { | ... | @@ -185,9 +187,17 @@ onMounted(() => { |
| 185 | }) | 187 | }) |
| 186 | 188 | ||
| 187 | const onSubmit = (values) => { | 189 | const onSubmit = (values) => { |
| 188 | - console.log('submit', values); | 190 | + // 合并自定义字段到提交表单字段 |
| 191 | + postData.value = _.assign(postData.value, values); | ||
| 192 | + console.warn(postData.value); | ||
| 189 | // console.warn(mockData.value); | 193 | // console.warn(mockData.value); |
| 190 | }; | 194 | }; |
| 195 | +const onActive = (item) => { | ||
| 196 | + // 返回自定义字段 | ||
| 197 | + if (item.key === 'sign') { | ||
| 198 | + postData.value['sign'] = item.value | ||
| 199 | + } | ||
| 200 | +} | ||
| 191 | </script> | 201 | </script> |
| 192 | 202 | ||
| 193 | <style lang="less" scoped> | 203 | <style lang="less" scoped> | ... | ... |
-
Please register or login to post a comment