|
@@ -6,15 +6,15 @@
|
|
|
<div class="settings-info">
|
|
|
<div class="settings-row">
|
|
|
<div class="settings-label">主题:</div>
|
|
|
- <div class="settings-value">{{ selectedTheme.themeName }}</div>
|
|
|
+ <div class="settings-value">{{ themeName }}</div>
|
|
|
</div>
|
|
|
<div class="settings-row">
|
|
|
<div class="settings-label">难度:</div>
|
|
|
- <div class="settings-badge">{{ selectedTheme.difficultyName }}</div>
|
|
|
+ <div class="settings-badge">{{ difficultyName }}</div>
|
|
|
</div>
|
|
|
<div class="settings-row">
|
|
|
<div class="settings-label">题目:</div>
|
|
|
- <div class="settings-value">{{ selectedTheme.puzzleName }}</div>
|
|
|
+ <div class="settings-value">{{ puzzleName }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
@@ -45,9 +45,9 @@
|
|
|
|
|
|
<div class="theme-list">
|
|
|
<div v-for="theme in themes"
|
|
|
- :key="theme.id"
|
|
|
+ :key="theme.themeId"
|
|
|
class="theme-item"
|
|
|
- :class="{ 'selected': selectedTheme.theme === theme.id, 'locked': theme.isLocked }"
|
|
|
+ :class="{ 'selected': selectedSettings.themeId === theme.themeId, 'locked': theme.isLocked }"
|
|
|
@click="!theme.isLocked && selectTheme(theme)">
|
|
|
<div class="theme-icon">
|
|
|
<IconFont :name="getIconName(theme)" size="20" color="#FF9F2D" />
|
|
@@ -83,8 +83,8 @@
|
|
|
<nut-button
|
|
|
v-for="diff in difficulties"
|
|
|
:key="diff.id"
|
|
|
- :type="selectedTheme.difficulty === diff.id ? 'primary' : 'default'"
|
|
|
- :color="selectedTheme.difficulty === diff.id ? '#FF9F2D' : ''"
|
|
|
+ :type="selectedSettings.difficulty === diff.id ? 'primary' : 'default'"
|
|
|
+ :color="selectedSettings.difficulty === diff.id ? '#FF9F2D' : ''"
|
|
|
class="difficulty-btn"
|
|
|
@click="selectDifficulty(diff)">
|
|
|
{{ diff.name }}
|
|
@@ -131,16 +131,16 @@
|
|
|
<!-- 题目列表 -->
|
|
|
<div v-else class="puzzle-list">
|
|
|
<div v-for="puzzle in filteredPuzzles"
|
|
|
- :key="puzzle.id"
|
|
|
+ :key="puzzle.puzzleId"
|
|
|
class="puzzle-item"
|
|
|
- :class="{ 'selected': selectedTheme.puzzle === puzzle.id }"
|
|
|
+ :class="{ 'selected': selectedSettings.puzzleId === puzzle.puzzleId }"
|
|
|
@click="selectPuzzle(puzzle)">
|
|
|
<div class="puzzle-header">
|
|
|
- <div class="puzzle-name">{{ puzzle.name }}</div>
|
|
|
- <div class="puzzle-meta">{{ selectedTheme.difficultyName }} · {{ puzzle.duration }}</div>
|
|
|
+ <div class="puzzle-name">{{ puzzle.title }}</div>
|
|
|
+ <div class="puzzle-meta">{{ difficultyName }} · {{ puzzle.duration }}</div>
|
|
|
</div>
|
|
|
<div class="puzzle-desc">{{ puzzle.desc }}</div>
|
|
|
- <IconFont v-if="selectedTheme.puzzle === puzzle.id"
|
|
|
+ <IconFont v-if="selectedSettings.puzzleId === puzzle.puzzleId"
|
|
|
name="check"
|
|
|
size="16"
|
|
|
color="#FF9F2D"
|
|
@@ -158,11 +158,12 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<script setup>
|
|
|
-import { ref, computed, reactive, onMounted } from 'vue';
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, computed, reactive, onMounted, watch } from 'vue';
|
|
|
import { useTurtleSoupStore } from '@/stores/games/turtlesoup';
|
|
|
import Taro from '@tarojs/taro';
|
|
|
import { IconFont } from '@nutui/icons-vue-taro';
|
|
|
+import { TurtleSoupGameSettings, TurtleSoupDifficulty, TurtleSoupTheme, TurtleSoupPuzzle } from '@/types/games/turtlesoup';
|
|
|
|
|
|
// 获取海龟汤store
|
|
|
const turtleSoupStore = useTurtleSoupStore();
|
|
@@ -181,54 +182,64 @@ const loading = ref({
|
|
|
puzzles: false
|
|
|
});
|
|
|
|
|
|
-// 主题数据
|
|
|
-const themes = ref([]);
|
|
|
+// 1. 只保存 settings
|
|
|
+const selectedSettings = reactive<TurtleSoupGameSettings>({
|
|
|
+ themeId: '',
|
|
|
+ puzzleId: '',
|
|
|
+ difficulty: TurtleSoupDifficulty.EASY,
|
|
|
+ maxPlayers: 10,
|
|
|
+ password: ''
|
|
|
+});
|
|
|
+
|
|
|
+// 2. 主题、题目数据
|
|
|
+const themes = ref<TurtleSoupTheme[]>([]);
|
|
|
+const puzzles = ref<TurtleSoupPuzzle[]>([]);
|
|
|
+
|
|
|
+// 3. 展示用 computed
|
|
|
+const themeObj = computed(() => themes.value.find(t => t.themeId === selectedSettings.themeId));
|
|
|
+const puzzleObj = computed(() => puzzles.value.find(p => p.puzzleId === selectedSettings.puzzleId));
|
|
|
+const themeName = computed(() => themeObj.value?.name || '');
|
|
|
+const puzzleName = computed(() => puzzleObj.value?.title || '');
|
|
|
+const difficultyName = computed(() => {
|
|
|
+ switch (selectedSettings.difficulty) {
|
|
|
+ case TurtleSoupDifficulty.EASY: return '简单';
|
|
|
+ case TurtleSoupDifficulty.MEDIUM: return '中等';
|
|
|
+ case TurtleSoupDifficulty.HARD: return '困难';
|
|
|
+ default: return '';
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 4. 主题/题目筛选
|
|
|
+const filteredPuzzles = computed(() =>
|
|
|
+ puzzles.value.filter(p =>
|
|
|
+ p.themeId === selectedSettings.themeId &&
|
|
|
+ p.difficulty === selectedSettings.difficulty
|
|
|
+ )
|
|
|
+);
|
|
|
|
|
|
// 难度数据
|
|
|
const difficulties = [
|
|
|
- { id: 'easy', name: '简单' },
|
|
|
- { id: 'medium', name: '中等' },
|
|
|
- { id: 'hard', name: '困难' },
|
|
|
+ { id: TurtleSoupDifficulty.EASY, name: '简单' },
|
|
|
+ { id: TurtleSoupDifficulty.MEDIUM, name: '中等' },
|
|
|
+ { id: TurtleSoupDifficulty.HARD, name: '困难' },
|
|
|
];
|
|
|
|
|
|
// 难度描述
|
|
|
const difficultyDescription = computed(() => {
|
|
|
- switch(selectedTheme.difficulty) {
|
|
|
- case 'easy':
|
|
|
+ switch(selectedSettings.difficulty) {
|
|
|
+ case TurtleSoupDifficulty.EASY:
|
|
|
return '简单难度适合初次接触海龟汤的玩家,解题时间约10-15分钟。';
|
|
|
- case 'medium':
|
|
|
+ case TurtleSoupDifficulty.MEDIUM:
|
|
|
return '中等难度需要一定的逻辑思维能力,解题时间约15-25分钟。';
|
|
|
- case 'hard':
|
|
|
+ case TurtleSoupDifficulty.HARD:
|
|
|
return '困难难度挑战你的思维极限,解题时间约25-40分钟。';
|
|
|
default:
|
|
|
return '选择一个难度查看详情';
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-// 谜题数据
|
|
|
-const puzzles = ref([]);
|
|
|
-
|
|
|
-// 根据主题和难度筛选谜题
|
|
|
-const filteredPuzzles = computed(() => {
|
|
|
- return puzzles.value.filter(puzzle =>
|
|
|
- puzzle.theme === selectedTheme.theme &&
|
|
|
- puzzle.difficulty === selectedTheme.difficulty
|
|
|
- );
|
|
|
-});
|
|
|
-
|
|
|
-// 当前选择的数据
|
|
|
-const selectedTheme = reactive({
|
|
|
- theme: '',
|
|
|
- themeName: '',
|
|
|
- themePrice: '',
|
|
|
- difficulty: 'easy',
|
|
|
- difficultyName: '简单',
|
|
|
- puzzle: '',
|
|
|
- puzzleName: ''
|
|
|
-});
|
|
|
-
|
|
|
// 获取IconFont组件支持的图标名称
|
|
|
-const getIconName = (theme) => {
|
|
|
+const getIconName = (theme: TurtleSoupTheme) => {
|
|
|
const iconMap = {
|
|
|
'classic': 'bookmark',
|
|
|
'universal': 'location',
|
|
@@ -250,7 +261,7 @@ const loadThemes = async () => {
|
|
|
|
|
|
if (themeData && themeData.length) {
|
|
|
themes.value = themeData.map(theme => ({
|
|
|
- id: theme.id,
|
|
|
+ themeId: theme.themeId,
|
|
|
name: theme.name,
|
|
|
price: theme.price ? `${theme.price}元/小时` : '免费',
|
|
|
desc: theme.description || '主题介绍',
|
|
@@ -262,7 +273,7 @@ const loadThemes = async () => {
|
|
|
console.log('主题列表加载成功,数量:', themes.value.length);
|
|
|
|
|
|
// 如果没有选择主题,默认选择第一个未锁定的主题
|
|
|
- if (!selectedTheme.theme) {
|
|
|
+ if (!selectedSettings.themeId) {
|
|
|
const defaultTheme = themes.value.find(theme => !theme.isLocked);
|
|
|
if (defaultTheme) {
|
|
|
console.log('自动选择默认主题:', defaultTheme.name);
|
|
@@ -285,36 +296,36 @@ const loadThemes = async () => {
|
|
|
|
|
|
// 加载题目列表
|
|
|
const loadPuzzles = async () => {
|
|
|
- if (!selectedTheme.theme) {
|
|
|
+ if (!selectedSettings.themeId) {
|
|
|
console.warn('加载题目前需要先选择主题');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
loading.value.puzzles = true;
|
|
|
- console.log('开始加载题目,主题:', selectedTheme.theme, '难度:', selectedTheme.difficulty);
|
|
|
+ console.log('开始加载题目,主题:', selectedSettings.themeId, '难度:', selectedSettings.difficulty);
|
|
|
|
|
|
// 从turtleSoupStore加载题目
|
|
|
const puzzleData = await turtleSoupStore.loadPuzzles(
|
|
|
- selectedTheme.theme,
|
|
|
- selectedTheme.difficulty
|
|
|
+ selectedSettings.themeId,
|
|
|
+ selectedSettings.difficulty
|
|
|
);
|
|
|
|
|
|
if (puzzleData && puzzleData.length) {
|
|
|
puzzles.value = puzzleData.map(puzzle => ({
|
|
|
- id: puzzle.id,
|
|
|
- name: puzzle.title || '未命名题目',
|
|
|
+ puzzleId: puzzle.puzzleId,
|
|
|
+ title: puzzle.title || '未命名题目',
|
|
|
difficulty: puzzle.difficulty,
|
|
|
duration: puzzle.averageDuration || '平均用时20分钟',
|
|
|
desc: puzzle.description || '题目描述',
|
|
|
- theme: puzzle.themeId
|
|
|
+ themeId: puzzle.themeId
|
|
|
}));
|
|
|
|
|
|
console.log('题目加载成功,数量:', puzzles.value.length);
|
|
|
|
|
|
// 如果没有选择题目,默认选择第一个题目
|
|
|
- if (!selectedTheme.puzzle && filteredPuzzles.value.length > 0) {
|
|
|
- console.log('自动选择默认题目:', filteredPuzzles.value[0].name);
|
|
|
+ if (!selectedSettings.puzzleId && filteredPuzzles.value.length > 0) {
|
|
|
+ console.log('自动选择默认题目:', filteredPuzzles.value[0].title);
|
|
|
selectPuzzle(filteredPuzzles.value[0]);
|
|
|
}
|
|
|
} else {
|
|
@@ -365,10 +376,10 @@ const prevStep = () => {
|
|
|
};
|
|
|
|
|
|
// 选择主题
|
|
|
-const selectTheme = (theme) => {
|
|
|
- selectedTheme.theme = theme.id;
|
|
|
- selectedTheme.themeName = theme.name;
|
|
|
- selectedTheme.themePrice = theme.price;
|
|
|
+const selectTheme = (theme: TurtleSoupTheme) => {
|
|
|
+ selectedSettings.themeId = theme.themeId;
|
|
|
+ // 主题变更时重置题目
|
|
|
+ selectedSettings.puzzleId = '';
|
|
|
console.log('选择主题:', theme.name);
|
|
|
|
|
|
// 更新到store或emit事件
|
|
@@ -376,20 +387,20 @@ const selectTheme = (theme) => {
|
|
|
};
|
|
|
|
|
|
// 选择难度
|
|
|
-const selectDifficulty = (difficulty) => {
|
|
|
- selectedTheme.difficulty = difficulty.id;
|
|
|
- selectedTheme.difficultyName = difficulty.name;
|
|
|
- console.log('选择难度:', difficulty.name);
|
|
|
+const selectDifficulty = (difficulty: TurtleSoupDifficulty) => {
|
|
|
+ selectedSettings.difficulty = difficulty;
|
|
|
+ // 难度变更时重置题目
|
|
|
+ selectedSettings.puzzleId = '';
|
|
|
+ console.log('选择难度:', difficulty);
|
|
|
|
|
|
// 更新到store或emit事件
|
|
|
updateSelection();
|
|
|
};
|
|
|
|
|
|
// 选择谜题
|
|
|
-const selectPuzzle = (puzzle) => {
|
|
|
- selectedTheme.puzzle = puzzle.id;
|
|
|
- selectedTheme.puzzleName = puzzle.name;
|
|
|
- console.log('选择题目:', puzzle.name);
|
|
|
+const selectPuzzle = (puzzle: TurtleSoupPuzzle) => {
|
|
|
+ selectedSettings.puzzleId = puzzle.puzzleId;
|
|
|
+ console.log('选择题目:', puzzle.title);
|
|
|
|
|
|
// 更新到store或emit事件
|
|
|
updateSelection();
|
|
@@ -398,11 +409,11 @@ const selectPuzzle = (puzzle) => {
|
|
|
// 确认选择
|
|
|
const confirmSelection = () => {
|
|
|
// 确保选择了题目
|
|
|
- if (!selectedTheme.puzzle && filteredPuzzles.value.length > 0) {
|
|
|
+ if (!selectedSettings.puzzleId && filteredPuzzles.value.length > 0) {
|
|
|
selectPuzzle(filteredPuzzles.value[0]);
|
|
|
}
|
|
|
|
|
|
- console.log('确认选择:', selectedTheme);
|
|
|
+ console.log('确认选择:', selectedSettings);
|
|
|
|
|
|
// 更新到store或emit事件
|
|
|
updateSelection();
|
|
@@ -417,7 +428,7 @@ const selectRandomPuzzle = () => {
|
|
|
const randomIndex = Math.floor(Math.random() * filteredPuzzles.value.length);
|
|
|
const randomPuzzle = filteredPuzzles.value[randomIndex];
|
|
|
selectPuzzle(randomPuzzle);
|
|
|
- console.log('随机选择题目:', randomPuzzle.name);
|
|
|
+ console.log('随机选择题目:', randomPuzzle.title);
|
|
|
|
|
|
// 显示提示
|
|
|
Taro.showToast({
|
|
@@ -437,17 +448,17 @@ const selectRandomPuzzle = () => {
|
|
|
|
|
|
// 更新选择,发出事件
|
|
|
const updateSelection = () => {
|
|
|
- emit('selection-change', { ...selectedTheme });
|
|
|
+ emit('selection-change', { ...selectedSettings });
|
|
|
};
|
|
|
|
|
|
// 购买主题 - 使用微信支付
|
|
|
-const purchaseTheme = async (theme) => {
|
|
|
+const purchaseTheme = async (theme: TurtleSoupTheme) => {
|
|
|
try {
|
|
|
Taro.showLoading({ title: '准备支付...' });
|
|
|
console.log('准备购买主题:', theme.name);
|
|
|
|
|
|
// 调用store方法获取支付参数
|
|
|
- const paymentParams = await turtleSoupStore.getThemePaymentParams(theme.id);
|
|
|
+ const paymentParams = await turtleSoupStore.getThemePaymentParams(theme.themeId);
|
|
|
|
|
|
if (!paymentParams) {
|
|
|
throw new Error('获取支付参数失败');
|
|
@@ -470,7 +481,7 @@ const purchaseTheme = async (theme) => {
|
|
|
|
|
|
try {
|
|
|
// 调用解锁主题API
|
|
|
- const result = await turtleSoupStore.unlockTheme(theme.id);
|
|
|
+ const result = await turtleSoupStore.unlockTheme(theme.themeId);
|
|
|
|
|
|
if (result && result.success) {
|
|
|
console.log('主题解锁成功');
|
|
@@ -483,7 +494,7 @@ const purchaseTheme = async (theme) => {
|
|
|
await loadThemes();
|
|
|
|
|
|
// 选择刚解锁的主题
|
|
|
- const updatedTheme = themes.value.find(t => t.id === theme.id);
|
|
|
+ const updatedTheme = themes.value.find(t => t.themeId === theme.themeId);
|
|
|
if (updatedTheme && !updatedTheme.isLocked) {
|
|
|
selectTheme(updatedTheme);
|
|
|
}
|
|
@@ -536,14 +547,22 @@ onMounted(async () => {
|
|
|
// 如果传入了初始主题,使用传入的初始值
|
|
|
if (Object.keys(props.initialTheme).length > 0) {
|
|
|
console.log('使用传入的初始主题:', props.initialTheme);
|
|
|
- Object.assign(selectedTheme, props.initialTheme);
|
|
|
+ Object.assign(selectedSettings, props.initialTheme);
|
|
|
|
|
|
// 初始化后加载题目
|
|
|
- if (selectedTheme.theme) {
|
|
|
+ if (selectedSettings.themeId) {
|
|
|
await loadPuzzles();
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+// 监听 themeId/difficulty 变化自动加载题目
|
|
|
+watch(() => [selectedSettings.themeId, selectedSettings.difficulty], async ([themeId, diff], [oldThemeId, oldDiff]) => {
|
|
|
+ if (themeId && (themeId !== oldThemeId || diff !== oldDiff)) {
|
|
|
+ await loadPuzzles();
|
|
|
+ selectedSettings.puzzleId = '';
|
|
|
+ }
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss">
|