Эх сурвалжийг харах

1、加入多路线配置数据
2、blockly组件加入图标

liyanbo 4 сар өмнө
parent
commit
61e03ad218

+ 144 - 113
src/api/blockly/blockly.js

@@ -2,6 +2,8 @@ import * as Blockly from "blockly";
 import 'blockly/msg/zh-hans';
 import { javascriptGenerator } from "blockly/javascript";
 import { pythonGenerator } from "blockly/python";
+// 导入图片资源
+import xianqianImage from '@/assets/images/blockly/component/xianqian.png';
 
 /**
  * 定义所有可用的自定义积木配置
@@ -11,26 +13,36 @@ const availableBlocks = {
   move_forward: {
     jsonConfig: {
       "type": "move_forward",
-      "message0": "向前移动",
+      "message0": "%1 向前移动",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向前移动"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向前移动一格",
-      "helpUrl": "",
-      "icon": {
-        "src": "▶",
-        "width": 16,
-        "height": 16,
-        "alt": "向前移动"
-      }
+      "helpUrl": ""
     },
     isGeneral: true // 标记为通用积木
   },
   move_forward_param: {
     jsonConfig: {
       "type": "move_forward_param",
-      "message0": "向前移动 %1 次",
+      "message0": "%1 向前移动 %2 次",
       "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向前移动多次"
+        },
         {
           "type": "field_number",
           "name": "PARAM",
@@ -44,39 +56,43 @@ const availableBlocks = {
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向前移动指定次数",
-      "helpUrl": "",
-      "icon": {
-        "src": "▶",
-        "width": 16,
-        "height": 16,
-        "alt": "向前移动多次"
-      }
+      "helpUrl": ""
     },
     isGeneral: true // 标记为通用积木
   },
   move_backward: {
     jsonConfig: {
       "type": "move_backward",
-      "message0": "向后移动",
+      "message0": "%1 向后移动",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向后移动"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向后移动一格",
-      "helpUrl": "",
-      "icon": {
-        "src": "▶",
-        "width": 16,
-        "height": 16,
-        "alt": "向后移动"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
   move_backward_param: {
     jsonConfig: {
       "type": "move_backward_param",
-      "message0": "向后移动 %1 次",
+      "message0": "%1 向后移动 %2 次",
       "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向后移动多次"
+        },
         {
           "type": "field_number",
           "name": "PARAM",
@@ -90,67 +106,70 @@ const availableBlocks = {
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向后移动指定次数",
-      "helpUrl": "",
-      "icon": {
-        "src": "▶",
-        "width": 16,
-        "height": 16,
-        "alt": "向后移动多次"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
   turn_left: {
     jsonConfig: {
       "type": "turn_left",
-      "message0": "向左转",
+      "message0": "%1 向左转",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向左转"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向左转",
-      "helpUrl": "",
-      "icon": {
-        "src": "◀",
-        "width": 16,
-        "height": 16,
-        "alt": "向左转"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
   turn_right: {
     jsonConfig: {
       "type": "turn_right",
-      "message0": "向右转",
+      "message0": "%1 向右转",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向右转"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向右转",
-      "helpUrl": "",
-      "icon": {
-        "src": "▶",
-        "width": 16,
-        "height": 16,
-        "alt": "向右转"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
   turn_around: {
     jsonConfig: {
       "type": "turn_around",
-      "message0": "向后转",
+      "message0": "%1 向后转",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "向后转"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 230,
       "tooltip": "控制角色向后转",
-      "helpUrl": "",
-      "icon": {
-        "src": "◀▶",
-        "width": 16,
-        "height": 16,
-        "alt": "向后转"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
@@ -158,36 +177,42 @@ const availableBlocks = {
   pickup_item: {
     jsonConfig: {
       "type": "pickup_item",
-      "message0": "拾取物品",
+      "message0": "%1 拾取物品",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "拾取物品"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 30,
       "tooltip": "尝试拾取当前位置的物品",
-      "helpUrl": "",
-      "icon": {
-        "src": "👆",
-        "width": 16,
-        "height": 16,
-        "alt": "拾取物品"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
   use_item: {
     jsonConfig: {
       "type": "use_item",
-      "message0": "使用物品",
+      "message0": "%1 使用物品",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "使用物品"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 30,
       "tooltip": "在当前位置使用物品",
-      "helpUrl": "",
-      "icon": {
-        "src": "👇",
-        "width": 16,
-        "height": 16,
-        "alt": "使用物品"
-      }
+      "helpUrl": ""
     },
     isGeneral: true
   },
@@ -195,8 +220,15 @@ const availableBlocks = {
   pause: {
     jsonConfig: {
       "type": "pause",
-      "message0": "暂停 %1 秒",
+      "message0": "%1 暂停 %2 秒",
       "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "暂停"
+        },
         {
           "type": "field_number",
           "name": "SECONDS",
@@ -210,49 +242,49 @@ const availableBlocks = {
       "nextStatement": null,
       "colour": 0,
       "tooltip": "让角色在原地停留指定的秒数",
-      "helpUrl": "",
-      "icon": {
-        "src": "⏸",
-        "width": 16,
-        "height": 16,
-        "alt": "暂停"
-      }
+      "helpUrl": ""
     },
     isGeneral: false
   },
   play_sound: {
     jsonConfig: {
       "type": "play_sound",
-      "message0": "声音",
+      "message0": "%1 声音",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "声音"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 160,
       "tooltip": "自动触发特效提示音,并根据当前方格类型执行相应操作",
-      "helpUrl": "",
-      "icon": {
-        "src": "🔊",
-        "width": 16,
-        "height": 16,
-        "alt": "声音"
-      }
+      "helpUrl": ""
     },
     isGeneral: false
   },
   construct: {
     jsonConfig: {
       "type": "construct",
-      "message0": "修建",
+      "message0": "%1 修建",
+      "args0": [
+        {
+          "type": "field_image",
+          "src": xianqianImage,
+          "width": 16,
+          "height": 16,
+          "alt": "修建"
+        }
+      ],
       "previousStatement": null,
       "nextStatement": null,
       "colour": 160,
       "tooltip": "修建处理",
-      "helpUrl": "",
-      "icon": {
-        "src": "🔧",
-        "width": 16,
-        "height": 16,
-        "alt": "修建"
-      }
+      "helpUrl": ""
     },
     isGeneral: false
   }
@@ -314,15 +346,15 @@ const availablePythonGenerators = {
     return 'move_forward()\n';
   },
   move_forward_param: function(block) {
-    const times = block.getFieldValue('PARAM');
-    return `move_forward(${times})\n`;
+    const stepCount = block.getFieldValue('PARAM');
+    return `move_forward(${stepCount})\n`;
   },
   move_backward: function(block) {
     return 'move_backward()\n';
   },
   move_backward_param: function(block) {
-    const times = block.getFieldValue('PARAM');
-    return `move_backward(${times})\n`;
+    const stepCount = block.getFieldValue('PARAM');
+    return `move_backward(${stepCount})\n`;
   },
   turn_left: function(block) {
     return 'turn_left()\n';
@@ -450,11 +482,10 @@ export function registerJavaScriptGenerators(blocklySpecialBlocks = []) {
   // 确保blocklySpecialBlocks始终是数组
   blocklySpecialBlocks = Array.isArray(blocklySpecialBlocks) ? blocklySpecialBlocks : [];
   // 遍历所有可用生成器
-  for (const [blockName, generatorFunction] of Object.entries(availableGenerators)) {
-    // 检查对应的积木是否为通用积木或允许的特殊积木
-    const blockConfig = availableBlocks[blockName];
-    if (blockConfig && (blockConfig.isGeneral || blocklySpecialBlocks.includes(blockName))) {
-      javascriptGenerator.forBlock[blockName] = generatorFunction;
+  for (const [blockName, generatorFunc] of Object.entries(availableGenerators)) {
+    // 如果是通用生成器,或者是允许的特殊生成器对应的生成器,则注册
+    if (availableBlocks[blockName].isGeneral || blocklySpecialBlocks.includes(blockName)) {
+      javascriptGenerator.forBlock[blockName] = generatorFunc;
     }
   }
 
@@ -472,12 +503,11 @@ export function registerJavaScriptGenerators(blocklySpecialBlocks = []) {
 export function registerPythonGenerators(blocklySpecialBlocks = []) {
   // 确保blocklySpecialBlocks始终是数组
   blocklySpecialBlocks = Array.isArray(blocklySpecialBlocks) ? blocklySpecialBlocks : [];
-  // 遍历所有可用Python生成器
-  for (const [blockName, generatorFunction] of Object.entries(availablePythonGenerators)) {
-    // 检查对应的积木是否为通用积木或允许的特殊积木
-    const blockConfig = availableBlocks[blockName];
-    if (blockConfig && (blockConfig.isGeneral || blocklySpecialBlocks.includes(blockName))) {
-      pythonGenerator.forBlock[blockName] = generatorFunction;
+  // 遍历所有可用生成器
+  for (const [blockName, generatorFunc] of Object.entries(availablePythonGenerators)) {
+    // 如果是通用生成器,或者是允许的特殊生成器对应的生成器,则注册
+    if (availableBlocks[blockName].isGeneral || blocklySpecialBlocks.includes(blockName)) {
+      pythonGenerator.forBlock[blockName] = generatorFunc;
     }
   }
 
@@ -490,8 +520,9 @@ export function registerPythonGenerators(blocklySpecialBlocks = []) {
 
 /**
  * 初始化Blockly工作区
- * @param {Object} options - 初始化选项
- * @returns {Blockly.Workspace} - 初始化后的Blockly工作区
+ * @param {HTMLElement} blocklyDiv - Blockly容器元素
+ * @param {HTMLElement} toolbox - 工具箱元素
+ * @returns {Blockly.WorkspaceSvg} - Blockly工作区实例
  */
 export function initBlockly(options = {}) {
   // 应用中文配置

BIN
src/assets/images/blockly/component/xianqian.png


+ 96 - 3
src/components/blockly/MapGame.vue

@@ -2,8 +2,18 @@
   <div class="title-box">
     <!-- 左侧标题部分 -->
     <div class="left-container">
-      <!-- 游戏编号 -->
-      <div class="game-badge">{{ gameSort }}</div>
+      <!-- 线路切换按钮 -->
+      <div class="route-buttons">
+        <button 
+          v-for="(route, index) in parsedRouteList" 
+          :key="index"
+          class="game-badge"
+          :class="{ 'active': currentRouteIndex === index }"
+          @click="switchRoute(index)"
+        >
+          {{ gameSort }}{{ index + 1 }}
+        </button>
+      </div>
     </div>
   </div>
 
@@ -223,6 +233,11 @@ const props = defineProps({
     type: String,
     default: ''
   },
+  // 路线列表
+  routeList: {
+    type: [String, Array],
+    default: ''
+  },
   // 课程列表
   courseList: {
     type: Array,
@@ -315,6 +330,19 @@ const gameTitle = computed(() => props.gameTitle);
 const currentGameData = ref(null);
 const playerInitialDirection = ref(0); // 人物初始朝向
 const gameSort = ref('Game'); // 默认排序
+const currentRouteIndex = ref(0); // 当前线路索引
+
+// 解析routeList
+const parsedRouteList = computed(() => {
+  if (!props.routeList) return [];
+  try {
+    const routeData = typeof props.routeList === 'string' ? JSON.parse(props.routeList) : props.routeList;
+    return Array.isArray(routeData) ? routeData : [];
+  } catch (error) {
+    console.error('解析routeList失败:', error);
+    return [];
+  }
+});
 
 // 运行控制标志
 let shouldStopExecution = false;
@@ -380,6 +408,8 @@ const gameState = reactive({
     endPoint: {},
     // 地图上所有可行走的点坐标集合,添加type属性区分普通点和冰块
     walkablePoints: [],
+    // 路线列表
+    routeList: [],
     // 保存原始的可行走点数据,用于重置
     originalWalkablePoints: [],
   }
@@ -571,6 +601,7 @@ const fetchGameData = async () => {
         mapStartPoint: props.mapStartPoint,
         mapEndPoint: props.mapEndPoint,
         mapWalkablePoints: props.mapWalkablePoints,
+        routeList: props.routeList,
         userDirection: props.userDirection,
         userImage: props.userImage,
         info: props.info
@@ -590,6 +621,36 @@ const fetchGameData = async () => {
   }
 };
 
+// 切换线路
+const switchRoute = (index) => {
+  if (index < 0 || index >= parsedRouteList.value.length) return;
+  
+  currentRouteIndex.value = index;
+  const route = parsedRouteList.value[index];
+  
+  // 更新人物朝向
+  if (route.direction !== undefined) {
+    playerInitialDirection.value = route.direction;
+    gameState.player.direction = route.direction;
+  }
+  
+  // 更新开始坐标
+  if (route.startPoint) {
+    const startPoint = typeof route.startPoint === 'string' ? JSON.parse(route.startPoint) : route.startPoint;
+    gameState.mapData.startPoint = { x: startPoint.x, y: startPoint.y };
+    gameState.player.position = { x: startPoint.x, y: startPoint.y };
+  }
+  
+  // 更新结束坐标
+  if (route.endPoint) {
+    const endPoint = typeof route.endPoint === 'string' ? JSON.parse(route.endPoint) : route.endPoint;
+    gameState.mapData.endPoint = { x: endPoint.x, y: endPoint.y };
+  }
+  
+  // 重新初始化可行走点集合
+  initWalkablePointsSet();
+};
+
 // 根据获取到的数据更新游戏状态
 const updateGameStateFromData = (gameData) => {
   try {
@@ -617,6 +678,17 @@ const updateGameStateFromData = (gameData) => {
     if (gameData.mapWalkablePoints) {
       gameState.mapData.walkablePoints = JSON.parse(gameData.mapWalkablePoints);
     }
+
+    // 路线列表
+    if (gameData.routeList) {
+      gameState.mapData.routeList = JSON.parse(gameData.routeList);
+    }
+    
+    // 初始加载时选中第一条线路
+    if (parsedRouteList.value.length > 0) {
+      switchRoute(0);
+    }
+    
     // 重新初始化可行走点集合
     initWalkablePointsSet();
   } catch (error) {
@@ -2123,11 +2195,17 @@ onUnmounted(() => {
   gap: 10px;
 }
 
+/* 线路按钮容器样式 */
+.route-buttons {
+  display: flex;
+  gap: rpx(5);
+  margin-left: rpx(3);
+}
+
 /* 右侧两个角为圆角的长方形格子样式 */
 .game-badge {
   width: rpx(70);
   height: rpx(20);
-  margin-left: rpx(3);
   background-color: #5fb5dc;
   color: #fff;
   border-radius: 0 rpx(20) rpx(20) 0;
@@ -2136,6 +2214,21 @@ onUnmounted(() => {
   justify-content: center;
   font-size: rpx(15);
   font-weight: bold;
+  border: none;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.game-badge:hover {
+  background-color: #3498db;
+  transform: translateY(-2px);
+  box-shadow: 0 rpx(5) rpx(10) rgba(0, 0, 0, 0.1);
+}
+
+.game-badge.active {
+  background-color: #e74c3c;
+  color: #fff;
+  font-weight: bold;
 }
 
 /* 右侧容器样式 */

+ 2 - 0
src/views/programming/Interface.vue

@@ -65,6 +65,7 @@
                    :map-end-point="course.blocklyEndPoint"
                    :map-walkable-points="course.blocklyWalkablePoints"
                    :user-direction="course.blocklyUserDirection"
+                   :route-list="course.blocklyRouteList"
                    :user-image="course.blocklyUserImage"
                    :info="course.blocklyInfo"
                    :game-title="course.courseName"
@@ -318,6 +319,7 @@ const handleParentCourseData = (courseData = props.courseData) => {
     blocklyEndPoint: courseData.blocklyEndPoint,
     blocklyWalkablePoints: courseData.blocklyWalkablePoints,
     blocklyUserDirection: courseData.blocklyUserDirection || 0,
+    blocklyRouteList: courseData.blocklyRouteList,
     blocklyUserImage: courseData.blocklyUserImage,
     blocklyInfo: courseData.blocklyInfo,
     blocklySpecialBlocks: courseData.blocklySpecialBlocks ? courseData.blocklySpecialBlocks.split(',') : [],

+ 1 - 1
src/views/programming/ProgrammingCourset.vue

@@ -244,7 +244,7 @@ const fetchCourseData = () => {
         newResData.forEach(item => {
           item.isDisabled = isDisabled.value
         });
-        
+
         // 创建图片映射,根据bcLabel显示对应图片
         const imageMap = {
           '1': explanation,