Grid, Tree ve Gantt için ImageViewer sütun yapısı eklendi
This commit is contained in:
parent
92058ed5e9
commit
6098758f34
16 changed files with 564 additions and 31 deletions
|
|
@ -30,7 +30,7 @@ public class EditingFormDto
|
||||||
public class EditingFormItemDto
|
public class EditingFormItemDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DataField Accepted Values: 'dxAutocomplete' | 'dxCalendar' | 'dxCheckBox' | 'dxColorBox' | 'dxDateBox' | 'dxDateRangeBox' | 'dxDropDownBox' | 'dxHtmlEditor' | 'dxLookup' | 'dxNumberBox' | 'dxRadioGroup' | 'dxRangeSlider' | 'dxSelectBox' | 'dxSlider' | 'dxSwitch' | 'dxTagBox' | 'dxTextArea' | 'dxTextBox';
|
/// DataField Accepted Values: 'dxAutocomplete' | 'dxCalendar' | 'dxCheckBox' | 'dxColorBox' | 'dxDateBox' | 'dxDateRangeBox' | 'dxDropDownBox' | 'dxHtmlEditor' | 'dxLookup' | 'dxNumberBox' | 'dxRadioGroup' | 'dxRangeSlider' | 'dxSelectBox' | 'dxSlider' | 'dxSwitch' | 'dxTagBox' | 'dxTextArea' | 'dxTextBox' | 'dxImageViewer' | 'dxImageUpload';
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("order")]
|
[JsonPropertyName("order")]
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -3728,11 +3728,11 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
{
|
{
|
||||||
new() {
|
new() {
|
||||||
Order=1, ColCount=2, ColSpan=1, ItemType="group", Items = [
|
Order=1, ColCount=2, ColSpan=1, ItemType="group", Items = [
|
||||||
new EditingFormItemDto { Order = 1, DataField = "Content", ColSpan=2, EditorType2 = EditorTypes.dxHtmlEditor, EditorOptions = EditorOptionValues.HtmlEditorOptions },
|
new EditingFormItemDto { Order = 1, DataField = "UserId", ColSpan=1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||||
new EditingFormItemDto { Order = 2, DataField = "UserId", ColSpan=1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
new EditingFormItemDto { Order = 2, DataField = "LikeCount", ColSpan=1, EditorType2 = EditorTypes.dxNumberBox },
|
||||||
new EditingFormItemDto { Order = 3, DataField = "LikeCount", ColSpan=1, EditorType2 = EditorTypes.dxNumberBox },
|
new EditingFormItemDto { Order = 3, DataField = "IsLiked", ColSpan=1, EditorType2 = EditorTypes.dxCheckBox },
|
||||||
new EditingFormItemDto { Order = 4, DataField = "IsLiked", ColSpan=1, EditorType2 = EditorTypes.dxCheckBox },
|
new EditingFormItemDto { Order = 4, DataField = "IsOwnPost", ColSpan=1, EditorType2 = EditorTypes.dxCheckBox },
|
||||||
new EditingFormItemDto { Order = 5, DataField = "IsOwnPost", ColSpan=1, EditorType2 = EditorTypes.dxCheckBox }
|
new EditingFormItemDto { Order = 5, DataField = "Content", ColSpan=2, EditorType2 = EditorTypes.dxHtmlEditor, EditorOptions = EditorOptionValues.HtmlEditorOptions },
|
||||||
]}
|
]}
|
||||||
}),
|
}),
|
||||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
|
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
|
||||||
|
|
|
||||||
|
|
@ -766,6 +766,7 @@ public static class PlatformConsts
|
||||||
public const string dxTagBox = "dxTagBox";
|
public const string dxTagBox = "dxTagBox";
|
||||||
public const string dxTextArea = "dxTextArea";
|
public const string dxTextArea = "dxTextArea";
|
||||||
public const string dxTextBox = "dxTextBox";
|
public const string dxTextBox = "dxTextBox";
|
||||||
|
public const string dxImageViewer = "dxImageViewer";
|
||||||
public const string dxImageUpload = "dxImageUpload";
|
public const string dxImageUpload = "dxImageUpload";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -837,6 +837,7 @@ export enum SubFormTabTypeEnum {
|
||||||
export enum PlatformEditorTypes {
|
export enum PlatformEditorTypes {
|
||||||
dxTagBox = 'dxTagBox',
|
dxTagBox = 'dxTagBox',
|
||||||
dxGridBox = 'dxGridBox',
|
dxGridBox = 'dxGridBox',
|
||||||
|
dxImageViewer = 'dxImageViewer',
|
||||||
dxImageUpload = 'dxImageUpload',
|
dxImageUpload = 'dxImageUpload',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,7 @@ function JsonRowOpDialogEditForm({
|
||||||
placeholder="Order"
|
placeholder="Order"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-3/12 ml-2">
|
<div className="w-2/12 ml-2">
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
|
@ -405,7 +405,7 @@ function JsonRowOpDialogEditForm({
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-2/12 ml-2 flex gap-2">
|
<div className="w-3/12 ml-2 flex gap-2">
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@ export const columnEditorTypeListOptions = [
|
||||||
{ value: 'dxDateRangeBox', label: 'dxDateRangeBox' },
|
{ value: 'dxDateRangeBox', label: 'dxDateRangeBox' },
|
||||||
{ value: 'dxDropDownBox', label: 'dxDropDownBox' },
|
{ value: 'dxDropDownBox', label: 'dxDropDownBox' },
|
||||||
{ value: PlatformEditorTypes.dxGridBox, label: PlatformEditorTypes.dxGridBox },
|
{ value: PlatformEditorTypes.dxGridBox, label: PlatformEditorTypes.dxGridBox },
|
||||||
|
{ value: PlatformEditorTypes.dxImageViewer, label: PlatformEditorTypes.dxImageViewer },
|
||||||
{ value: PlatformEditorTypes.dxImageUpload, label: PlatformEditorTypes.dxImageUpload },
|
{ value: PlatformEditorTypes.dxImageUpload, label: PlatformEditorTypes.dxImageUpload },
|
||||||
{ value: 'dxHtmlEditor', label: 'dxHtmlEditor' },
|
{ value: 'dxHtmlEditor', label: 'dxHtmlEditor' },
|
||||||
{ value: 'dxLookup', label: 'dxLookup' },
|
{ value: 'dxLookup', label: 'dxLookup' },
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { FieldDataChangedEvent, GroupItem } from 'devextreme/ui/form'
|
||||||
import { Dispatch, RefObject, useEffect, useRef, useState } from 'react'
|
import { Dispatch, RefObject, useEffect, useRef, useState } from 'react'
|
||||||
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
||||||
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
||||||
|
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
|
||||||
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
||||||
import { RowMode, SimpleItemWithColData } from './types'
|
import { RowMode, SimpleItemWithColData } from './types'
|
||||||
import { PlatformEditorTypes } from '@/proxy/form/models'
|
import { PlatformEditorTypes } from '@/proxy/form/models'
|
||||||
|
|
@ -606,6 +607,26 @@ const FormDevExpress = (props: {
|
||||||
className: 'font-semibold',
|
className: 'font-semibold',
|
||||||
}}
|
}}
|
||||||
></SimpleItemDx>
|
></SimpleItemDx>
|
||||||
|
) : formItem.editorType2 === PlatformEditorTypes.dxImageViewer ? (
|
||||||
|
<SimpleItemDx
|
||||||
|
cssClass="font-semibold"
|
||||||
|
key={getFormItemKey(formItem, i)}
|
||||||
|
dataField={formItem.dataField}
|
||||||
|
name={formItem.name}
|
||||||
|
colSpan={formItem.colSpan}
|
||||||
|
isRequired={formItem.isRequired}
|
||||||
|
render={() => (
|
||||||
|
<ImageViewerEditorComponent
|
||||||
|
value={formData[formItem.dataField!]}
|
||||||
|
options={formItem.imageUploadOptions}
|
||||||
|
editorOptions={getEditorOptions(formItem)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
label={{
|
||||||
|
text: translate('::' + formItem.colData?.captionName),
|
||||||
|
className: 'font-semibold',
|
||||||
|
}}
|
||||||
|
></SimpleItemDx>
|
||||||
) : (
|
) : (
|
||||||
<SimpleItemDx
|
<SimpleItemDx
|
||||||
cssClass="font-semibold"
|
cssClass="font-semibold"
|
||||||
|
|
|
||||||
154
ui/src/views/form/editors/ImageViewerEditorComponent.tsx
Normal file
154
ui/src/views/form/editors/ImageViewerEditorComponent.tsx
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { ImageUploadOptionsDto } from '@/proxy/form/models'
|
||||||
|
import { ReactElement } from 'react'
|
||||||
|
|
||||||
|
const parseJsonObject = (value: unknown) => {
|
||||||
|
if (!value) return undefined
|
||||||
|
if (typeof value === 'object') return value as Record<string, any>
|
||||||
|
if (typeof value !== 'string') return undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value)
|
||||||
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||||
|
? (parsed as Record<string, any>)
|
||||||
|
: undefined
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeImageSize = (value: unknown, fallback: number) => {
|
||||||
|
const size = Number(value)
|
||||||
|
return Number.isFinite(size) && size > 0 ? size : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageSource = (value: unknown) => {
|
||||||
|
if (!value) return ''
|
||||||
|
if (typeof value === 'string') return value.trim()
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const item = value as Record<string, unknown>
|
||||||
|
return String(item.url ?? item.src ?? item.fileUrl ?? item.path ?? item.value ?? '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(value).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isProbablyBase64Image = (value: string) =>
|
||||||
|
value.length > 80 && /^[A-Za-z0-9+/]+={0,2}$/.test(value)
|
||||||
|
|
||||||
|
const toImageSource = (value: string) => {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return ''
|
||||||
|
if (trimmed.startsWith('data:image/') || !isProbablyBase64Image(trimmed)) return trimmed
|
||||||
|
return `data:image/jpeg;base64,${trimmed}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitImageString = (value: string) => {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
if (trimmed.startsWith('data:image/')) return [trimmed]
|
||||||
|
|
||||||
|
return trimmed
|
||||||
|
.split(/\r?\n|\|\s*/)
|
||||||
|
.flatMap((part) => {
|
||||||
|
const text = part.trim()
|
||||||
|
if (!text || text.startsWith('data:image/')) return text ? [text] : []
|
||||||
|
return text.split(',').map((item) => item.trim())
|
||||||
|
})
|
||||||
|
.map(toImageSource)
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeImageValue = (value: unknown): string[] => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.flatMap((item) => normalizeImageValue(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
|
||||||
|
if (trimmed.startsWith('[')) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed)
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed.flatMap((item) => normalizeImageValue(item))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return [toImageSource(trimmed)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return splitImageString(trimmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = getImageSource(value)
|
||||||
|
return source ? [toImageSource(source)] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
const openImage = (url: string) => {
|
||||||
|
if (!url.startsWith('data:image/')) {
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const match = url.match(/^data:(image\/[^;]+);base64,(.*)$/)
|
||||||
|
if (!match) return
|
||||||
|
|
||||||
|
const binary = atob(match[2])
|
||||||
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))
|
||||||
|
const blobUrl = URL.createObjectURL(new Blob([bytes], { type: match[1] }))
|
||||||
|
window.open(blobUrl, '_blank', 'noopener,noreferrer')
|
||||||
|
window.setTimeout(() => URL.revokeObjectURL(blobUrl), 60000)
|
||||||
|
} catch {
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageViewerEditorComponent = ({
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
editorOptions,
|
||||||
|
}: {
|
||||||
|
value?: any
|
||||||
|
options?: ImageUploadOptionsDto
|
||||||
|
editorOptions?: any
|
||||||
|
}): ReactElement => {
|
||||||
|
const resolvedOptions = options ?? parseJsonObject(editorOptions) ?? editorOptions ?? {}
|
||||||
|
const urls = normalizeImageValue(value)
|
||||||
|
const thumbW = normalizeImageSize(resolvedOptions?.width, 80)
|
||||||
|
const thumbH = normalizeImageSize(resolvedOptions?.height, 80)
|
||||||
|
|
||||||
|
if (!urls.length) {
|
||||||
|
return <div className="text-gray-400 px-1 py-2">-</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', padding: 4 }}>
|
||||||
|
{urls.map((url, index) => (
|
||||||
|
<button
|
||||||
|
key={`${url}-${index}`}
|
||||||
|
type="button"
|
||||||
|
onClick={() => openImage(url)}
|
||||||
|
style={{ border: 0, background: 'transparent', padding: 0, cursor: 'zoom-in' }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt=""
|
||||||
|
style={{
|
||||||
|
width: thumbW,
|
||||||
|
height: thumbH,
|
||||||
|
objectFit: 'cover',
|
||||||
|
borderRadius: 4,
|
||||||
|
border: '1px solid #ddd',
|
||||||
|
display: 'block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ImageViewerEditorComponent }
|
||||||
|
|
@ -9,7 +9,11 @@ import {
|
||||||
} from '../../proxy/form/models'
|
} from '../../proxy/form/models'
|
||||||
import { Meta } from '@/proxy/routes/routes'
|
import { Meta } from '@/proxy/routes/routes'
|
||||||
|
|
||||||
export type EditorType2 = FormItemComponent | PlatformEditorTypes.dxGridBox | PlatformEditorTypes.dxImageUpload
|
export type EditorType2 =
|
||||||
|
| FormItemComponent
|
||||||
|
| PlatformEditorTypes.dxGridBox
|
||||||
|
| PlatformEditorTypes.dxImageViewer
|
||||||
|
| PlatformEditorTypes.dxImageUpload
|
||||||
export type SimpleItemWithColData = Overwrite<
|
export type SimpleItemWithColData = Overwrite<
|
||||||
SimpleItem,
|
SimpleItem,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,8 @@ const useGridData = (props: {
|
||||||
editorType:
|
editorType:
|
||||||
i.editorType2 == PlatformEditorTypes.dxGridBox
|
i.editorType2 == PlatformEditorTypes.dxGridBox
|
||||||
? 'dxDropDownBox'
|
? 'dxDropDownBox'
|
||||||
: i.editorType2 == PlatformEditorTypes.dxImageUpload
|
: i.editorType2 == PlatformEditorTypes.dxImageUpload ||
|
||||||
|
i.editorType2 == PlatformEditorTypes.dxImageViewer
|
||||||
? undefined
|
? undefined
|
||||||
: i.editorType2,
|
: i.editorType2,
|
||||||
colSpan: i.colSpan,
|
colSpan: i.colSpan,
|
||||||
|
|
@ -283,6 +284,7 @@ const useGridData = (props: {
|
||||||
colData,
|
colData,
|
||||||
tagBoxOptions: i.tagBoxOptions,
|
tagBoxOptions: i.tagBoxOptions,
|
||||||
gridBoxOptions: i.gridBoxOptions,
|
gridBoxOptions: i.gridBoxOptions,
|
||||||
|
imageUploadOptions: i.imageUploadOptions,
|
||||||
editorScript: i.editorScript,
|
editorScript: i.editorScript,
|
||||||
}
|
}
|
||||||
if (i.dataField.indexOf(':') >= 0) {
|
if (i.dataField.indexOf(':') >= 0) {
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
progressExpr={gridDto.gridOptions.ganttOptionDto?.progressExpr}
|
progressExpr={gridDto.gridOptions.ganttOptionDto?.progressExpr}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Toolbar multiline>
|
<Toolbar>
|
||||||
<Item name="undo" locateInMenu="auto" />
|
<Item name="undo" locateInMenu="auto" />
|
||||||
<Item name="redo" locateInMenu="auto" />
|
<Item name="redo" locateInMenu="auto" />
|
||||||
<Item name="separator" locateInMenu="auto" />
|
<Item name="separator" locateInMenu="auto" />
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ import {
|
||||||
} from './Utils'
|
} from './Utils'
|
||||||
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
||||||
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
||||||
|
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
|
||||||
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
||||||
import { useFilters } from './useFilters'
|
import { useFilters } from './useFilters'
|
||||||
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
||||||
|
|
@ -1247,7 +1248,8 @@ const Grid = (props: GridProps) => {
|
||||||
editorType:
|
editorType:
|
||||||
i.editorType2 == PlatformEditorTypes.dxGridBox
|
i.editorType2 == PlatformEditorTypes.dxGridBox
|
||||||
? 'dxDropDownBox'
|
? 'dxDropDownBox'
|
||||||
: i.editorType2 == PlatformEditorTypes.dxImageUpload
|
: i.editorType2 == PlatformEditorTypes.dxImageUpload ||
|
||||||
|
i.editorType2 == PlatformEditorTypes.dxImageViewer
|
||||||
? undefined
|
? undefined
|
||||||
: i.editorType2,
|
: i.editorType2,
|
||||||
colSpan: i.colSpan,
|
colSpan: i.colSpan,
|
||||||
|
|
@ -1255,6 +1257,14 @@ const Grid = (props: GridProps) => {
|
||||||
editorScript: i.editorScript,
|
editorScript: i.editorScript,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i.editorType2 == PlatformEditorTypes.dxImageViewer) {
|
||||||
|
item.template = 'cellEditImageViewer'
|
||||||
|
item.editorOptions = {
|
||||||
|
...item.editorOptions,
|
||||||
|
value: getValueByField(editingFormDataRef.current, i.dataField),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (i.dataField.indexOf(':') >= 0) {
|
if (i.dataField.indexOf(':') >= 0) {
|
||||||
item.label = { text: captionize(i.dataField.split(':')[1]) }
|
item.label = { text: captionize(i.dataField.split(':')[1]) }
|
||||||
}
|
}
|
||||||
|
|
@ -1822,6 +1832,15 @@ const Grid = (props: GridProps) => {
|
||||||
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
|
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
|
||||||
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
|
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
|
||||||
<Template name={'cellEditImageUpload'} render={ImageUploadEditorComponent} />
|
<Template name={'cellEditImageUpload'} render={ImageUploadEditorComponent} />
|
||||||
|
<Template
|
||||||
|
name={'cellEditImageViewer'}
|
||||||
|
render={(data) => (
|
||||||
|
<ImageViewerEditorComponent
|
||||||
|
{...data}
|
||||||
|
fallbackFormData={editingFormDataRef.current}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Template name="extraFilters">
|
<Template name="extraFilters">
|
||||||
<GridExtraFilterToolbar
|
<GridExtraFilterToolbar
|
||||||
filters={gridDto?.gridOptions.extraFilterDto ?? []}
|
filters={gridDto?.gridOptions.extraFilterDto ?? []}
|
||||||
|
|
@ -1831,7 +1850,6 @@ const Grid = (props: GridProps) => {
|
||||||
</Template>
|
</Template>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
visible={toolbarData.length > 0 || filterToolbarData.length > 0}
|
visible={toolbarData.length > 0 || filterToolbarData.length > 0}
|
||||||
multiline
|
|
||||||
>
|
>
|
||||||
{toolbarData.map((item) => (
|
{toolbarData.map((item) => (
|
||||||
<Item key={item.name} {...item}></Item>
|
<Item key={item.name} {...item}></Item>
|
||||||
|
|
|
||||||
|
|
@ -412,7 +412,8 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
editorType:
|
editorType:
|
||||||
i.editorType2 == PlatformEditorTypes.dxGridBox
|
i.editorType2 == PlatformEditorTypes.dxGridBox
|
||||||
? 'dxDropDownBox'
|
? 'dxDropDownBox'
|
||||||
: i.editorType2 == PlatformEditorTypes.dxImageUpload
|
: i.editorType2 == PlatformEditorTypes.dxImageUpload ||
|
||||||
|
i.editorType2 == PlatformEditorTypes.dxImageViewer
|
||||||
? undefined
|
? undefined
|
||||||
: i.editorType2,
|
: i.editorType2,
|
||||||
colSpan: i.colSpan,
|
colSpan: i.colSpan,
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import {
|
||||||
setGridPanelColor,
|
setGridPanelColor,
|
||||||
} from './Utils'
|
} from './Utils'
|
||||||
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
||||||
|
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
|
||||||
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
||||||
import { useFilters } from './useFilters'
|
import { useFilters } from './useFilters'
|
||||||
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
||||||
|
|
@ -1504,7 +1505,8 @@ const Tree = (props: TreeProps) => {
|
||||||
editorType:
|
editorType:
|
||||||
i.editorType2 == PlatformEditorTypes.dxGridBox
|
i.editorType2 == PlatformEditorTypes.dxGridBox
|
||||||
? 'dxDropDownBox'
|
? 'dxDropDownBox'
|
||||||
: i.editorType2 == PlatformEditorTypes.dxImageUpload
|
: i.editorType2 == PlatformEditorTypes.dxImageUpload ||
|
||||||
|
i.editorType2 == PlatformEditorTypes.dxImageViewer
|
||||||
? undefined
|
? undefined
|
||||||
: i.editorType2,
|
: i.editorType2,
|
||||||
colSpan: i.colSpan,
|
colSpan: i.colSpan,
|
||||||
|
|
@ -1512,6 +1514,14 @@ const Tree = (props: TreeProps) => {
|
||||||
editorScript: i.editorScript,
|
editorScript: i.editorScript,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i.editorType2 == PlatformEditorTypes.dxImageViewer) {
|
||||||
|
item.template = 'cellEditImageViewer'
|
||||||
|
item.editorOptions = {
|
||||||
|
...item.editorOptions,
|
||||||
|
value: getValueByField(editingFormDataRef.current, i.dataField),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (i.dataField.indexOf(':') >= 0) {
|
if (i.dataField.indexOf(':') >= 0) {
|
||||||
item.label = { text: captionize(i.dataField.split(':')[1]) }
|
item.label = { text: captionize(i.dataField.split(':')[1]) }
|
||||||
}
|
}
|
||||||
|
|
@ -1592,6 +1602,15 @@ const Tree = (props: TreeProps) => {
|
||||||
></Editing>
|
></Editing>
|
||||||
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
|
<Template name={'cellEditTagBox'} render={TagBoxEditorComponent} />
|
||||||
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
|
<Template name={'cellEditGridBox'} render={GridBoxEditorComponent} />
|
||||||
|
<Template
|
||||||
|
name={'cellEditImageViewer'}
|
||||||
|
render={(data) => (
|
||||||
|
<ImageViewerEditorComponent
|
||||||
|
{...data}
|
||||||
|
fallbackFormData={editingFormDataRef.current}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Template name="extraFilters">
|
<Template name="extraFilters">
|
||||||
<GridExtraFilterToolbar
|
<GridExtraFilterToolbar
|
||||||
filters={gridDto?.gridOptions.extraFilterDto ?? []}
|
filters={gridDto?.gridOptions.extraFilterDto ?? []}
|
||||||
|
|
@ -1601,7 +1620,6 @@ const Tree = (props: TreeProps) => {
|
||||||
</Template>
|
</Template>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
visible={(toolbarData?.length ?? 0) > 0 || (filterToolbarData?.length ?? 0) > 0}
|
visible={(toolbarData?.length ?? 0) > 0 || (filterToolbarData?.length ?? 0) > 0}
|
||||||
multiline
|
|
||||||
>
|
>
|
||||||
{toolbarData?.map((item) => (
|
{toolbarData?.map((item) => (
|
||||||
<Item key={item.name} {...item}></Item>
|
<Item key={item.name} {...item}></Item>
|
||||||
|
|
|
||||||
198
ui/src/views/list/editors/ImageViewerEditorComponent.tsx
Normal file
198
ui/src/views/list/editors/ImageViewerEditorComponent.tsx
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
import { ReactElement } from 'react'
|
||||||
|
|
||||||
|
const parseJsonObject = (value: unknown) => {
|
||||||
|
if (!value) return undefined
|
||||||
|
if (typeof value === 'object') return value as Record<string, any>
|
||||||
|
if (typeof value !== 'string') return undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value)
|
||||||
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||||
|
? (parsed as Record<string, any>)
|
||||||
|
: undefined
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeImageSize = (value: unknown, fallback: number) => {
|
||||||
|
const size = Number(value)
|
||||||
|
return Number.isFinite(size) && size > 0 ? size : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageSource = (value: unknown) => {
|
||||||
|
if (!value) return ''
|
||||||
|
if (typeof value === 'string') return value.trim()
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const item = value as Record<string, unknown>
|
||||||
|
return String(item.url ?? item.src ?? item.fileUrl ?? item.path ?? item.value ?? '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(value).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isProbablyBase64Image = (value: string) =>
|
||||||
|
value.length > 80 && /^[A-Za-z0-9+/]+={0,2}$/.test(value)
|
||||||
|
|
||||||
|
const toImageSource = (value: string) => {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return ''
|
||||||
|
if (trimmed.startsWith('data:image/') || !isProbablyBase64Image(trimmed)) return trimmed
|
||||||
|
return `data:image/jpeg;base64,${trimmed}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitImageString = (value: string) => {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
if (trimmed.startsWith('data:image/')) return [trimmed]
|
||||||
|
|
||||||
|
return trimmed
|
||||||
|
.split(/\r?\n|\|\s*/)
|
||||||
|
.flatMap((part) => {
|
||||||
|
const text = part.trim()
|
||||||
|
if (!text || text.startsWith('data:image/')) return text ? [text] : []
|
||||||
|
return text.split(',').map((item) => item.trim())
|
||||||
|
})
|
||||||
|
.map(toImageSource)
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeImageValue = (value: unknown): string[] => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.flatMap((item) => normalizeImageValue(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
|
||||||
|
if (trimmed.startsWith('[')) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed)
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed.flatMap((item) => normalizeImageValue(item))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return [toImageSource(trimmed)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return splitImageString(trimmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = getImageSource(value)
|
||||||
|
return source ? [toImageSource(source)] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
const openImage = (url: string) => {
|
||||||
|
if (!url.startsWith('data:image/')) {
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const match = url.match(/^data:(image\/[^;]+);base64,(.*)$/)
|
||||||
|
if (!match) return
|
||||||
|
|
||||||
|
const binary = atob(match[2])
|
||||||
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))
|
||||||
|
const blobUrl = URL.createObjectURL(new Blob([bytes], { type: match[1] }))
|
||||||
|
window.open(blobUrl, '_blank', 'noopener,noreferrer')
|
||||||
|
window.setTimeout(() => URL.revokeObjectURL(blobUrl), 60000)
|
||||||
|
} catch {
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveTemplateValue = (templateData: any) => {
|
||||||
|
const dataField =
|
||||||
|
templateData?.dataField ||
|
||||||
|
templateData?.item?.dataField ||
|
||||||
|
templateData?.editorOptions?.name ||
|
||||||
|
templateData?.name
|
||||||
|
|
||||||
|
const fieldCandidates = String(dataField || '')
|
||||||
|
.split(':')
|
||||||
|
.reduce<string[]>(
|
||||||
|
(fields, field) => {
|
||||||
|
const value = field.trim()
|
||||||
|
return value ? [...fields, value] : fields
|
||||||
|
},
|
||||||
|
dataField ? [String(dataField)] : [],
|
||||||
|
)
|
||||||
|
|
||||||
|
const formDataSources = [
|
||||||
|
templateData?.component?.option?.('formData'),
|
||||||
|
templateData?.formData,
|
||||||
|
templateData?.data,
|
||||||
|
templateData?.fallbackFormData,
|
||||||
|
].filter((source) => source && typeof source === 'object')
|
||||||
|
|
||||||
|
for (const formData of formDataSources) {
|
||||||
|
for (const field of fieldCandidates) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(formData, field)) {
|
||||||
|
return formData[field]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualKey = Object.keys(formData).find((key) =>
|
||||||
|
fieldCandidates.some((field) => key.toLowerCase() === field.toLowerCase()),
|
||||||
|
)
|
||||||
|
if (actualKey) {
|
||||||
|
return formData[actualKey]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return templateData?.value ?? templateData?.editorOptions?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveTemplateOptions = (templateData: any) => {
|
||||||
|
const itemOptions =
|
||||||
|
templateData?.editorOptions?.imageUploadOptions ??
|
||||||
|
templateData?.imageUploadOptions ??
|
||||||
|
parseJsonObject(templateData?.editorOptions?.editorOptions) ??
|
||||||
|
parseJsonObject(templateData?.editorOptions)
|
||||||
|
|
||||||
|
return itemOptions && typeof itemOptions === 'object' ? itemOptions : {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageViewerEditorComponent = (templateData: any): ReactElement => {
|
||||||
|
const value = resolveTemplateValue(templateData)
|
||||||
|
const options = resolveTemplateOptions(templateData)
|
||||||
|
const urls = normalizeImageValue(value)
|
||||||
|
const thumbW = normalizeImageSize(options?.width, 80)
|
||||||
|
const thumbH = normalizeImageSize(options?.height, 80)
|
||||||
|
|
||||||
|
if (!urls.length) {
|
||||||
|
return <div className="text-gray-400 px-1 py-2">-</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', padding: 4 }}>
|
||||||
|
{urls.map((url, index) => (
|
||||||
|
<button
|
||||||
|
key={`${url}-${index}`}
|
||||||
|
type="button"
|
||||||
|
onClick={() => openImage(url)}
|
||||||
|
style={{ border: 0, background: 'transparent', padding: 0, cursor: 'zoom-in' }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt=""
|
||||||
|
style={{
|
||||||
|
width: thumbW,
|
||||||
|
height: thumbH,
|
||||||
|
objectFit: 'cover',
|
||||||
|
borderRadius: 4,
|
||||||
|
border: '1px solid #ddd',
|
||||||
|
display: 'block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ImageViewerEditorComponent }
|
||||||
|
|
@ -137,16 +137,10 @@ const cellTemplateImage = (
|
||||||
cellInfo: DataGridTypes.ColumnCellTemplateData<any, any>,
|
cellInfo: DataGridTypes.ColumnCellTemplateData<any, any>,
|
||||||
) => {
|
) => {
|
||||||
if (cellInfo?.value) {
|
if (cellInfo?.value) {
|
||||||
const urls: string[] = Array.isArray(cellInfo.value)
|
const urls = normalizeImageCellValue(cellInfo.value)
|
||||||
? cellInfo.value.filter(Boolean)
|
const imgOptions = getImageColumnOptions(cellInfo.column)
|
||||||
: [cellInfo.value].filter(Boolean)
|
const w = normalizeImageSize(imgOptions.width, 40)
|
||||||
|
const h = normalizeImageSize(imgOptions.height, 40)
|
||||||
//const col = cellInfo.column as any
|
|
||||||
//const imgOptions = col?.extras?.imageUploadOptions ?? {}
|
|
||||||
//const w: number = imgOptions.width ?? 40
|
|
||||||
//const h: number = imgOptions.height ?? 40
|
|
||||||
const w: number = 40
|
|
||||||
const h: number = 40
|
|
||||||
|
|
||||||
cellElement.style.cssText += 'display:flex;flex-wrap:wrap;align-items:center;gap:4px;'
|
cellElement.style.cssText += 'display:flex;flex-wrap:wrap;align-items:center;gap:4px;'
|
||||||
cellElement.innerHTML = ''
|
cellElement.innerHTML = ''
|
||||||
|
|
@ -161,12 +155,128 @@ const cellTemplateImage = (
|
||||||
img.addEventListener('mouseenter', (e) => showImgPreview(url, e as MouseEvent))
|
img.addEventListener('mouseenter', (e) => showImgPreview(url, e as MouseEvent))
|
||||||
img.addEventListener('mousemove', (e) => showImgPreview(url, e as MouseEvent))
|
img.addEventListener('mousemove', (e) => showImgPreview(url, e as MouseEvent))
|
||||||
img.addEventListener('mouseleave', hideImgPreview)
|
img.addEventListener('mouseleave', hideImgPreview)
|
||||||
|
img.addEventListener('click', () => openImageSource(url))
|
||||||
|
|
||||||
cellElement.appendChild(img)
|
cellElement.appendChild(img)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizeImageSize = (value: unknown, fallback: number) => {
|
||||||
|
const size = Number(value)
|
||||||
|
return Number.isFinite(size) && size > 0 ? size : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseJsonObject = (value: unknown) => {
|
||||||
|
if (!value) return undefined
|
||||||
|
if (typeof value === 'object') return value as Record<string, any>
|
||||||
|
if (typeof value !== 'string') return undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value)
|
||||||
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||||
|
? (parsed as Record<string, any>)
|
||||||
|
: undefined
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageColumnOptions = (column: unknown) => {
|
||||||
|
const col = column as any
|
||||||
|
return (
|
||||||
|
col?.extras?.imageUploadOptions ??
|
||||||
|
parseJsonObject(col?.extras?.editorOptions) ??
|
||||||
|
parseJsonObject(col?.editorOptions) ??
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImageSource = (value: unknown) => {
|
||||||
|
if (!value) return ''
|
||||||
|
if (typeof value === 'string') return value.trim()
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const item = value as Record<string, unknown>
|
||||||
|
return String(item.url ?? item.src ?? item.fileUrl ?? item.path ?? item.value ?? '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(value).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isProbablyBase64Image = (value: string) =>
|
||||||
|
value.length > 80 && /^[A-Za-z0-9+/]+={0,2}$/.test(value)
|
||||||
|
|
||||||
|
const toImageSource = (value: string) => {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return ''
|
||||||
|
if (trimmed.startsWith('data:image/') || !isProbablyBase64Image(trimmed)) return trimmed
|
||||||
|
return `data:image/jpeg;base64,${trimmed}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const openImageSource = (url: string) => {
|
||||||
|
if (!url.startsWith('data:image/')) {
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const match = url.match(/^data:(image\/[^;]+);base64,(.*)$/)
|
||||||
|
if (!match) return
|
||||||
|
|
||||||
|
const binary = atob(match[2])
|
||||||
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))
|
||||||
|
const blobUrl = URL.createObjectURL(new Blob([bytes], { type: match[1] }))
|
||||||
|
window.open(blobUrl, '_blank', 'noopener,noreferrer')
|
||||||
|
window.setTimeout(() => URL.revokeObjectURL(blobUrl), 60000)
|
||||||
|
} catch {
|
||||||
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitImageString = (value: string) => {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
if (trimmed.startsWith('data:image/')) return [trimmed]
|
||||||
|
|
||||||
|
return trimmed
|
||||||
|
.split(/\r?\n|\|\s*/)
|
||||||
|
.flatMap((part) => {
|
||||||
|
const text = part.trim()
|
||||||
|
if (!text || text.startsWith('data:image/')) return text ? [text] : []
|
||||||
|
return text.split(',').map((item) => item.trim())
|
||||||
|
})
|
||||||
|
.map(toImageSource)
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeImageCellValue = (value: unknown): string[] => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.flatMap((item) => normalizeImageCellValue(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (!trimmed) return []
|
||||||
|
|
||||||
|
if (trimmed.startsWith('[')) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed)
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed.flatMap((item) => normalizeImageCellValue(item))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return [toImageSource(trimmed)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return splitImageString(trimmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = getImageSource(value)
|
||||||
|
return source ? [toImageSource(source)] : []
|
||||||
|
}
|
||||||
|
|
||||||
function calculateFilterExpressionMultiValue(
|
function calculateFilterExpressionMultiValue(
|
||||||
this: DataGridTypes.Column,
|
this: DataGridTypes.Column,
|
||||||
filterValue: any,
|
filterValue: any,
|
||||||
|
|
@ -777,11 +887,13 @@ const useListFormColumns = ({
|
||||||
if (!colData.lookupDto?.dataSourceType) {
|
if (!colData.lookupDto?.dataSourceType) {
|
||||||
const allFormItems = gridDto.gridOptions.editingFormDto.flatMap((group) => group.items)
|
const allFormItems = gridDto.gridOptions.editingFormDto.flatMap((group) => group.items)
|
||||||
const imageFormItem = allFormItems.find((a) => a?.dataField === colData.fieldName)
|
const imageFormItem = allFormItems.find((a) => a?.dataField === colData.fieldName)
|
||||||
if (imageFormItem?.editorType2 === PlatformEditorTypes.dxImageUpload) {
|
const isImageUploadEditor = imageFormItem?.editorType2 === PlatformEditorTypes.dxImageUpload
|
||||||
|
const isImageViewerEditor = imageFormItem?.editorType2 === PlatformEditorTypes.dxImageViewer
|
||||||
|
if (isImageUploadEditor || isImageViewerEditor) {
|
||||||
// imageUploadOptions'ı önce imageFormItem.imageUploadOptions'dan al,
|
// imageUploadOptions'ı önce imageFormItem.imageUploadOptions'dan al,
|
||||||
// yoksa editorOptions JSON içinden parse et (admin panelinde editorOptions ile yapılandırılır)
|
// yoksa editorOptions JSON içinden parse et (admin panelinde editorOptions ile yapılandırılır)
|
||||||
let imageUploadOptions = imageFormItem.imageUploadOptions
|
let imageUploadOptions = imageFormItem?.imageUploadOptions
|
||||||
if (!imageUploadOptions && imageFormItem.editorOptions) {
|
if (!imageUploadOptions && imageFormItem?.editorOptions) {
|
||||||
try {
|
try {
|
||||||
imageUploadOptions = JSON.parse(imageFormItem.editorOptions)
|
imageUploadOptions = JSON.parse(imageFormItem.editorOptions)
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -801,10 +913,12 @@ const useListFormColumns = ({
|
||||||
}
|
}
|
||||||
column.extras = {
|
column.extras = {
|
||||||
multiValue: imageUploadOptions?.multiple ?? false,
|
multiValue: imageUploadOptions?.multiple ?? false,
|
||||||
editorOptions: imageFormItem.editorOptions,
|
editorOptions: imageFormItem?.editorOptions ?? colData.editorOptions,
|
||||||
imageUploadOptions: imageUploadOptions,
|
imageUploadOptions: imageUploadOptions,
|
||||||
}
|
}
|
||||||
|
if (isImageUploadEditor) {
|
||||||
column.editCellTemplate = 'cellEditImageUpload'
|
column.editCellTemplate = 'cellEditImageUpload'
|
||||||
|
}
|
||||||
column.cellTemplate = cellTemplateImage as any
|
column.cellTemplate = cellTemplateImage as any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue