Repository: goplus/builder
Pull request #1393: feat: add NodeTaskPlayer
Author: login: HaoQiuji-Pavel
Created: 2025-03-03T06:56:49Z | Merged: 2025-03-03T08:30:12Z



Diff:
diff --git a/spx-gui/src/apis/guidance.ts b/spx-gui/src/apis/guidance.ts
new file mode 100644
index 000000000..dcaeefd23
--- /dev/null
+++ b/spx-gui/src/apis/guidance.ts
@@ -0,0 +1,88 @@
+import type { LocaleMessage } from '@/utils/i18n'
+import type { Position } from '@/components/editor/code-editor/common'
+
+export type Mask = {
+  startPos: Position
+  endPos: Position
+}
+
+export type Step = {
+  title: LocaleMessage // 步骤名称
+  description: LocaleMessage // 步骤描述
+  tip: LocaleMessage // 互动提示（需要条件触发）
+  duration: number // 用户当前步骤卡顿距离给提示的时长（单位：秒）
+  target: string[] // 目标元素语义化标注的key
+  type: 'coding' | 'following' // 存在两种类型的步骤，分别是Following步骤和Coding步骤
+  isCheck: boolean // 该步骤是否涉及快照比对
+  isApiControl: boolean // 是否需要去控制API Reference的展示
+  apis: string[] // 该步骤中需要展示的API Reference的definition列表
+  isAssetControl: boolean // 是否需要去控制素材的展示
+  assets: string[] // 该步骤中需要被展示的素材的id列表
+  isSpriteControl: boolean // 是否需要去控制精灵的展示
+  sprites: string[] // 该步骤中需要被展示的精灵的id列表
+  isSoundControl: boolean // 是否需要去控制声音的展示
+  sounds: string[] // 该步骤中需要被展示的声音的id列表
+  isCostumeControl: boolean // 是否需要去控制造型的展示
+  costumes: string[] // 该步骤中需要被展示的造型的id列表
+  isAnimationControl: boolean // 是否需要去控制动画的展示
+  animations: string[] // 该步骤中需要被展示的动画的id列表
+  isWidgetControl: boolean // 是否需要去控制组件的展示
+  widgets: string[] // 该步骤中需要被展示的组件的id列表
+  isBackdropControl: boolean // 是否需要去控制背景的展示
+  backdrops: string[] // 该步骤中需要被展示的背景的id列表
+  snapshot: {
+    startSnapshot: string // 初始快照
+    endSnapshot?: string // 结束快照
+  }
+  coding: {
+    // coding任务独有的数据结构
+    path: string // 编码文件路径
+    codeMasks: Mask[] // 完形填空的mask数组，一个mask对应一个空的答案
+    startPosition: Position // 答案展示的开始位置
+    endPosition: Position // 答案展示的结束位置
+  }
+}
+
+export type NodeTask = {
+  name: LocaleMessage // 节点名称
+  triggerTime: number // 节点在视频中的触发时间(单位：秒)
+  video: string
+  steps: Step[]
+}
+
+export type Level = {
+  // 一个故事线存在多个关卡
+  cover: string // 封面图片url
+  placement: Placement // 在界面上的位置信息（每个故事线中的关卡放置位置是不确定的，由人为指定）
+  title: LocaleMessage // 关卡标题
+  description: LocaleMessage // 关卡描述
+  video: string // 关卡的整体视频url（由NodeTask的视频拼接而来）
+  achievement: {
+    // 成就与关卡绑定，一个关卡可能存在多个成就
+    icon: string // 成就图标url
+    title: LocaleMessage // 成就名称
+  }
+  nodeTasks: NodeTask[]
+}
+
+export type StoryLine = {
+  id: string
+  backgroundImage: string // 故事线的背景图url
+  name: string // 故事线的名字（能够唯一标识该故事线，可用于为用户创建project时的projectName）
+  title: LocaleMessage // 故事线标题
+  description: LocaleMessage // 故事线描述
+  tag: 'easy' | 'medium' | 'hard' // 故事线难度标签
+  levels: Level[]
+}
+
+export type StoryLineStudy = {
+  storyLineId: string
+  lastFinishedLevelIndex: number // 故事线状态，其值为当前最新已完成的关卡下标
+}
+
+type Placement = {
+  /** X position in percentage */
+  x: number
+  /** Y position in percentage */
+  y: number
+}
diff --git a/spx-gui/src/components/guidance/NodeTaskPlayer.vue b/spx-gui/src/components/guidance/NodeTaskPlayer.vue
new file mode 100644
index 000000000..45d7fac66
--- /dev/null
+++ b/spx-gui/src/components/guidance/NodeTaskPlayer.vue
@@ -0,0 +1,36 @@
+<template>
+  <div class="node-task-player">
+    <!-- 渲染当前步骤的 StepPlayer -->
+    <!-- <StepPlayer
+      v-if="currentStep"
+      :step="currentStep"
+      @stepCompleted="handleStepCompleted"
+    /> -->
+  </div>
+</template>
+
+<script lang="ts" setup>
+import type { NodeTask, Step } from '@/apis/guidance';
+import { ref, computed } from 'vue';
+
+const props = defineProps<{ nodeTask: NodeTask }>();
+
+const emit = defineEmits<{ nodeTaskCompleted: [] }>();
+
+// 当前步骤的索引
+const currentStepIndex = ref(0)
+// 计算当前步骤（StepPlayer 渲染当前步骤）
+const currentStep = computed<Step | undefined>(() => props.nodeTask.steps[currentStepIndex.value])
+
+function handleStepCompleted(): void {
+  currentStepIndex.value++
+  if (currentStepIndex.value === props.nodeTask.steps.length) {
+    // 所有步骤完成后，通知父组件（LevelPlayer）节点任务完成
+    emit('nodeTaskCompleted')
+  }
+}
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file


