Browse Source

feat: 增加touch_key

lvkun 8 tháng trước cách đây
mục cha
commit
0f537627a4

+ 21 - 0
src/controller/CardController.ts

@@ -138,6 +138,27 @@ export class CardController {
     // }))
     const dataJson = JSON.parse(JSON.stringify(_dataJson)) as API.CardJson21
     console.log('卡片是21时返回的参数', dataJson)
+    dataJson.game_list.forEach((game: any) => {
+      if (!game.touch_key || !Array.isArray(game.touch_key)) {
+        game.touch_key = [
+          [{ value: 0, is_break: 1, music_name: '' }],
+          [{ value: 0, is_break: 1, music_name: '' }],
+          [{ value: 0, is_break: 1, music_name: '' }]
+        ]
+      }
+      game.touch_key.forEach((touch_keys: API.CardJson21TouchKey) => {
+        if (
+          touch_keys[0].music_name === undefined ||
+            touch_keys[0].music_name === null ||
+            touch_keys[0].music_name === '' ||
+            !touch_keys[0].music_name
+        ) {
+          touch_keys[0].music_name = ''
+        }
+      })
+    })
+    console.log('处理后的卡片json', dataJson)
+
     return { data: dataJson, cardType: dataJson.header.card_type }
   }
 

+ 36 - 11
src/pages/card/components/card-template-21.vue

@@ -3,6 +3,7 @@
 </template>
 <script lang='ts' setup>
 import { onMounted, PropType, ref, watch } from 'vue'
