lvkun996 9 сар өмнө
parent
commit
733bd0e83e

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 576 - 344
package-lock.json


+ 1 - 0
package.json

@@ -47,6 +47,7 @@
     "vue": "^3.3.4",
     "vue-class-component": "^8.0.0-0",
     "vue-countup-v3": "^1.4.0",
+    "vue-draggable-next": "^2.2.1",
     "vue-hooks-plus": "^1.6.2",
     "vue-router": "^4.0.3",
     "w3c-keyname": "^2.2.8"

+ 3 - 0
src/controller/CardController.ts

@@ -102,6 +102,9 @@ export class CardController {
 
     const dataJson = JSON.parse(data) as API.CardJson
 
+    // 思路,在ok_key里记录高亮的id === ? 就是value,用value和卡片模版对比,来进行高亮
+    // 直接点击对应的ok_key 或者 err_key, 再点击对应的模版按钮来往里push value,没点击一个选择一个音频
+
     dataJson.game_list = dataJson.game_list.map((item) => {
       return {
         ...item,

+ 210 - 35
src/pages/card/components/config-game.vue

@@ -37,21 +37,16 @@
             class="config-game-item"
             v-for="(folder, index) in cardJson.game_list"
             :key="index"
-            @dblclick="enterSubFolder(index)"
+            @dblclick="enterFolder(index)"
           >
             <!-- 状态图标 -->
             <div class="status-icon">
-              <CheckCircleFilled
-                v-if="validateGameConfig(folder).isValid"
-                class="status-complete"
-              />
-              <ExclamationCircleFilled
-                v-else
-                class="status-incomplete"
-              />
+              <CheckCircleFilled  v-if="validateGameConfig(folder).isValid"   class="status-complete" />
+              <ExclamationCircleFilled v-else    class="status-incomplete"   />
             </div>
 
             <img class="folder-icon" :src="require('@/assets/common/folder.svg')" alt="">
+
             <div class="folder-footer">
               <div>游戏 {{ index + 1 }}</div>
               <div>
@@ -73,22 +68,57 @@
               </div>
             </div>
           </a-col>
+          <a-empty style="margin: 0 auto;" v-if="cardJson.game_list.length === 0" description="暂无游戏"> </a-empty>
         </a-row>
 
-        <!-- <div v-else-if="currentLevel === 1">
+        <div v-else-if="currentLevel === 1">
           <a-row class="config-game-list" :gutter="[8, 8]">
-            <a-col :span="12" class="config-game-item" v-for="(item, index) in 4" :key="index" @dblclick="enterCardConfig(index)">
+            <a-col
+              :span="12"
+              class="config-game-item"
+              v-for="(item, index) in cardJson.game_list[currentGameIndex].items"
+              :key="index"
+              @dblclick="enterCardConfig(index)"
+            >
               <img class="folder-icon" :src="require('@/assets/common/folder.svg')" alt="">
-              <div>items {{index + 1}}</div>
+              <div>游戏 {{index + 1}}</div>
             </a-col>
           </a-row>
+          <a-empty  style="margin: 0 auto;" v-if="cardJson.game_list[currentGameIndex].items.length === 0" description="暂无游戏"> </a-empty>
         </div>
 
+        <!-- 三级内容:游戏步骤配置 -->
         <div v-else-if="currentLevel === 2">
           <div class="card-config-content">
-            <h3>卡片配置</h3>
+            <a-button type="primary" size="small" @click="createGameStep" style="margin-bottom: 16px;">
+              <template #icon><plus-outlined /></template>
+              新建游戏步骤
+            </a-button>
+            <draggable
+              v-if="cardJson.game_list[currentGameIndex]?.items?.[currentSubFolderIndex]?.steps"
+              class="step-list"
+              v-model="cardJson.game_list[currentGameIndex].items[currentSubFolderIndex].steps"
+              item-key="id"
+            >
+              <template #item="{ element: step, index }">
+                <div
+                  class="step-item"
+                  :class="{ 'step-item-active': activeStepIndex === index }"
+                  @click="selectStep(index)"
+                >
+                  <div class="step-number">步骤 {{ index + 1 }}</div>
+                  <div class="step-circles">
+                    <div class="circle" v-for="(circle, cIndex) in step.circles" :key="cIndex"></div>
+                  </div>
+                  <div class="step-actions">
+                    <delete-outlined @click.stop="deleteGameStep(index)" style="color: #ff4d4f;" />
+                  </div>
+                </div>
+              </template>
+            </draggable>
+            <a-empty v-if="cardJson.game_list[currentGameIndex].items.length === 0" description="暂无步骤"> </a-empty>
           </div>
-        </div> -->
+        </div>
       </div>
     </div>
   </modal-pro>
@@ -209,22 +239,33 @@ import { LeftOutlined, PlusOutlined, SmallDashOutlined, EditOutlined, DeleteOutl
 import { reactive, ref } from 'vue'
 import { message } from 'ant-design-vue'
 import SelectAudioNew from './select-audio-new.vue'
+import draggable from 'vue-draggable-next'
 
 // 文件夹数据
 const folders = reactive<{ name: string; rule: string; mainSubject: string }[]>([])
 
-// 一级game的form
+// 一级folders的form
 const game1FormJson = {
-  rule: '',
-  mainSubject: { music_name: '', is_break: 1 },
-  ordered_multiple_err: { music_name: '', is_break: 1 },
-  has_click_single: { music_name: '', is_break: 1 },
-  still_have: { music_name: '', is_break: 1 },
-  has_click_group: { music_name: '', is_break: 1 },
-  wait_30s: { music_name: '', is_break: 1 },
-  wait_90s: { music_name: '', is_break: 1 }
+  rule: '7',
+  mainSubject: { music_name: '102.mp3', is_break: 1 },
+  ordered_multiple_err: { music_name: '102.mp3', is_break: 1 },
+  has_click_single: { music_name: '102.mp3', is_break: 1 },
+  still_have: { music_name: '102.mp3', is_break: 1 },
+  has_click_group: { music_name: '102.mp3', is_break: 1 },
+  wait_30s: { music_name: '102.mp3', is_break: 1 },
+  wait_90s: { music_name: '102.mp3', is_break: 1 }
 }
 
+// 当前层级:0表示顶级文件夹,1表示sub文件夹,2表示游戏配置
+const currentLevel = ref(0)
+
+// 点击路径的index
+const currentFolderIndex = reactive({
+  folder: 0,
+  subFolder: 0,
+  step: 0
+})
+
 // 卡片category为21的json
 const cardJson = reactive<API.CardJson21>({
   game_list: []
@@ -253,7 +294,7 @@ const editForm = reactive({
 const subItemModalVisible = ref(false)
 const subItemForm = reactive({
   sub_subject: {
-    music_name: '',
+    music_name: '102.mp3',
     mb: '',
     ok: '',
     ob: '',
@@ -268,10 +309,12 @@ const subItemForm = reactive({
 
 // 当前选中的游戏索引
 const currentGameIndex = ref(0)
+
 // 当前选中的子文件夹索引
 const currentSubFolderIndex = ref(0)
-// 当前层级:0表示顶级文件夹,1表示二级文件夹,2表示卡片配置
-const currentLevel = ref(0)
+
+// 游戏步骤相关
+const activeStepIndex = ref<number | null>(null)
 
 // 获取弹窗标题
 const getModalTitle = () => {
@@ -326,7 +369,7 @@ const validateGameConfig = (gameConfig: any) => {
 }
 
 // 进入二级文件夹
-const enterSubFolder = (index: number) => {
+const enterFolder = (index: number) => {
   const gameConfig = cardJson.game_list[index]
   const validation = validateGameConfig(gameConfig)
 
@@ -345,7 +388,8 @@ const enterSubFolder = (index: number) => {
     const missingFieldNames = validation.missingFields.map(field => fieldNames[field] || field)
 
     // 使用 Ant Design 的 message 组件显示错误信息
-    message.error(`游戏 ${index + 1} 配置不完整,缺少以下字段:${missingFieldNames.join('、')}`)
+    message.error(`游戏 ${index + 1} 配置不完整,缺少以下字段:${missingFieldNames.join('、').replaceAll('、', '、\n')}`)
+
     return
   }
 
@@ -354,10 +398,50 @@ const enterSubFolder = (index: number) => {
     cardJson.game_list[index].items = []
   }
 
-  // 创建新的 item 对象并添加到 items 数组中
+  currentGameIndex.value = index
+  currentLevel.value = 1
+}
+
+const createSubItem = () => {
+  // 重置表单并打开弹窗
+  subItemForm.sub_subject.music_name = ''
+  subItemForm.sub_subject.mb = ''
+  subItemForm.sub_subject.ok = ''
+  subItemForm.sub_subject.ob = ''
+  subItemForm.sub_subject.err = ''
+  subItemForm.sub_subject.eb = ''
+  subItemForm.ok_key = []
+  subItemForm.err_key = []
+  subItemForm.ok_key_voice = []
+  subItemForm.err_key_voice = []
+
+  subItemModalVisible.value = true
+}
+
+// 新建二级文件夹
+const handleSaveSubItem = () => {
+  // 基础校验:必须选择子主题音乐
+  if (!subItemForm.sub_subject.music_name) {
+    message.warning('请选择子主题音乐')
+    return
+  }
+
+  const index = currentGameIndex.value
+  const currentGame = cardJson.game_list[index]
+  if (!currentGame) {
+    message.error('未选择有效的游戏')
+    return
+  }
+
+  // 确保 items 数组存在
+  if (!currentGame.items) {
+    currentGame.items = []
+  }
+
+  // 构造 items 下的新对象(遵循 API.CardJson21[game_list].items 的结构)
   const newItem = {
     sub_subject: {
-      music_name: '',
+      music_name: subItemForm.sub_subject.music_name,
       mb: '',
       ok: '',
       ob: '',
@@ -370,18 +454,60 @@ const enterSubFolder = (index: number) => {
     err_key_voice: []
   }
 
-  cardJson.game_list[index].items.push(newItem)
+  // 追加到 items
+  currentGame.items.push(newItem)
 
-  currentGameIndex.value = index
-  currentLevel.value = 1
+  // 关闭弹窗并重置关键字段,便于下次新增
+  subItemModalVisible.value = false
+  subItemForm.sub_subject.music_name = ''
 }
 
-const handleSaveSubItem = () => {}
-
 // 进入卡片配置
 const enterCardConfig = (index: number) => {
   currentSubFolderIndex.value = index
+  const currentItem = cardJson.game_list[currentGameIndex.value]?.items?.[index]
+  if (currentItem) {
+    // 初始化步骤数组(如果不存在)
+    if (!currentItem.steps) {
+      currentItem.steps = []
+    }
+  }
   currentLevel.value = 2
+  activeStepIndex.value = null // 重置选中的步骤
+}
+
+// 创建游戏步骤
+const createGameStep = () => {
+  const rule = cardJson.game_list[currentGameIndex.value].rule
+  const currentItem = cardJson.game_list[currentGameIndex.value]?.items?.[currentSubFolderIndex.value]
+  console.log('创建游戏步骤', currentItem)
+
+  if (currentItem) {
+    if (!currentItem.steps) {
+      currentItem.steps = []
+    }
+    const newStep = {
+      id: Date.now(), // 使用时间戳作为唯一ID
+      circles: [{}, {}, {}] // 示例:每个步骤包含3个小圆圈
+    }
+    currentItem.steps.push(newStep)
+  }
+}
+
+// 删除游戏步骤
+const deleteGameStep = (index: number) => {
+  const currentItem = cardJson.game_list[currentGameIndex.value]?.items?.[currentSubFolderIndex.value]
+  if (currentItem?.steps) {
+    currentItem.steps.splice(index, 1)
+    if (activeStepIndex.value === index) {
+      activeStepIndex.value = null
+    }
+  }
+}
+
+// 选中步骤
+const selectStep = (index: number) => {
+  activeStepIndex.value = index
 }
 
 // 返回上级文件夹
@@ -525,4 +651,53 @@ const handleCancelEdit = () => {
     text-align: center;
   }
 }
+
+.step-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.step-item {
+  display: flex;
+  align-items: center;
+  padding: 12px;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+  cursor: grab;
+  background-color: #fff;
+  transition: all 0.3s;
+
+  &:hover {
+    border-color: #40a9ff;
+  }
+}
+
+.step-item-active {
+  border-color: #1890ff;
+  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+}
+
+.step-number {
+  font-weight: 500;
+  margin-right: 16px;
+}
+
+.step-circles {
+  display: flex;
+  gap: 8px;
+  flex-grow: 1;
+}
+
+.circle {
+  width: 20px;
+  height: 20px;
+  border-radius: 50%;
+  background-color: #f0f0f0;
+  border: 1px solid #ccc;
+}
+
+.step-actions {
+  cursor: pointer;
+}
 </style>

+ 9 - 0
src/pages/card/components/game-stage-3.vue

@@ -0,0 +1,9 @@
+<template>
+vue3
+</template>
+<script lang='ts'  setup >
+
+</script>
+<style lang='less' scoped >
+
+</style>

+ 3 - 2
src/pages/card/index.vue

@@ -26,7 +26,7 @@
   </div>
 
   <CardTemplate
-    v-if="cardInfo.card_type == 5"
+    v-if="Number(cardInfo.card_type) === 5"
     ref="cardTemplateDom"
     @operation="handleOperation"
     :cardData="cardInfo"
@@ -266,7 +266,8 @@ const getCardJson21 = async () => {
 function onBack () { window.history.back() }
 
 onMounted(() => {
-  cardInfo.card_type == 5 ? getCardJson5() : getCardJson21()
+  cardInfo.card_type === 5 ? getCardJson5() : getCardJson21()
+  console.log('卡片类型', cardInfo.card_type)
 })
 </script>
 <style lang='less' scoped >

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 320 - 146
yarn.lock


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно