Enhance Iframe Integration in Form Builder

- Added new iframe parameters in FormBuilderFieldSettingsModal, allowing users to configure debug mode, hide completion messages, apply themes, and set custom URL parameters for button components linked to processes.
- Updated ComponentPreview.vue to generate dynamic workflow URLs with iframe parameters based on user settings, improving the flexibility of iframe integration.
- Implemented a URL preview feature in FormBuilderFieldSettingsModal to display the generated iframe URL based on the current configuration, enhancing user experience and usability.
- Enhanced form builder interface to include new settings for iframe integration, ensuring a more comprehensive configuration for button actions.
This commit is contained in:
Afiq 2025-08-06 13:13:16 +08:00
parent 3f452a46a3
commit 0023ddebcf
3 changed files with 190 additions and 4 deletions

View File

@ -1265,15 +1265,42 @@ const saveNestedComponentSettings = (updatedComponent) => {
const getButtonLink = () => {
if (!props.component || props.component.type !== 'button') return null;
const { linkType, linkUrl, linkProcessId, linkTarget } = props.component.props;
const { linkType, linkUrl, linkProcessId, linkTarget, iframeDebug, iframeHideComplete, iframeTheme, iframeCustomParams } = props.component.props;
if (linkType === 'url' && linkUrl) {
return linkUrl;
}
if (linkType === 'process' && linkProcessId) {
// Generate the process workflow URL
return `${window.location.origin}/workflow/${linkProcessId}`;
// Generate the process workflow URL with iframe parameters
const baseUrl = `${window.location.origin}/workflow/${linkProcessId}`;
const params = new URLSearchParams();
// Add debug parameter (false = iframe mode, true = debug mode)
if (iframeDebug !== undefined) {
params.append('debug', iframeDebug ? 'true' : 'false');
}
// Add hideComplete parameter
if (iframeHideComplete) {
params.append('hideComplete', 'true');
}
// Add theme parameter
if (iframeTheme) {
params.append('theme', iframeTheme);
}
// Add custom parameters
if (iframeCustomParams) {
const customParams = new URLSearchParams(iframeCustomParams);
customParams.forEach((value, key) => {
params.append(key, value);
});
}
const queryString = params.toString();
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
}
return null;

View File

@ -637,6 +637,62 @@
help="Choose how the link should open"
:classes="{ outer: 'field-wrapper' }"
/>
<!-- Iframe Parameters -->
<div class="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<h6 class="text-sm font-medium text-blue-800 mb-3">Iframe Integration Parameters</h6>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<FormKit
type="switch"
label="Debug Mode"
name="iframeDebug"
v-model="configModel.iframeDebug"
help="Enable debug mode (show UI chrome) - OFF for iframe mode"
:classes="{ outer: 'field-wrapper' }"
/>
<FormKit
type="switch"
label="Hide Completion"
name="iframeHideComplete"
v-model="configModel.iframeHideComplete"
help="Hide completion message (auto-advance)"
:classes="{ outer: 'field-wrapper' }"
/>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3">
<FormKit
type="select"
label="Theme"
name="iframeTheme"
v-model="configModel.iframeTheme"
:options="[
{ label: 'Default', value: '' },
{ label: 'Dark', value: 'dark' },
{ label: 'Light', value: 'light' }
]"
help="Apply custom theme to the workflow"
:classes="{ outer: 'field-wrapper' }"
/>
<FormKit
type="text"
label="Custom Parameters"
name="iframeCustomParams"
v-model="configModel.iframeCustomParams"
help="Additional URL parameters (e.g., 'param1=value1&param2=value2')"
:classes="{ outer: 'field-wrapper' }"
placeholder="param1=value1&param2=value2"
/>
</div>
<div class="mt-3 p-2 bg-gray-100 rounded text-xs text-gray-600">
<strong>Generated URL Preview:</strong><br>
<code class="text-blue-600">{{ getIframeUrlPreview() }}</code>
</div>
</div>
</div>
</div>
@ -2007,6 +2063,42 @@ watch(() => props.component, (newComponent) => {
}
}, { immediate: true })
// Generate iframe URL preview
const getIframeUrlPreview = () => {
if (!configModel.value.linkProcessId) {
return 'Select a process first...'
}
const baseUrl = `${window.location.origin}/workflow/${configModel.value.linkProcessId}`
const params = new URLSearchParams()
// Add debug parameter (false = iframe mode, true = debug mode)
if (configModel.value.iframeDebug !== undefined) {
params.append('debug', configModel.value.iframeDebug ? 'true' : 'false')
}
// Add hideComplete parameter
if (configModel.value.iframeHideComplete) {
params.append('hideComplete', 'true')
}
// Add theme parameter
if (configModel.value.iframeTheme) {
params.append('theme', configModel.value.iframeTheme)
}
// Add custom parameters
if (configModel.value.iframeCustomParams) {
const customParams = new URLSearchParams(configModel.value.iframeCustomParams)
customParams.forEach((value, key) => {
params.append(key, value)
})
}
const queryString = params.toString()
return queryString ? `${baseUrl}?${queryString}` : baseUrl
}
// Type changing functionality
const compatibilityGroups = {
// Text-based inputs (can switch between each other)
@ -2738,7 +2830,11 @@ const getDefaultPropsForType = (type) => {
linkType: 'none', // 'none', 'url', 'process'
linkUrl: '',
linkProcessId: '',
linkTarget: '_self' // '_self', '_blank'
linkTarget: '_self', // '_self', '_blank'
iframeDebug: false, // Show/hide debug UI
iframeHideComplete: false, // Hide completion message
iframeTheme: '', // Custom theme
iframeCustomParams: '' // Additional URL parameters
}
}