Reviews:
user: login: nighca
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T07:14:17Z
user: login: nighca
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T07:17:16Z
user: login: HaoQiuji-Pavel
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T07:29:42Z
user: login: nighca
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-03T08:30:05Z

Review comments:
diff_hunk: @@ -0,0 +1,36 @@
+<template>
+  <div class="node-task-player">
+    <!-- 渲染当前步骤的 StepPlayer -->
+    <StepPlayer
path: spx-gui/src/components/guidance/NodeTaskPlayer.vue
user: login: nighca
body: lint 的问题处理一下？

![image](https://github.com/user-attachments/assets/fc3bd583-8d03-4718-8656-1d46ba88672c)

created_at: 2025-03-03T07:11:57Z
author_association: COLLABORATOR
path: spx-gui/src/components/guidance/common/types.ts
user: login: nighca
body: 参考 `src/apis/project.ts`，这个也放到 `apis` 那边维护吧

created_at: 2025-03-03T07:13:49Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,36 @@
+<template>
+  <div class="node-task-player">
+    <!-- 渲染当前步骤的 StepPlayer -->
+    <StepPlayer
path: spx-gui/src/components/guidance/NodeTaskPlayer.vue
user: login: nighca
body: 似乎这里依赖了 #1394 的 `StepPlayer`，而 #1394 又依赖了这个 PR 中的 types 定义..

可以考虑拆一下 PR，比如先 PR 合入 types 定义（它对其他模块没有依赖），然后合入 #1394 ，然后合入 `NodeTaskPlayer`

或者把你这里 `NodeTaskPlayer` 对 `StepPlayer` 的依赖先干掉，比如先用 `<div>TODO</div>` 之类的东西在这里占位，等 `StepPlayer` 存在了再去依赖它
created_at: 2025-03-03T07:17:16Z
author_association: COLLABORATOR
path: spx-gui/src/components/guidance/common/types.ts
user: login: HaoQiuji-Pavel
body: 好的，上述问题已修改
created_at: 2025-03-03T07:29:42Z
author_association: NONE

PR comments:
user: login: HaoQiuji-Pavel
created_at: 2025-03-03T06:58:30Z
body: @nighca 寒星老师，这个pr添加了guidance需要的类型以及NodeTaskPlayer模块
author_association: NONE
user: login: qiniu-x[bot]
created_at: 2025-03-03T07:24:57Z
body: **[Git-flow]** Hi @HaoQiuji-Pavel, There are some suggestions for your information:

---


> 
### Rebase suggestions
* Following commits have **duplicated** messages 
 
  > feat: add NodeTaskPlayer 

  > feat: add NodeTaskPlayer 

 Which seems insignificant, recommend to use **`git rebase`** command to reorganize your PR. 



For other `git-flow` instructions, recommend refer to [these examples](https://github.com/qiniu/reviewbot/blob/master/docs/engineering-practice/git-flow-instructions_zh.md).


<details>

If you have any questions about this comment, feel free to raise an issue here:

- **https://github.com/qiniu/reviewbot**

</details>

author_association: NONE
user: login: qiniu-ci
created_at: 2025-03-03T07:28:09Z
body: 
This PR has been deployed to the preview environment. You can explore it using the [preview URL](http://goplus-builder-pr-1393.goplus-pr-review.svc.jfcs-qa1.local).

> [!WARNING]
> Please note that deployments in the preview environment are temporary and will be automatically cleaned up after a certain period. Make sure to explore it before it is removed. For any questions, contact the Go+ Builder team.

author_association: NONE