ソースを参照

fix: 一些修改

lvkun996 7 ヶ月 前
コミット
478441b5d2

+ 8 - 1
README.md

@@ -1,3 +1,10 @@
 # flicker-admin
-  遵从简洁设计
 
+
+
+
+
+## 卡片类型
+  5 推钮卡
+  21 点触卡
+  

+ 8 - 4
src/components/ModalPro/index.vue

@@ -2,8 +2,6 @@
   <a-modal
     ref="modalRef"
     :wrap-style="{ overflow: 'hidden',  pointerEvents: 'none' }"
-    @cancel="close"
-    @ok="handleOk"
     v-bind="{
       cancelText: '取消',
       okText: '确定',
@@ -12,18 +10,24 @@
     :mask="false"
     :style="props.styles"
     :confirmLoading="state.confirmLoading"
+    :footer="null"
   >
      <template #title>
       <slot name="title">
         <div class="modal-header">
           <div ref="modalTitleRef" class="modal-title">{{props.label || 'model'}}</div>
+          <div>
+            <a-space>
+              <a-button @click="close" > 取消</a-button>
+              <a-button @click="handleOk" type="primary" >确定</a-button>
+            </a-space>
+          </div>
         </div>
       </slot>
     </template>
     <template #modalRender="{ originVNode }">
       <div
         ref="modalContentRef"
-        :style="transformStyle"
         class="resizable-modal"
       >
         <component :is="originVNode" />
