refactor: remove display ID from items and ancient artifacts

Drop display_id column from items and ancient_artifacts tables
Remove display ID inputs, labels, and sorting logic across the stack

BREAKING CHANGE: behavior is not backward compatible.
This commit is contained in:
2026-05-04 21:32:00 +08:00
parent 2220d5d595
commit 28f4e6032c
16 changed files with 89 additions and 152 deletions

View File

@@ -16,7 +16,6 @@ const changeLabelKeys: Record<string, string> = {
标题: 'pages.checklist.task',
'Pokemon ID': 'pages.pokemon.id',
'Pokopia ID': 'pages.pokemon.id',
'Display ID': 'pages.items.displayId',
'Event item': 'common.eventItem',
'Event Pokemon': 'pages.pokemon.eventItem',
'Event Habitat': 'pages.habitats.eventItem',
@@ -118,12 +117,17 @@ function changeValue(value: string): string {
return values[value] ?? value;
}
function visibleChanges(entry: EditHistoryEntry) {
return entry.changes.filter((change) => change.label !== 'Display ID');
}
function historySummary(entry: EditHistoryEntry): string {
if (!entry.changes.length) {
const changes = visibleChanges(entry);
if (!changes.length) {
return actionLabel(entry.action);
}
return entry.changes.map((change) => changeLabel(change.label)).join(locale.value === 'zh-CN' ? '、' : ', ');
return changes.map((change) => changeLabel(change.label)).join(locale.value === 'zh-CN' ? '、' : ', ');
}
function formatDateTime(value: string): string {
@@ -175,8 +179,8 @@ function formatDateTime(value: string): string {
</summary>
<div class="edit-history-entry__content">
<dl v-if="entry.changes.length" class="edit-change-list">
<div v-for="change in entry.changes" :key="`${change.label}-${change.before}-${change.after}`">
<dl v-if="visibleChanges(entry).length" class="edit-change-list">
<div v-for="change in visibleChanges(entry)" :key="`${change.label}-${change.before}-${change.after}`">
<dt>{{ changeLabel(change.label) }}</dt>
<dd>
<span class="edit-change-list__label">{{ t('history.before') }}</span>

View File

@@ -246,7 +246,6 @@ export interface HabitatUsage {
}
export interface RecipeResultItem extends NamedEntity {
displayId: number;
image?: EntityImage | null;
category?: NamedEntity;
usage?: NamedEntity | null;
@@ -254,7 +253,6 @@ export interface RecipeResultItem extends NamedEntity {
export interface Item extends EditInfo {
id: number;
displayId: number;
name: string;
baseName?: string;
details: string;
@@ -276,7 +274,6 @@ export interface Item extends EditInfo {
export interface AncientArtifact extends EditInfo {
id: number;
displayId: number;
name: string;
baseName?: string;
details: string;
@@ -312,7 +309,6 @@ export interface Recipe extends EditInfo {
}
export interface ItemLink extends NamedEntity {
displayId: number;
image?: EntityImage | null;
category?: NamedEntity;
}
@@ -791,7 +787,6 @@ export interface PokemonImageOptionsResult {
}
export interface ItemPayload {
displayId: number;
name: string;
details: string;
translations?: TranslationMap;
@@ -808,7 +803,6 @@ export interface ItemPayload {
}
export interface AncientArtifactPayload {
displayId: number;
name: string;
details: string;
translations?: TranslationMap;

View File

@@ -448,15 +448,15 @@ const configLabel = (item: EditableConfig) => item.name;
const pokemonKey = (item: Pokemon) => item.id;
const pokemonLabel = (item: Pokemon) => `#${item.displayId} ${item.name}`;
const itemKey = (item: Item) => item.id;
const itemLabel = (item: Item) => `#${item.displayId} ${item.name}`;
const itemLabel = (item: Item) => item.name;
const ancientArtifactKey = (item: AncientArtifact) => item.id;
const ancientArtifactLabel = (item: AncientArtifact) => `#${item.displayId} ${item.name}`;
const ancientArtifactLabel = (item: AncientArtifact) => item.name;
const recipeKey = (item: Recipe) => item.id;
const recipeLabel = (item: Recipe) => item.name;
const dishCategoryKey = (item: DishCategory) => item.id;
const dishCategoryLabel = (item: DishCategory) => item.name;
const dishKey = (item: Dish) => item.id;
const dishLabel = (item: Dish) => `#${item.item.displayId} ${item.item.name}`;
const dishLabel = (item: Dish) => item.item.name;
const habitatKey = (item: Habitat) => item.id;
const habitatLabel = (item: Habitat) => item.name;
@@ -2260,7 +2260,7 @@ onMounted(() => {
@reorder="persistItemOrder"
>
<template #default="{ item }">
<RouterLink :to="`/items/${item.id}`">#{{ item.displayId }} {{ item.name }}</RouterLink>
<RouterLink :to="`/items/${item.id}`">{{ item.name }}</RouterLink>
<span class="row-actions">
<button v-if="can('items.delete')" type="button" :disabled="busy" @click="removeItem(item.id)">
<Icon :icon="iconDelete" class="ui-icon" aria-hidden="true" />
@@ -2288,7 +2288,7 @@ onMounted(() => {
@reorder="persistAncientArtifactOrder"
>
<template #default="{ item }">
<RouterLink :to="`/ancient-artifacts/${item.id}`">#{{ item.displayId }} {{ item.name }}</RouterLink>
<RouterLink :to="`/ancient-artifacts/${item.id}`">{{ item.name }}</RouterLink>
<span class="row-actions">
<button v-if="can('ancient-artifacts.delete')" type="button" :disabled="busy" @click="removeAncientArtifact(item.id)">
<Icon :icon="iconDelete" class="ui-icon" aria-hidden="true" />
@@ -2389,7 +2389,7 @@ onMounted(() => {
@reorder="persistDishOrder"
>
<template #default="{ item }">
<RouterLink :to="`/items/${item.item.id}`">#{{ item.item.displayId }} {{ item.item.name }}</RouterLink>
<RouterLink :to="`/items/${item.item.id}`">{{ item.item.name }}</RouterLink>
<span class="meta-line">{{ item.category.name }} / {{ item.flavor.name }}</span>
<span class="row-actions">
<button v-if="can('dish.update')" type="button" :disabled="busy" @click="editDish(item)">
@@ -2658,7 +2658,7 @@ onMounted(() => {
<label for="dish-category-cookware">{{ t('pages.dish.cookware') }}</label>
<select id="dish-category-cookware" v-model="dishCategoryForm.cookwareItemId" required>
<option value="">{{ t('common.none') }}</option>
<option v-for="item in dishItemRows" :key="`cookware-${item.id}`" :value="String(item.id)">#{{ item.displayId }} {{ item.name }}</option>
<option v-for="item in dishItemRows" :key="`cookware-${item.id}`" :value="String(item.id)">{{ item.name }}</option>
</select>
</div>
<div class="field">
@@ -2669,7 +2669,7 @@ onMounted(() => {
<label for="dish-category-main-material">{{ t('pages.dish.mainMaterial') }}</label>
<select id="dish-category-main-material" v-model="dishCategoryForm.mainMaterialItemId" required>
<option value="">{{ t('common.none') }}</option>
<option v-for="item in dishItemRows" :key="`category-main-material-${item.id}`" :value="String(item.id)">#{{ item.displayId }} {{ item.name }}</option>
<option v-for="item in dishItemRows" :key="`category-main-material-${item.id}`" :value="String(item.id)">{{ item.name }}</option>
</select>
</div>
</div>
@@ -2710,7 +2710,7 @@ onMounted(() => {
<label for="dish-item">{{ t('pages.dish.dishItem') }}</label>
<select id="dish-item" v-model="dishForm.itemId" required>
<option value="">{{ t('common.none') }}</option>
<option v-for="item in dishItemRows" :key="`dish-item-${item.id}`" :value="String(item.id)">#{{ item.displayId }} {{ item.name }}</option>
<option v-for="item in dishItemRows" :key="`dish-item-${item.id}`" :value="String(item.id)">{{ item.name }}</option>
</select>
</div>
<div class="field">
@@ -2726,14 +2726,14 @@ onMounted(() => {
<label for="dish-secondary-material-1">{{ t('pages.dish.secondaryMaterial') }}</label>
<select id="dish-secondary-material-1" v-model="dishForm.secondaryMaterialItemIds[0]">
<option value="">{{ t('common.none') }}</option>
<option v-for="item in dishItemRows" :key="`dish-secondary-material-1-${item.id}`" :value="String(item.id)">#{{ item.displayId }} {{ item.name }}</option>
<option v-for="item in dishItemRows" :key="`dish-secondary-material-1-${item.id}`" :value="String(item.id)">{{ item.name }}</option>
</select>
</div>
<div v-if="dishAllowsSecondSecondaryMaterial" class="field">
<label for="dish-secondary-material-2">{{ t('pages.dish.secondSecondaryMaterial') }}</label>
<select id="dish-secondary-material-2" v-model="dishForm.secondaryMaterialItemIds[1]">
<option value="">{{ t('common.none') }}</option>
<option v-for="item in dishItemRows" :key="`dish-secondary-material-2-${item.id}`" :value="String(item.id)">#{{ item.displayId }} {{ item.name }}</option>
<option v-for="item in dishItemRows" :key="`dish-secondary-material-2-${item.id}`" :value="String(item.id)">{{ item.name }}</option>
</select>
</div>
<div class="field">

View File

@@ -96,7 +96,7 @@ watch(
</section>
</section>
<section v-else class="page-stack">
<PageHeader :title="`#${artifact.displayId} ${artifact.name}`" :subtitle="artifact.category.name">
<PageHeader :title="artifact.name" :subtitle="artifact.category.name">
<template #kicker>{{ t('pages.ancientArtifacts.detailKicker') }}</template>
<template #actions>
<RouterLink v-if="canUpdateArtifact" class="ui-button ui-button--primary ui-button--small" :to="`/ancient-artifacts/${artifact.id}/edit`">
@@ -116,10 +116,6 @@ watch(
<div v-if="detailTab === 'details'" class="detail-grid">
<DetailSection :title="t('common.details')">
<dl class="entity-profile-facts">
<div>
<dt>{{ t('pages.ancientArtifacts.displayId') }}</dt>
<dd>#{{ artifact.displayId }}</dd>
</div>
<div>
<dt>{{ t('pages.ancientArtifacts.category') }}</dt>
<dd>{{ artifact.category.name }}</dd>

View File

@@ -36,7 +36,6 @@ const busy = ref(false);
const message = ref('');
const creatingSelect = ref('');
const artifactForm = ref({
displayId: 1,
name: '',
details: '',
translations: {} as TranslationMap,
@@ -98,7 +97,6 @@ async function loadEditor() {
if (isEditing.value) {
const artifact = await api.ancientArtifactDetail(routeId.value);
artifactForm.value = {
displayId: artifact.displayId,
name: artifact.baseName ?? artifact.name,
details: artifact.baseDetails ?? artifact.details,
translations: artifact.translations ?? {},
@@ -142,7 +140,6 @@ async function saveArtifact() {
try {
const payload: AncientArtifactPayload = {
displayId: artifactForm.value.displayId,
name: artifactNameForSave(),
details: artifactForm.value.details,
translations: artifactForm.value.translations,
@@ -190,11 +187,6 @@ onMounted(() => {
required
/>
<div class="field">
<label for="artifact-display-id">{{ t('pages.ancientArtifacts.displayId') }}</label>
<input id="artifact-display-id" v-model.number="artifactForm.displayId" type="number" min="1" required />
</div>
<TranslationFields
id-prefix="artifact-details"
v-model:base-value="artifactForm.details"

View File

@@ -126,7 +126,7 @@ watch(artifactQuery, loadArtifacts);
<EntityCard
v-for="artifact in artifacts"
:key="artifact.id"
:title="`#${artifact.displayId} ${artifact.name}`"
:title="artifact.name"
:subtitle="artifact.category.name"
:to="`/ancient-artifacts/${artifact.id}`"
:icon="iconArtifact"

View File

@@ -74,7 +74,7 @@ const dishCategoryModalTitle = computed(() =>
);
const dishModalTitle = computed(() => (dishForm.value.id ? t('pages.dish.editDish') : t('pages.dish.newDish')));
const itemSelectOptions = computed<TagsSelectOption[]>(() =>
items.value.map((item) => ({ id: item.id, name: item.name, label: `#${item.displayId} ${item.name}` }))
items.value.map((item) => ({ id: item.id, name: item.name }))
);
const optionalItemSelectOptions = computed<TagsSelectOption[]>(() => [{ id: '', name: t('common.none') }, ...itemSelectOptions.value]);
const categorySelectOptions = computed<TagsSelectOption[]>(() => categories.value.map((category) => ({ id: category.id, name: category.name })));
@@ -373,7 +373,7 @@ onMounted(loadPage);
<div>
<dt>{{ t('pages.dish.cookware') }}</dt>
<dd>
<RouterLink :to="`/items/${activeCategory.cookware.id}`">#{{ activeCategory.cookware.displayId }} {{ activeCategory.cookware.name }}</RouterLink>
<RouterLink :to="`/items/${activeCategory.cookware.id}`">{{ activeCategory.cookware.name }}</RouterLink>
</dd>
</div>
<div>
@@ -383,7 +383,7 @@ onMounted(loadPage);
<div>
<dt>{{ t('pages.dish.mainMaterial') }}</dt>
<dd>
<RouterLink :to="`/items/${activeCategory.mainMaterial.id}`">#{{ activeCategory.mainMaterial.displayId }} {{ activeCategory.mainMaterial.name }}</RouterLink>
<RouterLink :to="`/items/${activeCategory.mainMaterial.id}`">{{ activeCategory.mainMaterial.name }}</RouterLink>
</dd>
</div>
</dl>
@@ -411,7 +411,7 @@ onMounted(loadPage);
<Icon v-else :icon="iconItem" class="entity-card__icon" aria-hidden="true" />
</RouterLink>
<div class="dish-card__content">
<RouterLink class="dish-card__title" :to="`/items/${dish.item.id}`">#{{ dish.item.displayId }} {{ dish.item.name }}</RouterLink>
<RouterLink class="dish-card__title" :to="`/items/${dish.item.id}`">{{ dish.item.name }}</RouterLink>
<div class="dish-card__meta">
<span>{{ dish.flavor.name }}</span>
<span v-if="dish.pokemonSkill">{{ dish.pokemonSkill.name }}</span>

View File

@@ -149,7 +149,7 @@ watch(
</div>
</section>
<section v-else class="page-stack">
<PageHeader :title="`#${item.displayId} ${item.name}`" :subtitle="itemSubtitle">
<PageHeader :title="item.name" :subtitle="itemSubtitle">
<template #kicker>{{ detailKicker }}</template>
<template #actions>
<RouterLink v-if="canUpdateItem" class="ui-button ui-button--primary ui-button--small" :to="`/items/${item.id}/edit`">
@@ -182,10 +182,6 @@ watch(
<div class="entity-profile-main">
<section class="detail-section entity-profile-overview" :aria-label="t('common.details')">
<dl class="entity-profile-facts">
<div>
<dt>{{ t('pages.items.displayId') }}</dt>
<dd>#{{ item.displayId }}</dd>
</div>
<div>
<dt>{{ t('pages.items.category') }}</dt>
<dd>{{ item.category.name }}</dd>

View File

@@ -36,7 +36,6 @@ const busy = ref(false);
const message = ref('');
const creatingSelect = ref('');
const itemForm = ref({
displayId: 1,
name: '',
details: '',
translations: {} as TranslationMap,
@@ -117,7 +116,6 @@ async function loadEditor() {
if (isEditing.value) {
const item = await api.itemDetail(routeId.value);
itemForm.value = {
displayId: item.displayId,
name: item.baseName ?? item.name,
details: item.baseDetails ?? item.details,
translations: item.translations ?? {},
@@ -173,7 +171,6 @@ async function saveItem() {
try {
const payload: ItemPayload = {
displayId: itemForm.value.displayId,
name: itemNameForSave(),
details: itemForm.value.details,
translations: itemForm.value.translations,
@@ -226,11 +223,6 @@ onMounted(() => {
required
/>
<div class="field">
<label for="item-display-id">{{ t('pages.items.displayId') }}</label>
<input id="item-display-id" v-model.number="itemForm.displayId" type="number" min="1" required />
</div>
<TranslationFields
id-prefix="item-details"
v-model:base-value="itemForm.details"

View File

@@ -145,7 +145,7 @@ watch(itemQuery, loadItems);
<EntityCard
v-for="item in items"
:key="item.id"
:title="`#${item.displayId} ${item.name}`"
:title="item.name"
:subtitle="item.category.name"
:to="`/items/${item.id}`"
:icon="iconItem"

View File

@@ -114,7 +114,7 @@ watch(
</div>
</section>
<section v-else class="page-stack">
<PageHeader :title="`#${recipe.item.displayId} ${recipe.name}`" :subtitle="recipeSubtitle">
<PageHeader :title="recipe.name" :subtitle="recipeSubtitle">
<template #kicker>{{ t('pages.recipes.detailKicker') }}</template>
<template #actions>
<RouterLink v-if="canUpdateRecipe" class="ui-button ui-button--primary ui-button--small" :to="`/recipes/${recipe.id}/edit`">
@@ -145,7 +145,7 @@ watch(
<Icon :icon="iconRecipe" class="entity-card__icon" aria-hidden="true" />
</span>
</RouterLink>
<RouterLink class="entity-profile-title-link" :to="`/items/${recipe.item.id}`">#{{ recipe.item.displayId }} {{ recipe.item.name }}</RouterLink>
<RouterLink class="entity-profile-title-link" :to="`/items/${recipe.item.id}`">{{ recipe.item.name }}</RouterLink>
</div>
</section>

View File

@@ -155,7 +155,7 @@ watch(itemQuery, loadItems);
<EntityCard
v-for="item in items"
:key="item.id"
:title="`#${item.displayId} ${item.name}`"
:title="item.name"
:subtitle="item.category.name"
:to="recipeTarget(item)"
:icon="itemIcon(item)"