feat(comp): implement CodeField component with 300ms debounce validation and F2 popup support
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 11s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 11s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 18s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 8s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 8s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 11s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 11s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 18s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 8s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 8s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
<!-- Typed Field Component Layer: CodeField (WBS-COMP-2.4) -->
|
||||||
|
<template>
|
||||||
|
<div class="typed-code-field flex flex-col gap-1 w-full">
|
||||||
|
<div class="flex justify-between items-center text-xs">
|
||||||
|
<label class="font-bold text-slate-800">
|
||||||
|
{{ label }} <span v-if="required" class="text-rose-600">*</span>
|
||||||
|
</label>
|
||||||
|
<span v-if="isValidating" class="text-[11px] text-blue-600 font-medium">⏳ 중복 검사 중...</span>
|
||||||
|
<span v-else-if="validationStatus === 'VALID'" class="text-[11px] text-emerald-700 font-bold">✓ 사용 가능한 코드</span>
|
||||||
|
<span v-else-if="validationStatus === 'INVALID'" class="text-[11px] text-rose-600 font-bold">🚨 이미 존재하는 코드</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative flex items-center">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
:value="modelValue"
|
||||||
|
:placeholder="placeholder || 'CUST-001'"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
class="w-full h-9 px-3 pr-16 border border-slate-300 rounded font-mono text-xs text-slate-900 font-bold bg-white placeholder:text-slate-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 focus:outline-none disabled:bg-slate-100 disabled:text-slate-500 readonly:bg-slate-50 readonly:text-slate-700 uppercase"
|
||||||
|
@input="handleInput"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="absolute right-1 top-1 bottom-1 px-2.5 bg-slate-800 hover:bg-slate-700 text-amber-300 font-mono text-[11px] font-bold rounded transition"
|
||||||
|
@click="$emit('openPopup')"
|
||||||
|
>
|
||||||
|
F2
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
label: string;
|
||||||
|
modelValue: string;
|
||||||
|
placeholder?: string;
|
||||||
|
required?: boolean;
|
||||||
|
readonly?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
existingCodes?: string[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'openPopup']);
|
||||||
|
|
||||||
|
const isValidating = ref(false);
|
||||||
|
const validationStatus = ref<'NONE' | 'VALID' | 'INVALID'>('NONE');
|
||||||
|
let debounceTimer: any = null;
|
||||||
|
|
||||||
|
const mockExistingCodes = props.existingCodes || ['CUST-001', 'CUST-002', 'ITEM-001', 'WH-SEOUL-01'];
|
||||||
|
|
||||||
|
const handleInput = (e: Event) => {
|
||||||
|
const val = (e.target as HTMLInputElement).value.toUpperCase().trim();
|
||||||
|
emit('update:modelValue', val);
|
||||||
|
|
||||||
|
if (!val) {
|
||||||
|
validationStatus.value = 'NONE';
|
||||||
|
isValidating.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isValidating.value = true;
|
||||||
|
if (debounceTimer) clearTimeout(debounceTimer);
|
||||||
|
|
||||||
|
debounceTimer = setTimeout(() => {
|
||||||
|
isValidating.value = false;
|
||||||
|
if (mockExistingCodes.includes(val)) {
|
||||||
|
validationStatus.value = 'INVALID';
|
||||||
|
} else {
|
||||||
|
validationStatus.value = 'VALID';
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user