feat(discussion): add discussion feature for game entities
Create entity_discussion_comments table and API endpoints Add discussion tabs to Pokemon, Item, Recipe, and Habitat detail views Support top-level comments, single-level replies, and deletion
This commit is contained in:
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import DetailSection from '../components/DetailSection.vue';
|
||||
import EditHistoryPanel from '../components/EditHistoryPanel.vue';
|
||||
import EntityDiscussionPanel from '../components/EntityDiscussionPanel.vue';
|
||||
import EntityChips from '../components/EntityChips.vue';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
@@ -22,6 +23,7 @@ const weathers = ['晴天', '阴天', '雨天'];
|
||||
const showEditor = computed(() => route.name === 'habitat-edit');
|
||||
const detailTabs = computed<TabOption[]>(() => [
|
||||
{ value: 'details', label: t('common.details') },
|
||||
{ value: 'discussion', label: t('discussion.title') },
|
||||
{ value: 'history', label: t('history.editHistory') }
|
||||
]);
|
||||
|
||||
@@ -229,6 +231,10 @@ watch(
|
||||
</DetailSection>
|
||||
</div>
|
||||
|
||||
<div v-else-if="detailTab === 'discussion'" class="detail-tab-panel">
|
||||
<EntityDiscussionPanel entity-type="habitats" :entity-id="habitat.id" />
|
||||
</div>
|
||||
|
||||
<div v-else class="detail-tab-panel">
|
||||
<EditHistoryPanel :entity="habitat" :history="habitat.editHistory" />
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import DetailSection from '../components/DetailSection.vue';
|
||||
import EditHistoryPanel from '../components/EditHistoryPanel.vue';
|
||||
import EntityDiscussionPanel from '../components/EntityDiscussionPanel.vue';
|
||||
import EntityChips from '../components/EntityChips.vue';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
@@ -20,6 +21,7 @@ const detailTab = ref('details');
|
||||
const showEditor = computed(() => route.name === 'item-edit');
|
||||
const detailTabs = computed<TabOption[]>(() => [
|
||||
{ value: 'details', label: t('common.details') },
|
||||
{ value: 'discussion', label: t('discussion.title') },
|
||||
{ value: 'history', label: t('history.editHistory') }
|
||||
]);
|
||||
|
||||
@@ -195,6 +197,10 @@ watch(
|
||||
</DetailSection>
|
||||
</div>
|
||||
|
||||
<div v-else-if="detailTab === 'discussion'" class="detail-tab-panel">
|
||||
<EntityDiscussionPanel entity-type="items" :entity-id="item.id" />
|
||||
</div>
|
||||
|
||||
<div v-else class="detail-tab-panel">
|
||||
<EditHistoryPanel :entity="item" :history="item.editHistory" />
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import DetailSection from '../components/DetailSection.vue';
|
||||
import EditHistoryPanel from '../components/EditHistoryPanel.vue';
|
||||
import EntityDiscussionPanel from '../components/EntityDiscussionPanel.vue';
|
||||
import EntityChips from '../components/EntityChips.vue';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import PokemonStatsPanel from '../components/PokemonStatsPanel.vue';
|
||||
@@ -112,6 +113,7 @@ const skillDropRows = computed(() => pokemon.value?.skills.filter((skill) => ski
|
||||
const showEditor = computed(() => route.name === 'pokemon-edit');
|
||||
const detailTabs = computed<TabOption[]>(() => [
|
||||
{ value: 'details', label: t('common.details') },
|
||||
{ value: 'discussion', label: t('discussion.title') },
|
||||
{ value: 'history', label: t('history.editHistory') }
|
||||
]);
|
||||
const itemCategoryTabs = computed<TabOption[]>(() => {
|
||||
@@ -444,6 +446,10 @@ watch(
|
||||
</DetailSection>
|
||||
</div>
|
||||
|
||||
<div v-else-if="detailTab === 'discussion'" class="detail-tab-panel">
|
||||
<EntityDiscussionPanel entity-type="pokemon" :entity-id="pokemon.id" />
|
||||
</div>
|
||||
|
||||
<div v-else class="detail-tab-panel">
|
||||
<EditHistoryPanel :entity="pokemon" :history="pokemon.editHistory" />
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import DetailSection from '../components/DetailSection.vue';
|
||||
import EditHistoryPanel from '../components/EditHistoryPanel.vue';
|
||||
import EntityDiscussionPanel from '../components/EntityDiscussionPanel.vue';
|
||||
import EntityChips from '../components/EntityChips.vue';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
@@ -20,6 +21,7 @@ const detailTab = ref('details');
|
||||
const showEditor = computed(() => route.name === 'recipe-edit');
|
||||
const detailTabs = computed<TabOption[]>(() => [
|
||||
{ value: 'details', label: t('common.details') },
|
||||
{ value: 'discussion', label: t('discussion.title') },
|
||||
{ value: 'history', label: t('history.editHistory') }
|
||||
]);
|
||||
|
||||
@@ -105,6 +107,10 @@ watch(
|
||||
</DetailSection>
|
||||
</div>
|
||||
|
||||
<div v-else-if="detailTab === 'discussion'" class="detail-tab-panel">
|
||||
<EntityDiscussionPanel entity-type="recipes" :entity-id="recipe.id" />
|
||||
</div>
|
||||
|
||||
<div v-else class="detail-tab-panel">
|
||||
<EditHistoryPanel :entity="recipe" :history="recipe.editHistory" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user