+import { CardController } from '@/controller'
 
 const props = defineProps({
   cardData: {
@@ -53,6 +54,16 @@ const startY = -56 // 图片内第一个矩形的y偏移
 function generateRects () {
   rects.length = 0
   const firstRowGapX = 96
+  for (let i = 0; i < 3; i++) {
+    rects.push({
+      id: i,
+      name: CardController.cardButtonTitleMap.get(i)!,
+      x: startX + i * (rectWidth - 40 + firstRowGapX) + 1166,
+      y: startY + 116,
+      width: rectWidth - 40,
+      height: rectHeight - 100
+    })
+  }
   for (let row = 1; row < 7; row++) {
     for (let col = 1; col < 7; col++) {
       rects.push({
@@ -67,9 +78,25 @@ function generateRects () {
   }
 }
 
+// 生成顶部的配置按钮数据
+function generateTopRects () {
+  const firstRowGapX = 96
+  for (let i = 0; i < 3; i++) {
+    rects.push({
+      id: i,
+      name: CardController.cardButtonTitleMap.get(i)!,
+      x: startX + i * (rectWidth - 40 + firstRowGapX) + 1166,
+      y: startY + 116,
+      width: rectWidth - 40,
+      height: rectHeight - 100
+    })
+  }
+}
+
+// generateTopRects()
 // 图片状态
 const state = {
-  scale: 0.3, // 初始缩放为0.5
+  scale: 0.2, // 初始缩放为0.5
   minScale: 0.2,
   maxScale: 5,
   offsetX: 0,
@@ -89,22 +116,14 @@ const selection = {
   endY: 0
 }
 
-// 多选集合
-const selectedRectIds = ref<number[]>([])
-
 const emits = defineEmits(['operation'])
 
-// 框选操作 and 点选操作
-const handleOperation = (ids: number[]) => {
-  emits('operation', { ids })
-}
-
 function centerImage () {
   const canvas = canvasRef.value
   if (!canvas) return
   const imgW = img.width * state.scale
   const imgH = img.height * state.scale
-  state.offsetX = (canvas.width - imgW) / 2
+  state.offsetX = (canvas.width - imgW) / 2 - window.innerWidth / 4// 额外偏移量
   state.offsetY = (canvas.height - imgH) / 2
 }
 
@@ -382,8 +401,14 @@ watch(() => props.openState.defaultConfig21Open, () => {
 //   }
 // )
 
+const showReactByType = (type: 'content' | 'top') => {
+  type === 'content' ? generateRects() : generateTopRects()
+  draw()
+}
+
 defineExpose({
-  delSelectedRectIds
+  delSelectedRectIds,
+  showReactByType
 })
 
 </script>

+ 32 - 4
src/pages/card/components/config-game.vue

@@ -108,6 +108,10 @@
                           <EditOutlined />
                           <span style="margin-left: 8px;">编辑</span>
                         </a-menu-item>
+                        <a-menu-item key="top" @click="handleEditTop(index)">
+                          <VerticalAlignTopOutlined />
+                          <span style="margin-left: 8px;">顶部配置</span>
+                        </a-menu-item>
                         <a-menu-item key="delete" @click="handleDeleteSubItem(index)">
                           <DeleteOutlined style="color: #ff4d4f;" />
                           <span style="margin-left: 8px;">删除</span>
@@ -129,6 +133,7 @@
             <gameStage3
               v-if="cardJson.game_list[currentGameIndex]?.items?.[currentSubFolderIndex]"
               v-model="cardJson.game_list[currentGameIndex].items[currentSubFolderIndex]"
+              v-model:touch_key="cardJson.game_list[currentGameIndex].touch_key"
               :rule="cardJson.game_list[currentGameIndex].rule"
               @update:modelValue="changedSteps"
               @step-selected="emits('step-selected', $event)"
@@ -213,7 +218,7 @@
         <a-col :span="12">
           <a-form-item label="主题音乐:">
             <SelectAudioNew
-              v-model="editForm.mainSubject.music_name"
+              v-model="editForm.main_subject.music_name"
               placeholder="请选择主题音乐"
             />
           </a-form-item>
@@ -285,7 +290,17 @@
 
 <script lang='ts' setup>
 import { CardController } from '@/controller'
-import { LeftOutlined, PlusOutlined, SmallDashOutlined, EditOutlined, DeleteOutlined, CheckCircleFilled, ExclamationCircleFilled, SoundOutlined } from '@ant-design/icons-vue'
+import {
+  LeftOutlined,
+  PlusOutlined,
+  SmallDashOutlined,
+  EditOutlined,
+  DeleteOutlined,
+  CheckCircleFilled,
+  ExclamationCircleFilled,
+  SoundOutlined,
+  VerticalAlignTopOutlined
+} from '@ant-design/icons-vue'
 import { reactive, ref, PropType, computed, watch } from 'vue'
 import { message } from 'ant-design-vue'
 import SelectAudioNew from './select-audio-new.vue'
@@ -300,6 +315,10 @@ const props = defineProps({
   config: {
     type: Object as PropType<API.CardJson21>,
     default: () => ({})
+  },
+  templateDom21: {
+    type: Object as PropType<HTMLElement>,
+    default: () => document.body
   }
 })
 
@@ -334,7 +353,7 @@ const editModalVisible = ref(false)
 const editingIndex = ref(-1)
 const editForm = reactive({
   rule: '',
-  mainSubject: { music_name: '', is_break: 1 },
+  main_subject: { 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 },
@@ -414,6 +433,7 @@ const validateGameConfig = (gameConfig: any) => {
     'wait_30s.music_name',
     'wait_90s.music_name'
   ]
+  console.log('gameConfig:', gameConfig)
 
   const missingFields: string[] = []
 
@@ -444,7 +464,7 @@ const enterFolder = (index: number) => {
   if (!validation.isValid) {
     const fieldNames = {
       rule: '规则',
-      'mainSubject.music_name': '主题音乐',
+      'main_subject.music_name': '主题音乐',
       'ordered_multiple_err.music_name': '顺序多选错误音乐',
       'has_click_single.music_name': '重复单击音乐',
       'still_have.music_name': '再次点击音乐',
@@ -579,6 +599,14 @@ const handleEditSubItem = (index: number) => {
   editSubItemForm.sub_subject.err = currentItem.sub_subject.err
   editSubItemForm.sub_subject.eb = currentItem.sub_subject.eb
   editSubItemModalVisible.value = true
+  // props.templateDom21.showReactByType('content')
+}
+
+// 编辑顶部配置
+const handleEditTop = () => {
+  console.log('handleEditTop', props.templateDom21)
+
+  props.templateDom21.showReactByType('top')
 }
 
 // 处理删除子项目

+ 177 - 107
src/pages/card/components/game-stage-3.vue

@@ -1,101 +1,134 @@
 <template>
   <div class="game-stage-3">
-    <div class="step-list" >
-      <a-row  style="width: 100%">
-        <a-col :span="24" style="margin-bottom: 16px">
-          <a-space>
-            <div class="step-title" >正确按钮</div>
-            <a-button type="primary" size="small" @click="showAddStepModal('success')"> 新增 </a-button>
-
-          </a-space>
-        </a-col>
-     </a-row>
-      <VueDraggableNext
-        :list="steps.ok_key"
-        group="people"
-        item-key="id"
-        :animation="300"
-        @end="onDragEnd"
-      >
-        <div v-for="(element, index) in steps.ok_key" :key="index" class="step-item-wrapper">
-          <div
-            class="step-item"
-            :class="{ 'step-item-active': activeStepIndex === element.value }"
-            @click="selectStep(element.value)"
-          >
-            <div class="step-info">
-              <span class="step-number">按钮 {{ index + 1 }}</span>
-              <div class="step-details">
-                <span>按钮: <strong>{{ getButtonLabel(element.value) }}</strong></span>
-                <span>音频: <strong>{{ element.music_name }}</strong></span>
-              </div>
-            </div>
-            <div class="step-actions">
-              <a-popconfirm
-                title="确定要删除这个步骤吗?"
-                ok-text="确定"
-                cancel-text="取消"
-                @confirm.stop="deleteGameStep(index, 'ok_key')"
-
-                @cancel.stop
-              >
-                <delete-outlined @click.stop />
-              </a-popconfirm>
+    <div class="top-config">
+      <a-collapse :bordered="false">
+        <a-collapse-panel key="1">
+          <template #header>
+            <div class="collapse-header">
+              <span>顶部按钮配置</span>
             </div>
-          </div>
-        </div>
-      </VueDraggableNext>
-      <a-empty v-if="steps.ok_key.length === 0" description="暂无正确按钮,请点击新增按钮" />
+          </template>
+          <a-form layout="vertical">
+            <a-form-item label="按钮音频">
+              <SelectAudioNew
+                v-model="touch_keys[0][0].music_name"
+                placeholder="请选择队员按钮音频"
+              />
+            </a-form-item>
+
+            <a-form-item label="刷新按钮音频">
+              <SelectAudioNew
+                v-model="touch_keys[1][0].music_name"
+                placeholder="请选择刷新按钮音频"
+              />
+            </a-form-item>
 
+            <a-form-item label="提示按钮音频">
+              <SelectAudioNew
+                v-model="touch_keys[2][0].music_name"
+                placeholder="请选择提示按钮音频"
+              />
+            </a-form-item>
+          </a-form>
+        </a-collapse-panel>
+      </a-collapse>
     </div>
-    <div class="step-list" >
-      <a-row  style="width: 100%">
-      <a-col :span="24" style="margin-bottom: 16px">
-        <a-space>
-          <div class="step-title" >错误按钮</div>
-          <a-button type="primary" size="small" @click="showAddStepModal('error')"> 新增</a-button>
-        </a-space>
-      </a-col>
-      </a-row>
-      <VueDraggableNext
-        :list="steps.err_key"
-        group="people"
-        item-key="id"
-        :animation="300"
-        @end="onDragEnd"
-      >
-        <div v-for="(element, index) in steps.err_key" :key="index" class="step-item-wrapper">
-          <div
-            class="step-item"
-            :class="{ 'step-item-active': activeStepIndex === element.value }"
-            @click="selectStep(element.value)"
+    <div class="step-list" v-if="getBtnRuleByRule === 'multiple'">
+      <a-collapse :bordered="false" >
+        <a-collapse-panel key="2" >
+          <template #header>
+            <div class="collapse-header">
+              <span>正确按钮 ({{ steps.ok_key.length }})</span>
+              <a-button type="primary" size="small" @click.stop="showAddStepModal('success')">新增</a-button>
+            </div>
+          </template>
+
+          <VueDraggableNext
+            :list="steps.ok_key"
+            group="people"
+            item-key="id"
+            :animation="300"
+            @end="onDragEnd"
           >
-            <div class="step-info">
-              <span class="step-number">按钮 {{ index + 1 }}</span>
-              <div class="step-details">
-                <span>按钮: <strong>{{ getButtonLabel(element.value) }}</strong></span>
-                <span>音频: <strong>{{ element.music_name }}</strong></span>
+            <div v-for="(element, index) in steps.ok_key" :key="index" class="step-item-wrapper">
+              <div
+                class="step-item"
+                :class="{ 'step-item-active': activeStepIndex === element.value }"
+                @click="selectStep(element.value)"
+              >
+                <div class="step-info">
+                  <span class="step-number">按钮 {{ index + 1 }}</span>
+                  <div class="step-details">
+                    <span>按钮: <strong>{{ getButtonLabel(element.value) }}</strong></span>
+                    <span>音频: <strong>{{ element.music_name }}</strong></span>
+                  </div>
+                </div>
+                <div class="step-actions">
+                  <a-popconfirm
+                    title="确定要删除这个步骤吗?"
+                    ok-text="确定"
+                    cancel-text="取消"
+                    @confirm.stop="deleteGameStep(index, 'ok_key')"
+                    @cancel.stop
+                  >
+                    <delete-outlined @click.stop />
+                  </a-popconfirm>
+                </div>
               </div>
             </div>
-            <div class="step-actions">
-              <a-popconfirm
-                title="确定要删除这个步骤吗?"
-                ok-text="确定"
-                cancel-text="取消"
-                @confirm.stop="deleteGameStep(index, 'err_key')"
-
-                @cancel.stop
+          </VueDraggableNext>
+          <a-empty v-if="steps.ok_key.length === 0" description="暂无正确按钮,请点击新增按钮" />
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+    <div class="step-list" v-if="getBtnRuleByRule === 'multiple'">
+      <a-collapse :bordered="false">
+        <a-collapse-panel key="3">
+          <template #header>
+            <div class="collapse-header">
+              <span>错误按钮 ({{ steps.err_key.length }})</span>
+              <a-button type="primary" size="small" @click.stop="showAddStepModal('error')">新增</a-button>
+            </div>
+          </template>
+
+          <VueDraggableNext
+            :list="steps.err_key"
+            group="people"
+            item-key="id"
+            :animation="300"
+            @end="onDragEnd"
+          >
+            <div v-for="(element, index) in steps.err_key" :key="index" class="step-item-wrapper">
+              <div
+                class="step-item"
+                :class="{ 'step-item-active': activeStepIndex === element.value }"
+                @click="selectStep(element.value)"
               >
-                <delete-outlined @click.stop />
-              </a-popconfirm>
+                <div class="step-info">
+                  <span class="step-number">按钮 {{ index + 1 }}</span>
+                  <div class="step-details">
+                    <span>按钮: <strong>{{ getButtonLabel(element.value) }}</strong></span>
+                    <span>音频: <strong>{{ element.music_name }}</strong></span>
+                  </div>
+                </div>
+                <div class="step-actions">
+                  <a-popconfirm
+                    title="确定要删除这个步骤吗?"
+                    ok-text="确定"
+                    cancel-text="取消"
+                    @confirm.stop="deleteGameStep(index, 'err_key')"
+                    @cancel.stop
+                  >
+                    <delete-outlined @click.stop />
+                  </a-popconfirm>
+                </div>
+              </div>
             </div>
-          </div>
-        </div>
-      </VueDraggableNext>
-      <a-empty v-if="steps.err_key.length === 0" description="暂无错误按钮,请点击新增按钮" />
-
+          </VueDraggableNext>
+          <a-empty v-if="steps.err_key.length === 0" description="暂无错误按钮,请点击新增按钮" />
+        </a-collapse-panel>
+      </a-collapse>
     </div>
-
     <a-modal
       v-model:open="addStepModalVisible"
       width="600px"
@@ -188,19 +221,9 @@ import { VueDraggableNext } from 'vue-draggable-next'
 import SelectAudioNew from './select-audio-new.vue'
 import { message } from 'ant-design-vue'
 
-interface Step {
-  success: {
-    button_key: number;
-    audio_name: string;
-  }[]
-  error: {
-    button_key: number;
-    audio_name: string;
-  }[]
-}
-
 interface Props {
   modelValue: API.CardJson21Item,
+  touch_key: API.CardJson21TouchKey
   rule: API.Rule
 }
 
@@ -211,15 +234,27 @@ watch(
   },
   { immediate: true }
 )
+watch(
+  () => props.touch_key,
+  (newVal) => {
+    console.log('touch_keys 的改变:', props.touch_key)
+  },
+  { immediate: true }
+)
 
 const props = defineProps<Props>()
-const emits = defineEmits(['update:modelValue', 'step-selected'])
+const emits = defineEmits(['update:modelValue', 'step-selected', 'update:touch_key'])
 
 const steps = computed({
   get: () => props.modelValue,
   set: (value) => emits('update:modelValue', value)
 })
 
+const touch_keys = computed({
+  get: () => props.touch_key,
+  set: (value) => emits('update:touch_key', value)
+})
+
 // 有的是单选(7、10) 有的是多选(9 , 11, 12, 13, 14, 15, 16) 17 单和多都可以,直接用多选
 const getBtnRuleByRule = computed(() => {
   if (props.rule === 7 || props.rule === 10) {
@@ -375,11 +410,12 @@ const handleDeleteButton = (value: number) => {
   }
 
   .step-item-wrapper {
+    // width: 100%;
     margin-bottom: 12px;
   }
 
   .step-item {
-    width: 100%;
+    width: 90%;
     display: flex;
     align-items: center;
     justify-content: space-between;
@@ -435,13 +471,6 @@ const handleDeleteButton = (value: number) => {
 }
 </style>
 <style lang="less" scoped>
-.step-list {
-  width: 100%;
-  margin-bottom: 24px;
-  .step-title {
-    // margin-bottom: 24px;
-  }
-}
 .game-stage-3-modal {
   .header {
     display: flex;
@@ -488,6 +517,16 @@ const handleDeleteButton = (value: number) => {
   z-index: 10;
 }
 
+.top-config {
+  margin-bottom: 24px;
+}
+
+.button-type-hint {
+  font-size: 12px;
+  color: #999;
+  margin-top: 4px;
+}
+
 .button-wrapper {
   position: relative;
 }
@@ -505,4 +544,35 @@ const handleDeleteButton = (value: number) => {
   cursor: pointer;
   z-index: 10;
 }
+
+.panel-header {
+  display: flex;
+  justify-content: flex-end;
+  margin-bottom: 16px;
+}
+
+.collapse-header {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.step-list {
+  width: 100%;
+  margin-bottom: 24px;
+}
+
+:deep(.ant-collapse-content-box) {
+  padding: 16px;
+  width: 100%;
+}
+
+:deep(.ant-collapse) {
+  width: 100%;
+}
+
+:deep(.ant-collapse-item) {
+  width: 100%;
+}
 </style>

+ 1 - 0
src/pages/card/index.vue

@@ -98,6 +98,7 @@
     <ConfigGame
       v-model:config="cardJson21"
       v-model:open="openState.configGameOpen"
+      :templateDom21="cardTemplateDom21"
       @step-selected="handleStepSelected"
     />
   <!-- catetype = 21 时 的 游戏配置 -->

+ 11 - 2
src/pages/card/preview.vue

@@ -1,7 +1,16 @@
 <template>
   <div class="preview-page">
     <a-row :gutter="[32, 16]" justify="flex-start" style="margin-top: 24px;">
-      <a-col :span="4" v-for="card in cardList" :key="card.id" style="margin-bottom: 16px;">
+      <a-col
+        :xs="4"
+        :sm="4"
+        :md="4"
+        :lg="4"
+        :xl="4"
+        v-for="card in cardList"
+        :key="card.id"
+        style="margin-bottom: 16px;"
+      >
         <a-card @click="handleClick(card)" :bordered="false" hoverable style="width: 100%;height: 320px;" :title="card.name" >
           <div  class="card-bg" >
              <img style="width: 50%;Object-fit: contain;" :src="card.thumbnail" alt="example" />
@@ -75,6 +84,6 @@ onMounted(() => {
 /deep/ .ant-card-body {
   padding: 0px;
   overflow: hidden;
-  height: 265px;
+  height:  100%;
 }
 </style>

+ 2 - 0
src/typeing.d.ts

@@ -278,6 +278,8 @@ declare namespace API {
 
   type CardJson21Item = CardJson21['game_list'][0]['items'][0]
 
+  type CardJson21TouchKey = ({ 'value': number } & {'music_name': string, 'is_break': is_break })[]
+
   interface CardJson21 {
     'header': {
         'card_type': number,

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 141 - 372
yarn.lock


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác