feat(wbs): Vue 3 CRUD templates and OpenAPI Axios client refactoring
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
title?: string
|
||||
initialData?: Record<string, any>
|
||||
fields: Array<{
|
||||
name: string
|
||||
label: string
|
||||
type?: 'text' | 'number' | 'select' | 'textarea' | 'checkbox' | 'date'
|
||||
required?: boolean
|
||||
options?: Array<{ label: string; value: any }>
|
||||
placeholder?: string
|
||||
}>
|
||||
isEditing?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['save', 'cancel', 'delete'])
|
||||
|
||||
const formData = ref<Record<string, any>>({ ...(props.initialData || {}) })
|
||||
|
||||
const handleSave = () => {
|
||||
emit('save', formData.value)
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
if (confirm('해당 레코드를 삭제(Soft Delete)하시겠습니까?')) {
|
||||
emit('delete', formData.value)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card shadow-sm border">
|
||||
<div class="card-header bg-navy text-white d-flex justify-content-between align-items-center py-2 px-3">
|
||||
<h5 class="card-title m-0 font-weight-bold text-white fs-6">
|
||||
<i class="ti ti-edit me-1"></i> {{ title || (isEditing ? '데이터 수정' : '신규 데이터 등록') }}
|
||||
</h5>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-success fw-bold px-3" @click="handleSave">
|
||||
<span class="hotkey-badge me-1">F4</span>{{ isEditing ? '수정 저장' : '신규 저장' }}
|
||||
</button>
|
||||
<button v-if="isEditing" type="button" class="btn btn-sm btn-danger fw-bold px-3" @click="handleDelete">
|
||||
<span class="hotkey-badge me-1">F5</span>삭제
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary fw-bold px-3" @click="emit('cancel')">
|
||||
취소
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-3">
|
||||
<div class="row g-3">
|
||||
<div v-for="field in fields" :key="field.name" class="col-md-6 col-12">
|
||||
<label class="form-label fw-bold fs-7 mb-1">
|
||||
<span v-if="field.required" class="text-danger me-1">*</span>{{ field.label }}
|
||||
</label>
|
||||
|
||||
<template v-if="field.type === 'select'">
|
||||
<select v-model="formData[field.name]" class="form-select form-select-sm fw-bold">
|
||||
<option v-for="opt in field.options" :key="opt.value" :value="opt.value">
|
||||
{{ opt.label }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<template v-else-if="field.type === 'textarea'">
|
||||
<textarea v-model="formData[field.name]" class="form-control form-control-sm fw-bold" rows="3" :placeholder="field.placeholder"></textarea>
|
||||
</template>
|
||||
|
||||
<template v-else-if="field.type === 'checkbox'">
|
||||
<div class="form-check mt-2">
|
||||
<input v-model="formData[field.name]" type="checkbox" class="form-check-input" :id="field.name" />
|
||||
<label class="form-check-label fs-7 fw-bold" :for="field.name">{{ field.label }}</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<input
|
||||
v-model="formData[field.name]"
|
||||
:type="field.type || 'text'"
|
||||
class="form-control form-control-sm fw-bold"
|
||||
:placeholder="field.placeholder"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bg-navy {
|
||||
background-color: #1E293B;
|
||||
}
|
||||
.hotkey-badge {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 1px 4px;
|
||||
border-radius: 2px;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user