Files
skt-vuetify-templates/src/components/base/SKFormEditDialog.vue
T

198 lines
5.0 KiB
Vue

<template>
<v-dialog v-model="dialogModel" max-width="480" v-bind="$attrs">
<v-card>
<v-card-title class="text-subtitle-1 font-weight-medium">
<slot name="title">
{{ props.titleText }}
</slot>
</v-card-title>
<v-card-text class="pt-2">
<slot :form="form" name="content" :permission="formPermission" :status="formStatus">
<div class="d-flex flex-column ga-4">
<v-select
v-if="props.showStatus"
v-model="formStatus"
density="comfortable"
hide-details
item-title="title"
item-value="value"
:items="normalizedStatusOptions"
:label="props.statusLabelText"
variant="outlined"
/>
<v-select
v-if="props.showPermission"
v-model="formPermission"
density="comfortable"
hide-details
item-title="title"
item-value="value"
:items="normalizedPermissionOptions"
:label="props.permissionLabelText"
variant="outlined"
/>
<slot :form="form" name="fields"></slot>
</div>
</slot>
</v-card-text>
<v-card-actions class="px-4 pb-4">
<slot :cancel="handleCancel" name="actions" :submit="handleSubmit">
<v-spacer />
<v-btn :disabled="props.loading" variant="text" @click="handleCancel">
{{ props.cancelText }}
</v-btn>
<v-btn color="primary" :loading="props.loading" @click="handleSubmit">
{{ props.confirmText }}
</v-btn>
</slot>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { computed, reactive, watch } from 'vue'
type OptionValue = string | number
type Option = { title: string; value: OptionValue }
type GenericRecord = Record<string, unknown>
interface Props {
modelValue: boolean
item: GenericRecord | null
statusKey?: string
permissionKey?: string
showStatus?: boolean
showPermission?: boolean
statusOptions?: Array<Option | string | number>
permissionOptions?: Array<Option | string | number>
titleText?: string
statusLabelText?: string
permissionLabelText?: string
cancelText?: string
confirmText?: string
loading?: boolean
closeOnSubmit?: boolean
}
const props = withDefaults(defineProps<Props>(), {
statusKey: 'status',
permissionKey: 'permission',
showStatus: true,
showPermission: true,
statusOptions: () => [],
permissionOptions: () => [],
titleText: '編輯',
statusLabelText: '狀態',
permissionLabelText: '權限',
cancelText: '取消',
confirmText: '確認',
loading: false,
closeOnSubmit: true,
})
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'submit', value: GenericRecord): void
(e: 'cancel'): void
}>()
const dialogModel = computed({
get: () => props.modelValue,
set: (v: boolean) => emit('update:modelValue', v),
})
function normalizeOptions(options: Array<Option | string | number>) {
return options.map((o) => {
if (typeof o === 'string' || typeof o === 'number') {
return { title: String(o), value: o }
}
return o
})
}
const normalizedStatusOptions = computed(() => normalizeOptions(props.statusOptions))
const normalizedPermissionOptions = computed(() => normalizeOptions(props.permissionOptions))
const form = reactive<GenericRecord>({})
function resetForm(next: GenericRecord) {
for (const key of Object.keys(form)) {
delete form[key]
}
Object.assign(form, next)
}
const getDefaultStatus = (): OptionValue | '' => normalizedStatusOptions.value[0]?.value ?? ''
function getDefaultPermission(): OptionValue | '' {
return normalizedPermissionOptions.value[0]?.value ?? ''
}
const formStatus = computed<OptionValue | ''>({
get: () => {
const current = form[props.statusKey] as OptionValue | undefined
return current ?? getDefaultStatus()
},
set: (v) => {
form[props.statusKey] = v
},
})
const formPermission = computed<OptionValue | ''>({
get: () => {
const current = form[props.permissionKey] as OptionValue | undefined
return current ?? getDefaultPermission()
},
set: (v) => {
form[props.permissionKey] = v
},
})
function syncFromItem() {
const item = props.item ?? {}
resetForm({ ...item })
if (props.showStatus) {
const status = item[props.statusKey] as OptionValue | undefined
form[props.statusKey] = status ?? getDefaultStatus()
}
if (props.showPermission) {
const permission = item[props.permissionKey] as OptionValue | undefined
form[props.permissionKey] = permission ?? getDefaultPermission()
}
}
watch(
() => props.modelValue,
(open) => {
if (open) syncFromItem()
}
)
watch(
() => props.item,
() => {
if (props.modelValue) syncFromItem()
}
)
function handleCancel() {
emit('cancel')
dialogModel.value = false
}
function handleSubmit() {
emit('submit', { ...form })
if (props.closeOnSubmit) {
dialogModel.value = false
}
}
</script>