mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Signature: add a signature zone manually
This commit is contained in:
parent
18af2ca70b
commit
c23568032c
@ -83,7 +83,7 @@ export interface PDFPage {
|
|||||||
height: number,
|
height: number,
|
||||||
}
|
}
|
||||||
export interface SignatureZone {
|
export interface SignatureZone {
|
||||||
index: number,
|
index: number | null,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
width: number,
|
width: number,
|
||||||
@ -98,3 +98,5 @@ export interface Signature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type SignedState = 'pending' | 'signed' | 'rejected' | 'canceled' | 'error';
|
export type SignedState = 'pending' | 'signed' | 'rejected' | 'canceled' | 'error';
|
||||||
|
|
||||||
|
export type CanvasEvent = 'select' | 'add';
|
||||||
|
@ -80,8 +80,32 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 p-4" id="action-buttons" v-if="signedState !== 'signed'">
|
<div class="col-12 p-4" id="action-buttons" v-if="signedState !== 'signed'">
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-12 d-flex justify-content-end">
|
||||||
|
<div class="col-4 col-xl-3 gap-2 d-grid">
|
||||||
|
<button
|
||||||
|
v-if="adding"
|
||||||
|
class="btn btn-misc btn-cancel me-2 btn-sm"
|
||||||
|
@click="removeNewZone()"
|
||||||
|
>
|
||||||
|
{{ $t("remove_sign_zone") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 gap-2 d-grid">
|
||||||
|
<button
|
||||||
|
class="btn btn-create btn-sm"
|
||||||
|
:class="{ active: canvasEvent === 'add' }"
|
||||||
|
@click="toggleAddZone()"
|
||||||
|
>
|
||||||
|
{{ $t("add_sign_zone") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-4">
|
||||||
<button
|
<button
|
||||||
class="btn btn-action me-2"
|
class="btn btn-action me-2"
|
||||||
:disabled="!userSignatureZone"
|
:disabled="!userSignatureZone"
|
||||||
@ -90,7 +114,7 @@
|
|||||||
{{ $t("sign") }}
|
{{ $t("sign") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 d-flex justify-content-end">
|
<div class="col-8 d-flex justify-content-end">
|
||||||
<button
|
<button
|
||||||
class="btn btn-misc me-2"
|
class="btn btn-misc me-2"
|
||||||
:hidden="!userSignatureZone"
|
:hidden="!userSignatureZone"
|
||||||
@ -119,7 +143,12 @@
|
|||||||
import { ref, Ref, reactive } from "vue";
|
import { ref, Ref, reactive } from "vue";
|
||||||
import { useToast } from "vue-toast-notification";
|
import { useToast } from "vue-toast-notification";
|
||||||
import "vue-toast-notification/dist/theme-sugar.css";
|
import "vue-toast-notification/dist/theme-sugar.css";
|
||||||
import { Signature, SignatureZone, SignedState } from "../../types";
|
import {
|
||||||
|
CanvasEvent,
|
||||||
|
Signature,
|
||||||
|
SignatureZone,
|
||||||
|
SignedState,
|
||||||
|
} from "../../types";
|
||||||
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||||
import * as pdfjsLib from "pdfjs-dist";
|
import * as pdfjsLib from "pdfjs-dist";
|
||||||
import {
|
import {
|
||||||
@ -143,11 +172,12 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";
|
|||||||
|
|
||||||
const modalOpen: Ref<boolean> = ref(false);
|
const modalOpen: Ref<boolean> = ref(false);
|
||||||
const loading: Ref<boolean> = ref(false);
|
const loading: Ref<boolean> = ref(false);
|
||||||
|
const adding: Ref<boolean> = ref(false);
|
||||||
|
const canvasEvent: Ref<CanvasEvent> = ref("select");
|
||||||
const signedState: Ref<SignedState> = ref("pending");
|
const signedState: Ref<SignedState> = ref("pending");
|
||||||
const page: Ref<number> = ref(1);
|
const page: Ref<number> = ref(1);
|
||||||
const pageCount: Ref<number> = ref(0);
|
const pageCount: Ref<number> = ref(0);
|
||||||
let userSignatureZone: Ref<null | SignatureZone> = ref(null);
|
let userSignatureZone: Ref<null | SignatureZone> = ref(null);
|
||||||
let pdfSource: Ref<string> = ref("");
|
|
||||||
let pdf = {} as PDFDocumentProxy;
|
let pdf = {} as PDFDocumentProxy;
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -160,6 +190,8 @@ const $toast = useToast();
|
|||||||
|
|
||||||
const signature = window.signature;
|
const signature = window.signature;
|
||||||
|
|
||||||
|
console.log(signature);
|
||||||
|
|
||||||
const mountPdf = async (url: string) => {
|
const mountPdf = async (url: string) => {
|
||||||
const loadingTask = pdfjsLib.getDocument(url);
|
const loadingTask = pdfjsLib.getDocument(url);
|
||||||
pdf = await loadingTask.promise;
|
pdf = await loadingTask.promise;
|
||||||
@ -202,33 +234,31 @@ async function downloadAndOpen(): Promise<Blob> {
|
|||||||
|
|
||||||
const initPdf = () => {
|
const initPdf = () => {
|
||||||
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
|
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
|
||||||
canvas.addEventListener(
|
canvas.addEventListener("pointerup", canvasClick, false);
|
||||||
"pointerup",
|
|
||||||
(e: PointerEvent) => canvasClick(e, canvas),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
setTimeout(() => addZones(page.value), 800);
|
setTimeout(() => addZones(page.value), 800);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const scaleXToCanvas = (x: number, canvasWidth: number, PDFWidth: number) =>
|
||||||
|
Math.round((x * canvasWidth) / PDFWidth);
|
||||||
|
|
||||||
|
const scaleYToCanvas = (h: number, canvasHeight: number, PDFHeight: number) =>
|
||||||
|
Math.round((h * canvasHeight) / PDFHeight);
|
||||||
|
|
||||||
const hitSignature = (
|
const hitSignature = (
|
||||||
zone: SignatureZone,
|
zone: SignatureZone,
|
||||||
xy: number[],
|
xy: number[],
|
||||||
canvasWidth: number,
|
canvasWidth: number,
|
||||||
canvasHeight: number
|
canvasHeight: number
|
||||||
) => {
|
) =>
|
||||||
const scaleXToCanvas = (x: number) =>
|
scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width) < xy[0] &&
|
||||||
Math.round((x * canvasWidth) / zone.PDFPage.width);
|
xy[0] <
|
||||||
const scaleHeightToCanvas = (h: number) =>
|
scaleXToCanvas(zone.x + zone.width, canvasWidth, zone.PDFPage.width) &&
|
||||||
Math.round((h * canvasHeight) / zone.PDFPage.height);
|
zone.PDFPage.height -
|
||||||
const scaleYToCanvas = (y: number) =>
|
scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height) <
|
||||||
Math.round(zone.PDFPage.height - scaleHeightToCanvas(y));
|
xy[1] &&
|
||||||
return (
|
xy[1] <
|
||||||
scaleXToCanvas(zone.x) < xy[0] &&
|
scaleYToCanvas(zone.height - zone.y, canvasHeight, zone.PDFPage.height) +
|
||||||
xy[0] < scaleXToCanvas(zone.x + zone.width) &&
|
zone.PDFPage.height;
|
||||||
scaleYToCanvas(zone.y) < xy[1] &&
|
|
||||||
xy[1] < scaleYToCanvas(zone.y) + scaleHeightToCanvas(zone.height)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectZone = (z: SignatureZone, canvas: HTMLCanvasElement) => {
|
const selectZone = (z: SignatureZone, canvas: HTMLCanvasElement) => {
|
||||||
userSignatureZone.value = z;
|
userSignatureZone.value = z;
|
||||||
@ -239,7 +269,7 @@ const selectZone = (z: SignatureZone, canvas: HTMLCanvasElement) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const canvasClick = (e: PointerEvent, canvas: HTMLCanvasElement) =>
|
const selectZoneOnCanvas = (e: PointerEvent, canvas: HTMLCanvasElement) =>
|
||||||
signature.zones
|
signature.zones
|
||||||
.filter((z) => z.PDFPage.index + 1 === page.value)
|
.filter((z) => z.PDFPage.index + 1 === page.value)
|
||||||
.map((z) => {
|
.map((z) => {
|
||||||
@ -256,6 +286,13 @@ const canvasClick = (e: PointerEvent, canvas: HTMLCanvasElement) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const canvasClick = (e: PointerEvent) => {
|
||||||
|
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
|
||||||
|
canvasEvent.value === "select"
|
||||||
|
? selectZoneOnCanvas(e, canvas)
|
||||||
|
: addZoneOnCanvas(e, canvas);
|
||||||
|
};
|
||||||
|
|
||||||
const turnPage = async (upOrDown: number) => {
|
const turnPage = async (upOrDown: number) => {
|
||||||
userSignatureZone.value = null;
|
userSignatureZone.value = null;
|
||||||
page.value = page.value + upOrDown;
|
page.value = page.value + upOrDown;
|
||||||
@ -290,12 +327,6 @@ const drawZone = (
|
|||||||
) => {
|
) => {
|
||||||
const unselectedBlue = "#007bff";
|
const unselectedBlue = "#007bff";
|
||||||
const selectedBlue = "#034286";
|
const selectedBlue = "#034286";
|
||||||
const scaleXToCanvas = (x: number) =>
|
|
||||||
Math.round((x * canvasWidth) / zone.PDFPage.width);
|
|
||||||
const scaleHeightToCanvas = (h: number) =>
|
|
||||||
Math.round((h * canvasHeight) / zone.PDFPage.height);
|
|
||||||
const scaleYToCanvas = (y: number) =>
|
|
||||||
Math.round(zone.PDFPage.height - scaleHeightToCanvas(y));
|
|
||||||
ctx.strokeStyle =
|
ctx.strokeStyle =
|
||||||
userSignatureZone.value?.index === zone.index
|
userSignatureZone.value?.index === zone.index
|
||||||
? selectedBlue
|
? selectedBlue
|
||||||
@ -303,16 +334,22 @@ const drawZone = (
|
|||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
ctx.lineJoin = "bevel";
|
ctx.lineJoin = "bevel";
|
||||||
ctx.strokeRect(
|
ctx.strokeRect(
|
||||||
scaleXToCanvas(zone.x),
|
scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width),
|
||||||
scaleYToCanvas(zone.y),
|
zone.PDFPage.height -
|
||||||
scaleXToCanvas(zone.width),
|
scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height),
|
||||||
scaleHeightToCanvas(zone.height)
|
scaleXToCanvas(zone.width, canvasWidth, zone.PDFPage.width),
|
||||||
|
scaleYToCanvas(zone.height, canvasHeight, zone.PDFPage.height)
|
||||||
);
|
);
|
||||||
ctx.font = "bold 16px serif";
|
ctx.font = "bold 16px serif";
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
ctx.fillStyle = "black";
|
ctx.fillStyle = "black";
|
||||||
const xText = scaleXToCanvas(zone.x) + scaleXToCanvas(zone.width) / 2;
|
const xText =
|
||||||
const yText = scaleYToCanvas(zone.y) + scaleHeightToCanvas(zone.height) / 2;
|
scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width) +
|
||||||
|
scaleXToCanvas(zone.width, canvasWidth, zone.PDFPage.width) / 2;
|
||||||
|
const yText =
|
||||||
|
zone.PDFPage.height -
|
||||||
|
scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height) +
|
||||||
|
scaleYToCanvas(zone.height, canvasHeight, zone.PDFPage.height) / 2;
|
||||||
if (userSignatureZone.value?.index === zone.index) {
|
if (userSignatureZone.value?.index === zone.index) {
|
||||||
ctx.fillStyle = selectedBlue;
|
ctx.fillStyle = selectedBlue;
|
||||||
ctx.fillText("Signer ici", xText, yText);
|
ctx.fillText("Signer ici", xText, yText);
|
||||||
@ -414,14 +451,58 @@ const confirmSign = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const undoSign = async () => {
|
const undoSign = async () => {
|
||||||
// const canvas = document.querySelectorAll("canvas")[0];
|
signature.zones = signature.zones.filter((z) => z.index !== null);
|
||||||
// const ctx = canvas.getContext("2d");
|
|
||||||
// if (ctx && userSignatureZone.value) {
|
|
||||||
// //drawZone(userSignatureZone.value, ctx, canvas.width, canvas.height);
|
|
||||||
// }
|
|
||||||
await setPage(page.value);
|
await setPage(page.value);
|
||||||
setTimeout(() => addZones(page.value), 200);
|
setTimeout(() => addZones(page.value), 200);
|
||||||
userSignatureZone.value = null;
|
userSignatureZone.value = null;
|
||||||
|
adding.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleAddZone = () => {
|
||||||
|
canvasEvent.value === "select"
|
||||||
|
? (canvasEvent.value = "add")
|
||||||
|
: (canvasEvent.value = "select");
|
||||||
|
};
|
||||||
|
|
||||||
|
const addZoneOnCanvas = (e: PointerEvent, canvas: HTMLCanvasElement) => {
|
||||||
|
const BOX_WIDTH = 180;
|
||||||
|
const BOX_HEIGHT = 90;
|
||||||
|
const PDFPageHeight = canvas.height;
|
||||||
|
const PDFPageWidth = canvas.width;
|
||||||
|
|
||||||
|
const x = e.offsetX;
|
||||||
|
const y = e.offsetY;
|
||||||
|
const newZone: SignatureZone = {
|
||||||
|
index: null,
|
||||||
|
x:
|
||||||
|
scaleXToCanvas(x, canvas.width, PDFPageWidth) -
|
||||||
|
scaleXToCanvas(BOX_WIDTH / 2, canvas.width, PDFPageWidth),
|
||||||
|
y:
|
||||||
|
PDFPageHeight -
|
||||||
|
scaleYToCanvas(y, canvas.height, PDFPageHeight) +
|
||||||
|
scaleYToCanvas(BOX_HEIGHT / 2, canvas.height, PDFPageHeight),
|
||||||
|
width: BOX_WIDTH,
|
||||||
|
height: BOX_HEIGHT,
|
||||||
|
PDFPage: {
|
||||||
|
index: page.value - 1,
|
||||||
|
width: PDFPageWidth,
|
||||||
|
height: PDFPageHeight,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
signature.zones.push(newZone);
|
||||||
|
|
||||||
|
setTimeout(() => addZones(page.value), 200);
|
||||||
|
canvasEvent.value = "select";
|
||||||
|
adding.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeNewZone = async () => {
|
||||||
|
signature.zones = signature.zones.filter((z) => z.index !== null);
|
||||||
|
userSignatureZone.value = null;
|
||||||
|
await setPage(page.value);
|
||||||
|
setTimeout(() => addZones(page.value), 200);
|
||||||
|
canvasEvent.value = "select";
|
||||||
|
adding.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
downloadAndOpen();
|
downloadAndOpen();
|
||||||
|
@ -16,7 +16,9 @@ const appMessages = {
|
|||||||
last_sign_zone: 'Zone de signature précédente',
|
last_sign_zone: 'Zone de signature précédente',
|
||||||
next_sign_zone: 'Zone de signature suivante',
|
next_sign_zone: 'Zone de signature suivante',
|
||||||
electronic_signature_in_progress: 'Signature électronique en cours...',
|
electronic_signature_in_progress: 'Signature électronique en cours...',
|
||||||
loading: 'Chargement...'
|
loading: 'Chargement...',
|
||||||
|
add_sign_zone: 'Ajouter une zone de signature',
|
||||||
|
remove_sign_zone: 'Enlever la zone',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user