feat(reference-auto) : display referenceAuto in piece views + formula config in ModelTypeForm
- Piece interface: add referenceAuto field - piece/[id].vue: read-only display with auto badge - pieces/[id]/edit.vue: disabled input when referenceAuto is set - pieces-catalog.vue: new column "Réf. auto" - PieceItem.vue: badge + detail line for referenceAuto - ModelTypeForm.vue: formula + required fields config for PIECE category - modelTypes.ts: add referenceFormula/requiredFieldsForReference to types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
Rattachée à {{ piece.parentComponentName }}
|
||||
</span>
|
||||
<span v-if="pieceData.reference" class="badge badge-outline badge-sm">{{ pieceData.reference }}</span>
|
||||
<span v-if="pieceData.referenceAuto" class="badge badge-secondary badge-sm" title="Référence auto">{{ pieceData.referenceAuto }}</span>
|
||||
<template v-if="pieceConstructeursDisplay.length">
|
||||
<span
|
||||
v-for="constructeur in pieceConstructeursDisplay"
|
||||
@@ -106,6 +107,10 @@
|
||||
pieceData.reference || "Non définie"
|
||||
}}</span>
|
||||
</div>
|
||||
<div v-if="pieceData.referenceAuto">
|
||||
<span class="font-medium">Référence auto:</span>
|
||||
<span class="ml-2">{{ pieceData.referenceAuto }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Fournisseur:</span>
|
||||
<div v-if="!isEditMode" class="ml-2">
|
||||
@@ -301,6 +306,7 @@ const emit = defineEmits(['update', 'edit', 'custom-field-update', 'delete'])
|
||||
const pieceData = reactive({
|
||||
name: props.piece.name || '',
|
||||
reference: props.piece.reference || '',
|
||||
referenceAuto: props.piece.referenceAuto || null,
|
||||
prix: props.piece.prix || '',
|
||||
productId: props.piece.product?.id || props.piece.productId || null,
|
||||
quantity: props.piece.quantity ?? 1,
|
||||
|
||||
@@ -108,6 +108,57 @@
|
||||
</template>
|
||||
</section>
|
||||
|
||||
<section v-if="form.category === 'PIECE'" class="space-y-4">
|
||||
<header>
|
||||
<h3 class="text-lg font-semibold text-base-content">Génération de référence automatique</h3>
|
||||
<p class="mt-1 text-sm text-base-content/70">
|
||||
Définissez une formule pour générer automatiquement une référence technique à partir des champs personnalisés.
|
||||
Utilisez <code class="bg-base-200 px-1 rounded">{'{'}nom_du_champ{'}'}</code> comme placeholder.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="rounded-lg border border-base-300 p-4 space-y-4">
|
||||
<div>
|
||||
<label class="label" for="reference-formula">
|
||||
<span class="label-text">Formule</span>
|
||||
</label>
|
||||
<input
|
||||
id="reference-formula"
|
||||
v-model.trim="form.referenceFormula"
|
||||
type="text"
|
||||
class="input input-bordered w-full font-mono"
|
||||
placeholder="Ex: {serie}{diametre}{type}"
|
||||
:disabled="isReadonly"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-base-content/60">
|
||||
Laissez vide si ce type n'utilise pas de référence automatique.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="label" for="required-fields">
|
||||
<span class="label-text">Champs requis</span>
|
||||
</label>
|
||||
<input
|
||||
id="required-fields"
|
||||
v-model.trim="requiredFieldsInput"
|
||||
type="text"
|
||||
class="input input-bordered w-full"
|
||||
placeholder="Ex: serie, diametre, type"
|
||||
:disabled="isReadonly"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-base-content/60">
|
||||
Noms des champs séparés par des virgules. Si un champ requis est vide, la référence ne sera pas générée.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="form.referenceFormula" class="rounded bg-base-200 px-3 py-2 text-sm">
|
||||
<span class="text-base-content/70">Aperçu :</span>
|
||||
<span class="ml-1 font-mono font-semibold">{{ formulaPreview }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="flex flex-col gap-3 border-t border-base-300 pt-4 sm:flex-row sm:justify-end">
|
||||
<button type="button" class="btn btn-ghost" :disabled="saving" @click="emit('cancel')">
|
||||
Annuler
|
||||
@@ -177,12 +228,21 @@ const componentSubcomponentMaxDepth = computed(() =>
|
||||
)
|
||||
const isReadonly = computed(() => props.readonly === true)
|
||||
|
||||
const form = reactive<ModelTypePayload>({
|
||||
const form = reactive<ModelTypePayload & { referenceFormula?: string | null; requiredFieldsForReference?: string[] | null }>({
|
||||
name: '',
|
||||
code: '',
|
||||
category: props.initialCategory,
|
||||
notes: '',
|
||||
structure: undefined,
|
||||
referenceFormula: null,
|
||||
requiredFieldsForReference: null,
|
||||
})
|
||||
|
||||
const requiredFieldsInput = ref('')
|
||||
|
||||
const formulaPreview = computed(() => {
|
||||
if (!form.referenceFormula) return ''
|
||||
return form.referenceFormula.replace(/\{(\w+)\}/g, '___')
|
||||
})
|
||||
|
||||
const errors = reactive<{ name?: string }>({})
|
||||
@@ -248,6 +308,11 @@ const resetForm = () => {
|
||||
|
||||
errors.name = undefined
|
||||
|
||||
const incomingAny = incoming as Record<string, unknown>
|
||||
form.referenceFormula = typeof incomingAny.referenceFormula === 'string' ? incomingAny.referenceFormula : null
|
||||
form.requiredFieldsForReference = Array.isArray(incomingAny.requiredFieldsForReference) ? incomingAny.requiredFieldsForReference : null
|
||||
requiredFieldsInput.value = form.requiredFieldsForReference?.join(', ') ?? ''
|
||||
|
||||
resetStructures(incoming.structure, form.category)
|
||||
}
|
||||
|
||||
@@ -295,11 +360,16 @@ const handleSubmit = () => {
|
||||
}
|
||||
|
||||
if (form.category === 'PIECE') {
|
||||
const parsedRequiredFields = requiredFieldsInput.value
|
||||
? requiredFieldsInput.value.split(',').map(s => s.trim()).filter(Boolean)
|
||||
: null
|
||||
emit('submit', {
|
||||
...common,
|
||||
category: 'PIECE',
|
||||
structure: normalizePieceStructureForSave(clonePieceStructure(pieceStructure.value)),
|
||||
})
|
||||
referenceFormula: form.referenceFormula || null,
|
||||
requiredFieldsForReference: parsedRequiredFields?.length ? parsedRequiredFields : null,
|
||||
} as ModelTypePayload)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface Piece {
|
||||
id: string
|
||||
name: string
|
||||
reference?: string | null
|
||||
referenceAuto?: string | null
|
||||
description?: string | null
|
||||
typePieceId?: string | null
|
||||
typePiece?: { id: string; name?: string } | null
|
||||
|
||||
@@ -113,6 +113,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Référence auto (read-only, shown only if computed) -->
|
||||
<div v-if="piece.referenceAuto" class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Référence auto</span>
|
||||
</label>
|
||||
<div class="input input-bordered input-sm md:input-md bg-base-200 flex items-center gap-2">
|
||||
<span class="font-mono font-semibold">{{ piece.referenceAuto }}</span>
|
||||
<span class="badge badge-sm badge-ghost">auto</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Référence + Fournisseurs (if value or edit mode) -->
|
||||
<div
|
||||
v-if="isEditMode || piece.reference || editionForm.constructeurIds.length"
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
{{ row.piece.reference || '—' }}
|
||||
</template>
|
||||
|
||||
<template #cell-referenceAuto="{ row }">
|
||||
{{ row.piece.referenceAuto || '—' }}
|
||||
</template>
|
||||
|
||||
<template #cell-description="{ row }">
|
||||
<div v-if="row.piece.description" class="group relative">
|
||||
<span class="block cursor-help truncate">{{ row.piece.description }}</span>
|
||||
@@ -174,6 +178,7 @@ const columns = [
|
||||
{ key: 'preview', label: 'Aperçu', width: 'w-24' },
|
||||
{ key: 'name', label: 'Nom', sortable: true },
|
||||
{ key: 'reference', label: 'Référence' },
|
||||
{ key: 'referenceAuto', label: 'Réf. auto' },
|
||||
{ key: 'description', label: 'Description' },
|
||||
{ key: 'suppliers', label: 'Fournisseurs' },
|
||||
{ key: 'typePiece', label: 'Type de pièce', filterable: true, filterPlaceholder: 'Filtrer…' },
|
||||
|
||||
@@ -114,6 +114,19 @@
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-if="piece?.referenceAuto" class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Référence auto</span>
|
||||
</label>
|
||||
<input
|
||||
:value="piece.referenceAuto"
|
||||
type="text"
|
||||
class="input input-bordered input-sm md:input-md bg-base-200"
|
||||
disabled
|
||||
title="Générée automatiquement à partir du type et des champs personnalisés"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Fournisseur</span>
|
||||
|
||||
@@ -28,6 +28,8 @@ export interface ComponentModelTypePayload extends BaseModelTypePayload {
|
||||
export interface PieceModelTypePayload extends BaseModelTypePayload {
|
||||
category: 'PIECE';
|
||||
structure?: PieceModelStructure | null;
|
||||
referenceFormula?: string | null;
|
||||
requiredFieldsForReference?: string[] | null;
|
||||
}
|
||||
|
||||
export interface ProductModelTypePayload extends BaseModelTypePayload {
|
||||
@@ -46,6 +48,8 @@ export interface ModelType extends BaseModelTypePayload {
|
||||
updatedAt: string;
|
||||
category: ModelCategory;
|
||||
structure: ModelTypeStructure;
|
||||
referenceFormula?: string | null;
|
||||
requiredFieldsForReference?: string[] | null;
|
||||
}
|
||||
|
||||
export interface ModelTypeListParams {
|
||||
|
||||
Reference in New Issue
Block a user