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 @@
-
-
-
- {{ displayText }}
-
-
-
-
-
-
-
-
\ 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 @@
-
-
-
-
-
-
-
-
-
{{ JSON.stringify(store.schema, null, 2) }}
-
-
-
-
-
-
-
-
\ 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 @@
-
-
-
-
-
-
-
-
-
-
- {{ Object.keys(field.properties).length }} 个子属性
-
-
-
-
-
-
-
-
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: `
-
- `
-};
-
-// 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