From baf518307924c585396682f1eafcf395061a259a Mon Sep 17 00:00:00 2001 From: jingrow Date: Sat, 6 Sep 2025 02:08:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B8=82=E5=9C=BAAPP?= =?UTF-8?q?=E8=AE=A2=E9=98=85=E7=AD=89=E7=BA=A7=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src2/components/site/InstallAppDialog.vue | 8 +- .../pagetype/ai_node_schema/__init__.py | 0 .../pagetype/ai_node_schema/ai_node_schema.js | 303 ------ .../ai_node_schema/ai_node_schema.json | 49 - .../pagetype/ai_node_schema/ai_node_schema.py | 102 -- .../ai_node_schema/test_ai_node_schema.py | 9 - .../pagetype/app_source/app_source.json | 9 +- .../jcloud/pagetype/app_source/app_source.py | 4 +- .../js/schema_builder/SchemaBuilder.vue | 41 - .../components/EditableInput.vue | 104 -- .../components/PropertyDialog.vue | 171 ---- .../components/PropertyEditor.vue | 152 --- .../components/PropertyItem.vue | 146 --- .../components/PropertyProperties.vue | 207 ---- .../components/SchemaCanvas.vue | 157 --- .../schema_builder/components/SchemaField.vue | 165 ---- .../components/SchemaSidebar.vue | 237 ----- .../schema_builder/schema_builder.bundle.js | 892 ------------------ jcloud/public/js/schema_builder/store.js | 146 --- 19 files changed, 11 insertions(+), 2891 deletions(-) delete mode 100644 jcloud/jcloud/pagetype/ai_node_schema/__init__.py delete mode 100644 jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.js delete mode 100644 jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.json delete mode 100644 jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.py delete mode 100644 jcloud/jcloud/pagetype/ai_node_schema/test_ai_node_schema.py delete mode 100644 jcloud/public/js/schema_builder/SchemaBuilder.vue delete mode 100644 jcloud/public/js/schema_builder/components/EditableInput.vue delete mode 100644 jcloud/public/js/schema_builder/components/PropertyDialog.vue delete mode 100644 jcloud/public/js/schema_builder/components/PropertyEditor.vue delete mode 100644 jcloud/public/js/schema_builder/components/PropertyItem.vue delete mode 100644 jcloud/public/js/schema_builder/components/PropertyProperties.vue delete mode 100644 jcloud/public/js/schema_builder/components/SchemaCanvas.vue delete mode 100644 jcloud/public/js/schema_builder/components/SchemaField.vue delete mode 100644 jcloud/public/js/schema_builder/components/SchemaSidebar.vue delete mode 100644 jcloud/public/js/schema_builder/schema_builder.bundle.js delete mode 100644 jcloud/public/js/schema_builder/store.js diff --git a/dashboard/src2/components/site/InstallAppDialog.vue b/dashboard/src2/components/site/InstallAppDialog.vue index 4a1bf8d..f2fc902 100644 --- a/dashboard/src2/components/site/InstallAppDialog.vue +++ b/dashboard/src2/components/site/InstallAppDialog.vue @@ -94,10 +94,10 @@ export default { // 如果应用不可安装,显示对应提示 if (!checkResult.installable) { const subscriptionTypeMap = { - 2: 'Pro', - 3: 'Business', - 4: 'Enterprise', - 5: 'Ultimate' + 2: '299元/月', + 3: '399元/月', + 4: '499元/月', + 5: '599元/月' }; const requiredPlan = subscriptionTypeMap[checkResult.required_plan_level] || diff --git a/jcloud/jcloud/pagetype/ai_node_schema/__init__.py b/jcloud/jcloud/pagetype/ai_node_schema/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.js b/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.js deleted file mode 100644 index 486bb97..0000000 --- a/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.js +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (c) 2025, Jingrow and contributors -// For license information, please see license.txt - -jingrow.ui.form.on("AI Node Schema", { - refresh(frm) { - console.log("AI Node Schema refresh called", frm); - - // 添加编辑节点Schema按钮到右上角 - if (frm.pg.node_type) { - frm.add_custom_button(__('编辑节点Schema'), function() { - open_schema_editor(frm); - }); - - frm.add_custom_button(__('保存到文件'), function() { - save_schema_to_file(frm); - }); - } - - console.log("AI Node Schema 按钮添加成功"); - }, - - node_type(frm) { - // 当节点类型改变时,自动加载对应的 schema - if (frm.pg.node_type && !frm.pg.node_schema) { - load_schema_for_node_type(frm, frm.pg.node_type); - } - } -}); - -function open_schema_editor(frm) { - // 检查是否有 node_type - if (!frm.pg.node_type) { - jingrow.msgprint(__("请先选择节点类型"), __("提示")); - return; - } - - // 创建 schema 编辑器对话框 - let d = new jingrow.ui.Dialog({ - title: __("编辑 {0} Schema", [frm.pg.node_type]), - size: "extra-large", - fields: [ - { - fieldtype: "HTML", - fieldname: "schema_editor", - options: ` -
-
-
- ` - } - ], - primary_action_label: __("保存"), - primary_action: function() { - save_schema_from_editor(frm, d); - }, - secondary_action_label: __("关闭"), - secondary_action: function() { - d.hide(); - } - }); - - d.show(); - - // 初始化 Vue 编辑器 - setTimeout(() => { - init_vue_schema_editor(d, frm); - }, 100); -} - -function init_vue_schema_editor(dialog, frm) { - const container = dialog.fields_dict.schema_editor.$wrapper.find('#schema-builder')[0]; - - // 动态加载 Vue.js 和组件 - load_vue_js().then(() => { - // Vue.js 加载完成后,加载 schema builder 组件 - load_schema_builder_components().then(() => { - create_vue_app(container, frm, dialog); - }).catch(error => { - jingrow.msgprint(__("加载 Schema 编辑器失败: {0}", [error.message]), __("错误")); - }); - }).catch(error => { - jingrow.msgprint(__("加载 Vue.js 失败: {0}", [error.message]), __("错误")); - }); -} - -function load_vue_js() { - return new Promise((resolve, reject) => { - // 检查是否已经加载 - if (typeof Vue !== 'undefined' && typeof Pinia !== 'undefined') { - resolve(); - return; - } - - // 先加载 Vue.js - const vueScript = document.createElement('script'); - vueScript.src = 'https://unpkg.com/vue@3/dist/vue.global.js'; - vueScript.onload = () => { - // 然后加载 VueDemi - const vueDemiScript = document.createElement('script'); - vueDemiScript.src = 'https://unpkg.com/vue-demi@0.14.6/lib/index.iife.js'; - vueDemiScript.onload = () => { - // 最后加载 Pinia - const piniaScript = document.createElement('script'); - piniaScript.src = 'https://unpkg.com/pinia@2/dist/pinia.iife.js'; - piniaScript.onload = () => { - // 等待加载完成 - setTimeout(() => { - if (typeof Vue !== 'undefined' && typeof Pinia !== 'undefined') { - resolve(); - } else { - reject(new Error('Vue.js 或 Pinia 加载失败')); - } - }, 100); - }; - piniaScript.onerror = () => { - reject(new Error('无法加载 Pinia')); - }; - document.head.appendChild(piniaScript); - }; - vueDemiScript.onerror = () => { - reject(new Error('无法加载 VueDemi')); - }; - document.head.appendChild(vueDemiScript); - }; - vueScript.onerror = () => { - reject(new Error('无法加载 Vue.js')); - }; - document.head.appendChild(vueScript); - }); -} - -function create_vue_app(container, frm, dialog) { - // 创建 Vue 应用 - const { createApp } = Vue; - const { createPinia } = Pinia; - - const app = createApp({ - template: ` - - `, - components: { - SchemaBuilder: window.SchemaBuilder - }, - setup() { - const nodeType = frm.pg.node_type || ""; - - // 确保 initialSchema 是对象而不是字符串 - let initialSchema = {}; - if (frm.pg.node_schema) { - if (typeof frm.pg.node_schema === 'string') { - try { - initialSchema = JSON.parse(frm.pg.node_schema); - } catch (e) { - console.error('解析 node_schema 失败:', e); - initialSchema = {}; - } - } else if (typeof frm.pg.node_schema === 'object') { - initialSchema = frm.pg.node_schema; - } - } - - function handleSave(schemaData) { - // 保存时也需要转换为字符串 - frm.set_value('node_schema', JSON.stringify(schemaData, null, 2)); - frm.save().then(() => { - jingrow.msgprint(__("Schema 保存成功")); - dialog.hide(); - }); - } - - function handleChange(schemaData) { - // 实时更新表单数据 - 需要转换为字符串 - frm.set_value('node_schema', JSON.stringify(schemaData, null, 2)); - } - - return { - nodeType, - initialSchema, - handleSave, - handleChange - }; - } - }); - - // 使用 Pinia - app.use(createPinia()); - - // 挂载应用 - app.mount(container); - - // 保存应用引用到对话框 - dialog.vue_app = app; -} - -function load_schema_builder_components() { - return new Promise((resolve, reject) => { - // 检查是否已经加载 - if (window.SchemaBuilder && window.SchemaStore) { - resolve(); - return; - } - - // 动态加载组件(不使用 module 类型) - const script = document.createElement('script'); - script.src = '/assets/jcloud/js/schema_builder/schema_builder.bundle.js'; - script.onload = () => { - // 等待组件加载完成 - setTimeout(() => { - if (window.SchemaBuilder && window.SchemaStore) { - resolve(); - } else { - reject(new Error('组件加载失败')); - } - }, 100); - }; - script.onerror = () => { - reject(new Error('无法加载组件文件')); - }; - document.head.appendChild(script); - }); -} - -function save_schema_from_editor(frm, dialog) { - try { - // 如果使用 Vue 编辑器 - if (dialog.vue_app) { - // Vue 编辑器会自动保存,这里只需要关闭对话框 - dialog.hide(); - return; - } - - // 如果使用 JSON 编辑器 - if (dialog.json_editor) { - const schema_data = dialog.json_editor.getValue(); - frm.set_value('node_schema', schema_data); - frm.save().then(() => { - jingrow.msgprint(__("Schema 保存成功")); - dialog.hide(); - }); - } - } catch (e) { - jingrow.msgprint(__("保存失败: {0}", [e.message]), __("错误")); - } -} - -function load_schema_for_node_type(frm, node_type) { - jingrow.call('jingrow.ai.utils.node_schema.get_node_schema', { - node_type: node_type - }).then(schema_data => { - // 确保 schema_data 是对象 - let parsedSchema = {}; - if (typeof schema_data === 'string') { - try { - parsedSchema = JSON.parse(schema_data); - } catch (e) { - console.error('解析 schema 失败:', e); - parsedSchema = {}; - } - } else if (typeof schema_data === 'object') { - parsedSchema = schema_data; - } - - frm.set_value('node_schema', parsedSchema); - jingrow.msgprint(__("节点 Schema 加载成功")); - }).catch(e => { - jingrow.msgprint(__("加载失败: {0}", [e.message]), __("错误")); - }); -} - -function save_schema_to_file(frm) { - if (!frm.pg.node_type || !frm.pg.node_schema) { - jingrow.msgprint(__("请先选择节点类型并设置 Schema 数据"), __("提示")); - return; - } - - // 确保 schema_data 是对象 - let schemaData = frm.pg.node_schema; - if (typeof schemaData === 'string') { - try { - schemaData = JSON.parse(schemaData); - } catch (e) { - jingrow.msgprint(__("Schema 数据格式错误"), __("错误")); - return; - } - } - - // 使用现有的保存方法 - jingrow.call('jcloud.jcloud.pagetype.ai_node_schema.ai_node_schema.save_node_schema', { - node_type: frm.pg.node_type, - schema_data: schemaData - }).then(() => { - jingrow.msgprint(__("Schema 已保存到文件")); - }).catch(e => { - jingrow.msgprint(__("保存失败: {0}", [e.message]), __("错误")); - }); -} - -// 简化实现,移除复杂的对话框和状态指示器 diff --git a/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.json b/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.json deleted file mode 100644 index 2151d87..0000000 --- a/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "creation": "2025-08-27 01:45:28.762568", - "engine": "InnoDB", - "field_order": [ - "node_type", - "node_schema" - ], - "fields": [ - { - "fieldname": "node_type", - "fieldtype": "Data", - "label": "Node Type" - }, - { - "fieldname": "node_schema", - "fieldtype": "JSON", - "label": "Node Schema" - } - ], - "grid_page_length": 50, - "index_web_pages_for_search": 1, - "links": [], - "modified": "2025-08-27 01:46:44.409389", - "modified_by": "Administrator", - "module": "Jcloud", - "name": "AI Node Schema", - "owner": "Administrator", - "pagetype": "PageType", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "row_format": "Dynamic", - "sort_field": "modified", - "sort_order": "DESC", - "states": [] -} \ No newline at end of file diff --git a/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.py b/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.py deleted file mode 100644 index 62d336b..0000000 --- a/jcloud/jcloud/pagetype/ai_node_schema/ai_node_schema.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2025, Jingrow and contributors -# For license information, please see license.txt - -import jingrow -from jingrow.model.document import Document - - -class AINodeSchema(Document): - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from jingrow.types import DF - - node_schema: DF.JSON | None - node_type: DF.Data | None - # end: auto-generated types - - def validate(self): - """验证 schema JSON 格式""" - if self.node_schema: - try: - import json - json.loads(json.dumps(self.node_schema)) - except Exception as e: - jingrow.throw(__("Schema JSON 格式错误: {0}", str(e))) - - def before_save(self): - """保存前验证""" - self.validate() - - @jingrow.whitelist(allow_guest=True) - def get_available_node_types(self): - """获取可用的节点类型""" - import os - import json - - node_types = [] - nodes_path = jingrow.get_app_path("jingrow", "ai", "pagetype", "ai_agent", "nodes") - - if os.path.exists(nodes_path): - for item in os.listdir(nodes_path): - item_path = os.path.join(nodes_path, item) - if os.path.isdir(item_path): - schema_file = os.path.join(item_path, f"{item}.json") - if os.path.exists(schema_file): - try: - with open(schema_file, 'r', encoding='utf-8') as f: - schema_data = json.load(f) - node_types.append({ - "value": item, - "label": schema_data.get("title", item), - "description": schema_data.get("description", "") - }) - except Exception as e: - jingrow.log_error("加载节点 schema 失败", f"节点: {item}, 错误: {str(e)}") - - return node_types - - @jingrow.whitelist(allow_guest=True) - def load_node_schema(self, node_type): - """加载指定节点类型的 schema""" - import os - import json - - schema_file = jingrow.get_app_path("jingrow", "ai", "pagetype", "ai_agent", "nodes", node_type, f"{node_type}.json") - - if os.path.exists(schema_file): - try: - with open(schema_file, 'r', encoding='utf-8') as f: - return json.load(f) - except Exception as e: - jingrow.throw(__("加载节点 schema 失败: {0}", str(e))) - else: - jingrow.throw(__("节点类型 {0} 的 schema 文件不存在", node_type)) - - @jingrow.whitelist(allow_guest=True) - def save_node_schema(self, node_type, schema_data): - """保存节点 schema 到文件""" - import os - import json - - # 验证 schema 格式 - try: - json.loads(json.dumps(schema_data)) - except Exception as e: - jingrow.throw(__("Schema JSON 格式错误: {0}", str(e))) - - schema_file = jingrow.get_app_path("jingrow", "ai", "pagetype", "ai_agent", "nodes", node_type, f"{node_type}.json") - - try: - # 确保目录存在 - os.makedirs(os.path.dirname(schema_file), exist_ok=True) - - with open(schema_file, 'w', encoding='utf-8') as f: - json.dump(schema_data, f, ensure_ascii=False, indent=2) - - jingrow.msgprint(__("节点 schema 保存成功")) - except Exception as e: - jingrow.throw(__("保存节点 schema 失败: {0}", str(e))) diff --git a/jcloud/jcloud/pagetype/ai_node_schema/test_ai_node_schema.py b/jcloud/jcloud/pagetype/ai_node_schema/test_ai_node_schema.py deleted file mode 100644 index 81b7368..0000000 --- a/jcloud/jcloud/pagetype/ai_node_schema/test_ai_node_schema.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2025, Jingrow and Contributors -# See license.txt - -# import jingrow -from jingrow.tests.utils import JingrowTestCase - - -class TestAINodeSchema(JingrowTestCase): - pass diff --git a/jcloud/jcloud/pagetype/app_source/app_source.json b/jcloud/jcloud/pagetype/app_source/app_source.json index 2811e4e..050b340 100644 --- a/jcloud/jcloud/pagetype/app_source/app_source.json +++ b/jcloud/jcloud/pagetype/app_source/app_source.json @@ -2,7 +2,6 @@ "actions": [], "allow_rename": 1, "creation": "2022-01-28 20:07:40.451028", - "pagetype": "PageType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ @@ -166,15 +165,16 @@ "index_web_pages_for_search": 1, "links": [ { - "link_pagetype": "Error Log", - "link_fieldname": "reference_name" + "link_fieldname": "reference_name", + "link_pagetype": "Error Log" } ], - "modified": "2024-04-05 10:12:54.374115", + "modified": "2025-09-06 01:23:01.905284", "modified_by": "Administrator", "module": "Jcloud", "name": "App Source", "owner": "Administrator", + "pagetype": "PageType", "permissions": [ { "create": 1, @@ -201,6 +201,7 @@ "write": 1 } ], + "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", "states": [], diff --git a/jcloud/jcloud/pagetype/app_source/app_source.py b/jcloud/jcloud/pagetype/app_source/app_source.py index b672315..8fa3b56 100644 --- a/jcloud/jcloud/pagetype/app_source/app_source.py +++ b/jcloud/jcloud/pagetype/app_source/app_source.py @@ -24,15 +24,15 @@ class AppSource(Document): from typing import TYPE_CHECKING if TYPE_CHECKING: - from jingrow.types import DF from jcloud.jcloud.pagetype.app_source_version.app_source_version import AppSourceVersion + from jingrow.types import DF app: DF.Link app_title: DF.Data branch: DF.Data enabled: DF.Check - jingrow: DF.Check github_installation_id: DF.Data | None + jingrow: DF.Check last_github_poll_failed: DF.Check last_github_response: DF.Code | None last_synced: DF.Datetime | None diff --git a/jcloud/public/js/schema_builder/SchemaBuilder.vue b/jcloud/public/js/schema_builder/SchemaBuilder.vue deleted file mode 100644 index 986e995..0000000 --- a/jcloud/public/js/schema_builder/SchemaBuilder.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - \ No newline at end of file diff --git a/jcloud/public/js/schema_builder/components/EditableInput.vue b/jcloud/public/js/schema_builder/components/EditableInput.vue deleted file mode 100644 index 785149d..0000000 --- a/jcloud/public/js/schema_builder/components/EditableInput.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - - - \ No newline at end of file diff --git a/jcloud/public/js/schema_builder/components/PropertyDialog.vue b/jcloud/public/js/schema_builder/components/PropertyDialog.vue deleted file mode 100644 index c16a825..0000000 --- a/jcloud/public/js/schema_builder/components/PropertyDialog.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - - - diff --git a/jcloud/public/js/schema_builder/components/PropertyEditor.vue b/jcloud/public/js/schema_builder/components/PropertyEditor.vue deleted file mode 100644 index df40ba5..0000000 --- a/jcloud/public/js/schema_builder/components/PropertyEditor.vue +++ /dev/null @@ -1,152 +0,0 @@ - - - - - \ No newline at end of file diff --git a/jcloud/public/js/schema_builder/components/PropertyItem.vue b/jcloud/public/js/schema_builder/components/PropertyItem.vue deleted file mode 100644 index 01a37ae..0000000 --- a/jcloud/public/js/schema_builder/components/PropertyItem.vue +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - diff --git a/jcloud/public/js/schema_builder/components/PropertyProperties.vue b/jcloud/public/js/schema_builder/components/PropertyProperties.vue deleted file mode 100644 index f60feba..0000000 --- a/jcloud/public/js/schema_builder/components/PropertyProperties.vue +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - - diff --git a/jcloud/public/js/schema_builder/components/SchemaCanvas.vue b/jcloud/public/js/schema_builder/components/SchemaCanvas.vue deleted file mode 100644 index c38f604..0000000 --- a/jcloud/public/js/schema_builder/components/SchemaCanvas.vue +++ /dev/null @@ -1,157 +0,0 @@ - - - - - \ No newline at end of file diff --git a/jcloud/public/js/schema_builder/components/SchemaField.vue b/jcloud/public/js/schema_builder/components/SchemaField.vue deleted file mode 100644 index 7c81482..0000000 --- a/jcloud/public/js/schema_builder/components/SchemaField.vue +++ /dev/null @@ -1,165 +0,0 @@ - - - - - diff --git a/jcloud/public/js/schema_builder/components/SchemaSidebar.vue b/jcloud/public/js/schema_builder/components/SchemaSidebar.vue deleted file mode 100644 index 6b12188..0000000 --- a/jcloud/public/js/schema_builder/components/SchemaSidebar.vue +++ /dev/null @@ -1,237 +0,0 @@ - - - - - \ No newline at end of file diff --git a/jcloud/public/js/schema_builder/schema_builder.bundle.js b/jcloud/public/js/schema_builder/schema_builder.bundle.js deleted file mode 100644 index 3d10e7f..0000000 --- a/jcloud/public/js/schema_builder/schema_builder.bundle.js +++ /dev/null @@ -1,892 +0,0 @@ -// Schema Builder Bundle - 完全按照 Jingrow 表单构建器模式重新实现 - -// 检查依赖 -if (typeof Vue === 'undefined') { - console.error('Vue.js 未加载'); -} -if (typeof Pinia === 'undefined') { - console.error('Pinia 未加载'); -} - -// 使用全局 Vue 和 Pinia -const { createApp, ref, computed, nextTick, watch } = Vue; -const { createPinia, defineStore } = Pinia; - -// Schema Store -const useSchemaStore = defineStore("schema-builder-store", () => { - // State - const schema = ref({ - $schema: "https://json-schema.org/draft/2020-12/schema", - type: "object", - title: "AI 节点配置", - properties: {}, - required: [] - }); - - const properties = ref([]); - const selectedField = ref(null); - const preview = ref(false); - const readOnly = ref(false); - const dirty = ref(false); - - // Getters - const notUsingInput = computed(() => { - const activeElement = document.activeElement; - return !activeElement || - activeElement.readOnly || - activeElement.disabled || - (activeElement.tagName !== "INPUT" && activeElement.tagName !== "TEXTAREA"); - }); - - // Actions - function addProperty(type = "string") { - const key = `property_${Date.now()}`; - const newProperty = { - key, - type, - title: "新属性", - description: "", - required: false - }; - - properties.value.push(newProperty); - updateSchema(); - selectedField.value = key; - dirty.value = true; - } - - function removeProperty(key) { - const index = properties.value.findIndex(p => p.key === key); - if (index > -1) { - properties.value.splice(index, 1); - updateSchema(); - if (selectedField.value === key) { - selectedField.value = null; - } - dirty.value = true; - } - } - - function updateProperty(key, updatedProperty) { - const index = properties.value.findIndex(p => p.key === key); - if (index > -1) { - properties.value[index] = { ...properties.value[index], ...updatedProperty }; - updateSchema(); - dirty.value = true; - } - } - - function updateSchema() { - schema.value.properties = {}; - schema.value.required = []; - - properties.value.forEach(prop => { - schema.value.properties[prop.key] = { - type: prop.type, - title: prop.title, - description: prop.description - }; - - if (prop.required) { - schema.value.required.push(prop.key); - } - }); - } - - function togglePreview() { - preview.value = !preview.value; - } - - function saveSchema() { - dirty.value = false; - } - - function loadSchema(schemaData) { - if (typeof schemaData === "string") { - try { - schemaData = JSON.parse(schemaData); - } catch (e) { - console.error("Invalid JSON schema:", e); - return; - } - } - - schema.value = schemaData; - properties.value = []; - - if (schemaData.properties) { - Object.entries(schemaData.properties).forEach(([key, prop]) => { - properties.value.push({ - key, - type: prop.type || "string", - title: prop.title || key, - description: prop.description || "", - required: schemaData.required?.includes(key) || false - }); - }); - } - - dirty.value = false; - } - - return { - schema, - properties, - selectedField, - preview, - readOnly, - dirty, - notUsingInput, - addProperty, - removeProperty, - updateProperty, - updateSchema, - togglePreview, - saveSchema, - loadSchema - }; -}); - -// EditableInput 组件 -const EditableInput = { - props: { - text: { - type: String, - default: "" - }, - placeholder: { - type: String, - default: "" - }, - emptyLabel: { - type: String, - default: "" - } - }, - emits: ['update:modelValue'], - setup(props, { emit }) { - const isEditing = ref(false); - const editValue = ref(""); - const inputRef = ref(null); - - const displayText = computed(() => { - if (props.text) return props.text; - if (props.emptyLabel) return props.emptyLabel; - return props.placeholder || "点击编辑"; - }); - - async function startEdit() { - isEditing.value = true; - editValue.value = props.text; - await nextTick(); - inputRef.value?.focus(); - inputRef.value?.select(); - } - - function finishEdit() { - if (isEditing.value) { - emit("update:modelValue", editValue.value); - isEditing.value = false; - } - } - - function cancelEdit() { - editValue.value = props.text; - isEditing.value = false; - } - - return { - isEditing, - editValue, - inputRef, - displayText, - startEdit, - finishEdit, - cancelEdit - }; - }, - template: ` - - - {{ displayText }} - - - - ` -}; - -// SchemaField 组件 -const SchemaField = { - props: { - field: { - type: Object, - required: true - }, - fieldKey: { - type: String, - required: true - }, - selected: { - type: Boolean, - default: false - } - }, - emits: ['select', 'edit', 'delete'], - setup(props, { emit }) { - const store = useSchemaStore(); - const hovered = ref(false); - - function selectField() { - emit('select', props.fieldKey); - } - - function editField() { - emit('edit', props.fieldKey); - } - - function deleteField() { - emit('delete', props.fieldKey); - } - - return { - store, - hovered, - selectField, - editField, - deleteField - }; - }, - components: { - EditableInput - }, - template: ` -
-
-
- - {{ field.type }} -
-
- - -
-
- -
- -
-
- ` -}; - -// SchemaCanvas 组件 -const SchemaCanvas = { - setup() { - const store = useSchemaStore(); - - function selectField(fieldKey) { - store.selectedField = fieldKey; - } - - function editField(fieldKey) { - store.selectedField = fieldKey; - } - - function deleteField(fieldKey) { - store.removeProperty(fieldKey); - } - - function saveSchema() { - store.saveSchema(); - } - - return { - store, - selectField, - editField, - deleteField, - saveSchema - }; - }, - components: { - SchemaField, - EditableInput - }, - template: ` -
-
-
- -
-
- - -
-
- -
-
-
-
属性配置
-
- -
-
- -
-
-
- -
-
{{ JSON.stringify(store.schema, null, 2) }}
-
-
-
- ` -}; - -// SchemaSidebar 组件 -const SchemaSidebar = { - setup() { - const store = useSchemaStore(); - - const propertyTypes = [ - { - value: "string", - label: "字符串", - description: "文本输入", - icon: "text", - iconText: "T" - }, - { - value: "number", - label: "数字", - description: "数字输入", - icon: "number", - iconText: "123" - }, - { - value: "boolean", - label: "布尔值", - description: "是/否选择", - icon: "check", - iconText: "✓" - }, - { - value: "array", - label: "数组", - description: "列表数据", - icon: "list", - iconText: "[]" - }, - { - value: "object", - label: "对象", - description: "嵌套对象", - icon: "object", - iconText: "{}" - } - ]; - - const selectedField = computed(() => { - if (!store.selectedField) return null; - return store.properties.find(p => p.key === store.selectedField); - }); - - function onDragStart(event, type) { - event.dataTransfer.setData("application/json", JSON.stringify(type)); - } - - function updateField() { - if (selectedField.value) { - store.updateProperty(selectedField.value.key, selectedField.value); - } - } - - return { - store, - propertyTypes, - selectedField, - onDragStart, - updateField - }; - }, - template: ` -
- - - -
- ` -}; - -// 主 SchemaBuilder 组件 -const SchemaBuilder = { - props: { - initialSchema: { - type: Object, - default: () => ({}) - }, - nodeType: { - type: String, - default: "" - } - }, - emits: ['save', 'change'], - setup(props, { emit }) { - const store = useSchemaStore(); - - // 加载初始 schema - if (props.initialSchema && Object.keys(props.initialSchema).length > 0) { - store.loadSchema(props.initialSchema); - } - - // 监听 schema 变化 - watch(() => store.schema, (newSchema) => { - emit('change', newSchema); - }, { deep: true }); - - function handleSave() { - emit('save', store.schema); - } - - return { - store, - handleSave - }; - }, - components: { - SchemaCanvas, - SchemaSidebar - }, - template: ` -
-
- -
-
- -
-
- ` -}; - -// 添加样式 -const style = document.createElement('style'); -style.textContent = ` - .icon-text { - display: inline-block; - width: 20px; - height: 20px; - line-height: 20px; - text-align: center; - background-color: var(--gray-200); - border-radius: 50%; - font-size: 12px; - font-weight: bold; - color: var(--text-muted); - } - - .schema-builder-container { - display: flex; - height: 100%; - } - - .schema-main { - flex: 1; - background-color: var(--disabled-control-bg); - border-radius: var(--border-radius); - border: 1px solid var(--border-color); - background-color: var(--card-bg); - margin: 5px; - } - - .schema-sidebar { - width: 300px; - border-left: 1px solid var(--border-color); - border-bottom-right-radius: var(--border-radius); - background-color: var(--fg-color); - } - - .schema-canvas { - padding: 1rem; - } - - .schema-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - padding-bottom: 1rem; - border-bottom: 1px solid var(--border-color); - } - - .schema-title { - flex: 1; - } - - .schema-title span { - font-weight: 600; - color: var(--heading-color); - font-size: 1.2rem; - } - - .schema-actions { - display: flex; - gap: 0.5rem; - } - - .properties-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - } - - .properties-header h5 { - margin: 0; - color: var(--heading-color); - } - - .properties-list { - min-height: 200px; - border: 1px dashed var(--border-color); - border-radius: var(--border-radius); - padding: 1rem; - } - - .schema-preview pre { - background-color: var(--bg-light-gray); - padding: 1rem; - border-radius: var(--border-radius); - overflow-x: auto; - font-size: var(--text-sm); - } - - .schema-field { - background-color: var(--bg-light-gray); - border-radius: var(--border-radius-sm); - border: 1px solid transparent; - padding: 0.75rem; - margin-bottom: 0.5rem; - cursor: pointer; - } - - .schema-field:not(:last-child) { - margin-bottom: 0.5rem; - } - - .schema-field.hovered, - .schema-field.selected { - border-color: var(--border-primary); - background-color: var(--fg-color); - } - - .field-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5rem; - } - - .field-info { - display: flex; - align-items: center; - gap: 0.5rem; - flex: 1; - } - - .field-info span { - font-weight: 500; - color: var(--heading-color); - } - - .field-type { - background-color: var(--gray-200); - color: var(--text-muted); - padding: 0.2rem 0.5rem; - border-radius: var(--border-radius-sm); - font-size: var(--text-xs); - font-weight: normal; - } - - .field-actions { - display: flex; - gap: 0.25rem; - opacity: 0; - transition: opacity 0.2s; - } - - .schema-field:hover .field-actions { - opacity: 1; - } - - .field-description { - margin-bottom: 0.5rem; - } - - .field-description span { - color: var(--text-muted); - font-size: var(--text-sm); - } - - .sidebar-header { - padding: 1rem; - border-bottom: 1px solid var(--border-color); - } - - .sidebar-header h5 { - margin: 0; - color: var(--heading-color); - } - - .sidebar-content { - flex: 1; - overflow-y: auto; - padding: 1rem; - } - - .property-types { - margin-bottom: 2rem; - } - - .property-type-item { - display: flex; - align-items: center; - padding: 0.75rem; - margin-bottom: 0.5rem; - background-color: var(--bg-light-gray); - border-radius: var(--border-radius); - cursor: grab; - border: 1px solid transparent; - transition: all 0.2s; - } - - .property-type-item:hover { - border-color: var(--border-primary); - background-color: var(--fg-color); - } - - .property-type-item:active { - cursor: grabbing; - } - - .type-icon { - margin-right: 0.75rem; - color: var(--text-muted); - } - - .type-info { - flex: 1; - } - - .type-name { - font-weight: 500; - color: var(--heading-color); - margin-bottom: 0.25rem; - } - - .type-description { - font-size: var(--text-sm); - color: var(--text-muted); - } - - .sidebar-actions h6 { - margin-bottom: 1rem; - color: var(--heading-color); - } - - .field-settings .setting-item { - margin-bottom: 1rem; - } - - .field-settings label { - display: block; - margin-bottom: 0.5rem; - font-weight: 500; - color: var(--heading-color); - } - - .field-settings .form-control { - width: 100%; - padding: 0.5rem; - border: 1px solid var(--border-color); - border-radius: var(--border-radius); - font-size: var(--text-sm); - } - - .field-settings .form-control:focus { - outline: none; - border-color: var(--border-primary); - } - - .field-settings textarea.form-control { - resize: vertical; - min-height: 80px; - } - - .editable-input { - display: inline-block; - cursor: pointer; - } - - .editable-input:hover { - background-color: var(--bg-light-gray); - border-radius: var(--border-radius-sm); - padding: 0.2rem 0.4rem; - margin: -0.2rem -0.4rem; - } - - .editable-input.is-editing .form-control { - border: 1px solid var(--border-primary); - outline: none; - background-color: var(--fg-color); - } - - .editable-text { - display: inline-block; - min-width: 1rem; - min-height: 1.2rem; - } -`; -document.head.appendChild(style); - -// 导出到全局 -window.SchemaBuilder = SchemaBuilder; -window.SchemaStore = useSchemaStore; - -console.log("Schema Builder Bundle 加载完成"); \ No newline at end of file diff --git a/jcloud/public/js/schema_builder/store.js b/jcloud/public/js/schema_builder/store.js deleted file mode 100644 index 9e95eec..0000000 --- a/jcloud/public/js/schema_builder/store.js +++ /dev/null @@ -1,146 +0,0 @@ -import { defineStore } from "pinia"; -import { ref, computed } from "vue"; - -export const useSchemaStore = defineStore("schema-builder-store", () => { - // State - const schema = ref({ - $schema: "https://json-schema.org/draft/2020-12/schema", - type: "object", - title: "AI 节点配置", - properties: {}, - required: [] - }); - - const properties = ref([]); - const selectedField = ref(null); - const preview = ref(false); - const readOnly = ref(false); - const dirty = ref(false); - - // Getters - const notUsingInput = computed(() => { - // 检查是否正在使用输入框 - const activeElement = document.activeElement; - return !activeElement || - activeElement.readOnly || - activeElement.disabled || - (activeElement.tagName !== "INPUT" && activeElement.tagName !== "TEXTAREA"); - }); - - // Actions - function addProperty(type = "string") { - const key = `property_${Date.now()}`; - const newProperty = { - key, - type, - title: "新属性", - description: "", - required: false - }; - - properties.value.push(newProperty); - updateSchema(); - selectedField.value = key; - dirty.value = true; - } - - function removeProperty(key) { - const index = properties.value.findIndex(p => p.key === key); - if (index > -1) { - properties.value.splice(index, 1); - updateSchema(); - if (selectedField.value === key) { - selectedField.value = null; - } - dirty.value = true; - } - } - - function updateProperty(key, updatedProperty) { - const index = properties.value.findIndex(p => p.key === key); - if (index > -1) { - properties.value[index] = { ...properties.value[index], ...updatedProperty }; - updateSchema(); - dirty.value = true; - } - } - - function updateSchema() { - // 更新 schema 对象 - schema.value.properties = {}; - schema.value.required = []; - - properties.value.forEach(prop => { - schema.value.properties[prop.key] = { - type: prop.type, - title: prop.title, - description: prop.description - }; - - if (prop.required) { - schema.value.required.push(prop.key); - } - }); - } - - function togglePreview() { - preview.value = !preview.value; - } - - function saveSchema() { - // 触发保存事件 - dirty.value = false; - // 这里可以触发父组件的保存事件 - } - - function loadSchema(schemaData) { - if (typeof schemaData === "string") { - try { - schemaData = JSON.parse(schemaData); - } catch (e) { - console.error("Invalid JSON schema:", e); - return; - } - } - - schema.value = schemaData; - properties.value = []; - - // 解析 properties - if (schemaData.properties) { - Object.entries(schemaData.properties).forEach(([key, prop]) => { - properties.value.push({ - key, - type: prop.type || "string", - title: prop.title || key, - description: prop.description || "", - required: schemaData.required?.includes(key) || false - }); - }); - } - - dirty.value = false; - } - - return { - // State - schema, - properties, - selectedField, - preview, - readOnly, - dirty, - - // Getters - notUsingInput, - - // Actions - addProperty, - removeProperty, - updateProperty, - updateSchema, - togglePreview, - saveSchema, - loadSchema - }; -}); \ No newline at end of file