|
@@ -33,9 +33,10 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 顶级文件夹内容 -->
|
|
<!-- 顶级文件夹内容 -->
|
|
|
- <a-row v-if="currentLevel === 0" justify="flex-start" class="config-game-list">
|
|
|
|
|
|
|
+ <a-row v-if="currentLevel === 0" justify="flex-start" :gutter="[8, 8]" class="config-game-list">
|
|
|
<a-col
|
|
<a-col
|
|
|
:span="7"
|
|
:span="7"
|
|
|
|
|
+ style="height: 120px;"
|
|
|
class="config-game-item"
|
|
class="config-game-item"
|
|
|
v-for="(folder, index) in cardJson.game_list"
|
|
v-for="(folder, index) in cardJson.game_list"
|
|
|
:key="index"
|
|
:key="index"
|
|
@@ -90,7 +91,32 @@
|
|
|
@dblclick="enterCardConfig(index)"
|
|
@dblclick="enterCardConfig(index)"
|
|
|
>
|
|
>
|
|
|
<img class="folder-icon" :src="require('@/assets/common/folder.svg')" alt="">
|
|
<img class="folder-icon" :src="require('@/assets/common/folder.svg')" alt="">
|
|
|
- <div>游戏 {{index + 1}}</div>
|
|
|
|
|
|
|
+ <div class="folder-info">
|
|
|
|
|
+ <div v-if="item.sub_subject?.music_name" class="music-badge">
|
|
|
|
|
+ <sound-outlined />
|
|
|
|
|
+ <span class="music-name">{{ item.sub_subject.music_name }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="folder-footer">
|
|
|
|
|
+ <div>游戏 {{ index + 1 }}</div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <a-dropdown :trigger="['hover']">
|
|
|
|
|
+ <SmallDashOutlined class="action-icon" />
|
|
|
|
|
+ <template #overlay>
|
|
|
|
|
+ <a-menu>
|
|
|
|
|
+ <a-menu-item key="edit" @click="handleEditSubItem(index)">
|
|
|
|
|
+ <EditOutlined />
|
|
|
|
|
+ <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>
|
|
|
|
|
+ </a-menu-item>
|
|
|
|
|
+ </a-menu>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-dropdown>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</a-col>
|
|
</a-col>
|
|
|
</VueDraggableNext>
|
|
</VueDraggableNext>
|
|
|
</a-row>
|
|
</a-row>
|
|
@@ -126,6 +152,40 @@
|
|
|
v-model="subItemForm.sub_subject.music_name"
|
|
v-model="subItemForm.sub_subject.music_name"
|
|
|
placeholder="请选择子主题音乐"
|
|
placeholder="请选择子主题音乐"
|
|
|
/>
|
|
/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 已选择的音乐名称展示区域 -->
|
|
|
|
|
+ <div v-if="subItemForm.sub_subject.music_name" class="selected-music-display">
|
|
|
|
|
+ <div class="music-info">
|
|
|
|
|
+ <sound-outlined />
|
|
|
|
|
+ <span class="music-name">{{ subItemForm.sub_subject.music_name }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ </a-form>
|
|
|
|
|
+ </a-modal>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 编辑子项目弹窗 -->
|
|
|
|
|
+ <a-modal
|
|
|
|
|
+ v-model:open="editSubItemModalVisible"
|
|
|
|
|
+ title="编辑子项目"
|
|
|
|
|
+ width="400px"
|
|
|
|
|
+ @ok="handleSaveSubItemEdit"
|
|
|
|
|
+ @cancel="handleCancelSubItemEdit"
|
|
|
|
|
+ >
|
|
|
|
|
+ <a-form :model="editSubItemForm" layout="vertical">
|
|
|
|
|
+ <a-form-item label="子主题音乐">
|
|
|
|
|
+ <SelectAudioNew
|
|
|
|
|
+ v-model="editSubItemForm.sub_subject.music_name"
|
|
|
|
|
+ placeholder="请选择子主题音乐"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 已选择的音乐名称展示区域 -->
|
|
|
|
|
+ <div v-if="editSubItemForm.sub_subject.music_name" class="selected-music-display">
|
|
|
|
|
+ <div class="music-info">
|
|
|
|
|
+ <sound-outlined />
|
|
|
|
|
+ <span class="music-name">{{ editSubItemForm.sub_subject.music_name }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
</a-form>
|
|
</a-form>
|
|
|
</a-modal>
|
|
</a-modal>
|
|
@@ -225,7 +285,7 @@
|
|
|
|
|
|
|
|
<script lang='ts' setup>
|
|
<script lang='ts' setup>
|
|
|
import { CardController } from '@/controller'
|
|
import { CardController } from '@/controller'
|
|
|
-import { LeftOutlined, PlusOutlined, SmallDashOutlined, EditOutlined, DeleteOutlined, CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons-vue'
|
|
|
|
|
|
|
+import { LeftOutlined, PlusOutlined, SmallDashOutlined, EditOutlined, DeleteOutlined, CheckCircleFilled, ExclamationCircleFilled, SoundOutlined } from '@ant-design/icons-vue'
|
|
|
import { reactive, ref, PropType, computed, watch } from 'vue'
|
|
import { reactive, ref, PropType, computed, watch } from 'vue'
|
|
|
import { message } from 'ant-design-vue'
|
|
import { message } from 'ant-design-vue'
|
|
|
import SelectAudioNew from './select-audio-new.vue'
|
|
import SelectAudioNew from './select-audio-new.vue'
|
|
@@ -283,6 +343,24 @@ const editForm = reactive({
|
|
|
wait_90s: { music_name: '', is_break: 1 }
|
|
wait_90s: { music_name: '', is_break: 1 }
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+// 编辑子项目弹窗相关
|
|
|
|
|
+const editSubItemModalVisible = ref(false)
|
|
|
|
|
+const editingSubItemIndex = ref(-1)
|
|
|
|
|
+const editSubItemForm = reactive({
|
|
|
|
|
+ sub_subject: {
|
|
|
|
|
+ music_name: '',
|
|
|
|
|
+ mb: '',
|
|
|
|
|
+ ok: '',
|
|
|
|
|
+ ob: '',
|
|
|
|
|
+ err: '',
|
|
|
|
|
+ eb: ''
|
|
|
|
|
+ },
|
|
|
|
|
+ ok_key: [],
|
|
|
|
|
+ err_key: [],
|
|
|
|
|
+ ok_key_voice: {},
|
|
|
|
|
+ err_key_voice: {}
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
// 子项目弹窗相关
|
|
// 子项目弹窗相关
|
|
|
const subItemModalVisible = ref(false)
|
|
const subItemModalVisible = ref(false)
|
|
|
const subItemForm = reactive({
|
|
const subItemForm = reactive({
|
|
@@ -489,6 +567,54 @@ const handleDelete = (index: number) => {
|
|
|
cardJson.value.game_list.splice(index, 1)
|
|
cardJson.value.game_list.splice(index, 1)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 处理编辑子项目
|
|
|
|
|
+const handleEditSubItem = (index: number) => {
|
|
|
|
|
+ editingSubItemIndex.value = index
|
|
|
|
|
+ // 将当前子项目数据复制到编辑表单
|
|
|
|
|
+ const currentItem = cardJson.value.game_list[currentGameIndex.value].items[index]
|
|
|
|
|
+ editSubItemForm.sub_subject.music_name = currentItem.sub_subject.music_name
|
|
|
|
|
+ editSubItemForm.sub_subject.mb = currentItem.sub_subject.mb
|
|
|
|
|
+ editSubItemForm.sub_subject.ok = currentItem.sub_subject.ok
|
|
|
|
|
+ editSubItemForm.sub_subject.ob = currentItem.sub_subject.ob
|
|
|
|
|
+ editSubItemForm.sub_subject.err = currentItem.sub_subject.err
|
|
|
|
|
+ editSubItemForm.sub_subject.eb = currentItem.sub_subject.eb
|
|
|
|
|
+ editSubItemModalVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理删除子项目
|
|
|
|
|
+const handleDeleteSubItem = (index: number) => {
|
|
|
|
|
+ // 从子项目列表中删除指定索引的项目
|
|
|
|
|
+ cardJson.value.game_list[currentGameIndex.value].items.splice(index, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 保存子项目编辑
|
|
|
|
|
+const handleSaveSubItemEdit = () => {
|
|
|
|
|
+ if (editingSubItemIndex.value >= 0) {
|
|
|
|
|
+ // 基础校验:必须选择子主题音乐
|
|
|
|
|
+ if (!editSubItemForm.sub_subject.music_name) {
|
|
|
|
|
+ message.warning('请选择子主题音乐')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将编辑表单的数据保存到子项目列表
|
|
|
|
|
+ const currentItem = cardJson.value.game_list[currentGameIndex.value].items[editingSubItemIndex.value]
|
|
|
|
|
+ currentItem.sub_subject.music_name = editSubItemForm.sub_subject.music_name
|
|
|
|
|
+ currentItem.sub_subject.mb = editSubItemForm.sub_subject.mb || 1
|
|
|
|
|
+ currentItem.sub_subject.ok = editSubItemForm.sub_subject.ok || ''
|
|
|
|
|
+ currentItem.sub_subject.ob = editSubItemForm.sub_subject.ob || 1
|
|
|
|
|
+ currentItem.sub_subject.err = editSubItemForm.sub_subject.err || ''
|
|
|
|
|
+ currentItem.sub_subject.eb = editSubItemForm.sub_subject.eb || 1
|
|
|
|
|
+ }
|
|
|
|
|
+ editSubItemModalVisible.value = false
|
|
|
|
|
+ editingSubItemIndex.value = -1
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 取消子项目编辑
|
|
|
|
|
+const handleCancelSubItemEdit = () => {
|
|
|
|
|
+ editSubItemModalVisible.value = false
|
|
|
|
|
+ editingSubItemIndex.value = -1
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 保存编辑
|
|
// 保存编辑
|
|
|
const handleSaveEdit = () => {
|
|
const handleSaveEdit = () => {
|
|
|
if (editingIndex.value >= 0) {
|
|
if (editingIndex.value >= 0) {
|
|
@@ -552,16 +678,17 @@ const saveGameConfig = async () => {
|
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
|
justify-content: flex-start;
|
|
justify-content: flex-start;
|
|
|
gap: 16px;
|
|
gap: 16px;
|
|
|
|
|
+ height: 100%;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.config-game-item {
|
|
.config-game-item {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
- justify-content: space-around;
|
|
|
|
|
|
|
+ justify-content: space-between;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
- border-radius: 4px;
|
|
|
|
|
- transition: background-color 0.3s;
|
|
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ // transition: all 0.3s;
|
|
|
background-color: #f5f5f5;
|
|
background-color: #f5f5f5;
|
|
|
padding: 0px !important;
|
|
padding: 0px !important;
|
|
|
border: 1px solid #e9ecf1;
|
|
border: 1px solid #e9ecf1;
|
|
@@ -569,6 +696,8 @@ const saveGameConfig = async () => {
|
|
|
margin-right: 16px;
|
|
margin-right: 16px;
|
|
|
margin-bottom: 16px;
|
|
margin-bottom: 16px;
|
|
|
position: relative;
|
|
position: relative;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
|
|
.status-icon {
|
|
.status-icon {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
@@ -600,11 +729,61 @@ const saveGameConfig = async () => {
|
|
|
background-color: #fff;
|
|
background-color: #fff;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
padding: 0 12px;
|
|
padding: 0 12px;
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
|
|
|
.action-icon {
|
|
.action-icon {
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
opacity: 0;
|
|
opacity: 0;
|
|
|
transition: opacity 0.3s;
|
|
transition: opacity 0.3s;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: #8c8c8c;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .folder-info {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 12px 0;
|
|
|
|
|
+ margin-bottom: 32px;
|
|
|
|
|
+
|
|
|
|
|
+ .folder-title {
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .music-badge {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+ background-color: #e6f7ff;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ padding: 3px 10px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
|
|
|
+
|
|
|
|
|
+ .anticon {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .music-name {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ max-width: 80px;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -678,4 +857,33 @@ const saveGameConfig = async () => {
|
|
|
.step-actions {
|
|
.step-actions {
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+.selected-music-display {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+ padding: 10px 12px;
|
|
|
|
|
+ background-color: #f6f8fa;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ border: 1px solid #e1e4e8;
|
|
|
|
|
+ transition: all 0.3s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.music-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+
|
|
|
|
|
+ .anticon {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .music-name {
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ max-width: 300px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|