|
|
@@ -403,6 +403,18 @@
|
|
|
>
|
|
|
</div>
|
|
|
|
|
|
+ <div class="json-section">
|
|
|
+ <h3>JSON 数据</h3>
|
|
|
+ <textarea v-model="jsonData" placeholder="在此输入JSON格式的积木块数据..."></textarea>
|
|
|
+ <div class="controls">
|
|
|
+ <button @click="loadWorkspaceFromJson">加载JSON到工作区</button>
|
|
|
+ <button @click="exportWorkspaceToJson">导出工作区为JSON</button>
|
|
|
+ </div>
|
|
|
+ <div v-if="statusMessage" :class="['status', statusType]" style="color: #1a1a1a">
|
|
|
+ {{ statusMessage }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 代码显示区 -->
|
|
|
<div class="code-section">
|
|
|
<h4>生成的代码</h4>
|
|
|
@@ -535,7 +547,7 @@ import { playMusic, stopMusic, onMusicEnded } from "@/api/blockly/music.js";
|
|
|
|
|
|
const router = useRouter();
|
|
|
// 台灯预览显示状态
|
|
|
-const showLampPreview = ref(true)
|
|
|
+const showLampPreview = ref(false)
|
|
|
|
|
|
// 返回虚拟实验室
|
|
|
const goBackLab = () => {
|
|
|
@@ -1289,12 +1301,8 @@ onMounted(async () => {
|
|
|
.appendField('音乐类型:')
|
|
|
.appendField(
|
|
|
new Blockly.FieldDropdown([
|
|
|
- ['平静', '平静'],
|
|
|
- ['欢快', '欢快'],
|
|
|
+ ['热闹', '热闹'],
|
|
|
['舒缓', '舒缓'],
|
|
|
- ['激情', '激情'],
|
|
|
- ['冥想', '冥想'],
|
|
|
- ['自然', '自然']
|
|
|
]),
|
|
|
'MUSIC_TYPE'
|
|
|
);
|
|
|
@@ -1350,8 +1358,11 @@ onMounted(async () => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+ // 使用合并的监听器函数
|
|
|
+ addCombinedChangeListener();
|
|
|
+
|
|
|
// 使用当前Blockly版本支持的标准方法加载初始内容
|
|
|
- const initialXml = "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n" +
|
|
|
+ /*const initialXml = "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n" +
|
|
|
" <variables>\n" +
|
|
|
" <variable id=\"kAVG*zJLw/q)l/(/eIMM\">inputText</variable>\n" +
|
|
|
" <variable id=\"[7F(niLz{.fvWvY.VT/N\">lampConfig</variable>\n" +
|
|
|
@@ -1412,7 +1423,7 @@ onMounted(async () => {
|
|
|
console.warn('XML加载失败,尝试清空工作区:', xmlError);
|
|
|
// 如果XML加载失败,清空工作区
|
|
|
state.workspace.clear();
|
|
|
- }
|
|
|
+ }*/
|
|
|
|
|
|
// 添加拖拽修复
|
|
|
if (state.workspace) {
|
|
|
@@ -1437,10 +1448,26 @@ onMounted(async () => {
|
|
|
ElMessage.error('Blockly初始化失败,请刷新页面重试');
|
|
|
}
|
|
|
|
|
|
+ // 修改工作区变化监听器,使其包含拖拽修复逻辑
|
|
|
+ state.workspace.addChangeListener((event) => {
|
|
|
+ // 生成代码
|
|
|
+ generateCode("javascript");
|
|
|
+
|
|
|
+ // 拖拽修复逻辑
|
|
|
+ if (event.type === Blockly.Events.BLOCK_CREATE) {
|
|
|
+ const block = state.workspace.getBlockById(event.blockId);
|
|
|
+ if (block) {
|
|
|
+ block.setEditable(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 加载初始JSON数据
|
|
|
+ // workspace = state.workspace;
|
|
|
+ // loadWorkspaceFromJson();
|
|
|
+
|
|
|
// 添加工作区变化监听器
|
|
|
- if (state.workspace) {
|
|
|
- state.workspace.addChangeListener(() => generateCode("javascript"));
|
|
|
- }
|
|
|
+ // state.workspace.addChangeListener(() => generateCode("javascript"));
|
|
|
});
|
|
|
|
|
|
// 组件卸载时清除所有资源
|
|
|
@@ -1460,6 +1487,450 @@ onUnmounted(() => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// 响应式变量
|
|
|
+const jsonData = ref(`{
|
|
|
+ "blocks": {
|
|
|
+ "languageVersion": 0,
|
|
|
+ "blocks": [
|
|
|
+ {
|
|
|
+ "type": "text_print",
|
|
|
+ "x": 100,
|
|
|
+ "y": 100,
|
|
|
+ "inputs": {
|
|
|
+ "TEXT": {
|
|
|
+ "block": {
|
|
|
+ "type": "text",
|
|
|
+ "fields": {
|
|
|
+ "TEXT": "Hello, Blockly!"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "type": "controls_if",
|
|
|
+ "x": 100,
|
|
|
+ "y": 200,
|
|
|
+ "inputs": {
|
|
|
+ "IF0": {
|
|
|
+ "block": {
|
|
|
+ "type": "logic_compare",
|
|
|
+ "fields": {
|
|
|
+ "OP": "EQ"
|
|
|
+ },
|
|
|
+ "inputs": {
|
|
|
+ "A": {
|
|
|
+ "block": {
|
|
|
+ "type": "math_number",
|
|
|
+ "fields": {
|
|
|
+ "NUM": 5
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ "B": {
|
|
|
+ "block": {
|
|
|
+ "type": "math_number",
|
|
|
+ "fields": {
|
|
|
+ "NUM": 5
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ "DO0": {
|
|
|
+ "block": {
|
|
|
+ "type": "text_print",
|
|
|
+ "inputs": {
|
|
|
+ "TEXT": {
|
|
|
+ "block": {
|
|
|
+ "type": "text",
|
|
|
+ "fields": {
|
|
|
+ "TEXT": "条件成立!"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}`);
|
|
|
+const statusMessage = ref('');
|
|
|
+const statusType = ref('');
|
|
|
+// let workspace = null;
|
|
|
+
|
|
|
+// 从JSON加载工作区 - 改进版
|
|
|
+const loadWorkspaceFromJson = () => {
|
|
|
+ try {
|
|
|
+ const json = JSON.parse(jsonData.value);
|
|
|
+
|
|
|
+ // 1. 确保工作区存在
|
|
|
+ if (!state.workspace) {
|
|
|
+ showStatus('工作区未初始化', 'error');
|
|
|
+ console.error('工作区未初始化');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('开始加载JSON数据:', JSON.stringify(json).substring(0, 200) + '...');
|
|
|
+
|
|
|
+ // 无论使用哪种策略加载成功后,确保所有块可拖拽
|
|
|
+ const ensureBlocksDraggable = () => {
|
|
|
+ const allBlocks = state.workspace.getAllBlocks();
|
|
|
+ allBlocks.forEach(block => {
|
|
|
+ // 强制设置为可编辑状态,不做任何条件检查
|
|
|
+ block.setEditable(true);
|
|
|
+ // 添加调试日志,确认每个块都被设置为可编辑
|
|
|
+ console.log(`设置块${block.id}为可编辑状态`);
|
|
|
+ });
|
|
|
+ return allBlocks.length;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 2. 尝试多种加载策略
|
|
|
+ try {
|
|
|
+ // 策略1: 先清空工作区再加载
|
|
|
+ state.workspace.clear();
|
|
|
+ Blockly.serialization.workspaces.load(json, state.workspace);
|
|
|
+ const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
|
|
|
+ showStatus(`工作区已成功从JSON加载!共加载${blockCount}个积木。`, 'success');
|
|
|
+ console.log(`策略1成功: 加载了${blockCount}个积木`);
|
|
|
+ return;
|
|
|
+ } catch (error1) {
|
|
|
+ console.warn('策略1失败,尝试策略2:', error1);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 策略2: 清理JSON后再加载
|
|
|
+ state.workspace.clear();
|
|
|
+ const cleanedJson = deepCleanJsonForConnectionIssues(json);
|
|
|
+ Blockly.serialization.workspaces.load(cleanedJson, state.workspace);
|
|
|
+ const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
|
|
|
+ showStatus(`工作区已通过清理后的JSON成功加载!共加载${blockCount}个积木。`, 'success');
|
|
|
+ console.log(`策略2成功: 加载了${blockCount}个积木`);
|
|
|
+ return;
|
|
|
+ } catch (error2) {
|
|
|
+ console.warn('策略2失败,尝试策略3:', error2);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 策略3: 直接使用XML序列化API作为中间格式
|
|
|
+ state.workspace.clear();
|
|
|
+
|
|
|
+ // 先将JSON转换为XML字符串
|
|
|
+ const xmlText = jsonToXml(json);
|
|
|
+ console.log('生成的XML:', xmlText.substring(0, 200) + '...');
|
|
|
+
|
|
|
+ // 加载XML到工作区
|
|
|
+ const xmlDom = Blockly.utils.xml.textToDom(xmlText);
|
|
|
+ Blockly.Xml.domToWorkspace(xmlDom, state.workspace);
|
|
|
+
|
|
|
+ const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
|
|
|
+ showStatus(`工作区已通过XML中间格式成功加载!共加载${blockCount}个积木。`, 'success');
|
|
|
+ console.log(`策略3成功: 加载了${blockCount}个积木`);
|
|
|
+ return;
|
|
|
+ } catch (error3) {
|
|
|
+ console.warn('策略3失败,尝试策略4:', error3);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 策略4: 创建新的工作区实例并使用XML导入
|
|
|
+ // 先释放旧工作区资源
|
|
|
+ if (state.workspace) {
|
|
|
+ state.workspace.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新的工作区
|
|
|
+ const blocklyDiv = document.getElementById('blocklyDiv');
|
|
|
+ const toolbox = document.getElementById('toolbox');
|
|
|
+
|
|
|
+ state.workspace = Blockly.inject(blocklyDiv, {
|
|
|
+ toolbox: toolbox,
|
|
|
+ collapse: true,
|
|
|
+ comments: true,
|
|
|
+ disable: false,
|
|
|
+ maxBlocks: Infinity,
|
|
|
+ trashcan: true,
|
|
|
+ horizontalLayout: false,
|
|
|
+ toolboxPosition: 'start',
|
|
|
+ css: true,
|
|
|
+ media: 'https://unpkg.com/blockly/media/',
|
|
|
+ rtl: false,
|
|
|
+ scrollbars: true,
|
|
|
+ sounds: false,
|
|
|
+ oneBasedIndex: true,
|
|
|
+ grid: {
|
|
|
+ spacing: 20,
|
|
|
+ length: 3,
|
|
|
+ colour: "#ccc",
|
|
|
+ snap: true
|
|
|
+ },
|
|
|
+ zoom: {
|
|
|
+ controls: true,
|
|
|
+ wheel: true,
|
|
|
+ startScale: 1.0,
|
|
|
+ maxScale: 3,
|
|
|
+ minScale: 0.3,
|
|
|
+ scaleSpeed: 1.2
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 重新添加工作区变化监听器 - 使用合并后的监听器
|
|
|
+ addCombinedChangeListener();
|
|
|
+
|
|
|
+ // 使用XML作为中间格式导入
|
|
|
+ const xmlText = jsonToXml(json);
|
|
|
+ console.log('创建新工作区,生成的XML:', xmlText.substring(0, 200) + '...');
|
|
|
+ const xmlDom = Blockly.utils.xml.textToDom(xmlText);
|
|
|
+ Blockly.Xml.domToWorkspace(xmlDom, state.workspace);
|
|
|
+
|
|
|
+ const blockCount = ensureBlocksDraggable(); // 调用ensureBlocksDraggable确保所有块可拖拽
|
|
|
+ showStatus(`工作区已通过创建新实例并使用XML格式成功加载!共加载${blockCount}个积木。`, 'success');
|
|
|
+ console.log(`策略4成功: 加载了${blockCount}个积木`);
|
|
|
+ return;
|
|
|
+ } catch (error4) {
|
|
|
+ // 所有策略都失败
|
|
|
+ showStatus('JSON解析错误: ' + error4.message, 'error');
|
|
|
+ console.error('所有加载策略均失败:', error4);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ showStatus('JSON解析错误: ' + error.message, 'error');
|
|
|
+ console.error('JSON解析错误:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 2. 创建一个合并的工作区变化监听器函数
|
|
|
+function addCombinedChangeListener() {
|
|
|
+ if (!state.workspace) return;
|
|
|
+
|
|
|
+ // 移除旧的监听器(如果有)
|
|
|
+ state.workspace.removeAllChangeListeners();
|
|
|
+
|
|
|
+ // 添加新的合并监听器
|
|
|
+ state.workspace.addChangeListener((event) => {
|
|
|
+ // 生成代码
|
|
|
+ generateCode("javascript");
|
|
|
+
|
|
|
+ // 拖拽修复逻辑
|
|
|
+ if (event.type === Blockly.Events.BLOCK_CREATE || event.type === Blockly.Events.BLOCK_ADD)
|
|
|
+ {
|
|
|
+ const block = state.workspace.getBlockById(event.blockId);
|
|
|
+ if (block) {
|
|
|
+ // 强制设置为可编辑状态
|
|
|
+ block.setEditable(true);
|
|
|
+ console.log(`监听器设置块${block.id}为可编辑状态`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 导出工作区为JSON - 增强版
|
|
|
+const exportWorkspaceToJson = () => {
|
|
|
+ try {
|
|
|
+ if (!state.workspace) {
|
|
|
+ showStatus('工作区未初始化', 'error');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用state.workspace替代workspace
|
|
|
+ const stateJson = Blockly.serialization.workspaces.save(state.workspace);
|
|
|
+ jsonData.value = JSON.stringify(stateJson, null, 2);
|
|
|
+
|
|
|
+ // 显示导出的积木数量
|
|
|
+ const blockCount = state.workspace.getAllBlocks().length;
|
|
|
+ showStatus(`工作区已成功导出为JSON!共导出${blockCount}个积木。`, 'success');
|
|
|
+ console.log(`导出JSON成功: 导出了${blockCount}个积木`);
|
|
|
+ } catch (error) {
|
|
|
+ showStatus('导出错误: ' + error.message, 'error');
|
|
|
+ console.error('导出错误:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 深度清理JSON中可能导致连接问题的部分
|
|
|
+function deepCleanJsonForConnectionIssues(json) {
|
|
|
+ const cleanedJson = JSON.parse(JSON.stringify(json));
|
|
|
+
|
|
|
+ // 1. 清理blocks结构
|
|
|
+ if (cleanedJson.blocks && cleanedJson.blocks.blocks) {
|
|
|
+ cleanedJson.blocks.blocks.forEach(block => {
|
|
|
+ // 2. 移除inputs中的connection引用
|
|
|
+ if (block.inputs) {
|
|
|
+ Object.keys(block.inputs).forEach(inputKey => {
|
|
|
+ const input = block.inputs[inputKey];
|
|
|
+ if (input.connection) {
|
|
|
+ delete input.connection;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 递归清理嵌套的block
|
|
|
+ if (input.block) {
|
|
|
+ cleanBlockConnections(input.block);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 清理next和previous连接
|
|
|
+ if (block.next) {
|
|
|
+ if (block.next.connection) {
|
|
|
+ delete block.next.connection;
|
|
|
+ }
|
|
|
+ if (block.next.block) {
|
|
|
+ cleanBlockConnections(block.next.block);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (block.previous) {
|
|
|
+ if (block.previous.connection) {
|
|
|
+ delete block.previous.connection;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return cleanedJson;
|
|
|
+}
|
|
|
+
|
|
|
+// 递归清理单个block的连接信息
|
|
|
+function cleanBlockConnections(block) {
|
|
|
+ if (!block) return;
|
|
|
+
|
|
|
+ // 清理inputs中的连接
|
|
|
+ if (block.inputs) {
|
|
|
+ Object.keys(block.inputs).forEach(inputKey => {
|
|
|
+ const input = block.inputs[inputKey];
|
|
|
+ if (input.connection) {
|
|
|
+ delete input.connection;
|
|
|
+ }
|
|
|
+ if (input.block) {
|
|
|
+ cleanBlockConnections(input.block);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理next和previous连接
|
|
|
+ if (block.next) {
|
|
|
+ if (block.next.connection) {
|
|
|
+ delete block.next.connection;
|
|
|
+ }
|
|
|
+ if (block.next.block) {
|
|
|
+ cleanBlockConnections(block.next.block);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (block.previous) {
|
|
|
+ if (block.previous.connection) {
|
|
|
+ delete block.previous.connection;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 将JSON转换为XML(增强版)
|
|
|
+function jsonToXml(json) {
|
|
|
+ // 先检查JSON是否有效
|
|
|
+ if (!json || typeof json !== 'object') {
|
|
|
+ console.error('无效的JSON数据');
|
|
|
+ return '<xml xmlns="https://developers.google.com/blockly/xml"></xml>';
|
|
|
+ }
|
|
|
+
|
|
|
+ let xml = '<xml xmlns="https://developers.google.com/blockly/xml">';
|
|
|
+
|
|
|
+ // 1. 处理变量
|
|
|
+ if (json.variables && json.variables.variables) {
|
|
|
+ xml += '<variables>';
|
|
|
+ json.variables.variables.forEach(variable => {
|
|
|
+ xml += `<variable id="${escapeXml(variable.id)}">${escapeXml(variable.name)}</variable>`;
|
|
|
+ });
|
|
|
+ xml += '</variables>';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 处理积木块 - 这是关键部分
|
|
|
+ if (json.blocks && json.blocks.blocks) {
|
|
|
+ // 找出所有根节点积木(没有previous连接的积木)
|
|
|
+ const rootBlocks = json.blocks.blocks.filter(block => !block.previous);
|
|
|
+ console.log(`找到${rootBlocks.length}个根积木块`);
|
|
|
+
|
|
|
+ // 递归生成每个根积木块的XML
|
|
|
+ rootBlocks.forEach(block => {
|
|
|
+ xml += generateBlockXml(block);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ console.warn('JSON中没有找到blocks数据');
|
|
|
+ }
|
|
|
+
|
|
|
+ xml += '</xml>';
|
|
|
+ return xml;
|
|
|
+}
|
|
|
+
|
|
|
+// 递归生成单个积木块的XML
|
|
|
+function generateBlockXml(block) {
|
|
|
+ if (!block) return '';
|
|
|
+
|
|
|
+ let blockXml = `<block type="${escapeXml(block.type)}" id="${escapeXml(block.id)}" x="${block.x || 0}" y="${block.y || 0}">`;
|
|
|
+
|
|
|
+ // 处理fields
|
|
|
+ if (block.fields) {
|
|
|
+ Object.keys(block.fields).forEach(fieldName => {
|
|
|
+ const fieldValue = block.fields[fieldName];
|
|
|
+ blockXml += `<field name="${escapeXml(fieldName)}">${escapeXml(String(fieldValue))}</field>`;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理inputs
|
|
|
+ if (block.inputs) {
|
|
|
+ Object.keys(block.inputs).forEach(inputName => {
|
|
|
+ const input = block.inputs[inputName];
|
|
|
+ if (input.block) {
|
|
|
+ blockXml += `<value name="${escapeXml(inputName)}">`;
|
|
|
+ blockXml += generateBlockXml(input.block);
|
|
|
+ blockXml += '</value>';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理嵌套的next积木
|
|
|
+ if (block.next && block.next.block) {
|
|
|
+ blockXml += '<next>';
|
|
|
+ blockXml += generateBlockXml(block.next.block);
|
|
|
+ blockXml += '</next>';
|
|
|
+ }
|
|
|
+
|
|
|
+ blockXml += '</block>';
|
|
|
+ return blockXml;
|
|
|
+}
|
|
|
+
|
|
|
+// XML转义函数
|
|
|
+function escapeXml(text) {
|
|
|
+ if (typeof text !== 'string') {
|
|
|
+ text = String(text);
|
|
|
+ }
|
|
|
+ return text
|
|
|
+ .replace(/&/g, '&')
|
|
|
+ .replace(/</g, '<')
|
|
|
+ .replace(/>/g, '>')
|
|
|
+ .replace(/"/g, '"')
|
|
|
+ .replace(/'/g, ''');
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// 显示状态消息
|
|
|
+const showStatus = (message, type) => {
|
|
|
+ statusMessage.value = message;
|
|
|
+ statusType.value = type;
|
|
|
+
|
|
|
+ // 3秒后自动清除状态消息
|
|
|
+ setTimeout(() => {
|
|
|
+ statusMessage.value = '';
|
|
|
+ }, 3000);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
// 注册JavaScript代码生成器
|
|
|
function registerJavaScriptGenerators() {
|
|
|
// 语音输入
|
|
|
@@ -2018,6 +2489,7 @@ window.aiService = aiService;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ //运行结果区域样式
|
|
|
.result-section {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
@@ -2025,7 +2497,8 @@ window.aiService = aiService;
|
|
|
|
|
|
.run-result-content {
|
|
|
color: black;
|
|
|
- flex: 1;
|
|
|
+ // 固定高度为300px,可根据需要调整
|
|
|
+ height: 180px;
|
|
|
background-color: #f8f9fa;
|
|
|
border: 1px solid #ddd;
|
|
|
border-radius: 4px;
|
|
|
@@ -2033,6 +2506,30 @@ window.aiService = aiService;
|
|
|
overflow-y: auto;
|
|
|
font-family: monospace;
|
|
|
font-size: 14px;
|
|
|
+
|
|
|
+ // 优化滚动条样式 - 适用于Webkit浏览器(Chrome, Safari)
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-track {
|
|
|
+ background: #f1f1f1;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: #c1c1c1;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: #a8a8a8;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Firefox滚动条样式
|
|
|
+ scrollbar-width: thin;
|
|
|
+ scrollbar-color: #c1c1c1 #f1f1f1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -2176,4 +2673,31 @@ window.aiService = aiService;
|
|
|
color: #666;
|
|
|
padding: 5px 0;
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.json-section {
|
|
|
+ margin-top: 15px;
|
|
|
+ padding: 15px;
|
|
|
+ background: #f1f8ff;
|
|
|
+ border-radius: 8px;
|
|
|
+ border: 1px solid #d1e7ff;
|
|
|
+}
|
|
|
+
|
|
|
+.json-section h3 {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #2c3e50;
|
|
|
+}
|
|
|
+
|
|
|
+textarea {
|
|
|
+ width: 100%;
|
|
|
+ min-height: 120px;
|
|
|
+ padding: 10px;
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ border-radius: 5px;
|
|
|
+ font-family: 'Courier New', monospace;
|
|
|
+ resize: vertical;
|
|
|
+}
|
|
|
</style>
|