feat: signature upload tab & i18n
This commit is contained in:
parent
e5859474be
commit
4809a081f6
3 changed files with 158 additions and 74 deletions
|
|
@ -1,24 +1,53 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import SignaturePad from 'signature_pad';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
defineExpose({ clearCanvas });
|
||||
defineExpose({ clearCanvas, clearUpload });
|
||||
|
||||
let canvasRef: HTMLCanvasElement | null = null;
|
||||
const $q = useQuasar();
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement>();
|
||||
const signaturePad = ref();
|
||||
const currentColor = ref('blue');
|
||||
const tab = ref('draw');
|
||||
|
||||
const initializeSignaturePad = () => {
|
||||
if (canvasRef) {
|
||||
signaturePad.value = new SignaturePad(canvasRef, {
|
||||
backgroundColor: 'rgb(248,249,250)',
|
||||
const isDarkActive = computed(() => $q.dark.isActive);
|
||||
|
||||
const uploadFile = ref<File | undefined>(undefined);
|
||||
const profileUrl = ref<string | null>('');
|
||||
const inputFile = (() => {
|
||||
const element = document.createElement('input');
|
||||
element.type = 'file';
|
||||
element.accept = 'image/*';
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => {
|
||||
if (typeof reader.result === 'string') profileUrl.value = reader.result;
|
||||
});
|
||||
|
||||
element.addEventListener('change', () => {
|
||||
uploadFile.value = element.files?.[0];
|
||||
if (uploadFile.value) {
|
||||
reader.readAsDataURL(uploadFile.value);
|
||||
}
|
||||
});
|
||||
|
||||
return element;
|
||||
})();
|
||||
|
||||
function initializeSignaturePad(canva?: HTMLCanvasElement) {
|
||||
if (canva) {
|
||||
signaturePad.value = new SignaturePad(canva, {
|
||||
backgroundColor: isDarkActive.value
|
||||
? 'rgb(21,25,29)'
|
||||
: 'rgb(248,249,250)',
|
||||
penColor: 'blue',
|
||||
});
|
||||
} else {
|
||||
console.warn('Canvas reference not found. SignaturePad not initialized.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function changeColor(color: string) {
|
||||
signaturePad.value.penColor = color;
|
||||
|
|
@ -29,9 +58,19 @@ function clearCanvas() {
|
|||
signaturePad.value.clear();
|
||||
}
|
||||
|
||||
function clearUpload() {
|
||||
profileUrl.value = '';
|
||||
}
|
||||
|
||||
watch(
|
||||
() => tab.value,
|
||||
async () => {
|
||||
initializeSignaturePad(canvasRef.value);
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
canvasRef = document.querySelector('canvas');
|
||||
initializeSignaturePad();
|
||||
initializeSignaturePad(canvasRef.value);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -44,76 +83,112 @@ onMounted(() => {
|
|||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
>
|
||||
<q-tab
|
||||
name="draw"
|
||||
label="Draw"
|
||||
style="border-top-left-radius: var(--radius-2)"
|
||||
/>
|
||||
<q-tab disable name="upload" label="Upload" />
|
||||
<div class="row justify-between full-width items-center">
|
||||
<div class="row">
|
||||
<q-tab
|
||||
name="draw"
|
||||
label="Draw"
|
||||
style="border-top-left-radius: var(--radius-2)"
|
||||
/>
|
||||
<q-tab name="upload" label="Upload" />
|
||||
</div>
|
||||
|
||||
<div class="q-pr-md">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
v-if="tab === 'upload'"
|
||||
:label="$t('newUpload')"
|
||||
color="info"
|
||||
@click="inputFile.click()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-tabs>
|
||||
<q-separator />
|
||||
|
||||
<q-tab-panels v-model="tab" animated>
|
||||
<q-tab-panel name="draw">
|
||||
<div class="column relative-position">
|
||||
<div
|
||||
class="absolute-top-right q-ma-md q-gutter-x-md row items-center"
|
||||
<div v-show="tab === 'draw'" class="q-pa-md">
|
||||
<div class="column relative-position">
|
||||
<div class="absolute-top-right q-ma-md q-gutter-x-md row items-center">
|
||||
<span
|
||||
class="dot"
|
||||
:class="{ active: currentColor === 'black' }"
|
||||
style="background-color: black"
|
||||
@click="changeColor('black')"
|
||||
>
|
||||
<span
|
||||
class="dot"
|
||||
:class="{ active: currentColor === 'black' }"
|
||||
style="background-color: black"
|
||||
@click="changeColor('black')"
|
||||
>
|
||||
<q-icon
|
||||
v-if="currentColor === 'black'"
|
||||
name="mdi-check"
|
||||
color="white"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
:class="{ active: currentColor === 'red' }"
|
||||
class="dot"
|
||||
style="background-color: red"
|
||||
@click="changeColor('red')"
|
||||
>
|
||||
<q-icon
|
||||
v-if="currentColor === 'red'"
|
||||
name="mdi-check"
|
||||
color="white"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
:class="{ active: currentColor === 'blue' }"
|
||||
class="dot"
|
||||
style="background-color: blue"
|
||||
@click="changeColor('blue')"
|
||||
>
|
||||
<q-icon
|
||||
v-if="currentColor === 'blue'"
|
||||
name="mdi-check"
|
||||
color="white"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<canvas
|
||||
class="signature-canvas"
|
||||
ref="canvasRef"
|
||||
id="signature-pad"
|
||||
width="700"
|
||||
height="300"
|
||||
></canvas>
|
||||
<q-icon
|
||||
v-if="currentColor === 'black'"
|
||||
name="mdi-check"
|
||||
color="white"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
:class="{ active: currentColor === 'red' }"
|
||||
class="dot"
|
||||
style="background-color: red"
|
||||
@click="changeColor('red')"
|
||||
>
|
||||
<q-icon
|
||||
v-if="currentColor === 'red'"
|
||||
name="mdi-check"
|
||||
color="white"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
:class="{ active: currentColor === 'blue' }"
|
||||
class="dot"
|
||||
style="background-color: blue"
|
||||
@click="changeColor('blue')"
|
||||
>
|
||||
<q-icon
|
||||
v-if="currentColor === 'blue'"
|
||||
name="mdi-check"
|
||||
color="white"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
|
||||
<!-- <div class="text-right q-gutter-x-md">
|
||||
<q-btn id="clear" label="Clear" @click="signaturePad.clear()" />
|
||||
</div> -->
|
||||
<canvas
|
||||
class="signature-canvas"
|
||||
ref="canvasRef"
|
||||
id="signature-pad"
|
||||
width="700"
|
||||
height="310"
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tab === 'upload'" class="q-pa-md">
|
||||
<div
|
||||
class="bordered upload-border rounded column items-center justify-center"
|
||||
style="height: 312px"
|
||||
>
|
||||
<q-img
|
||||
v-if="profileUrl"
|
||||
:src="profileUrl"
|
||||
style="object-fit: cover; width: 100%; height: 100%"
|
||||
/>
|
||||
<div v-else>
|
||||
<q-icon
|
||||
name="mdi-cloud-upload"
|
||||
size="10rem"
|
||||
style="color: hsla(var(--text-mute) / 0.2)"
|
||||
/>
|
||||
<div>
|
||||
<q-btn
|
||||
unelevated
|
||||
color="info"
|
||||
:label="$t('uploadFile')"
|
||||
icon="mdi-plus"
|
||||
@click="inputFile.click()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
|
|
@ -140,4 +215,9 @@ onMounted(() => {
|
|||
position: absolute;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upload-border {
|
||||
border-style: dashed;
|
||||
border-color: hsl(var(--info-bg));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ export default {
|
|||
theme: 'Theme',
|
||||
light: 'Light',
|
||||
dark: 'Dark',
|
||||
uploadFile: 'Upload File',
|
||||
newUpload: 'New Upload',
|
||||
baseOnDevice: 'Base on Device',
|
||||
...status,
|
||||
...main,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ export default {
|
|||
theme: 'ธีม',
|
||||
light: 'สว่าง',
|
||||
dark: 'มืด',
|
||||
uploadFile: 'อัปโหลดไฟล์',
|
||||
newUpload: 'อัปโหลดใหม่',
|
||||
baseOnDevice: 'สีตามอุปกรณ์',
|
||||
...status,
|
||||
...main,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue