{
    "type": "page",
    "title": "Welcome to Steedos",
    "body": [
        {
            "type": "input-number",
            "label": "数字",
            "name": "number",
            "keyboard": true,
            "id": "u:55258dda1b12"
        },
        {
            "type": "button",
            "label": "新建",
            "onEvent": {
                "click": {
                    "actions": [
                        {
                            "ignoreError": false,
                            "actionType": "custom",
                            "script": "\nconst B6_TABLES_API = 'https://5100-steedos-builder6-2f49pu35txa.ws-us117.gitpod.io/api/tables/v2';\nconst baseId = 'test';\nconst tableId = '674d49ac0d476806265942249';\n\nconst gridApi = agGridRefs && agGridRefs[tableId];\n\nfunction scrollToBottomAfterRefresh() {\n  const rowCount = gridApi.getDisplayedRowCount();\n  if (rowCount > 0) {\n    // Scroll to the last row\n    gridApi.ensureIndexVisible(rowCount - 1, 'bottom');\n  }\n\n  // Remove the event listener to prevent scrolling on subsequent updates\n  gridApi.removeEventListener('storeRefreshed', scrollToBottomAfterRefresh);\n}\n\nfunction getFieldDefaultValue(colDef) {\n  const fieldConfig = colDef.cellEditorParams && colDef.cellEditorParams.fieldConfig;\n  if (!fieldConfig) {\n    //左侧行勾选框列没有cellEditorParams.fieldConfig\n    return;\n  }\n  const defaultValue = fieldConfig.default_value;\n  if (typeof defaultValue !== \"undefined\") {\n    return defaultValue;\n  }\n  if (fieldConfig.type === \"boolean\") {\n    return false;\n  }\n}\n\n// 创建一个新行数据，可以初始化为默认值或空值\nfunction createNewRowData() {\n  const newRow = {};\n  // 可以给每个字段一个默认值，例如：\n  const allGridColumns = gridApi.getAllGridColumns();\n  // 字段类型值转换\n  const colDefs = _.map(allGridColumns, \"colDef\");\n  colDefs.forEach(colDef => {\n    const dfValue = getFieldDefaultValue(colDef); // 设置为空或设置默认值\n    if (typeof dfValue != \"undefined\") {\n      newRow[colDef.field] = dfValue;\n    }\n  });\n\n  // 设置默认值后需要进行依赖字段默认值的公式计算\n  agGridExports.setRowDataFormulaValues(newRow, gridApi);\n  return newRow;\n}\n\n// 新增行的功能\nasync function addNewRow() {\n  if (!gridApi) {\n    console.error('Grid api not available. Ensure grid is initialized properly.');\n    return;\n  }\n\n  // 有排序和过滤条件情况下不允许新建数据，因为新建后不知道是哪一行\n  const gridState = gridApi.getState();\n  const sortState = gridState.sort;\n  const filterState = gridState.filter;\n  if (!_.isEmpty(sortState)) {\n    alert(\"请先移除排序\");\n    return;\n  }\n\n  if (!_.isEmpty(filterState)) {\n    alert(\"请先移除过滤条件\");\n    return;\n  }\n\n  const newRow = createNewRowData();\n\n  // 将新增数据发送到服务器\n  try {\n    const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId, {\n      method: 'POST',\n      credentials: 'include',\n      headers: {\n        'Content-Type': 'application/json'\n      },\n      body: JSON.stringify(newRow)\n    });\n\n    if (response.ok) {\n      console.log('New row added successfully');\n      // 新增数据成功后刷新网格数据\n      gridApi.addEventListener('storeRefreshed', scrollToBottomAfterRefresh);\n      gridApi.refreshServerSide({ purge: false });//purge设置为true会造成上面scrollToBottomAfterRefresh不生效\n\n    } else {\n      console.error('Failed to add new row:', response.statusText);\n    }\n  } catch (error) {\n    console.error('Error adding new row:', error);\n  }\n}\n\naddNewRow();",
                            "args": {}
                        }
                    ]
                }
            },
            "id": "u:54aff36d25d8",
            "editorState": "default"
        },
        {
            "type": "button",
            "label": "删除",
            "onEvent": {
                "click": {
                    "actions": [
                        {
                            "ignoreError": false,
                            "actionType": "custom",
                            "script": "const B6_TABLES_API = 'https://5100-steedos-builder6-2f49pu35txa.ws-us117.gitpod.io/api/tables/v2';\nconst baseId = 'test';\nconst tableId = '674d49ac0d476806265942249';\n\nconst amisNotify = event.context && event.context.env && event.context.env.notify || alert;\n\nconst gridApi = agGridRefs && agGridRefs[tableId];\n\nfunction getAllRowData() {\n  const rowData = [];\n  const rowCount = gridApi.getDisplayedRowCount();\n\n  for (let i = 0; i < rowCount; i++) {\n    const rowNode = gridApi.getDisplayedRowAtIndex(i);\n    rowData.push(rowNode.data);\n  }\n\n  return rowData;\n}\n\nfunction getSelectedRowData() {\n  // Get the selected nodes and extract their data\n  const gridState = gridApi.getState();\n  const rowSelectionState = gridState.rowSelection;\n  const isSelectAll = rowSelectionState && rowSelectionState.selectAll;\n  if (isSelectAll) {\n    // 用户勾选了表头全选勾选框时，gridApi.getSelectedNodes()取不到数据，这里手动获取列表上的行数据\n    // gridApi.getState().rowSelection.toggledNodes 中记录了全选时用户取消了哪些选项的id值集合\n    const toggledNodes = rowSelectionState.toggledNodes;\n    let allRowData = getAllRowData();\n    if (_.isEmpty(toggledNodes)) {\n      return allRowData;\n    }\n    else {\n      const selectedData = allRowData.filter(dataItem => toggledNodes.indexOf(dataItem._id) < 0);\n      return selectedData;\n    }\n  }\n  else {\n    const selectedNodes = gridApi.getSelectedNodes();\n    const selectedData = selectedNodes.map(node => node.data);\n    return selectedData;\n  }\n}\n\nasync function deleteSelectedRows() {\n  if (!gridApi) {\n    console.error('Grid api not available. Ensure grid is initialized properly.');\n    return;\n  }\n\n  const selectedData = getSelectedRowData();\n  const selectedIds = selectedData.map(data => data._id);\n  console.log('Deleting Rows:', selectedIds);\n\n  // Check if any rows are selected\n  if (selectedIds.length === 0) {\n    amisNotify(\"warn\", \"没有选中任何行！\");\n    return;\n  }\n\n  try {\n    /* 后续支持批量删除接口可以改为只调用一次删除接口\n    // Call the API to delete the selected rows\n    const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId, {\n        method: 'DELETE',\n        headers: {\n            'Content-Type': 'application/json'\n        },\n        body: JSON.stringify({ ids: selectedIds })\n    });\n\n    const data = await response.json();\n\n    if (data.success) {\n        // Remove the selected rows from the grid\n        gridApi.applyTransaction({ remove: selectedData });\n    } else {\n        amisNotify('删除行时发生错误');\n    }*/\n\n\n    for (const data of selectedData) {\n      const id = data._id;\n      // Call the API to delete each selected row\n      const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId + '/' + id, {\n        method: 'DELETE',\n        credentials: 'include',\n        headers: {\n          'Content-Type': 'application/json'\n        }\n      });\n\n      const result = await response.json();\n      \n      if (result.deleted) {\n        // Remove the row from the grid only if the deletion was successful\n        gridApi.applyServerSideTransaction({ remove: [data] });\n      } else {\n        amisNotify(\"warn\", \"删除 ID 为 \" + id + \" 的行失败\");\n      }\n    }\n  } catch (error) {\n    console.error('Error deleting rows:', error);\n    \n    amisNotify('删除行时发生错误');\n  }\n}\n\ndeleteSelectedRows();",
                            "args": {}
                        }
                    ]
                }
            },
            "id": "u:47c5bc4d3ef5",
            "editorState": "default"
        },
        {
            "type": "ag-grid",
            "id": "u:84a2c6efa298",
            "dsType": "api",
            "className": "bg-gray-100 border-b sm:rounded sm:border border-gray-300 p-4 mb-4 h-full",
            "dataFilter": "const B6_TABLES_API = 'https://5100-steedos-builder6-2f49pu35txa.ws-us117.gitpod.io/api/tables/v2';\nconst baseId = 'test';\nconst tableId = '674d49ac0d476806265942249';\n// 启用 AG Grid 企业版\nAgGrid.LicenseManager.setLicenseKey(\"YOUR_LICENSE_KEY_HERE\");\n\nlet gridApi; // 提前声明 gridApi 以便全局访问\nlet gridOptions; // 提前声明 gridOptions 以便全局访问\nlet table;\nlet evaluate = AmisCore.evaluate;\n\n// 用于window全局获取gridApi\nif (typeof window.agGridRefs === \"undefined\") { \n    window.agGridRefs = {};\n}\nagGridRefs = window.agGridRefs;\n\n// 用于window全局获取公共变量及函数\nif (typeof window.agGridExports === \"undefined\") {\n    window.agGridExports = {};\n}\nagGridExports = window.agGridExports;\n\n// 用于window全局获取每个grid对应的gridOptions\nagGridExports.gridOptions = {};\n\nfunction padZero(num) {\n    num = num.toString();\n    return num.length < 2 ? \"0\" + num : num;\n}\n\nfunction getDataTypeDefinitions() {\n    return {\n        date: {\n            baseDataType: 'date',\n            extendsDataType: 'date',\n            valueParser: function (params) {\n                // ag-grid官网明确说valueParser是用来实现保存数据前数据转换的，但是实测这个函数并不会被触发，包括修改字段值触发保存在内任何时候都不会触发\n                // 另外valueSetter也能实现类似功能，但是一样实测不会被触发\n                // 所以只能手动在调用保存接口前实现相关转换逻辑\n                // 见：\n                // https://www.ag-grid.com/javascript-data-grid/column-properties/#reference-editing-valueParser\n                // https://www.ag-grid.com/javascript-data-grid/cell-data-types/#overriding-the-pre-defined-cell-data-type-definitions\n                console.log(\"valueParser:\", params.newValue);\n                var fieldName = params.colDef.field;\n                var fieldValue = params.newValue;\n                if (!fieldValue) return null;\n                var newDate = new Date(fieldValue);\n\n                // 设置为选中日期的 UTC 0 点\n                newDate.setUTCHours(0, 0, 0, 0);\n\n                return newDate;\n            },\n            valueGetter: function (params) {\n                var fieldType = params.colDef.cellEditorParams.fieldConfig.type;\n                var fieldName = params.colDef.field;\n                var fieldValue = params.data[fieldName];\n                if (!fieldValue) return null;\n\n                var date = new Date(fieldValue);\n                return date;\n            },\n            valueFormatter: function (params) {\n                var fieldType = params.colDef.cellEditorParams.fieldConfig.type;\n                var date = new Date(params.value);\n\n                if (!params.value) return \"\";\n\n                if (fieldType === \"date\") {\n                    return date.getFullYear() + '-' + padZero(date.getMonth() + 1) + '-' + padZero(date.getDate());\n                } else if (fieldType === \"datetime\") {\n                    // Convert to local time considering timezone\n                    var localDate = new Date(date.getTime());\n                    return localDate.getFullYear() + '-' + padZero(localDate.getMonth() + 1) + '-' + padZero(localDate.getDate()) + ' ' + padZero(localDate.getHours()) + ':' + padZero(localDate.getMinutes());\n                }\n\n                return \"\";\n            }\n        },\n        formula: {\n            baseDataType: 'text',\n            extendsDataType: 'text',\n            fields: {}\n        }\n    };\n}\n\nfunction tooltipValueGetter(params) {\n    if (params.data.__verificationErrors && params.data.__verificationErrors.length) {\n        return params.data.__verificationErrors[0];\n    }\n}\n\nfunction getColumnDef(field, dataTypeDefinitions) {\n    var cellDataType,\n        cellEditorParams,\n        cellEditor,\n        valueFormatter,\n        valueGetter,\n        fieldOptions,\n        editable = true,\n        filter,\n        filterParams;\n\n    // 根据字段类型设置 dataType\n    cellEditorParams = {\n        fieldConfig: field\n    };\n    filterParams = {\n        debounceMs: 200,\n        maxNumConditions: 1\n    };\n    switch (field.type) {\n        case 'text':\n        case 'textarea':\n            cellDataType = 'text';\n            filter = 'agTextColumnFilter';\n            Object.assign(filterParams, {\n                filterOptions: [\"contains\", \"notContains\", \"equals\", \"startsWith\", \"endsWith\"]\n            });\n            break;\n        case 'number':\n            cellDataType = 'number';\n            Object.assign(cellEditorParams, {\n                precision: field.precision || 0\n            });\n            filter = 'agNumberColumnFilter';\n            Object.assign(filterParams, {\n                filterOptions: [\"equals\", \"greaterThan\", \"greaterThanOrEqual\", \"lessThan\", \"lessThanOrEqual\"]\n            });\n            break;\n        case 'select':\n            cellDataType = 'text';\n            fieldOptions = field.options && field.options.split(`\\n`).map(function (n) { return n.trim(); }) || [];\n            fieldOptions.unshift(null);\n            Object.assign(cellEditorParams, {\n                values: fieldOptions\n            });\n            cellEditor = \"agSelectCellEditor\";\n            filter = 'agSetColumnFilter';\n            Object.assign(filterParams, {\n                values: fieldOptions\n            });\n            break;\n        case 'select-multiple':\n            cellDataType = 'object';\n            fieldOptions = field.options && field.options.split(`\\n`).map(function (n) { return n.trim(); }) || [];\n            Object.assign(cellEditorParams, {\n                values: fieldOptions\n            });\n            // cellEditor = MultiSelectCellEditor;\n            filter = 'agSetColumnFilter';\n            Object.assign(filterParams, {\n                values: fieldOptions\n            });\n            break;\n        case 'date':\n            cellDataType = 'date';\n            cellEditor = \"agDateCellEditor\";\n            valueFormatter = dataTypeDefinitions.date.valueFormatter;\n            // 如果不定义valueGetter，双击单元格进入编辑状态时，值显示为空\n            valueGetter = dataTypeDefinitions.date.valueGetter;\n            filter = 'agDateColumnFilter';\n            Object.assign(filterParams, {\n                filterOptions: [\"equals\", \"greaterThan\", \"greaterThanOrEqual\", \"lessThan\", \"lessThanOrEqual\"]\n            });\n            break;\n        case 'datetime':\n            cellDataType = 'date';\n            cellEditor = DateTimeEditor;\n            // 因为日期时间依赖了DateTimeEditor.init函数中对初始值定义，所以这里没必要再走一次valueGetter\n            // valueGetter = dataTypeDefinitions.date.valueGetter;\n            filter = 'agDateColumnFilter';\n            Object.assign(filterParams, {\n                filterOptions: [\"equals\", \"greaterThan\", \"greaterThanOrEqual\", \"lessThan\", \"lessThanOrEqual\"]\n            });\n            break;\n        case 'boolean':\n            cellDataType = 'boolean';\n            filter = 'agSetColumnFilter';\n            Object.assign(filterParams, {\n                values: [true, false],\n                suppressSelectAll: true,\n                comparator: function (a, b) {\n                    // 将 true 显示在 false 之前\n                    if (a === true && b === false) return -1;\n                    if (a === false && b === true) return 1;\n                    return 0;\n                },\n                valueFormatter: function (params) {\n                    return params.value ? '是' : '否';\n                }\n            });\n            break;\n        case 'formula':\n            cellDataType = 'formula';\n            editable = false;\n            // 记录所有公式字段配置方便取出来用\n            dataTypeDefinitions.formula.fields[field.name.toLowerCase()] = field;\n            break;\n        default:\n            cellDataType = 'text'; // 默认类型\n    }\n\n    return {\n        field: field.name.toLowerCase(),\n        headerName: field.label,\n        cellDataType: cellDataType,\n        cellEditorParams: cellEditorParams,\n        cellEditor: cellEditor,\n        editable: editable,\n        valueFormatter: valueFormatter,\n        valueGetter: valueGetter,\n        tooltipValueGetter: tooltipValueGetter,\n        filter: filter,\n        filterParams: filterParams\n    };\n\n}\n\nfunction initGridOptions() {\n    if (!table || !table.fields) {\n        return config;\n    }\n    var dataTypeDefinitions = getDataTypeDefinitions();\n\n    var columnDefs = table.fields.map(function (field) {\n        return getColumnDef(field, dataTypeDefinitions);\n    });\n\n    var pageSize = 1000;\n    // 初始化网格配置\n    gridOptions = {\n        columnDefs: columnDefs,\n        dataTypeDefinitions: dataTypeDefinitions,\n        rowClassRules: {\n            'ag-grid-verification-errors-row': function (params) {\n                return params.data && params.data.__verificationErrors && params.data.__verificationErrors.length;\n            }\n        },\n        rowData: null, // 初始为空，通过 API 动态加载\n        rowModelType: 'serverSide',\n        pagination: false,\n        paginationPageSizeSelector: false,\n        paginationPageSize: pageSize,\n        cacheBlockSize: pageSize,\n        editType: 'fullRow',\n        cellSelection: {\n            handle: {\n                mode: 'range',\n            }\n        },\n        onRowValueChanged: onRowValueChanged,\n        defaultColDef: {\n            flex: 1,\n            minWidth: 100,\n            resizable: true\n        },\n        getRowId: function (params) { return params.data._id; },\n        selectionColumnDef: {\n            pinned: 'left'\n        },\n        rowSelection: {\n            mode: \"multiRow\",\n            selectAll: \"all\",\n            checkboxes: true,\n            headerCheckbox: true\n        },\n        serverSideDatasource: getServerSideDatasource()\n    };\n\n    gridOptions = Object.assign({}, config, gridOptions);\n    console.log(\"amis agGrid gridOptions:\", gridOptions);\n    return gridOptions;\n}\n\n\nconst FilterTypesMap = {\n    'equals': '=',\n    'notEqual': '!=',\n    'contains': 'contains',\n    'notContains': 'notcontains',\n    'startsWith': 'startswith',\n    'endsWith': 'endswith',\n    'lessThan': '<',\n    'lessThanOrEqual': '<=',\n    'greaterThan': '>',\n    'greaterThanOrEqual': '>=',\n    'empty': 'empty' //TODO 不支持\n}\n\n/**\n * 把ag-grid filterModel 转为魔方filters格式\n * @param filterModel \n */\nfunction filterModelToOdataFilters(filterModel, colDefs) {\n    const filters = [];\n    _.forEach(filterModel, (value, key) => {\n        const fieldConfig = colDefs[key].cellEditorParams.fieldConfig;\n        if (value.type === 'between') {\n            if (value.filterType === \"number\") {\n                filters.push([key, \"between\", [value.numberFrom, value.numberTo]]);\n            } else {\n                if (value.filter) {\n                    filters.push([key, value.type, value.filter]);\n                } else {\n                    filters.push([key, \"between\", [value.dateFrom, value.dateTo]]);\n                }\n            }\n\n        } else {\n            let filterItem;\n            switch (fieldConfig.type) {\n                case 'text':\n                case 'textarea':\n                    filterItem = [key, FilterTypesMap[value.type], value.filter];\n                    filters.push(filterItem);\n                    break;\n                case 'number':\n                    filterItem = [key, FilterTypesMap[value.type], value.filter];\n                    filters.push(filterItem);\n                    break;\n                case 'select':\n                case 'select-multiple':\n                    // 因为不需要支持多选，这里先不处理，如果要支持多选使用anyof过滤操作符应该就可以了，比如[\"category\", \"anyof\", selectedCategories]\n                    const filterValues = value.values;\n                    if (filterValues.length) {\n                        let filterItem = [];\n                        for (let i = 0; i < filterValues.length; i++) {\n                            filterItem.push([key, \"=\", filterValues[i]]);\n                            if (i < filterValues.length - 1) {\n                                filterItem.push(\"or\");\n                            }\n                        }\n                        filters.push(filterItem);\n                    }\n                    break;\n                case 'date':\n                case 'datetime':\n                    let dateValue = new Date(value.dateFrom);\n                    if (fieldConfig.type === \"date\") {\n                        // 设置为日期的 UTC 0 点\n                        const timezoneOffset = dateValue.getTimezoneOffset();\n                        dateValue = new Date(dateValue.getTime() - timezoneOffset * 60 * 1000);\n                    }\n                    filterItem = [key, FilterTypesMap[value.type], dateValue];\n                    filters.push(filterItem);\n                    break;\n                case 'boolean':\n                    let filterValue = value.values[0];\n                    if (typeof filterValue !== \"boolean\") {\n                        filterValue = filterValue === \"true\"\n                    }\n                    filterItem = [key, \"=\", filterValue];\n                    filters.push(filterItem);\n                    break;\n                case 'formula':\n                    // 不支持公式字段过滤\n                    break;\n            }\n        }\n    })\n    return filters;\n}\n\nfunction getServerSideDatasource() {\n    return {\n        getRows: function (params) {\n            console.log('Server Side Datasource - Requesting rows from server:', params.request);\n            gridApi = params.api;\n            agGridRefs[tableId] = gridApi;\n            // 模拟Promise来处理异步流程，尽量贴近async/await效果\n            return new Promise(function (resolve, reject) {\n                try {\n                    var colDefs = _.keyBy(_.map(params.api.getAllGridColumns(), function (col) { return col.colDef; }), \"field\");\n                    var modelFilters = filterModelToOdataFilters(params.request.filterModel, colDefs);\n                    console.log('Server Side Datasource - Requesting rows by modelFilters:', modelFilters);\n                    var url = B6_TABLES_API + '/' + baseId + '/' + tableId;\n                    // 翻页\n                    var startRow = params.request.startRow;\n                    var endRow = params.request.endRow;\n                    var pageSize = params.api.paginationGetPageSize();\n\n                    var separator = url.indexOf('?') !== -1 ? '&' : '?';\n                    url += separator + 'skip=' + startRow + '&top=' + pageSize;\n\n                    // 过滤\n                    if (modelFilters.length > 0) {\n                        separator = url.indexOf('?') !== -1 ? '&' : '?';\n                        url += separator + 'filters=' + JSON.stringify(modelFilters);\n                    }\n\n                    // 排序\n                    var sortModel = params.request.sortModel;\n                    var sort = [];\n                    _.forEach(sortModel, function (sortField) {\n                        sort.push(sortField.colId + ' ' + sortField.sort);\n                    });\n                    console.log('Server Side Datasource - Requesting rows by sortModel:', sortModel);\n                    if (sort.length > 0) {\n                        separator = url.indexOf('?') !== -1 ? '&' : '?';\n                        url += separator + 'sort=' + sort.join(\",\");\n                    }\n\n                    fetch(url, {\n                        credentials: 'include'\n                    }).then(function (response) {\n                        return response.json();\n                    }).then(function (data) {\n                        console.log('Server Side Datasource - data:', data);\n                        params.success({\n                            rowData: data.data,\n                            rowCount: data.totalCount\n                        });\n                        resolve();\n                    }).catch(function (error) {\n                        console.error('Error fetching data from server:', error);\n                        params.fail();\n                        reject(error);\n                    });\n                } catch (error) {\n                    console.error('Error fetching data from server:', error);\n                    params.fail();\n                    reject(error);\n                }\n            });\n        }\n    };\n}\n\nfunction setRowDataFormulaValues(rowData, targetGridApi) {\n    // 获取数据类型定义\n    var dataTypeDefinitions = targetGridApi.getGridOption(\"dataTypeDefinitions\");\n    var formulaFields = dataTypeDefinitions.formula.fields;\n\n    // 遍历每个公式字段并计算\n    for (var formulaFieldName in formulaFields) {\n        if (formulaFields.hasOwnProperty(formulaFieldName)) {\n            var formulaField = formulaFields[formulaFieldName];\n            var formula = formulaField.formula;\n            rowData[formulaFieldName] = evaluate(formula, rowData, { evalMode: true });\n        }\n    }\n}\n\nagGridExports.setRowDataFormulaValues = setRowDataFormulaValues;\n\n// 监听行数据改变事件\nasync function onRowValueChanged(event) {\n    const data = event.data;\n    console.log('Saving updated data to server:', data);\n    try {\n        const allGridColumns = event.api.getAllGridColumns();\n        // 字段类型值转换\n        const colDefs = _.keyBy(_.map(allGridColumns, \"colDef\"), \"field\");\n        _.each(data, function (n, k) {\n            const colDef = colDefs[k];\n            if (colDef) {\n                const fieldConfig = colDef.cellEditorParams.fieldConfig;\n                if (n && fieldConfig.type === \"date\") {\n                    // 设置为选中日期的 UTC 0 点\n                    const timezoneOffset = n.getTimezoneOffset();\n                    const utcDate = new Date(n.getTime() - timezoneOffset * 60 * 1000);\n                    data[k] = utcDate;\n                }\n            }\n        });\n        // 校验\n        const rowNode = event.node;\n        let validated = true;\n        const verifications = table && table.verifications || [];\n        const verificationErrors = [];\n        verifications.forEach(function (verification) {\n            validated = evaluate(verification.rule, data, { evalMode: true });\n            if (!validated) {\n                verificationErrors.push(verification.alert);\n            }\n        });\n        validated = verificationErrors.length === 0;\n        if (!validated) {\n            console.log(\"The table verifications is not passed for the row data:\", verifications, data);\n            event.api.startEditingCell({\n                rowIndex: event.rowIndex,\n                colKey: allGridColumns[0].colId\n            });\n            rowNode.setData(Object.assign({}, data, { __verificationErrors: verificationErrors }));\n            env.notify(\"warn\", verificationErrors.join(`\\n`))\n            return;\n        }\n        // 循环所有公式字段执行公式计算并设置值到data中\n        setRowDataFormulaValues(data, event.api);\n        // 保存更新的数据到服务端\n        delete data.__verificationErrors\n        const response = await fetch(B6_TABLES_API + '/' + baseId + '/' + tableId + '/' + data._id, {\n            credentials: 'include',\n            method: 'PUT',\n            headers: {\n                'Content-Type': 'application/json'\n            },\n            body: JSON.stringify(data)\n        });\n        const responseData = await response.json();\n        console.log('Data saved successfully:', responseData);\n        rowNode.setData(responseData);\n    } catch (error) {\n        console.error('Error saving data:', error);\n    }\n}\n\n\n\nreturn fetch(B6_TABLES_API + '/meta/bases/' + baseId + '/tables/' + tableId, { credentials: 'include' })\n    .then(function (response) {\n        return response.json().then(function (data) {\n            table = data;\n            console.log(table);\n            return data;\n        });\n    })\n    .then(function (data) {\n        var gridOptions = initGridOptions();\n        agGridExports.gridOptions[tableId] = gridOptions;\n        return gridOptions;\n    })\n    .catch(function (error) {\n        console.error('Error initializing grid:', error);\n        reject(error);\n    });\n\n",
            "dataSource": "${items}",
            "config": ""
        }
    ],
    "regions": [
        "body"
    ],
    "data": {
        "initialValues": {},
        "appId": "builder",
        "title": ""
    },
    "id": "u:198ee5f0561e",
    "asideResizor": false,
    "editorState": "default",
    "pullRefresh": {
        "disabled": true
    }
}