@@ -177,7 +181,7 @@ const transformStyle = computed<CSSProperties>(() => {
 }
 .modal-title {
   flex: 1;
-  cursor: move;
+  // cursor: move;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;

+ 46 - 4
src/controller/CardController.ts

@@ -4,7 +4,7 @@ import { message } from 'ant-design-vue'
 
 export class CardController {
   static cardType = [
-    { value: 5, title: '钮卡' },
+    { value: 5, title: '钮卡' },
     { value: 21, title: '点触卡' }
   ]
 
@@ -58,7 +58,12 @@ export class CardController {
   }
 
   static async add (data: API.CardJson) {
-    const { status } = await addCard(data.header.card_type, data)
+    const _data = data
+    if (data.header.card_type === 5) {
+      CardController.completeCard5Json(_data)
+    }
+
+    const { status } = await addCard(data.header.card_type, _data)
     status === 200 ? message.success('保存成功') : message.error('保存失败')
     return status
   }
@@ -108,7 +113,6 @@ export class CardController {
    */
   static async card5JsonById (_dataJson: API.CardJson) {
     const dataJson = JSON.parse(JSON.stringify(_dataJson)) as API.CardJson
-
     (dataJson as API.CardJson).touch_key = (dataJson as API.CardJson).touch_key.map((item, index) => {
       if ('music_name' in item) {
         return {
@@ -192,7 +196,7 @@ export class CardController {
           music_name: '',
           is_break: 1,
           value: '',
-          select: false
+          select: true
         })
       }
     }
@@ -268,4 +272,42 @@ export class CardController {
       game_list: []
     }
   }
+
+  // 用户提交json时,为保证数据的正确统一性,没填写的字段进行补全
+  static completeCard5Json (dataJson: API.CardJson) {
+    const card5Json = CardController.generateCard5Json()
+
+    Object.keys(card5Json.header).forEach((key) => {
+      if (dataJson.header[key] === undefined || dataJson.header[key] === null || dataJson.header[key] === '') {
+        dataJson.header[key] = card5Json.header[key]
+      }
+    })
+
+    // 给slide_knob赋值
+    if (dataJson.slide_knob === null || dataJson.slide_knob === undefined) {
+      dataJson.slide_knob = card5Json.slide_knob
+    } else {
+      Object.keys(card5Json.slide_knob!).forEach((key) => {
+        // 给score赋值
+        if (typeof card5Json.slide_knob![key] === 'string' && !dataJson.slide_knob![key]) {
+          dataJson.slide_knob![key] = card5Json.slide_knob![key]
+        } else {
+        // 给颜色数组赋值
+          if (dataJson.slide_knob![key] === undefined || dataJson.slide_knob![key] === null || dataJson.slide_knob![key].length === 0) {
+            dataJson.slide_knob![key] = card5Json.slide_knob![key]
+          }
+        }
+      })
+    }
+    // 给touch_key赋值
+    if (dataJson.touch_key === null || dataJson.touch_key === undefined) {
+      dataJson.touch_key = card5Json.touch_key
+    } else {
+      Object.keys(card5Json.touch_key!).forEach((key) => {
+        if (dataJson.touch_key![key] === undefined || dataJson.touch_key![key] === null || dataJson.touch_key![key] === '') {
+          dataJson.touch_key![key] = card5Json.touch_key![key]
+        }
+      })
+    }
+  }
 }

+ 7 - 24
src/pages/card/components/card-default.vue

@@ -71,8 +71,10 @@ const formState = reactive<{default: FormState[]}>({
     { title: '', keyName: 'title', music_name: '', is_break: 1, label: '读题音频' },
     { card_insert: '', keyName: 'card_insert', music_name: '', is_break: 1, label: '卡片插入音频' },
     { card_remove: '', keyName: 'card_remove', music_name: '', is_break: 1, label: '卡片移除音频' },
+    { button_rep: '', keyName: 'button_rep', music_name: '', is_break: 1, label: 'button_rep' },
     { button_rep: '', keyName: 'button_rep', music_name: '', is_break: 1, label: '按钮音频' },
     { ack_ok: '', keyName: 'ack_ok', music_name: '', is_break: 1, label: '一次全部正确' },
+    { ack_err: '', keyName: 'ack_err', music_name: '', is_break: 1, label: '一次全部错误' },
     { ack_mdf: '', keyName: 'ack_mdf', music_name: '', is_break: 1, label: '多次全部正确' },
     { remind_ack: '', keyName: 'remind_ack', music_name: '', is_break: 1, label: '答错两次以上' },
     { remind_button: '', keyName: 'remind_button', music_name: '', is_break: 1, label: '提示钮' },
@@ -82,25 +84,9 @@ const formState = reactive<{default: FormState[]}>({
   ]
 })
 
-// watch(
-//   () => props.config,
-//   () => {
-//     formState.default.forEach(item => {
-//       item.music_name = props.config.header[item.keyName].music_name || item.music_name
-//     })
-//   },
-//   {
-//     immediate: true
-//   }
-// )
-
 const searchKey = ref('')
 
-// 当前表单的验证状态 为true的时候 父组件校验通过
 const isValid = ref(false)
-// cost { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef, {
-//   onValidate: (...args) => console.log(...args),
-// });
 
 const handleChange = () => {
   searchKey.value = ''
@@ -117,12 +103,13 @@ const handleSearch = (text: string) => {
 
 const getDefaultJson = async () => {
   const { data } = await CardController.getDefaultJson()
-  console.log(' props.config.header:', data)
+  // formState.default[0].title = props.config.header.title.music_name
+  // formState.default[0].music_name = props.config.header.title.music_name
+  // formState.default[0].is_break = props.config.header.title.is_break
 
   formState.default.forEach(item => {
-    console.log('错误的music_name:', item)
-
-    const is_break = props.config.header[item.keyName].is_break
+    console.log('item', item)
+    const is_break = props.config.header[item.keyName]?.is_break
 
     if (typeof item === 'object' && !!item) {
       item.music_name = props.config.header[item.keyName].music_name || data[item.keyName].music_name || ''
@@ -132,10 +119,6 @@ const getDefaultJson = async () => {
 }
 
 const generateDefaultJson = async () => {
-  // formState.forEach(item => item.music_name = )
-
-  // const a = await formDom.value.validate()
-  // console.log('generateDefaultJson', a)
   return new Promise((resolve, reject) => {
     formDom.value.validate().then(res => {
       console.log('then', res)

+ 35 - 13
src/pages/card/components/card-template.vue

@@ -28,6 +28,28 @@ const rectGapY = 146 // 矩形间纵向间隔
 const startX = 112 // 图片内第一个矩形的x偏移
 const startY = -56 // 图片内第一个矩形的y偏移
 
+// 未选择 选中了但是没有音频 选中了有音频 三种模式的矩形样式
+const rectStyles = {
+  unselected: {
+    fillStyle: 'rgba(255, 255, 255, 0.5)',
+    strokeStyle: '#007bff',
+    lineWidth: 2,
+    lineDash: []
+  },
+  selectedNoAudio: {
+    fillStyle: 'rgba(255, 0, 0, 0.5)',
+    strokeStyle: '#ff0000',
+    lineWidth: 2,
+    lineDash: [4, 6]
+  },
+  selectedWithAudio: {
+    fillStyle: 'rgba(0, 255, 0, 0.5)',
+    strokeStyle: '#ff6600',
+    lineWidth: 2,
+    lineDash: []
+  }
+}
+
 // 生成矩形数据
 function generateRects () {
   rects.length = 0
@@ -165,19 +187,19 @@ function draw () {
     })
 
     // 画选择框
-    if (selection.selecting) {
-      ctx.save()
-      ctx.strokeStyle = '#1890ff'
-      ctx.setLineDash([6, 4])
-      ctx.lineWidth = 2
-      ctx.globalAlpha = 0.7
-      const x = Math.min(selection.startX, selection.endX)
-      const y = Math.min(selection.startY, selection.endY)
-      const w = Math.abs(selection.endX - selection.startX)
-      const h = Math.abs(selection.endY - selection.startY)
-      ctx.strokeRect(x, y, w, h)
-      ctx.restore()
-    }
+    // if (selection.selecting) {
+    //   ctx.save()
+    //   ctx.strokeStyle = '#1890ff'
+    //   ctx.setLineDash([6, 4])
+    //   ctx.lineWidth = 2
+    //   ctx.globalAlpha = 0.7
+    //   const x = Math.min(selection.startX, selection.endX)
+    //   const y = Math.min(selection.startY, selection.endY)
+    //   const w = Math.abs(selection.endX - selection.startX)
+    //   const h = Math.abs(selection.endY - selection.startY)
+    //   ctx.strokeRect(x, y, w, h)
+    //   ctx.restore()
+    // }
   }
 }
 

+ 18 - 3
src/pages/card/components/config-button.vue

@@ -1,6 +1,14 @@
 <template>
-  <div class="button-group">
-    <div
+
+    <a-collapse :bordered="false" v-model:activeKey="collapseKeys.button">
+      <a-collapse-panel key="1">
+        <template #header>
+          <div class="collapse-header">
+            <span>按钮配置:</span>
+          </div>
+        </template>
+    <div class="button-group">
+     <div
       class="button-row"
       v-for="row in 6"
       :key="row"
@@ -24,7 +32,10 @@
         {{ color.value }}
       </div>
     </div>
-  </div>
+      </div>
+      </a-collapse-panel>
+    </a-collapse>
+
 </template>
 
 <script lang="ts" setup>
@@ -46,6 +57,10 @@ const colors = [
   { value: 'B', hex: '#1890ff', key: '100000' } // 蓝
 ]
 
+const collapseKeys = ref({
+  button: '1'
+})
+
 const slide_knob = reactive({
   score1: '',
   score2: '',

+ 85 - 56
src/pages/card/components/config-card.vue

@@ -1,46 +1,62 @@
 <template>
-  <div class="config-card" >
-    <a-form
-        :model="dynamicValidateForm"
-        name="basic"
-        :label-col="{ span: 6 }"
-        :wrapper-col="{ span: 16 }"
-        autocomplete="off"
-        ref="formDom"
-      >
-        <template
-          v-for="(item, index) in dynamicValidateForm.default"
-          :key="item.music_name"
-        >
-          <a-card
-            v-if="item.selected"
-            :title="item.name"
-            style="margin-bottom: 16px"
+  <a-collapse :bordered="false" v-model:activeKey="collapseKeys.touch">
+    <a-collapse-panel key="1">
+      <template #header>
+        <div class="collapse-header">
+          <span>按钮配置:</span>
+        </div>
+      </template>
+
+      <div class="config-card" >
+        <a-form
+            :model="dynamicValidateForm"
+            name="basic"
+            :label-col="{ span: 6 }"
+            :wrapper-col="{ span: 16 }"
+            autocomplete="off"
+            ref="formDom"
           >
-              <template #extra>
-                <a-button type="primary" @click="onDelete(item)">删除</a-button>
-              </template>
-              <a-form-item
-                label="音频名称"
-                :name="['default', index, 'music_name']"
-                :rules="[{ required: true, message: '请选择音频' }]"
+            <template
+              v-for="(item, index) in dynamicValidateForm.default"
+              :key="item.music_name"
+            >
+              <a-card
+                v-if="item.selected"
+                :title="item.name"
+                style="margin-bottom: 16px"
               >
-                <SelectAudioNew
-                  v-model="item.music_name"
-                  placeholder="请选择或搜索音频"
-                />
-              </a-form-item>
-
-              <a-form-item
-                label="播放权重"
-                name="is_break"
-              >
-                <select-break v-model:value="item.is_break" />
-              </a-form-item>
-          </a-card>
-        </template>
-    </a-form>
-  </div>
+                  <template #extra>
+                    <a-button type="primary" @click="onDelete(item)">删除</a-button>
+                  </template>
+                  <a-form-item
+                    label="音频名称"
+                    :name="['default', index, 'music_name']"
+                    :rules="[{ required: true, message: '请选择音频' }]"
+                  >
+                    <SelectAudioNew
+                      v-model="item.music_name"
+                      placeholder="请选择或搜索音频"
+                    />
+                  </a-form-item>
+
+                  <a-form-item
+                    label="播放权重"
+                    name="is_break"
+                  >
+                    <select-break v-model:value="item.is_break" />
+                  </a-form-item>
+              </a-card>
+            </template>
+        </a-form>
+        <a-empty
+          v-if="isEmptyComputed"
+          description="暂无推钮配置,点击左侧卡片矩形区域配置推钮"
+        />
+      </div>
+    </a-collapse-panel>
+
+  </a-collapse>
+
 </template>
 <script lang='ts'  setup >
 import { reactive, watch, onMounted, ref, Ref, defineExpose, computed } from 'vue'
@@ -54,6 +70,10 @@ const props = defineProps<{
 
 const emits = defineEmits(['delete', 'finish', 'finishFailed', 'verify'])
 
+const collapseKeys = ref({
+  touch: '1'
+})
+
 const dynamicValidateForm = reactive<{default: API.CardJson['touch_key']}>({
   default: []
 })
@@ -97,8 +117,12 @@ watch(
   { immediate: true, deep: true }
 )
 
+const isEmptyComputed = ref(true)
+
 const initConfigJson = () => {
   dynamicValidateForm.default = props.config.touch_key
+  isEmptyComputed.value = dynamicValidateForm.default.findIndex(item => item.selected) === -1
+  console.log('dynamicValidateForm', dynamicValidateForm)
 }
 
 watch(
@@ -119,23 +143,28 @@ const onDelete = (item: API.CardJson['touch_key'][0]) => {
 
 const generateConfigfCardJson = () => {
   return new Promise((resolve, reject) => {
-    formDom.value.validate().then(res => {
-      isValid.value = true
-      emits('verify', true)
-      resolve({
-        status: 200,
-        data: dynamicValidateForm.default,
-        verify: true
-      })
-    }).catch(err => {
-      console.log('catch', err)
-      emits('verify', false)
-      resolve({
-        status: 0,
-        data: dynamicValidateForm.default,
-        verify: false
-      })
+    resolve({
+      status: 200,
+      data: dynamicValidateForm.default,
+      verify: true
     })
+    // formDom.value.validate().then(res => {
+    //   isValid.value = true
+    //   emits('verify', true)
+    //   resolve({
+    //     status: 200,
+    //     data: dynamicValidateForm.default,
+    //     verify: true
+    //   })
+    // }).catch(err => {
+    //   console.log('catch', err)
+    //   emits('verify', false)
+    //   resolve({
+    //     status: 0,
+    //     data: dynamicValidateForm.default,
+    //     verify: false
+    //   })
+    // })
   })
 }
 

+ 0 - 53
src/pages/card/components/game-stage-4.vue

@@ -1,53 +0,0 @@
-<template>
-  <div class="lists-container">
-    <div class="list-column">
-      <h3>Todo</h3>
-      <draggable
-        v-model="todoList"
-        group="tasks"
-        class="drag-area"
-        :animation="150"
-      >
-        <div
-          v-for="item in todoList"
-          :key="item.id"
-          class="task-item"
-        >
-          {{ item.text }}
-        </div>
-      </draggable>
-    </div>
-
-    <div class="list-column">
-      <h3>Done</h3>
-      <draggable
-        v-model="doneList"
-        group="tasks"
-        class="drag-area"
-        :animation="150"
-      >
-        <div
-          v-for="item in doneList"
-          :key="item.id"
-          class="task-item done"
-        >
-          {{ item.text }}
-        </div>
-      </draggable>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref } from 'vue'
-import { VueDraggableNext as draggable } from 'vue-draggable-next'
-
-const todoList = ref([
-  { id: 1, text: 'Learn Vue 3' },
-  { id: 2, text: 'Build awesome apps' }
-])
-
-const doneList = ref([
-  { id: 3, text: 'Read documentation' }
-])
-</script>

+ 0 - 1
src/pages/card/components/select-audio-new.vue

@@ -165,7 +165,6 @@ const getAudioList = async () => {
     // 获取音频列表后,开始预加载音频
     if (audioList.value.length > 0) {
       const audioUrls = audioList.value.map(audio => audio.url).filter(url => url)
-      console.log('开始预加载音频列表,数量:', audioUrls.length)
 
       // 异步预加载,不阻塞UI
       audioManager.preloadAudioList(audioUrls).catch(error => {

+ 91 - 77
src/pages/card/index.vue

@@ -12,16 +12,21 @@
         </a-tooltip>
       </a-space>
     </div>
-    <div class="card-category" v-if="cardType"  >
+    <div class="card-category" v-if="cardType" @click="openChangeCardType" >
       <a-space>
         <div class="category"> 卡片类别:</div>
         <div class="title" >{{CardController.cardType.toMap('value', 'title').get(cardType)}}</div>
+        <a-tooltip placement="bottom" >
+          <template #title>
+            <span>点击可以修改卡片类别</span>
+          </template>
+          <InfoCircleOutlined />
+        </a-tooltip>
       </a-space>
     </div>
     <a-space>
       <a-button v-if="Number(cardType) === 5" @click="openDefaultConfig(5)">基础配置</a-button>
       <a-button v-if="Number(cardType) === 21"  @click="openDefaultConfig(21)">基础配置</a-button>
-      <a-button v-if="Number(cardType) === 5"  @click="openState.configButtonOpen = true">按钮配置</a-button>
       <a-button v-if="Number(cardType) === 5" type="primary" @click="openConfigCard(5)">打开配置板</a-button>
       <a-button v-if="Number(cardType) === 21" type="primary" @click="openConfigCard(21)">打开配置板</a-button>
     </a-space>
@@ -46,7 +51,7 @@
 
   <!-- 右侧配置区域 -->
   <a-drawer
-    :width="500"
+    :width="600"
     title="卡片配置"
     placement="right"
     :open="openState.configCardOpen"
@@ -54,6 +59,7 @@
     @close="onClose"
     :keyboard="false"
     :mask="false"
+    className="drawer-warapper"
   >
     <template #extra>
       <a-space>
@@ -61,7 +67,8 @@
         <a-button type="primary" @click="saveConfigCard">保存</a-button>
       </a-space>
     </template>
-    <!-- <ConfigButton ref="configButtonDom" :config="cardJson" /> -->
+    <ConfigButton ref="configButtonDom" :config="cardJson" />
+    <div style="height: 12px;"></div>
     <ConfigCard
       :config="cardJson"
       @verify="value => handleVerify('cardIsValid', value)"
@@ -78,7 +85,7 @@
     @confirm="saveDefaultConfig"
     :mask="false"
 
-    styles="position: fixed;top: 60px;right: 90px; pointer-events: none;"
+    styles="position: fixed;top: 60px;left: 90px; pointer-events: none;"
   >
    <CardDefault  ref="cardDefaultDom"  :config="cardJson" />
 
@@ -105,25 +112,16 @@
     />
   <!-- catetype = 21 时 的 游戏配置 -->
 
-  <modal-pro
-    label="按钮配置"
-    :open='openState.configButtonOpen'
-    @close="openState.configButtonOpen = false"
-    @confirm="saveButtonConfig"
-    @verify="value => handleVerify('buttonIsValid', value)"
-    :mask="false"
-    styles="position: fixed;top: 60px;right: 90px; pointer-events: none;"
-  >
-   <ConfigButton ref="configButtonDom" :config="cardJson" />
-  </modal-pro>
-
   <!-- 卡片类型选择 -->
   <a-modal
     :visible="openState.cardTypeOpen"
     @cancel="openState.cardTypeOpen = false"
     :footer="null"
     title="选择卡片类型"
-    :mask="false"
+    :mask="true"
+    :maskClosable="false"
+    :keyboard="false"
+    :closable="false"
     width="500px"
   >
     <div class="card-type-selection">
@@ -139,7 +137,7 @@
             <path d="M19 3H5C3.89 3 3 3.89 3 5V19C3 20.11 3.89 21 5 21H19C20.11 21 21 20.11 21 19V5C21 3.89 20.11 3 19 3ZM19 19H5V5H19V19ZM12 8C10.34 8 9 9.34 9 11C9 12.66 10.34 14 12 14C13.66 14 15 12.66 15 11C15 9.34 13.66 8 12 8Z" />
           </svg>
         </div>
-        <div class="card-type-title">推钮</div>
+        <div class="card-type-title">推钮</div>
         <div class="card-type-desc">推动按钮类型的题卡</div>
       </div>
 
@@ -153,7 +151,7 @@
             <path d="M9 11.24V7.5C9 6.12 10.12 5 11.5 5S14 6.12 14 7.5V11.24C15.21 11.9 16 13.18 16 14.5C16 16.43 14.43 18 12.5 18S9 16.43 9 14.5C9 13.18 9.79 11.9 11 11.24ZM13 7.5C13 6.67 12.33 6 11.5 6S10 6.67 10 7.5V10.97C10.32 10.84 10.66 10.75 11 10.71V8H12V10.71C12.34 10.75 12.68 10.84 13 10.97V7.5Z" />
           </svg>
         </div>
-        <div class="card-type-title">触摸</div>
+        <div class="card-type-title">点触卡</div>
         <div class="card-type-desc">点击题卡回答问题</div>
       </div>
     </div>
@@ -173,7 +171,7 @@ import ConfigCard from './components/config-card.vue'
 import CardDefault from './components/card-default.vue'
 import CardDefault21 from './components/card-default-21.vue'
 import ConfigButton from './components/config-button.vue'
-import { useRoute } from 'vue-router'
+import { useRoute, useRouter } from 'vue-router'
 import { CardController } from '@/controller'
 import { message } from 'ant-design-vue'
 import ConfigGame from './components/config-game.vue'
@@ -186,6 +184,8 @@ const operationTip = `
 
 const cardInfo = useRoute().query as unknown as API.Card
 
+const router = useRouter()
+
 const cardType = ref<5 | 21>(5)
 const selectedCardType = ref<5 | 21>(5)
 
@@ -194,7 +194,6 @@ const openState = reactive({
   configGameOpen: false,
   defaultConfigOpen: false,
   defaultConfig21Open: false,
-  configButtonOpen: false,
   cardTypeOpen: false
 })
 
@@ -237,86 +236,72 @@ const handleVerify = (verifyKey: 'buttonIsValid' | 'cardIsValid', value: boolean
   isValids[verifyKey] = value
 }
 
-const onSave = async () => {
-  // 每次保存对默认、touch_key、侧边按钮进行校验,是否提交过
-  // console.log('configButtonDom.value:', configButtonDom.value)
+const onSave = async (cardJsonDefault: API.CardJsonDefault | null = null) => {
+  console.log('cardJsonDefault:', cardJsonDefault)
 
-  // const { data: defaultJson } = await CardController.getDefaultJson()
-  // const defaultConfigVaild = Object.keys(defaultJson).filter(x => !!defaultJson[x]).findIndex(key => !defaultJson[key].music_name) !== -1
-  // && defaultConfigVaild
-  console.log('isValids:', isValids)
-  if (isValids.cardIsValid && isValids.buttonIsValid) {
+  let header
+  if (cardJsonDefault == null) {
     const { data } = await CardController.getDefaultJson()
-    console.log({
-      ...cardJson,
-      header: {
-        ...data,
-        ...cardJson.header,
-        title: data != null
-          ? { music_name: data[0].music_name, is_break: 1, page: cardJson.header.title.page }
-          : {
-              music_name: '', is_break: 1, page: cardJson.header.title.page
-            }
-      }
-    })
-
-    await CardController.add({
-      ...cardJson,
-      header: {
-        ...data,
-        ...cardJson.header,
-        title: data != null
-          ? { music_name: data[0].music_name, is_break: 1, page: cardJson.header.title.page }
-          : {
-              music_name: '', is_break: 1, page: cardJson.header.title.page
-            }
-      }
-    })
-    message.success('卡片配置成功')
-    return
-  }
-
-  if (isValids.cardIsValid) {
-    message.success('按钮配置已保存')
+    header = data
+    header.title = cardJson.header.title
+  } else {
+    header = { ...cardJsonDefault }
   }
-  if (isValids.buttonIsValid) message.success('推钮配置已经保存')
+  console.log('cardInfo', header.title, cardInfo)
+
+  header.title.page = cardInfo.page
+  header.title.category = 1
+  header.title.id = 1
+  header.title.sub_id = 1
+  header.card_type = cardType.value
+  header.grade = 1
+  await CardController.add({
+    ...cardJson,
+    header: header
+  })
 }
 
 const saveConfigCard = async () => {
+  const { result: buttonResult, verify: buttonVerify } = configButtonDom.value.generateButtonJson()
   const { data: configCardJson, verify } = await cardConfigDom.value.generateConfigfCardJson()
   cardJson.touch_key = configCardJson
+  cardJson.slide_knob = buttonResult
   isValids.cardIsValid = verify
-  verify && onSave()
+  buttonVerify && verify && onSave()
 }
 
 const saveDefaultConfig = async () => {
   const obj = {} as API.CardJsonDefault
-  console.log('cardDefaultDom.value:', cardDefaultDom.value)
 
   const { data } = await cardDefaultDom.value.generateDefaultJson()
   console.log('生成的默认配置:', data)
   data.forEach(item => obj[item.keyName] = { music_name: item.music_name, is_break: item.is_break })
+  obj.title = {
+    ...cardJson.header.title,
+    ...obj.title
+  }
   CardController.addDefaulJson(obj)
-  onSave()
+  console.log('saveDefaultConfig:', obj)
+  onSave(obj)
 }
 
 const saveDefault21Config = async () => {
-  const { data, code } = await cardDefaultDom21.value.generateDefaultJson()
+  const { data, status } = await cardDefaultDom21.value.generateDefaultJson()
 
   data.forEach(item => cardJson21.header[item.keyName] = { music_name: item.music_name, is_break: item.is_break })
 
-  if (code === 200) {
+  if (status === 200) {
     openState.defaultConfig21Open = false
+    await CardController.add({
+      header: {
+        ...cardJson21.header,
+        title: { ...cardJson21.header.title, page: Number(cardInfo.page) }
+      },
+      game_list: cardJson21.game_list
+    })
   }
 }
 
-const saveButtonConfig = () => {
-  const { result, verify } = configButtonDom.value.generateButtonJson()
-  cardJson.slide_knob = result
-  isValids.buttonIsValid = verify
-  verify && onSave()
-}
-
 const openConfigCard = (type: 5 | 21) => {
   type === 5 ? openState.configCardOpen = true : openState.configGameOpen = true
 }
@@ -357,9 +342,17 @@ const handleOperation = ({ ids }: {ids: number[]}) => {
 }
 
 const handleCardTypeSelect = () => {
+  // 相等就不清除数据
+
+  if (changeCardType.value && cardType.value === selectedCardType.value) {
+    // 相等就不清除数据
+    changeCardType.value = false
+    openState.cardTypeOpen = false
+    return
+  }
+
   cardType.value = selectedCardType.value
   openState.cardTypeOpen = false
-  console.log('selectedCardType.value:', cardInfo)
 
   // 根据选择的卡片类型初始化对应的数据模板
   if (cardType.value === 5) {
@@ -374,7 +367,21 @@ const handleCardTypeSelect = () => {
     cardJson21.header = CardController.generateCard21Json().header
     cardJson21.header.title.page = Number(cardInfo.page)
     cardJson21.game_list = CardController.generateCard21Json().game_list
-    console.log(cardJson21)
+  }
+  openState.configGameOpen = false
+  openState.configCardOpen = false
+  openState.defaultConfigOpen = false
+  openState.defaultConfig21Open = false
+  changeCardType.value = false
+}
+
+const changeCardType = ref(false)
+const openChangeCardType = () => {
+  changeCardType.value = true
+  openState.cardTypeOpen = true
+
+  if (changeCardType.value) {
+    message.warning('修改卡片类型需要重新设置题卡数据')
   }
 }
 
@@ -416,7 +423,7 @@ const getCardJsonById = async () => {
   }
 }
 
-function onBack () { window.history.back() }
+function onBack () { router.back() }
 
 onMounted(() => {
   getCardJsonById()
@@ -538,6 +545,13 @@ onMounted(() => {
   justify-content: center;
 }
 
+.drawer-warapper {
+  top: 60px;
+}
+
+ :deep( .ant-drawer-right>.ant-drawer-content-wrapper) {
+  top: 60px;
+}
 //  .ant-modal-root .ant-modal-wrap  {
 //    pointer-events: none !important;
 // }

+ 0 - 3
src/pages/card/preview.vue

@@ -69,9 +69,6 @@ watch(
 )
 
 onMounted(() => {
-  console.log('route:', route)
-  // getCardList()
-  // 这里可以添加其他逻辑
 })
 </script>
 <style lang='less' scoped >

+ 0 - 7
src/utils/AudioManaer.ts

@@ -55,18 +55,15 @@ class AudioManager {
     try {
       // 如果已经缓存,直接返回
       if (this.audioCache.has(url)) {
-        console.log('音频已在缓存中:', url)
         return
       }
 
-      console.log('开始预加载音频:', url)
       const response = await fetch(url)
       const arrayBuffer = await response.arrayBuffer()
       const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer)
 
       // 存入缓存
       this.audioCache.set(url, audioBuffer)
-      console.log('音频预加载完成:', url)
     } catch (error) {
       console.error('音频预加载失败:', url, error)
     }
@@ -74,16 +71,12 @@ class AudioManager {
 
   // 批量预加载音频
   public async preloadAudioList (urls: string[]): Promise<void> {
-    console.log('开始批量预加载音频,数量:', urls.length)
-
     // 使用 Promise.allSettled 来并发加载,即使某些音频加载失败也不会影响其他音频
     const promises = urls.map(url => this.preloadAudio(url))
     const results = await Promise.allSettled(promises)
 
     const successCount = results.filter(result => result.status === 'fulfilled').length
     const failCount = results.filter(result => result.status === 'rejected').length
-
-    console.log(`批量预加载完成: 成功 ${successCount} 个,失败 ${failCount} 个`)
   }
 
   // 检查音频是否已缓存