117 lines
3.1 KiB
Vue
117 lines
3.1 KiB
Vue
<template>
|
|
<div class="page-layout-renderer w-full">
|
|
<div
|
|
v-if="layout && layout.fields.length > 0"
|
|
class="grid grid-cols-6 gap-4 auto-rows-[80px]"
|
|
>
|
|
<div
|
|
v-for="fieldItem in sortedFields"
|
|
:key="fieldItem.fieldId"
|
|
:style="getFieldStyle(fieldItem)"
|
|
class="flex flex-col min-h-[60px]"
|
|
>
|
|
<FieldRenderer
|
|
v-if="fieldItem.field"
|
|
:field="fieldItem.field"
|
|
:model-value="modelValue?.[fieldItem.field.apiName]"
|
|
:record-data="modelValue"
|
|
:mode="readonly ? VM.DETAIL : VM.EDIT"
|
|
@update:model-value="handleFieldUpdate(fieldItem.field.apiName, $event)"
|
|
@update:related-fields="handleRelatedFieldsUpdate"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fallback: Simple two-column layout if no page layout is configured -->
|
|
<div v-else class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div
|
|
v-for="field in fields"
|
|
:key="field.id"
|
|
class="flex flex-col min-h-[60px]"
|
|
>
|
|
<FieldRenderer
|
|
:field="field"
|
|
:model-value="modelValue?.[field.apiName]"
|
|
:record-data="modelValue"
|
|
:mode="readonly ? VM.DETAIL : VM.EDIT"
|
|
@update:model-value="handleFieldUpdate(field.apiName, $event)"
|
|
@update:related-fields="handleRelatedFieldsUpdate"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, watch } from 'vue'
|
|
import type { FieldConfig, ViewMode } from '~/types/field-types'
|
|
import type { PageLayoutConfig, FieldLayoutItem } from '~/types/page-layout'
|
|
import FieldRenderer from '@/components/fields/FieldRenderer.vue'
|
|
import { ViewMode as VM } from '~/types/field-types'
|
|
|
|
const props = defineProps<{
|
|
fields: FieldConfig[]
|
|
layout?: PageLayoutConfig | null
|
|
modelValue?: Record<string, any>
|
|
readonly?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: Record<string, any>]
|
|
}>()
|
|
|
|
// Map field IDs to field objects and sort by position
|
|
const sortedFields = computed(() => {
|
|
if (!props.layout || !props.layout.fields) return []
|
|
|
|
const fieldsMap = new Map(props.fields.map(f => [f.id, f]))
|
|
|
|
return props.layout.fields
|
|
.map(item => ({
|
|
...item,
|
|
field: fieldsMap.get(item.fieldId),
|
|
}))
|
|
.filter(item => item.field)
|
|
.sort((a, b) => {
|
|
// Sort by y position first, then x position
|
|
if (a.y !== b.y) return a.y - b.y
|
|
return a.x - b.x
|
|
})
|
|
})
|
|
|
|
const getFieldStyle = (item: FieldLayoutItem) => {
|
|
return {
|
|
gridColumnStart: item.x + 1,
|
|
gridColumnEnd: `span ${item.w}`,
|
|
gridRowStart: item.y + 1,
|
|
gridRowEnd: `span ${item.h}`,
|
|
}
|
|
}
|
|
|
|
const handleFieldUpdate = (fieldName: string, value: any) => {
|
|
if (props.readonly) return
|
|
|
|
const updated = {
|
|
...props.modelValue,
|
|
[fieldName]: value,
|
|
}
|
|
|
|
emit('update:modelValue', updated)
|
|
}
|
|
|
|
const handleRelatedFieldsUpdate = (values: Record<string, any>) => {
|
|
if (props.readonly) return
|
|
|
|
const updated = {
|
|
...props.modelValue,
|
|
...values,
|
|
}
|
|
|
|
emit('update:modelValue', updated)
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Additional styles if needed */
|
|
</style>
|