Form komponenti SelectBox Lookup

This commit is contained in:
Sedat Öztürk 2025-09-21 23:05:13 +03:00
parent a913220999
commit e14d6930c2
11 changed files with 267 additions and 48 deletions

View file

@ -1,5 +1,5 @@
{
"commit": "3f69cc5",
"commit": "a913220",
"releases": [
{
"version": "1.0.8",

View file

@ -11,8 +11,10 @@ import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
import { RowMode, SimpleItemWithColData } from './types'
import { PlatformEditorTypes } from '@/proxy/form/models'
import { useLookupDataSource } from './useLookupDataSource'
const FormDevExpress = (props: {
listFormCode: string
isSubForm?: boolean
mode: RowMode
refForm: RefObject<FormDx>
@ -20,7 +22,8 @@ const FormDevExpress = (props: {
formItems: GroupItem[]
setFormData: Dispatch<any>
}) => {
const { isSubForm, mode, refForm, formData, formItems, setFormData } = props
const { listFormCode, isSubForm, mode, refForm, formData, formItems, setFormData } = props
const { getLookupDataSource } = useLookupDataSource({ listFormCode, isSubForm })
return (
<form className={`${DX_CLASSNAMES} p-2`}>
@ -66,6 +69,10 @@ const FormDevExpress = (props: {
...formItem.editorOptions,
...(mode === 'view' ? { readOnly: true } : {}),
}}
dataSource={getLookupDataSource(
formItem.colData?.editorOptions,
formItem.colData,
)}
></TagBoxEditorComponent>
)}
>
@ -88,6 +95,10 @@ const FormDevExpress = (props: {
...formItem.editorOptions,
...(mode === 'view' ? { readOnly: true } : {}),
}}
dataSource={getLookupDataSource(
formItem.colData?.editorOptions,
formItem.colData,
)}
></GridBoxEditorComponent>
)}
>
@ -99,6 +110,16 @@ const FormDevExpress = (props: {
{...formItem}
editorOptions={{
...formItem.editorOptions,
...(formItem.colData?.lookupDto?.dataSourceType
? {
dataSource: getLookupDataSource(
formItem.colData?.editorOptions,
formItem.colData,
),
valueExpr: formItem.colData?.lookupDto?.valueExpr,
displayExpr: formItem.colData?.lookupDto?.displayExpr,
}
: {}),
...(mode === 'view' ? { readOnly: true } : { autoFocus: i === 1 }),
}}
/>

View file

@ -94,6 +94,7 @@ const FormEdit = (
formData={formData}
formItems={formItems}
setFormData={setFormData}
listFormCode={listFormCode}
/>
<SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} />
</Container>

View file

@ -179,6 +179,7 @@ const FormNew = (
formData={formData}
formItems={formItems}
setFormData={setFormData}
listFormCode={listFormCode}
/>
</Container>
)

View file

@ -88,6 +88,7 @@ const FormView = (
formData={formData}
formItems={formItems}
setFormData={() => {}}
listFormCode={listFormCode}
/>
<SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} refreshData={fetchData} />
</Container>

View file

@ -1,5 +1,4 @@
import { GridBoxOptionsDto } from '@/proxy/form/models'
import { GridColumnData } from '@/views/list/GridColumnData'
import { ColumnFormatDto, GridBoxOptionsDto } from '@/proxy/form/models'
import { Button } from 'devextreme-react/button'
import DataGrid from 'devextreme-react/data-grid'
import DropDownBox from 'devextreme-react/drop-down-box'
@ -12,29 +11,32 @@ const GridBoxEditorComponent = ({
onValueChanged,
col,
editorOptions,
dataSource
}: {
value: any
options?: GridBoxOptionsDto
values: any // all row data
onValueChanged: (e: any) => void
col?: GridColumnData
col?: ColumnFormatDto
editorOptions: any
dataSource: any
}): ReactElement => {
const gridRef = useRef<DataGrid>(null)
const val = Array.isArray(value) ? value : [value]
const lookup = col?.lookup
const dataSource: any =
typeof lookup?.dataSource === 'function'
? lookup.dataSource(values)
: (lookup?.dataSource ?? [])
const lookupDto = col?.lookupDto
// const dataSource: any =
// typeof lookupDto?.dataSource === 'function'
// ? lookupDto.dataSource(values)
// : (lookupDto?.dataSource ?? [])
return (
<DropDownBox
{...editorOptions}
value={val}
dataSource={dataSource}
valueExpr={lookup?.valueExpr}
displayExpr={lookup?.displayExpr}
valueExpr={lookupDto?.valueExpr}
displayExpr={lookupDto?.displayExpr}
showClearButton={options?.showClearButton}
acceptCustomValue={options?.acceptCustomValue}
onValueChanged={(e) => {
@ -51,7 +53,7 @@ const GridBoxEditorComponent = ({
<DataGrid
ref={gridRef}
dataSource={dataSource}
key={lookup?.valueExpr}
key={lookupDto?.valueExpr}
filterRow={{
visible: options?.filterRowVisible,
applyFilter: 'auto',

View file

@ -1,7 +1,5 @@
import { TagBoxOptionsDto } from '@/proxy/form/models'
import { GridColumnData } from '@/views/list/GridColumnData'
import { ColumnFormatDto, TagBoxOptionsDto } from '@/proxy/form/models'
import TagBox from 'devextreme-react/tag-box'
import { ApplyValueMode } from 'devextreme/common'
import { ReactElement } from 'react'
const TagBoxEditorComponent = ({
@ -12,29 +10,32 @@ const TagBoxEditorComponent = ({
onValueChanged,
col,
editorOptions,
dataSource
}: {
value: any
setDefaultValue: boolean
options?: TagBoxOptionsDto
values: any // all row data
onValueChanged: (e: any) => void
col?: GridColumnData
col?: ColumnFormatDto
editorOptions: any
dataSource: any
}): ReactElement => {
const val = Array.isArray(value) ? value : [value]
const lookup = col?.lookup
const dataSource =
typeof lookup?.dataSource === 'function'
? lookup.dataSource(values)
: (lookup?.dataSource ?? [])
const lookupDto = col?.lookupDto
// const dataSource =
// typeof lookup?.dataSource === 'function'
// ? lookup.dataSource(values)
// : (lookup?.dataSource ?? [])
return (
<TagBox
{...editorOptions}
{...(setDefaultValue ? { defaultValue: val } : { value: val })}
dataSource={dataSource}
valueExpr={lookup?.valueExpr}
displayExpr={lookup?.displayExpr}
valueExpr={lookupDto?.valueExpr}
displayExpr={lookupDto?.displayExpr}
showClearButton={options?.showClearButton}
showSelectionControls={options?.showSelectionControls ?? true}
maxDisplayedTags={options?.maxDisplayedTags}

View file

@ -1,14 +1,13 @@
import { FormItemComponent, SimpleItem } from 'devextreme/ui/form'
import { GridColumnData } from '../list/GridColumnData'
import { Overwrite } from '../../utils/types'
import { GridBoxOptionsDto, PlatformEditorTypes, TagBoxOptionsDto } from '../../proxy/form/models'
import { ColumnFormatDto, GridBoxOptionsDto, PlatformEditorTypes, TagBoxOptionsDto } from '../../proxy/form/models'
import { Meta } from '@/@types/routes'
export type EditorType2 = FormItemComponent | PlatformEditorTypes.dxGridBox
export type SimpleItemWithColData = Overwrite<
SimpleItem,
{
colData?: GridColumnData
colData?: ColumnFormatDto
tagBoxOptions?: TagBoxOptionsDto
gridBoxOptions?: GridBoxOptionsDto
canRead: boolean

View file

@ -244,7 +244,7 @@ const useGridData = (props: {
colSpan: i.colSpan,
isRequired: i.isRequired,
editorOptions,
colData: cols?.find((x) => x.dataField === i.dataField),
colData: gridDto?.columnFormats.find((x) => x.fieldName === i.dataField),
tagBoxOptions: i.tagBoxOptions,
gridBoxOptions: i.gridBoxOptions,
}

View file

@ -0,0 +1,153 @@
import { dynamicFetch } from '@/services/form.service'
import { UiLookupDataSourceTypeEnum } from '@/proxy/form/models'
import CustomStore from 'devextreme/data/custom_store'
import { useCallback } from 'react'
const createLookupStaticDataSource = (
load: () => any,
filter: any = null,
key: any = 'key',
sort: any = 'name',
) => ({
store: new CustomStore({
key,
loadMode: 'raw',
load,
}),
sort,
filter,
})
const createLookupQueryDataSource = (
listFormCode?: string,
listFormFieldName?: string,
filters?: any[],
isSubForm?: boolean,
) => {
return new CustomStore({
loadMode: 'raw',
load: async () => {
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
return
}
try {
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
listFormCode,
listFormFieldName,
filters,
})
return response.data.map((a: any) => ({
key: a.Key,
name: a.Name,
group: a.Group,
}))
} catch (error: any) {
return null
}
},
})
}
const createLookupApiDataSource = (
listFormCode?: string,
lookupQuery?: string,
filters?: any[],
keyName?: string,
isSubForm?: boolean,
) => {
return new CustomStore({
key: keyName,
loadMode: 'raw',
load: async () => {
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
return
}
if (!lookupQuery) {
return
}
const [method, url, body, keySelector, nameSelector, groupSelector] = lookupQuery.split(';')
if (filters?.length) {
for (let i = 0; i < filters.length; i++) {
body.replace(`@param${i}`, filters[i])
}
}
try {
const response = await dynamicFetch(url, method, null, body)
let { data } = response
if (!data) {
return
}
if (!Array.isArray(data)) {
data = [data]
}
return data.map((a: any) => ({
key: eval(keySelector),
name: eval(nameSelector),
group: eval(groupSelector),
}))
} catch {
return
}
},
})
}
export const useLookupDataSource = ({
listFormCode,
isSubForm,
}: {
listFormCode: string
isSubForm?: boolean
}) => {
const getLookupDataSource = useCallback(
(options: any, colData: any) => {
const { lookupDto } = colData
const filters = []
if (lookupDto.cascadeParentFields) {
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
filters.push([
lookupDto?.cascadeRelationField,
lookupDto?.cascadeFilterOperator,
options?.data[lookupDto?.cascadeParentField],
])
} else {
const data = options?.data ?? options
for (const cascadeParentField of lookupDto.cascadeParentFields.split(',')) {
filters.push(data[cascadeParentField])
}
}
}
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
return createLookupStaticDataSource(
() => JSON.parse(lookupDto?.lookupQuery),
filters.length ? filters : null,
)
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.Query) {
return createLookupQueryDataSource(listFormCode, colData.fieldName, filters, isSubForm)
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.WebService) {
return createLookupApiDataSource(
listFormCode,
lookupDto?.lookupQuery,
filters,
colData.lookupDto?.valueExpr?.toLowerCase(),
isSubForm,
)
} else {
return {
store: [],
}
}
},
[listFormCode, isSubForm],
)
return { getLookupDataSource }
}

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { GridDto } from '@/proxy/form/models'
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
import { captionize } from 'devextreme/core/utils/inflector'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { Pagination, Select } from '@/components/ui'
@ -7,13 +7,13 @@ import classNames from 'classnames'
import { getList } from '@/services/form.service'
import { FaInbox } from 'react-icons/fa'
import FormDevExpress from '../form/FormDevExpress'
import { GroupItem, SimpleItem } from 'devextreme/ui/form'
import { GroupItem } from 'devextreme/ui/form'
import { Form as FormDx } from 'devextreme-react/form'
import FormButtons from '../form/FormButtons'
import { useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/routes/route.constant'
import CustomStore from 'devextreme/data/custom_store'
import { PermissionResults } from '../form/types'
import { PermissionResults, SimpleItemWithColData } from '../form/types'
import { usePermission } from '@/utils/hooks/usePermission'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -54,22 +54,61 @@ const CardItem = ({
const formItems: GroupItem[] = useMemo(() => {
if (!gridDto) return []
const items: SimpleItem[] = gridDto.columnFormats
.filter((col) => col.visible && col.listOrderNo > 0)
.sort((a, b) => a.listOrderNo - b.listOrderNo)
.slice(0, 10)
.map((col) => ({
dataField: col.fieldName,
label: { text: captionize(col.captionName || col.fieldName!) },
}))
return [
{
itemType: 'group',
colCount: 1,
items: items,
},
]
return gridDto?.gridOptions.editingFormDto
?.sort((a: any, b: any) => {
return a.order >= b.order ? 1 : -1
})
.map((e: any) => {
return {
itemType: e.itemType,
colCount: e.colCount,
colSpan: e.colSpan,
caption: e.caption,
items: e.items
?.sort((a: any, b: any) => {
return a.order >= b.order ? 1 : -1
})
.map((i: EditingFormItemDto) => {
let editorOptions = {}
try {
editorOptions = i.editorOptions && JSON.parse(i.editorOptions)
} catch {}
const item: SimpleItemWithColData = {
canRead:
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canRead ??
false,
canUpdate:
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canUpdate ??
false,
canCreate:
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canCreate ??
false,
canExport:
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canExport ??
false,
dataField: i.dataField,
name: i.dataField,
editorType2: i.editorType2,
editorType:
i.editorType2 == PlatformEditorTypes.dxGridBox ? 'dxDropDownBox' : i.editorType2,
colSpan: i.colSpan,
isRequired: i.isRequired,
editorOptions,
colData: gridDto.columnFormats.find((x) => x.fieldName === i.dataField),
tagBoxOptions: i.tagBoxOptions,
gridBoxOptions: i.gridBoxOptions,
}
if (i.dataField.indexOf(':') >= 0) {
item.label = { text: captionize(i.dataField.split(':')[1]) }
}
item.editorOptions = {
...item.editorOptions,
readOnly: true,
}
return item
}),
} as GroupItem
})
}, [gridDto])
const permissionResults: PermissionResults = {
@ -130,6 +169,7 @@ const CardItem = ({
formData={formData}
formItems={formItems}
setFormData={setFormData}
listFormCode={listFormCode}
/>
</div>
</div>
@ -194,7 +234,7 @@ const Card = ({ listFormCode, searchParams }: CardProps) => {
const allowedSizes = pagerOptions?.allowedPageSizes
?.split(',')
.map((s) => Number(s.trim()))
.filter((n) => !isNaN(n) && n > 0) || [20, 50, 100]
.filter((n) => !isNaN(n) && n > 0) || [10, 20, 50, 100]
setPageSizeOptions(allowedSizes.map((size) => ({ value: size, label: `${size} page` })))
}, [gridDto, listFormCode, searchParams])