Files
onboard/resources/js/Pages/Admin/ReportTemplates/Components/SectionEditor.vue
2026-06-21 23:40:55 +09:00

93 lines
4.1 KiB
Vue

<script setup>
import { NSelect, NInput, NTransfer, NDynamicInput, NFlex, NText, NEmpty, NFormItem, NForm } from 'naive-ui'
import { computed } from 'vue'
const props = defineProps({
section: { type: Object, required: true },
sources: { type: Array, default: () => [] },
})
const sourceOptions = computed(() => props.sources.map(s => ({ label: s.label, value: s.key })))
const selectedSource = computed(() => props.sources.find(s => s.key === props.section.source) ?? null)
const columnTransferOptions = computed(() => Object.entries(selectedSource.value?.columns ?? {})
.map(([value, label]) => ({ label, value })))
const filterableFieldOptions = computed(() => Object.entries(selectedSource.value?.filterableFields ?? {})
.map(([value, def]) => ({ label: def.label, value })))
const filterValueOptions = (fieldKey) => {
const options = selectedSource.value?.filterableFields?.[fieldKey]?.options
return options ? Object.entries(options).map(([value, label]) => ({ label, value })) : null
}
const createFilter = () => ({
field: filterableFieldOptions.value[0]?.value ?? null,
value: null,
})
const onSourceChange = () => {
// Колонки и фильтры принадлежат конкретному источнику — при смене источника они теряют смысл
props.section.columns = []
props.section.filters = []
}
</script>
<template>
<NFlex vertical :size="12" style="width: 100%;">
<NForm label-placement="top">
<NFlex :size="12" :wrap="true">
<NFormItem label="Источник данных" style="min-width: 240px; margin-bottom: 0;">
<NSelect v-model:value="section.source" :options="sourceOptions" @update:value="onSourceChange" />
</NFormItem>
<NFormItem label="Заголовок секции" style="min-width: 240px; flex: 1; margin-bottom: 0;">
<NInput v-model:value="section.title" :placeholder="selectedSource?.label" />
</NFormItem>
</NFlex>
</NForm>
<div>
<NText depth="3" style="font-size: 12px; display: block; margin-bottom: 6px;">Колонки</NText>
<NTransfer
v-model:value="section.columns"
:options="columnTransferOptions"
source-filterable
source-title="Доступные колонки"
target-title="Колонки секции (в этом порядке)"
style="height: 280px;"
/>
</div>
<div v-if="filterableFieldOptions.length">
<NText depth="3" style="font-size: 12px; display: block; margin-bottom: 6px;">Фильтры (необязательно)</NText>
<NDynamicInput v-model:value="section.filters" :on-create="createFilter">
<template #default="{ value }">
<NFlex align="center" :size="8" style="flex: 1;">
<NSelect
v-model:value="value.field"
:options="filterableFieldOptions"
style="width: 220px;"
placeholder="Поле"
/>
<NSelect
v-if="filterValueOptions(value.field)"
v-model:value="value.value"
:options="filterValueOptions(value.field)"
style="width: 220px;"
placeholder="Значение"
/>
<NInput
v-else
v-model:value="value.value"
style="width: 220px;"
placeholder="Значение"
/>
</NFlex>
</template>
</NDynamicInput>
<NEmpty v-if="!section.filters.length" description="Без фильтров — попадут все записи" size="small" style="padding: 8px 0;" />
</div>
</NFlex>
</template>