You need to sign in or sign up before continuing.
index.vue 5.3 KB
<!--
 * @Date: 2022-09-06 16:29:31
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2022-09-07 16:50:16
 * @FilePath: /data-table/src/components/SignField/index.vue
 * @Description: 电子签名控件
-->
<template>
  <div class="sign-page">
    <div class="label">{{ item.label }}{{ valid }}<span v-if="item.required">&nbsp;*</span></div>
    <div style="padding: 1rem; position: relative;">
      <!-- <div style="padding: 1rem; position: relative; height: 150px; background-color: #FCFCFC;border: 1px solid #EAEAEA; border-radius: 5px;"> -->
      <vue-esign ref="esign" class="sign-wrapper" style="" :isCrop="isCrop" :lineWidth="lineWidth"
        :lineColor="lineColor" :bgColor.sync="bgColor" />
      <div v-if="show_sign" class="whiteboard">
        <div class="text" @click="startSign">
          <van-icon name="edit" />&nbsp;点击开始签署电子签名
        </div>
      </div>
    </div>
    <div v-if="!show_sign">
      <div v-if="show_control" class="control-sign">
        <van-row gutter="20" style="padding: 0 1rem;">
          <van-col :span="12">
            <van-button type="default" block @click="handleGenerate">确认签名</van-button>
          </van-col>
          <van-col :span="12">
            <van-button type="default" block @click="cancelSign">取消签名</van-button>
          </van-col>
        </van-row>
      </div>
      <div v-else style="padding: 0 1rem;">
        <van-button type="danger" block @click="handleReset">删除签名</van-button>
      </div>
    </div>
    <div v-if="show_empty" class="van-field__error-message" style="padding: 0 1rem 1rem 1rem;">电子签名不能为空</div>
  </div>
</template>

<script setup>
import { v4 as uuidv4 } from 'uuid';
import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common'
import { showSuccessToast, showFailToast } from 'vant';

const props = defineProps({
  item: Object
});

const emit = defineEmits(['active']);

const esign = ref(null);

const lineWidth = ref(6)
const lineColor = ref('#000000')
const bgColor = ref('#FCFCFC')
const isCrop = ref(false)
const show_control = ref(true)
const image_url = ref('')
const show_empty = ref(false)

const handleReset = () => {
  // 清空画板
  esign.value.reset();
  show_control.value = true;
  // 删除可能存在的签名
  image_url.value = ''
  props.item.value = { key: 'sign', value: '' };
  emit('active', props.item.value)
}

const handleGenerate = () => {
  esign.value.generate()
  .then(async res => {
    // let fileName = "img1.png";
    // let file = this.dataURLtoFile(res, fileName);
    // console.log("file", file);
    let affix = uuidv4();
    let base64url = res.slice(res.indexOf(',') + 1);  // 截取前缀的base64   data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnoAAAJeCAYAA.......
    // 获取七牛token
    const { token, key, code } = await qiniuTokenAPI({ filename: `${affix}_sign`, file: base64url });
    if (code) {
      const config = {
        headers: {
          'Content-Type': 'application/octet-stream',
          'Authorization': 'UpToken ' + token,    // UpToken后必须有一个 ' '(空格)
        }
      }
      // 上传七牛服务器
      const { filekey, hash, image_info } = await qiniuUploadAPI('http://upload.qiniup.com/putb64/-1/key/' + key, base64url, config)
      if (filekey) {
        // 保存图片
        const { data } = await saveFileAPI({ filekey, hash, format: image_info.format, height: image_info.height, width: image_info.width });
        props.item.value = { key: 'sign', value: data.src};
        image_url.value = data.src;
        show_control.value = false;
        show_empty.value = false;
        emit('active', props.item.value)
      }
    }
  })
  .catch(err => {
    // 签名生成失败
    console.warn(err);
    if (err) {
      showFailToast('签名生成失败');
    }
  })
}

//将图片base64转换为文件
const dataURLtoFile = (dataurl, filename) => {
  var arr = dataurl.split(","),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
}

const show_sign = ref(true);
const startSign = () => {
  show_sign.value = false;
  show_empty.value = false;
}
const cancelSign = () => {
  show_sign.value = true;
  show_empty.value = false;
  handleReset()
}

const validSign = () => {
  // 必填项 未生成签名
  if (props.item.component_props.required && !image_url.value) {
    show_empty.value = true;
  } else {
    show_empty.value = false;
  }
  return !show_empty.value
}

defineExpose({ validSign });
</script>

<!-- <script>
export default {
  methods: {
    validSign () {
      console.warn(0);
    }
  }
}
</script> -->


<style lang="less" scoped>
.sign-page {
  // padding-bottom: 1rem;
  .label {
    padding: 1rem 1rem 0 1rem;
    font-size: 0.9rem;
    font-weight: bold;

    span {
      color: red;
    }
  }
  .sign-wrapper {
    border: 1px solid #EAEAEA;
    border-radius: 5px;
  }
  .whiteboard {
    position: absolute;
    height: 100%;
    width: 100%;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    .text {
      position: absolute;
      width: 100%;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  }

  .control-sign {
    padding-bottom: 1rem;
  }
}
</style>