feat[preview]: 适配签名组件
- 适配签名组件 PreviewSign
This commit is contained in:
@@ -1,12 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref, useTemplateRef, toRefs } from 'vue';
|
import { computed, onMounted, ref, useTemplateRef } from 'vue';
|
||||||
const props = defineProps<{
|
import CommonApi from '@/api/common.js';
|
||||||
element: any;
|
// question 属性
|
||||||
active: boolean;
|
const element = defineModel<question>('element', { default: {} });
|
||||||
index: number;
|
// 属性框是否激活
|
||||||
}>();
|
const active = defineModel<boolean>('active', { default: false });
|
||||||
|
// 题目索引
|
||||||
const { element, active } = toRefs(props);
|
const index = defineModel<number>('index', { default: 0 });
|
||||||
|
// 答案
|
||||||
|
const answer = defineModel<string>('answer', { default: '' });
|
||||||
|
// 错误信息
|
||||||
|
const errorMessage = defineModel<string>('errorMessage', { default: '' });
|
||||||
|
|
||||||
const signatureCanvas = useTemplateRef('signatureCanvas');
|
const signatureCanvas = useTemplateRef('signatureCanvas');
|
||||||
|
|
||||||
@@ -54,10 +58,7 @@ const togglePen = () => {
|
|||||||
isEraser.value = !isEraser.value;
|
isEraser.value = !isEraser.value;
|
||||||
setPenStyle();
|
setPenStyle();
|
||||||
};
|
};
|
||||||
const errorMessage = defineModel('errorMessage', {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
});
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!signatureCanvas.value) return;
|
if (!signatureCanvas.value) return;
|
||||||
// 将 canvas 宽度和窗口的宽度保持一致
|
// 将 canvas 宽度和窗口的宽度保持一致
|
||||||
@@ -146,10 +147,24 @@ const clearCanvas = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存画布
|
* 保存画布
|
||||||
|
* @param type {'dataUrl' | 'blob'} 保存类型
|
||||||
|
* @return {string | Blob | undefined | null}
|
||||||
*/
|
*/
|
||||||
const saveCanvas = () => {
|
const saveCanvas = (type: 'dataUrl' | 'blob'): string | Blob | undefined | null => {
|
||||||
|
let img: string | Blob | undefined | null = undefined;
|
||||||
if (!ctx || !signatureCanvas.value) return;
|
if (!ctx || !signatureCanvas.value) return;
|
||||||
return signatureCanvas.value.toDataURL('image/png');
|
if (type === 'blob') {
|
||||||
|
signatureCanvas.value.toBlob(
|
||||||
|
(blob) => {
|
||||||
|
img = blob;
|
||||||
|
},
|
||||||
|
'image/png',
|
||||||
|
{ quality: 0.9 }
|
||||||
|
);
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'dataUrl') return signatureCanvas.value.toDataURL('image/png');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,10 +183,19 @@ const emit = defineEmits(['update:element']);
|
|||||||
const emitValue = () => {
|
const emitValue = () => {
|
||||||
emit('update:element', element.value);
|
emit('update:element', element.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
*/
|
||||||
|
async function handleUploadImg() {
|
||||||
|
const file = new File([saveCanvas('blob')!], 'sign.png', { type: 'image/png' });
|
||||||
|
const { url } = await CommonApi.cosUpload(file);
|
||||||
|
// 传递答案
|
||||||
|
answer.value = url;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- <van-cell>-->
|
|
||||||
<van-field
|
<van-field
|
||||||
:label="element.stem"
|
:label="element.stem"
|
||||||
:required="element.config.is_required === 1"
|
:required="element.config.is_required === 1"
|
||||||
@@ -211,13 +235,12 @@ const emitValue = () => {
|
|||||||
:class="isEraser ? 'mobilefont-huabi' : 'mobilefont-rubber'"
|
:class="isEraser ? 'mobilefont-huabi' : 'mobilefont-rubber'"
|
||||||
@click="togglePen"
|
@click="togglePen"
|
||||||
></span>
|
></span>
|
||||||
<span class="icon mobilefont mobilefont-shangchuan" @click="saveCanvas"></span>
|
<span class="icon mobilefont mobilefont-shangchuan" @click="handleUploadImg"></span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="sign-tips">请在空白区域书写您的签名</div>
|
<div v-else class="sign-tips">请在空白区域书写您的签名</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
<!-- </van-cell>-->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -323,14 +323,15 @@
|
|||||||
<!-- v-model:answer="question.answer"-->
|
<!-- v-model:answer="question.answer"-->
|
||||||
<!-- isMobile-->
|
<!-- isMobile-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- <!– 签名题 –>-->
|
<!-- 签名题 -->
|
||||||
<!-- <q-sign-->
|
<preview-sign
|
||||||
<!-- v-else-if="question.question_type === 22"-->
|
v-else-if="question.question_type === 22"
|
||||||
<!-- :config="question.config"-->
|
:config="question.config"
|
||||||
<!-- :question="question"-->
|
:question="question"
|
||||||
<!-- v-model:answer="question.answer"-->
|
v-model:answer="question.answer"
|
||||||
<!-- isMobile-->
|
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
||||||
<!-- />-->
|
@change-answer="onRelation($event, question)"
|
||||||
|
/>
|
||||||
<!-- <!– 知情同意书 –>-->
|
<!-- <!– 知情同意书 –>-->
|
||||||
<!-- <q-consent-->
|
<!-- <q-consent-->
|
||||||
<!-- v-else-if="question.question_type === 23"-->
|
<!-- v-else-if="question.question_type === 23"-->
|
||||||
@@ -540,6 +541,7 @@ import { getQuestionIndex } from '@/utils/utils.js';
|
|||||||
import icon from '@/assets/img/create-right-back.png';
|
import icon from '@/assets/img/create-right-back.png';
|
||||||
import PreviewCheckbox from '@/views/Survey/views/Preview/components/questions/PreviewCheckbox.vue';
|
import PreviewCheckbox from '@/views/Survey/views/Preview/components/questions/PreviewCheckbox.vue';
|
||||||
import PreviewRate from '@/views/Survey/views/Preview/components/questions/PreviewRate.vue';
|
import PreviewRate from '@/views/Survey/views/Preview/components/questions/PreviewRate.vue';
|
||||||
|
import PreviewSign from '@/views/Survey/views/Preview/components/questions/PreviewSign.vue';
|
||||||
const isPreview = defineModel('isPreview', {
|
const isPreview = defineModel('isPreview', {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import SignQuestion from '@/views/Design/components/Questions/SignQuestion.vue';
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const question = defineModel<question>('question', { default: {} });
|
||||||
|
const answer = defineModel<{ value: string }>('answer', { default: undefined });
|
||||||
|
const answerValue = ref<string>('');
|
||||||
|
const answerIndex = defineModel<number>('answerIndex', { default: 0 });
|
||||||
|
// emit
|
||||||
|
const emit = defineEmits(['changeAnswer']);
|
||||||
|
|
||||||
|
// 如果 answer 不为空,需要解析答案
|
||||||
|
answer.value?.value && parseAnswer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听答案,如果答案变动,重新生成答案,然后提交
|
||||||
|
*/
|
||||||
|
watch(answerValue, (newValue) => {
|
||||||
|
emit('changeAnswer', { value: newValue });
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* 解析答案
|
||||||
|
* answer 格式
|
||||||
|
* {
|
||||||
|
* value: 'url'
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function parseAnswer() {
|
||||||
|
answerValue.value = answer.value.value;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<sign-question
|
||||||
|
:element="question"
|
||||||
|
:active="false"
|
||||||
|
:index="answerIndex"
|
||||||
|
v-model:answer="answerValue"
|
||||||
|
:error-message="question.error"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
Reference in New Issue
Block a user