Fix bricks-framework component usage: replace invalid List with custom implementation, replace Input/Checkbox/Select/Textarea with proper UiStr/UiInt/UiCheck/UiCode/UiText components with required name attributes
This commit is contained in:
parent
bc3e94b64a
commit
34949ea16c
@ -29,8 +29,10 @@
|
|||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiStr",
|
||||||
|
"id": "chat-input",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "chat-input",
|
||||||
"placeholder": "输入消息...",
|
"placeholder": "输入消息...",
|
||||||
"width": "100%"
|
"width": "100%"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,9 +25,10 @@
|
|||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiStr",
|
||||||
"id": "session-name",
|
"id": "session-name",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "session-name",
|
||||||
"label": "Session Name",
|
"label": "Session Name",
|
||||||
"placeholder": "Enter session name...",
|
"placeholder": "Enter session name...",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
@ -35,9 +36,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Select",
|
"widgettype": "UiCode",
|
||||||
"id": "model-select",
|
"id": "model-select",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "model-select",
|
||||||
"label": "AI Model",
|
"label": "AI Model",
|
||||||
"placeholder": "Select model...",
|
"placeholder": "Select model...",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
@ -46,9 +48,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Textarea",
|
"widgettype": "UiText",
|
||||||
"id": "initial-prompt",
|
"id": "initial-prompt",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "initial-prompt",
|
||||||
"label": "Initial Prompt (Optional)",
|
"label": "Initial Prompt (Optional)",
|
||||||
"placeholder": "Enter initial context or instructions...",
|
"placeholder": "Enter initial context or instructions...",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
|
|||||||
149
wwwroot/scripts/list-component.js
Normal file
149
wwwroot/scripts/list-component.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Custom List component for bricks-framework
|
||||||
|
// This component renders a list of items with custom item templates
|
||||||
|
|
||||||
|
bricks.List = class extends bricks.VBox {
|
||||||
|
constructor(opts) {
|
||||||
|
super(opts);
|
||||||
|
this.data_url = opts.data_url || null;
|
||||||
|
this.items = opts.items || [];
|
||||||
|
this.itemHeight = opts.itemHeight || 50;
|
||||||
|
this._loading = false;
|
||||||
|
|
||||||
|
// Set default dimensions if not provided
|
||||||
|
if (!this.options.width) this.options.width = '100%';
|
||||||
|
if (!this.options.height) this.options.height = '100%';
|
||||||
|
|
||||||
|
// Load data if data_url is provided
|
||||||
|
if (this.data_url) {
|
||||||
|
this.loadData();
|
||||||
|
} else if (this.items.length > 0) {
|
||||||
|
this.renderItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadData() {
|
||||||
|
if (this._loading || !this.data_url) return;
|
||||||
|
this._loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.data_url);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
this.items = Array.isArray(data) ? data : (data.items || []);
|
||||||
|
this.renderItems();
|
||||||
|
} else {
|
||||||
|
console.error('Failed to load list data:', response.status);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading list data:', error);
|
||||||
|
} finally {
|
||||||
|
this._loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItems() {
|
||||||
|
// Clear existing children
|
||||||
|
this.subwidgets = [];
|
||||||
|
this.children = [];
|
||||||
|
|
||||||
|
// Render each item using the template defined in subwidgets
|
||||||
|
this.items.forEach((item, index) => {
|
||||||
|
const itemWidget = this.createItemWidget(item, index);
|
||||||
|
if (itemWidget) {
|
||||||
|
this.appendChild(itemWidget);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Refresh the display
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
createItemWidget(itemData, index) {
|
||||||
|
if (!this.template || this.template.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the template and process it with item data
|
||||||
|
const processedTemplate = this.processTemplate(this.template[0], itemData, index);
|
||||||
|
if (processedTemplate) {
|
||||||
|
const widget = bricks.Factory.create(processedTemplate);
|
||||||
|
if (widget) {
|
||||||
|
// Store reference to item data for event handling
|
||||||
|
widget._listItemData = itemData;
|
||||||
|
widget._listItemIndex = index;
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
processTemplate(template, itemData, index) {
|
||||||
|
// This is a simplified template processor
|
||||||
|
// In a real implementation, you would need proper Jinja2-like processing
|
||||||
|
const processed = JSON.parse(JSON.stringify(template));
|
||||||
|
|
||||||
|
// Process options recursively
|
||||||
|
this.processObject(processed, itemData, index);
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
processObject(obj, itemData, index) {
|
||||||
|
if (typeof obj !== 'object' || obj === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in obj) {
|
||||||
|
if (typeof obj[key] === 'string') {
|
||||||
|
// Simple string replacement for {{item.xxx}} patterns
|
||||||
|
if (obj[key].includes('{{item.')) {
|
||||||
|
const match = obj[key].match(/{{item\.([^}]+)}}/);
|
||||||
|
if (match && match[1]) {
|
||||||
|
const fieldName = match[1];
|
||||||
|
obj[key] = obj[key].replace(`{{item.${fieldName}}}`, itemData[fieldName] || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle conditional expressions like {{ '#22C55E' if item.status == 'Connected' else '#EF4444' }}
|
||||||
|
if (obj[key].includes('{{ ') && obj[key].includes(' if item.')) {
|
||||||
|
const fullMatch = obj[key].match(/{{\s*(.*?)\s+if\s+item\.(\w+)\s*==\s*'([^']+)'\s+else\s+(.*?)\s*}}/);
|
||||||
|
if (fullMatch) {
|
||||||
|
const trueValue = fullMatch[1].trim().replace(/^['"]|['"]$/g, '');
|
||||||
|
const fieldName = fullMatch[2];
|
||||||
|
const conditionValue = fullMatch[3];
|
||||||
|
const falseValue = fullMatch[4].trim().replace(/^['"]|['"]$/g, '');
|
||||||
|
|
||||||
|
obj[key] = itemData[fieldName] === conditionValue ? trueValue : falseValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||||
|
this.processObject(obj[key], itemData, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public method to reload data
|
||||||
|
reload() {
|
||||||
|
if (this.data_url) {
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public method to set items directly
|
||||||
|
setItems(items) {
|
||||||
|
this.items = items || [];
|
||||||
|
this.renderItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override appendChild to capture the template
|
||||||
|
appendChild(widget) {
|
||||||
|
if (!this.template) {
|
||||||
|
this.template = this.subwidgets || [];
|
||||||
|
}
|
||||||
|
super.appendChild(widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the List component
|
||||||
|
bricks.register('List', bricks.List);
|
||||||
|
|
||||||
|
console.log('Custom List component registered with bricks framework');
|
||||||
@ -25,9 +25,10 @@
|
|||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiStr",
|
||||||
"id": "service-search",
|
"id": "service-search",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "service-search",
|
||||||
"placeholder": "Search services...",
|
"placeholder": "Search services...",
|
||||||
"width": "300px",
|
"width": "300px",
|
||||||
"icon": "fa fa-search"
|
"icon": "fa fa-search"
|
||||||
@ -107,7 +108,7 @@
|
|||||||
"widgettype": "Badge",
|
"widgettype": "Badge",
|
||||||
"options": {
|
"options": {
|
||||||
"text": "{{item.status}}",
|
"text": "{{item.status}}",
|
||||||
"bgcolor": "{{ '#22C55E' if data.status == 'Connected' else '#EF4444' }}",
|
"bgcolor": "{{ '#22C55E' if item.status == 'Connected' else '#EF4444' }}",
|
||||||
"color": "#FFFFFF",
|
"color": "#FFFFFF",
|
||||||
"borderRadius": "12px",
|
"borderRadius": "12px",
|
||||||
"padding": "4px 12px",
|
"padding": "4px 12px",
|
||||||
@ -119,7 +120,7 @@
|
|||||||
{
|
{
|
||||||
"widgettype": "Text",
|
"widgettype": "Text",
|
||||||
"options": {
|
"options": {
|
||||||
"text": "{{data.endpoint}}",
|
"text": "{{item.endpoint}}",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"color": "#CBD5E1",
|
"color": "#CBD5E1",
|
||||||
"marginBottom": "8px"
|
"marginBottom": "8px"
|
||||||
@ -150,7 +151,7 @@
|
|||||||
"actiontype": "urlwidget",
|
"actiontype": "urlwidget",
|
||||||
"target": "app.main-content",
|
"target": "app.main-content",
|
||||||
"options": {
|
"options": {
|
||||||
"url": "{{entire_url('edit_service.ui?id={{data.id}}')}}"
|
"url": "{{entire_url('edit_service.ui?id={{item.id}}')}}"
|
||||||
},
|
},
|
||||||
"mode": "replace"
|
"mode": "replace"
|
||||||
}
|
}
|
||||||
@ -175,7 +176,7 @@
|
|||||||
"target": "self",
|
"target": "self",
|
||||||
"rfname": "remove_service",
|
"rfname": "remove_service",
|
||||||
"params": {
|
"params": {
|
||||||
"service_id": "{{data.id}}"
|
"service_id": "{{item.id}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -199,7 +200,7 @@
|
|||||||
"target": "self",
|
"target": "self",
|
||||||
"rfname": "test_service_connection",
|
"rfname": "test_service_connection",
|
||||||
"params": {
|
"params": {
|
||||||
"service_id": "{{data.id}}"
|
"service_id": "{{item.id}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -25,9 +25,10 @@
|
|||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiStr",
|
||||||
"id": "session-search",
|
"id": "session-search",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "session-search",
|
||||||
"placeholder": "Search sessions...",
|
"placeholder": "Search sessions...",
|
||||||
"width": "300px",
|
"width": "300px",
|
||||||
"icon": "fa fa-search"
|
"icon": "fa fa-search"
|
||||||
|
|||||||
@ -59,9 +59,10 @@
|
|||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiCode",
|
||||||
"id": "default-model",
|
"id": "default-model",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "default-model",
|
||||||
"label": "Default AI Model",
|
"label": "Default AI Model",
|
||||||
"placeholder": "Select default model...",
|
"placeholder": "Select default model...",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
@ -69,20 +70,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiInt",
|
||||||
"id": "session-timeout",
|
"id": "session-timeout",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "session-timeout",
|
||||||
"label": "Session Timeout (minutes)",
|
"label": "Session Timeout (minutes)",
|
||||||
"type": "number",
|
|
||||||
"placeholder": "Enter timeout in minutes...",
|
"placeholder": "Enter timeout in minutes...",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
"marginBottom": "16px"
|
"marginBottom": "16px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Checkbox",
|
"widgettype": "UiCheck",
|
||||||
"id": "auto-save",
|
"id": "auto-save",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "auto-save",
|
||||||
"label": "Auto-save sessions",
|
"label": "Auto-save sessions",
|
||||||
"checked": true,
|
"checked": true,
|
||||||
"marginBottom": "24px"
|
"marginBottom": "24px"
|
||||||
@ -160,8 +162,9 @@
|
|||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiStr",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "model-name",
|
||||||
"label": "Model Name",
|
"label": "Model Name",
|
||||||
"value": "{{data.name}}",
|
"value": "{{data.name}}",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
@ -169,8 +172,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Input",
|
"widgettype": "UiStr",
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "provider",
|
||||||
"label": "Provider",
|
"label": "Provider",
|
||||||
"value": "{{data.provider}}",
|
"value": "{{data.provider}}",
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
@ -284,20 +288,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Checkbox",
|
{
|
||||||
"id": "require-auth",
|
"widgettype": "UiCheck",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Require authentication for web access",
|
"name": "require-auth",
|
||||||
"checked": false,
|
"label": "Require authentication for API access",
|
||||||
|
"checked": "{{security.require_auth}}",
|
||||||
"marginBottom": "12px"
|
"marginBottom": "12px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Checkbox",
|
"widgettype": "UiCheck",
|
||||||
"id": "encrypt-storage",
|
|
||||||
"options": {
|
"options": {
|
||||||
|
"name": "encrypt-storage",
|
||||||
"label": "Encrypt local storage",
|
"label": "Encrypt local storage",
|
||||||
"checked": false,
|
"checked": "{{security.encrypt_storage}}",
|
||||||
|
"marginBottom": "24px"
|
||||||
"marginBottom": "24px"
|
"marginBottom": "24px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user