View File

@ -559,6 +559,61 @@
:classes="{ outer: 'mb-0', input: 'text-sm' }"
/>
</div>
<!-- Iframe Parameters (for button with process link) -->
<div v-if="showQuickField('iframeDebug') && quickSettings.linkType === 'process'" class="space-y-2">
<div class="setting-item">
<label class="setting-toggle">
<input
type="checkbox"
v-model="quickSettings.iframeDebug"
@change="updateQuickSetting('iframeDebug', $event.target.checked)"
class="toggle-input"
/>
<span class="toggle-slider"></span>
<span class="toggle-label">Debug Mode (OFF = iframe)</span>
</label>
</div>
<div class="setting-item">
<label class="setting-toggle">
<input
type="checkbox"
v-model="quickSettings.iframeHideComplete"
@change="updateQuickSetting('iframeHideComplete', $event.target.checked)"
class="toggle-input"
/>
<span class="toggle-slider"></span>
<span class="toggle-label">Hide Completion</span>
</label>
</div>
<div class="setting-item">
<label class="setting-label">Theme</label>
<FormKit
type="select"
v-model="quickSettings.iframeTheme"
@input="updateQuickSetting('iframeTheme', $event)"
:options="[
{ label: 'Default', value: '' },
{ label: 'Dark', value: 'dark' },
{ label: 'Light', value: 'light' }
]"
:classes="{ outer: 'mb-0', input: 'text-sm' }"
/>
</div>
<div class="setting-item">
<label class="setting-label">Custom Params</label>
<FormKit
type="text"
v-model="quickSettings.iframeCustomParams"
@input="updateQuickSetting('iframeCustomParams', $event)"
placeholder="param1=value1&param2=value2"
:classes="{ outer: 'mb-0', input: 'text-sm' }"
/>
</div>
</div>
</div>
</div>
</div>
@ -2867,6 +2922,10 @@ watch(() => formStore.selectedComponent, (newComponent) => {
linkType: newComponent.props.linkType || 'none',
linkUrl: newComponent.props.linkUrl || '',
linkProcessId: newComponent.props.linkProcessId || '',
iframeDebug: newComponent.props.iframeDebug || false,
iframeHideComplete: newComponent.props.iframeHideComplete || false,
iframeTheme: newComponent.props.iframeTheme || '',
iframeCustomParams: newComponent.props.iframeCustomParams || '',
required: newComponent.props.validation?.includes('required') || false
};
@ -2894,6 +2953,10 @@ const showQuickField = (fieldName) => {
linkType: ['button'],
linkUrl: ['button'],
linkProcessId: ['button'],
iframeDebug: ['button'],
iframeHideComplete: ['button'],
iframeTheme: ['button'],
iframeCustomParams: ['button'],
width: ['text', 'textarea', 'number', 'email', 'password', 'url', 'tel', 'mask', 'select', 'checkbox', 'radio', 'switch', 'date', 'time', 'datetime-local', 'range', 'color', 'file', 'otp', 'dropzone', 'button', 'heading', 'paragraph', 'info-display'],
required: ['text', 'textarea', 'number', 'email', 'password', 'url', 'tel', 'mask', 'select', 'checkbox', 'radio', 'date', 'time', 'datetime-local', 'file', 'otp', 'dropzone']
};