tantee-nuxt-commons 0.0.169 → 0.0.170
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/Alert.vue +0 -1
- package/dist/runtime/components/form/ActionPad.vue +1 -1
- package/dist/runtime/components/form/Dialog.vue +1 -1
- package/dist/runtime/components/form/EditPad.vue +1 -1
- package/dist/runtime/components/form/File.vue +189 -126
- package/dist/runtime/components/form/Iterator.vue +2 -0
- package/dist/runtime/components/form/Pad.vue +3 -72
- package/dist/runtime/components/form/SignPad.vue +126 -141
- package/dist/runtime/components/form/images/Field.vue +197 -168
- package/dist/runtime/components/label/DateCount.vue +13 -117
- package/dist/runtime/components/pdf/View.vue +32 -42
- package/dist/runtime/composables/assetFile.d.ts +31 -0
- package/dist/runtime/composables/assetFile.js +134 -0
- package/dist/runtime/composables/document/templateFormTable.js +1 -4
- package/package.json +1 -1
|
@@ -1,139 +1,160 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { VueSignaturePad } from 'vue-signature-pad'
|
|
2
|
+
import { VueSignaturePad } from 'vue-signature-pad'
|
|
3
3
|
import { VInput } from 'vuetify/components/VInput'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
interface SignatureOptions {
|
|
7
|
-
penColor: string
|
|
8
|
-
minWidth?: number
|
|
9
|
-
maxWidth?: number
|
|
10
|
-
}
|
|
4
|
+
import { ref, computed, withDefaults, defineProps, defineExpose, watch } from 'vue'
|
|
5
|
+
import { useAssetFile, type Base64Asset, type Base64File } from '../../composables/assetFile'
|
|
11
6
|
|
|
12
7
|
interface SignatureProps extends /* @vue-ignore */ InstanceType<typeof VInput['$props']> {
|
|
13
8
|
title?: string
|
|
14
9
|
btnName?: string
|
|
15
10
|
titleConfirm?: string
|
|
16
|
-
|
|
11
|
+
penColor?: string
|
|
12
|
+
/** hydrate when id is present but base64String is missing */
|
|
13
|
+
autoHydrate?: boolean
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
const props = withDefaults(defineProps<SignatureProps>(), {
|
|
20
|
-
title: '
|
|
17
|
+
title: 'Signature',
|
|
21
18
|
btnName: 'Draw Your Signature',
|
|
22
19
|
titleConfirm: 'I Accept My Signature',
|
|
20
|
+
penColor: '#303F9F',
|
|
21
|
+
autoHydrate: true,
|
|
23
22
|
})
|
|
24
23
|
|
|
25
|
-
const
|
|
26
|
-
const signaturePadRef = useTemplateRef<typeof VueSignaturePad>("signaturePadRef")
|
|
24
|
+
const { hydrateAssetFile } = useAssetFile()
|
|
27
25
|
|
|
28
|
-
const
|
|
29
|
-
const colorOptions: string[] = ['#303F9F', '#1A2023', '#2E7D32', '#AC04BF']
|
|
30
|
-
const defaultColor: string = colorOptions[0]
|
|
31
|
-
const selectedColor: Ref<string> = ref(defaultColor)
|
|
32
|
-
const signatureOptions: Ref<SignatureOptions> = ref({ penColor: defaultColor, minWidth: 0.5, maxWidth: 4 })
|
|
33
|
-
const isDialogOpen: Ref<boolean> = ref(false)
|
|
26
|
+
const model = defineModel<Base64Asset | string | null>({ default: null })
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
// refs
|
|
29
|
+
const inputRef = ref<InstanceType<typeof VInput> | null>(null)
|
|
30
|
+
const signaturePadRef = ref<InstanceType<typeof VueSignaturePad> | null>(null)
|
|
38
31
|
|
|
39
|
-
const
|
|
40
|
-
signaturePadRef.value?.clearSignature()
|
|
41
|
-
}
|
|
32
|
+
const isDialogOpen = ref(false)
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
// signature pad options from props
|
|
35
|
+
const signatureOptions = computed(() => ({
|
|
36
|
+
penColor: props.penColor,
|
|
37
|
+
minWidth: 0.5,
|
|
38
|
+
maxWidth: 4,
|
|
39
|
+
}))
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
// internal normalized value
|
|
42
|
+
const normalized = ref<Base64Asset | null>(null)
|
|
43
|
+
const imageDataUrl = computed(() => {
|
|
44
|
+
const val = normalized.value
|
|
45
|
+
if (!val?.base64String) return null
|
|
52
46
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const { isEmpty, data } = signaturePadRef.value?.saveSignature()
|
|
56
|
-
signatureData.value = isEmpty ? null : data
|
|
57
|
-
}
|
|
47
|
+
return useAssetFile().ensureDataUrl(val.base64String.trim(),(val as Base64File).fileType || "image/png")
|
|
48
|
+
})
|
|
58
49
|
|
|
59
|
-
const changePenColor = (color: string): void => {
|
|
60
|
-
selectedColor.value = color
|
|
61
|
-
signatureOptions.value = { penColor: color }
|
|
62
|
-
}
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
signatureOptions.value = { penColor: defaultColor }
|
|
67
|
-
isDialogOpen.value = true
|
|
68
|
-
nextTick(() =>{
|
|
69
|
-
if (props.modelValue) {
|
|
70
|
-
signaturePadRef.value?.fromDataURL(props.modelValue)
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
51
|
+
// guards
|
|
52
|
+
let syncing = false // block re-entrancy while normalizing/hydrating
|
|
74
53
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
signaturePadHeight.value = `${screenHeight * 0.4}px`
|
|
54
|
+
function wrapToAsset(input: Base64Asset | string | null): Base64Asset | null {
|
|
55
|
+
if (input == null) return null
|
|
56
|
+
return typeof input === 'string' ? { base64String: input } : input
|
|
79
57
|
}
|
|
80
58
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
59
|
+
/** Normalize & (optionally) hydrate whenever external model changes */
|
|
60
|
+
watch(
|
|
61
|
+
model,
|
|
62
|
+
async (val) => {
|
|
63
|
+
if (syncing) return
|
|
64
|
+
syncing = true
|
|
65
|
+
try {
|
|
66
|
+
const asAsset = wrapToAsset(val)
|
|
67
|
+
normalized.value = asAsset
|
|
68
|
+
|
|
69
|
+
// If parent provided a string, convert and write back once
|
|
70
|
+
if (typeof val === 'string') {
|
|
71
|
+
model.value = asAsset
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Hydrate if requested and needed (id present, no base64String)
|
|
75
|
+
if (props.autoHydrate && asAsset?.id != null && !asAsset.base64String) {
|
|
76
|
+
await hydrateAssetFile(asAsset) // mutates asAsset in-place
|
|
77
|
+
// reflect hydrated base64String back to parent (guarded)
|
|
78
|
+
model.value = asAsset
|
|
79
|
+
}
|
|
80
|
+
} finally {
|
|
81
|
+
syncing = false
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{ immediate: true }
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
// signature actions
|
|
88
|
+
const undoSignature = () => signaturePadRef.value?.undoSignature()
|
|
89
|
+
const clearSignature = () => signaturePadRef.value?.clearSignature()
|
|
90
|
+
|
|
91
|
+
const closeDialog = () => {
|
|
92
|
+
isDialogOpen.value = false
|
|
93
|
+
signaturePadRef.value?.clearSignature()
|
|
94
|
+
signaturePadRef.value?.clearCacheImages?.()
|
|
95
|
+
}
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
const saveSignature = () => {
|
|
98
|
+
isDialogOpen.value = false
|
|
99
|
+
const result = signaturePadRef.value?.saveSignature()
|
|
100
|
+
if (!result) return
|
|
101
|
+
const { isEmpty, data } = result
|
|
102
|
+
if (isEmpty) {
|
|
103
|
+
normalized.value = null
|
|
104
|
+
model.value = null
|
|
105
|
+
} else {
|
|
106
|
+
const asset: Base64Asset = { base64String: data }
|
|
107
|
+
normalized.value = asset
|
|
108
|
+
model.value = asset
|
|
109
|
+
}
|
|
110
|
+
}
|
|
88
111
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
112
|
+
const openSignatureDialog = async () => {
|
|
113
|
+
// ensure hydration before opening to preview correctly
|
|
114
|
+
if (props.autoHydrate && normalized.value?.id != null && !normalized.value.base64String) {
|
|
115
|
+
await hydrateAssetFile(normalized.value)
|
|
116
|
+
model.value = normalized.value
|
|
117
|
+
}
|
|
93
118
|
|
|
94
|
-
|
|
95
|
-
window.removeEventListener('resize', updateSignaturePadHeight)
|
|
96
|
-
})
|
|
119
|
+
isDialogOpen.value = true
|
|
97
120
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
121
|
+
const existing = normalized.value?.base64String
|
|
122
|
+
if (existing) {
|
|
123
|
+
// seed after dialog mount
|
|
124
|
+
requestAnimationFrame(() => signaturePadRef.value?.fromDataURL(existing))
|
|
125
|
+
}
|
|
126
|
+
}
|
|
101
127
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
// validation passthrough
|
|
129
|
+
const isValid = computed(() => inputRef.value?.isValid)
|
|
130
|
+
const errorMessages = computed(() => inputRef.value?.errorMessages)
|
|
105
131
|
|
|
106
132
|
defineExpose({
|
|
107
133
|
errorMessages,
|
|
108
134
|
isValid,
|
|
109
|
-
reset: ()=>inputRef.value?.reset(),
|
|
110
|
-
resetValidation
|
|
111
|
-
validate
|
|
135
|
+
reset: () => inputRef.value?.reset(),
|
|
136
|
+
resetValidation: () => inputRef.value?.resetValidation(),
|
|
137
|
+
validate: () => inputRef.value?.validate(),
|
|
112
138
|
})
|
|
113
139
|
</script>
|
|
114
140
|
|
|
115
141
|
<template>
|
|
116
|
-
<v-input v-model="
|
|
117
|
-
<template #default="{isReadonly,isDisabled}">
|
|
118
|
-
<v-card
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
>
|
|
122
|
-
<v-card-text v-if="signatureData">
|
|
123
|
-
<v-img
|
|
124
|
-
:src="signatureData"
|
|
125
|
-
cover
|
|
126
|
-
/>
|
|
142
|
+
<v-input v-model="model" v-bind="$attrs" ref="inputRef">
|
|
143
|
+
<template #default="{ isReadonly, isDisabled }">
|
|
144
|
+
<v-card class="w-100" flat :variant="$attrs.variant" :title="props.title">
|
|
145
|
+
<v-card-text v-if="normalized?.base64String">
|
|
146
|
+
<v-img :src="imageDataUrl" cover />
|
|
127
147
|
<v-icon
|
|
128
148
|
class="position-absolute"
|
|
129
149
|
style="top: 8px; right: 8px; z-index: 10;"
|
|
130
|
-
@click="
|
|
131
|
-
v-if="
|
|
150
|
+
@click="model = null; normalized = null"
|
|
151
|
+
v-if="!isReadonly?.value"
|
|
132
152
|
>
|
|
133
153
|
mdi mdi-close-circle
|
|
134
154
|
</v-icon>
|
|
135
155
|
</v-card-text>
|
|
136
|
-
|
|
156
|
+
|
|
157
|
+
<v-card-actions v-if="!isReadonly?.value">
|
|
137
158
|
<v-btn
|
|
138
159
|
append-icon="mdi mdi-draw-pen"
|
|
139
160
|
block
|
|
@@ -141,79 +162,43 @@ defineExpose({
|
|
|
141
162
|
color="primary"
|
|
142
163
|
variant="flat"
|
|
143
164
|
@click="openSignatureDialog"
|
|
144
|
-
:readonly="isReadonly
|
|
145
|
-
:disabled="isDisabled
|
|
165
|
+
:readonly="isReadonly?.value"
|
|
166
|
+
:disabled="isDisabled?.value"
|
|
146
167
|
>
|
|
147
168
|
{{ props.btnName }}
|
|
148
169
|
</v-btn>
|
|
149
170
|
</v-card-actions>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
height="auto"
|
|
153
|
-
persistent
|
|
154
|
-
width="100%"
|
|
155
|
-
>
|
|
171
|
+
|
|
172
|
+
<v-dialog v-model="isDialogOpen" height="auto" persistent width="100%">
|
|
156
173
|
<v-card>
|
|
157
174
|
<v-toolbar>
|
|
158
175
|
<v-toolbar-title class="text-no-wrap">
|
|
159
176
|
{{ props.title }}
|
|
160
177
|
</v-toolbar-title>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@click="undoSignature"
|
|
164
|
-
>
|
|
178
|
+
|
|
179
|
+
<v-btn icon @click="undoSignature">
|
|
165
180
|
<v-icon>fa-solid fa-arrow-rotate-left</v-icon>
|
|
166
181
|
</v-btn>
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@click="clearSignature"
|
|
170
|
-
>
|
|
182
|
+
|
|
183
|
+
<v-btn icon @click="clearSignature">
|
|
171
184
|
<v-icon>fa-solid fa-trash</v-icon>
|
|
172
185
|
</v-btn>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
<v-btn
|
|
176
|
-
:color="selectedColor"
|
|
177
|
-
:icon="true"
|
|
178
|
-
v-bind="activatorProps"
|
|
179
|
-
>
|
|
180
|
-
<v-icon>fa-solid fa-paintbrush</v-icon>
|
|
181
|
-
</v-btn>
|
|
182
|
-
</template>
|
|
183
|
-
<v-list>
|
|
184
|
-
<v-row>
|
|
185
|
-
<v-col class="text-center">
|
|
186
|
-
<v-avatar
|
|
187
|
-
v-for="(color, index) in colorOptions"
|
|
188
|
-
:key="index"
|
|
189
|
-
:color="color"
|
|
190
|
-
:value="color"
|
|
191
|
-
class="mr-1"
|
|
192
|
-
@click="changePenColor(color)"
|
|
193
|
-
>
|
|
194
|
-
<v-icon color="white">
|
|
195
|
-
{{ selectedColor === color ? 'fa-solid fa-check' : '' }}
|
|
196
|
-
</v-icon>
|
|
197
|
-
</v-avatar>
|
|
198
|
-
</v-col>
|
|
199
|
-
</v-row>
|
|
200
|
-
</v-list>
|
|
201
|
-
</v-menu>
|
|
202
|
-
<v-btn
|
|
203
|
-
icon
|
|
204
|
-
@click="closeDialog"
|
|
205
|
-
>
|
|
186
|
+
|
|
187
|
+
<v-btn icon @click="closeDialog">
|
|
206
188
|
<v-icon>fa-solid fa-xmark</v-icon>
|
|
207
189
|
</v-btn>
|
|
208
190
|
</v-toolbar>
|
|
191
|
+
|
|
209
192
|
<v-card-text>
|
|
210
193
|
<VueSignaturePad
|
|
211
194
|
ref="signaturePadRef"
|
|
212
195
|
:options="signatureOptions"
|
|
213
|
-
|
|
196
|
+
height="40vh"
|
|
214
197
|
/>
|
|
215
198
|
</v-card-text>
|
|
199
|
+
|
|
216
200
|
<v-divider />
|
|
201
|
+
|
|
217
202
|
<v-card-actions class="justify-center">
|
|
218
203
|
<v-btn
|
|
219
204
|
class="text-none"
|