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

@@ -593,7 +593,6 @@ Pokemon 详情页展示:
物品可配置: 物品可配置:
- Display ID用于物品和 Event Items 各自列表内展示与排序;`display_id``is_event_item` 组合唯一
- 名称 - 名称
- 介绍 - 介绍
- 是否为 Event Item`is_event_item` - 是否为 Event Item`is_event_item`
@@ -638,8 +637,8 @@ Items 与 Event Items 使用相同数据模型:
- 按分类展示为标签页 - 按分类展示为标签页
- 按用途筛选 - 按用途筛选
- 按标签筛选 - 按标签筛选
- Display ID 和自定义排序展示 - 按自定义排序展示
- 物品列表卡片使用与 Pokemon 列表一致的居中图鉴式布局,只展示物品图标、`#Display ID 名称` 和分类;不展示标签、入手方式或编辑元信息。 - 物品列表卡片使用与 Pokemon 列表一致的居中图鉴式布局,只展示物品图标、名称和分类;不展示标签、入手方式或编辑元信息。
- 有用途的物品在卡片左上角以斜 Ribbon 展示用途名称。 - 有用途的物品在卡片左上角以斜 Ribbon 展示用途名称。
- 已配置图标时,物品卡片展示图标缩略图;未配置图标时保留默认物品标记。 - 已配置图标时,物品卡片展示图标缩略图;未配置图标时保留默认物品标记。
@@ -648,7 +647,6 @@ Items 与 Event Items 使用相同数据模型:
- 基本信息 - 基本信息
- 当前图标图片;未配置图标时展示默认物品标记占位符 - 当前图标图片;未配置图标时展示默认物品标记占位符
- 顶部按图标 / 占位符与核心信息概览并排展示,移动端改为单列;顶部概览卡片不显示 `Image` / `Details` 通用区块标题,也不展示图片历史缩略图 - 顶部按图标 / 占位符与核心信息概览并排展示,移动端改为单列;顶部概览卡片不显示 `Image` / `Details` 通用区块标题,也不展示图片历史缩略图
- Display ID
- 介绍 - 介绍
- 分类 - 分类
- 用途 - 用途
@@ -667,7 +665,6 @@ Items 与 Event Items 使用相同数据模型:
Ancient Artifacts 是独立 Wiki 内容类型,可配置: Ancient Artifacts 是独立 Wiki 内容类型,可配置:
- Display ID用于展示与排序
- 名称 - 名称
- 介绍 - 介绍
- 图片:使用 Ancient Artifacts 上传目录,支持图片历史 - 图片:使用 Ancient Artifacts 上传目录,支持图片历史
@@ -684,12 +681,11 @@ Ancient Artifacts 列表功能:
- 搜索 - 搜索
- 按分类展示为标签页 - 按分类展示为标签页
- 按标签筛选 - 按标签筛选
- Display ID 和自定义排序展示 - 按自定义排序展示
- 列表卡片使用与 Pokemon 列表一致的居中图鉴式布局,展示图片 / 默认 Ancient Artifact 标记、`#Display ID 名称` 和分类;不展示编辑元信息。 - 列表卡片使用与 Pokemon 列表一致的居中图鉴式布局,展示图片 / 默认 Ancient Artifact 标记、名称和分类;不展示编辑元信息。
Ancient Artifacts 详情页展示: Ancient Artifacts 详情页展示:
- Display ID
- 名称 - 名称
- 图片;未配置图片时展示默认 Ancient Artifact 标记 - 图片;未配置图片时展示默认 Ancient Artifact 标记
- 介绍 - 介绍
@@ -719,8 +715,8 @@ Ancient Artifacts 详情页展示:
- 独立于物品列表展示 - 独立于物品列表展示
- 按结果物品分类展示 - 按结果物品分类展示
-结果物品 Display ID 和自定义排序展示 - 按自定义排序展示
- 材料单列表卡片使用与 Pokemon 列表一致的居中图鉴式布局,按结果物品展示图标、`#Display ID 名称` 和分类;不展示编辑元信息。 - 材料单列表卡片使用与 Pokemon 列表一致的居中图鉴式布局,按结果物品展示图标、名称和分类;不展示编辑元信息。
- 有用途的结果物品在卡片左上角以斜 Ribbon 展示用途名称。 - 有用途的结果物品在卡片左上角以斜 Ribbon 展示用途名称。
- Create Recipe 按钮展示在结果物品名称下方;已有材料单的卡片保留同等按钮空间但不显示按钮;标记为无材料单的物品展示禁用按钮;可创建材料单的物品展示可点击按钮并进入创建流程。 - Create Recipe 按钮展示在结果物品名称下方;已有材料单的卡片保留同等按钮空间但不显示按钮;标记为无材料单的物品展示禁用按钮;可创建材料单的物品展示可点击按钮并进入创建流程。

View File

@@ -974,7 +974,6 @@ CREATE TABLE IF NOT EXISTS acquisition_methods (
CREATE TABLE IF NOT EXISTS items ( CREATE TABLE IF NOT EXISTS items (
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
display_id integer NOT NULL CHECK (display_id > 0),
name text NOT NULL UNIQUE, name text NOT NULL UNIQUE,
details text NOT NULL DEFAULT '', details text NOT NULL DEFAULT '',
category_key text NOT NULL DEFAULT 'other', category_key text NOT NULL DEFAULT 'other',
@@ -1011,7 +1010,6 @@ CREATE TABLE IF NOT EXISTS items (
CREATE TABLE IF NOT EXISTS ancient_artifacts ( CREATE TABLE IF NOT EXISTS ancient_artifacts (
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
display_id integer NOT NULL UNIQUE CHECK (display_id > 0),
name text NOT NULL UNIQUE, name text NOT NULL UNIQUE,
details text NOT NULL DEFAULT '', details text NOT NULL DEFAULT '',
category_key text NOT NULL CHECK (category_key IN ('lost-relics-l', 'lost-relics-s', 'fossils')), category_key text NOT NULL CHECK (category_key IN ('lost-relics-l', 'lost-relics-s', 'fossils')),
@@ -1221,7 +1219,6 @@ ALTER TABLE life_tags
ADD COLUMN IF NOT EXISTS is_default boolean NOT NULL DEFAULT false; ADD COLUMN IF NOT EXISTS is_default boolean NOT NULL DEFAULT false;
ALTER TABLE items ALTER TABLE items
ADD COLUMN IF NOT EXISTS display_id integer,
ADD COLUMN IF NOT EXISTS details text NOT NULL DEFAULT '', ADD COLUMN IF NOT EXISTS details text NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS category_key text, ADD COLUMN IF NOT EXISTS category_key text,
ADD COLUMN IF NOT EXISTS usage_key text; ADD COLUMN IF NOT EXISTS usage_key text;
@@ -1242,10 +1239,6 @@ BEGIN
END IF; END IF;
END $$; END $$;
UPDATE items
SET display_id = id
WHERE display_id IS NULL;
UPDATE items i UPDATE items i
SET category_key = CASE lower(trim(c.name)) SET category_key = CASE lower(trim(c.name))
WHEN 'furniture' THEN 'furniture' WHEN 'furniture' THEN 'furniture'
@@ -1303,7 +1296,6 @@ WHERE usage_key IS NOT NULL
AND usage_key NOT IN ('decoration', 'relaxation', 'toy', 'road'); AND usage_key NOT IN ('decoration', 'relaxation', 'toy', 'road');
ALTER TABLE items ALTER TABLE items
ALTER COLUMN display_id SET NOT NULL,
ALTER COLUMN category_key SET NOT NULL, ALTER COLUMN category_key SET NOT NULL,
ALTER COLUMN category_key SET DEFAULT 'other'; ALTER COLUMN category_key SET DEFAULT 'other';
@@ -1313,7 +1305,6 @@ ALTER TABLE items
DROP CONSTRAINT IF EXISTS items_usage_key_check; DROP CONSTRAINT IF EXISTS items_usage_key_check;
ALTER TABLE items ALTER TABLE items
ADD CONSTRAINT items_display_id_positive CHECK (display_id > 0),
ADD CONSTRAINT items_category_key_check CHECK (category_key IN ( ADD CONSTRAINT items_category_key_check CHECK (category_key IN (
'furniture', 'furniture',
'misc', 'misc',
@@ -1330,6 +1321,20 @@ ALTER TABLE items
)), )),
ADD CONSTRAINT items_usage_key_check CHECK (usage_key IS NULL OR usage_key IN ('decoration', 'relaxation', 'toy', 'road')); ADD CONSTRAINT items_usage_key_check CHECK (usage_key IS NULL OR usage_key IN ('decoration', 'relaxation', 'toy', 'road'));
DROP INDEX IF EXISTS items_display_event_item_key;
DROP INDEX IF EXISTS items_display_order_idx;
DROP INDEX IF EXISTS ancient_artifacts_display_order_idx;
ALTER TABLE ancient_artifacts
DROP CONSTRAINT IF EXISTS ancient_artifacts_display_id_key,
DROP CONSTRAINT IF EXISTS ancient_artifacts_display_id_check;
ALTER TABLE items
DROP COLUMN IF EXISTS display_id;
ALTER TABLE ancient_artifacts
DROP COLUMN IF EXISTS display_id;
CREATE INDEX IF NOT EXISTS environments_sort_order_idx ON environments(sort_order, id); CREATE INDEX IF NOT EXISTS environments_sort_order_idx ON environments(sort_order, id);
CREATE INDEX IF NOT EXISTS skills_sort_order_idx ON skills(sort_order, id); CREATE INDEX IF NOT EXISTS skills_sort_order_idx ON skills(sort_order, id);
CREATE INDEX IF NOT EXISTS favorite_things_sort_order_idx ON favorite_things(sort_order, id); CREATE INDEX IF NOT EXISTS favorite_things_sort_order_idx ON favorite_things(sort_order, id);
@@ -1342,10 +1347,7 @@ CREATE INDEX IF NOT EXISTS item_categories_sort_order_idx ON item_categories(sor
CREATE INDEX IF NOT EXISTS item_usages_sort_order_idx ON item_usages(sort_order, id); CREATE INDEX IF NOT EXISTS item_usages_sort_order_idx ON item_usages(sort_order, id);
CREATE INDEX IF NOT EXISTS acquisition_methods_sort_order_idx ON acquisition_methods(sort_order, id); CREATE INDEX IF NOT EXISTS acquisition_methods_sort_order_idx ON acquisition_methods(sort_order, id);
CREATE INDEX IF NOT EXISTS items_sort_order_idx ON items(sort_order, id); CREATE INDEX IF NOT EXISTS items_sort_order_idx ON items(sort_order, id);
CREATE UNIQUE INDEX IF NOT EXISTS items_display_event_item_key ON items(display_id, is_event_item);
CREATE INDEX IF NOT EXISTS items_display_order_idx ON items(is_event_item, display_id, sort_order, id);
CREATE INDEX IF NOT EXISTS ancient_artifacts_sort_order_idx ON ancient_artifacts(sort_order, id); CREATE INDEX IF NOT EXISTS ancient_artifacts_sort_order_idx ON ancient_artifacts(sort_order, id);
CREATE INDEX IF NOT EXISTS ancient_artifacts_display_order_idx ON ancient_artifacts(display_id, sort_order, id);
CREATE INDEX IF NOT EXISTS recipes_sort_order_idx ON recipes(sort_order, id); CREATE INDEX IF NOT EXISTS recipes_sort_order_idx ON recipes(sort_order, id);
CREATE INDEX IF NOT EXISTS dish_categories_sort_order_idx ON dish_categories(sort_order, id); CREATE INDEX IF NOT EXISTS dish_categories_sort_order_idx ON dish_categories(sort_order, id);
CREATE INDEX IF NOT EXISTS dish_flavors_sort_order_idx ON dish_flavors(sort_order, id); CREATE INDEX IF NOT EXISTS dish_flavors_sort_order_idx ON dish_flavors(sort_order, id);

View File

@@ -201,7 +201,6 @@ type PokemonCsvData = {
}; };
type ItemPayload = { type ItemPayload = {
displayId: number;
name: string; name: string;
details: string; details: string;
translations: TranslationInput; translations: TranslationInput;
@@ -220,7 +219,6 @@ type ItemPayload = {
}; };
type AncientArtifactPayload = { type AncientArtifactPayload = {
displayId: number;
name: string; name: string;
details: string; details: string;
translations: TranslationInput; translations: TranslationInput;
@@ -541,7 +539,6 @@ type PokemonChangeSource = {
favorite_things: Array<{ name: string }>; favorite_things: Array<{ name: string }>;
} & TranslationChangeSource; } & TranslationChangeSource;
type ItemChangeSource = { type ItemChangeSource = {
displayId: number;
name: string; name: string;
details: string; details: string;
isEventItem: boolean; isEventItem: boolean;
@@ -554,7 +551,6 @@ type ItemChangeSource = {
tags: Array<{ name: string }>; tags: Array<{ name: string }>;
} & TranslationChangeSource; } & TranslationChangeSource;
type AncientArtifactChangeSource = { type AncientArtifactChangeSource = {
displayId: number;
name: string; name: string;
details: string; details: string;
image: EntityImageValue | null; image: EntityImageValue | null;
@@ -2251,7 +2247,6 @@ async function itemEditChanges(
const tagNames = await entityNameMap(client, 'favorite_things', after.tagIds); const tagNames = await entityNameMap(client, 'favorite_things', after.tagIds);
pushChange(changes, 'Name', before.name, after.name); pushChange(changes, 'Name', before.name, after.name);
pushChange(changes, 'Display ID', String(before.displayId), String(after.displayId));
pushChange(changes, 'Description', before.details, after.details); pushChange(changes, 'Description', before.details, after.details);
pushTranslationChanges(changes, before.translations, after.translations, ['name', 'details']); pushTranslationChanges(changes, before.translations, after.translations, ['name', 'details']);
pushChange(changes, 'Event item', boolValue(before.isEventItem), boolValue(after.isEventItem)); pushChange(changes, 'Event item', boolValue(before.isEventItem), boolValue(after.isEventItem));
@@ -2277,7 +2272,6 @@ async function ancientArtifactEditChanges(
const tagNames = await entityNameMap(client, 'favorite_things', after.tagIds); const tagNames = await entityNameMap(client, 'favorite_things', after.tagIds);
pushChange(changes, 'Name', before.name, after.name); pushChange(changes, 'Name', before.name, after.name);
pushChange(changes, 'Display ID', String(before.displayId), String(after.displayId));
pushChange(changes, 'Description', before.details, after.details); pushChange(changes, 'Description', before.details, after.details);
pushTranslationChanges(changes, before.translations, after.translations, ['name', 'details']); pushTranslationChanges(changes, before.translations, after.translations, ['name', 'details']);
pushChange(changes, 'Image', imagePathLabel(before.image?.path), imagePathLabel(after.imagePath)); pushChange(changes, 'Image', imagePathLabel(before.image?.path), imagePathLabel(after.imagePath));
@@ -2574,7 +2568,7 @@ export async function globalSearch(paramsQuery: QueryParams = {}, locale = defau
${uploadedImageJson('i.image_path')} AS image ${uploadedImageJson('i.image_path')} AS image
FROM items i FROM items i
WHERE ${itemName} ILIKE $1 WHERE ${itemName} ILIKE $1
ORDER BY i.display_id, ${orderByEntity('i')} ORDER BY ${orderByEntity('i')}
LIMIT $2 LIMIT $2
`, `,
[pattern, limit] [pattern, limit]
@@ -2591,7 +2585,7 @@ export async function globalSearch(paramsQuery: QueryParams = {}, locale = defau
${uploadedImageJson('a.image_path')} AS image ${uploadedImageJson('a.image_path')} AS image
FROM ancient_artifacts a FROM ancient_artifacts a
WHERE ${artifactName} ILIKE $1 WHERE ${artifactName} ILIKE $1
ORDER BY a.display_id, ${orderByEntity('a')} ORDER BY ${orderByEntity('a')}
LIMIT $2 LIMIT $2
`, `,
[pattern, limit] [pattern, limit]
@@ -6223,7 +6217,6 @@ function itemProjection(locale: string): string {
return ` return `
SELECT SELECT
i.id, i.id,
i.display_id AS "displayId",
${itemName} AS name, ${itemName} AS name,
i.name AS "baseName", i.name AS "baseName",
${itemDetails} AS details, ${itemDetails} AS details,
@@ -6324,8 +6317,8 @@ export async function listItems(paramsQuery: QueryParams, locale = defaultLocale
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
const orderClause = recipeOrder const orderClause = recipeOrder
? `ORDER BY CASE WHEN item_recipe.id IS NULL THEN 1 ELSE 0 END, item_recipe.sort_order, item_recipe.id, i.display_id, ${orderByEntity('i')}` ? `ORDER BY CASE WHEN item_recipe.id IS NULL THEN 1 ELSE 0 END, item_recipe.sort_order, item_recipe.id, ${orderByEntity('i')}`
: `ORDER BY i.display_id, ${orderByEntity('i')}`; : `ORDER BY ${orderByEntity('i')}`;
return query(`${itemProjection(locale)} ${whereClause} ${orderClause}`, params); return query(`${itemProjection(locale)} ${whereClause} ${orderClause}`, params);
} }
@@ -6382,7 +6375,6 @@ export async function getItem(id: number, locale = defaultLocale) {
), '[]'::json) AS materials, ), '[]'::json) AS materials,
json_build_object( json_build_object(
'id', result_item.id, 'id', result_item.id,
'displayId', result_item.display_id,
'name', ${resultItemName}, 'name', ${resultItemName},
'image', ${uploadedImageJson('result_item.image_path')}, 'image', ${uploadedImageJson('result_item.image_path')},
'category', ${systemListJsonSql('result_item.category_key', itemCategoryOptions, locale)}, 'category', ${systemListJsonSql('result_item.category_key', itemCategoryOptions, locale)},
@@ -6489,7 +6481,6 @@ function cleanItemPayload(payload: Record<string, unknown>): ItemPayload {
const usage = usageId === null ? null : systemListOptionById(itemUsageOptions, usageId, 'server.validation.usageRequired'); const usage = usageId === null ? null : systemListOptionById(itemUsageOptions, usageId, 'server.validation.usageRequired');
return { return {
displayId: requirePositiveInteger(payload.displayId, 'server.validation.itemDisplayIdRequired'),
name: cleanName(payload.name, 'server.validation.itemNameRequired'), name: cleanName(payload.name, 'server.validation.itemNameRequired'),
details: cleanOptionalText(payload.details), details: cleanOptionalText(payload.details),
translations: cleanTranslations(payload.translations, ['name', 'details']), translations: cleanTranslations(payload.translations, ['name', 'details']),
@@ -6546,7 +6537,6 @@ export async function createItem(payload: Record<string, unknown>, userId: numbe
const result = await client.query<{ id: number }>( const result = await client.query<{ id: number }>(
` `
INSERT INTO items ( INSERT INTO items (
display_id,
name, name,
details, details,
category_key, category_key,
@@ -6561,11 +6551,10 @@ export async function createItem(payload: Record<string, unknown>, userId: numbe
created_by_user_id, created_by_user_id,
updated_by_user_id updated_by_user_id
) )
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $13) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $12)
RETURNING id RETURNING id
`, `,
[ [
cleanPayload.displayId,
cleanPayload.name, cleanPayload.name,
cleanPayload.details, cleanPayload.details,
cleanPayload.categoryKey, cleanPayload.categoryKey,
@@ -6599,23 +6588,21 @@ export async function updateItem(id: number, payload: Record<string, unknown>, u
const result = await client.query( const result = await client.query(
` `
UPDATE items UPDATE items
SET display_id = $1, SET name = $1,
name = $2, details = $2,
details = $3, category_key = $3,
category_key = $4, usage_key = $4,
usage_key = $5, dyeable = $5,
dyeable = $6, dual_dyeable = $6,
dual_dyeable = $7, pattern_editable = $7,
pattern_editable = $8, no_recipe = $8,
no_recipe = $9, is_event_item = $9,
is_event_item = $10, image_path = $10,
image_path = $11, updated_by_user_id = $11,
updated_by_user_id = $12,
updated_at = now() updated_at = now()
WHERE id = $13 WHERE id = $12
`, `,
[ [
cleanPayload.displayId,
cleanPayload.name, cleanPayload.name,
cleanPayload.details, cleanPayload.details,
cleanPayload.categoryKey, cleanPayload.categoryKey,
@@ -6665,7 +6652,6 @@ function ancientArtifactProjection(locale: string): string {
return ` return `
SELECT SELECT
a.id, a.id,
a.display_id AS "displayId",
${artifactName} AS name, ${artifactName} AS name,
a.name AS "baseName", a.name AS "baseName",
${artifactDetails} AS details, ${artifactDetails} AS details,
@@ -6719,7 +6705,7 @@ export async function listAncientArtifacts(paramsQuery: QueryParams = {}, locale
} }
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
return query(`${ancientArtifactProjection(locale)} ${whereClause} ORDER BY a.display_id, ${orderByEntity('a')}`, params); return query(`${ancientArtifactProjection(locale)} ${whereClause} ORDER BY ${orderByEntity('a')}`, params);
} }
export async function getAncientArtifact(id: number, locale = defaultLocale) { export async function getAncientArtifact(id: number, locale = defaultLocale) {
@@ -6738,7 +6724,6 @@ function cleanAncientArtifactPayload(payload: Record<string, unknown>): AncientA
const category = systemListOptionById(ancientArtifactCategoryOptions, categoryId, 'server.validation.categoryRequired'); const category = systemListOptionById(ancientArtifactCategoryOptions, categoryId, 'server.validation.categoryRequired');
return { return {
displayId: requirePositiveInteger(payload.displayId, 'server.validation.artifactDisplayIdRequired'),
name: cleanName(payload.name, 'server.validation.artifactNameRequired'), name: cleanName(payload.name, 'server.validation.artifactNameRequired'),
details: cleanOptionalText(payload.details), details: cleanOptionalText(payload.details),
translations: cleanTranslations(payload.translations, ['name', 'details']), translations: cleanTranslations(payload.translations, ['name', 'details']),
@@ -6768,7 +6753,6 @@ export async function createAncientArtifact(payload: Record<string, unknown>, us
const result = await client.query<{ id: number }>( const result = await client.query<{ id: number }>(
` `
INSERT INTO ancient_artifacts ( INSERT INTO ancient_artifacts (
display_id,
name, name,
details, details,
category_key, category_key,
@@ -6777,11 +6761,10 @@ export async function createAncientArtifact(payload: Record<string, unknown>, us
created_by_user_id, created_by_user_id,
updated_by_user_id updated_by_user_id
) )
VALUES ($1, $2, $3, $4, $5, $6, $7, $7) VALUES ($1, $2, $3, $4, $5, $6, $6)
RETURNING id RETURNING id
`, `,
[ [
cleanPayload.displayId,
cleanPayload.name, cleanPayload.name,
cleanPayload.details, cleanPayload.details,
cleanPayload.categoryKey, cleanPayload.categoryKey,
@@ -6809,16 +6792,15 @@ export async function updateAncientArtifact(id: number, payload: Record<string,
const result = await client.query( const result = await client.query(
` `
UPDATE ancient_artifacts UPDATE ancient_artifacts
SET display_id = $1, SET name = $1,
name = $2, details = $2,
details = $3, category_key = $3,
category_key = $4, image_path = $4,
image_path = $5, updated_by_user_id = $5,
updated_by_user_id = $6,
updated_at = now() updated_at = now()
WHERE id = $7 WHERE id = $6
`, `,
[cleanPayload.displayId, cleanPayload.name, cleanPayload.details, cleanPayload.categoryKey, cleanPayload.imagePath, userId, id] [cleanPayload.name, cleanPayload.details, cleanPayload.categoryKey, cleanPayload.imagePath, userId, id]
); );
if (result.rowCount === 0) { if (result.rowCount === 0) {
return false; return false;
@@ -6917,7 +6899,6 @@ export async function getRecipe(id: number, locale = defaultLocale) {
), '[]'::json) AS materials, ), '[]'::json) AS materials,
json_build_object( json_build_object(
'id', result_item.id, 'id', result_item.id,
'displayId', result_item.display_id,
'name', ${resultItemName}, 'name', ${resultItemName},
'image', ${uploadedImageJson('result_item.image_path')}, 'image', ${uploadedImageJson('result_item.image_path')},
'category', ${systemListJsonSql('result_item.category_key', itemCategoryOptions, locale)}, 'category', ${systemListJsonSql('result_item.category_key', itemCategoryOptions, locale)},
@@ -7060,14 +7041,12 @@ function dishCategoryProjection(locale: string): string {
${auditSelect('dc', 'category_created_user', 'category_updated_user')}, ${auditSelect('dc', 'category_created_user', 'category_updated_user')},
json_build_object( json_build_object(
'id', cookware_item.id, 'id', cookware_item.id,
'displayId', cookware_item.display_id,
'name', ${cookwareName}, 'name', ${cookwareName},
'image', ${uploadedImageJson('cookware_item.image_path')}, 'image', ${uploadedImageJson('cookware_item.image_path')},
'category', ${systemListJsonSql('cookware_item.category_key', itemCategoryOptions, locale)} 'category', ${systemListJsonSql('cookware_item.category_key', itemCategoryOptions, locale)}
) AS cookware, ) AS cookware,
json_build_object( json_build_object(
'id', main_material_item.id, 'id', main_material_item.id,
'displayId', main_material_item.display_id,
'name', ${mainMaterialName}, 'name', ${mainMaterialName},
'image', ${uploadedImageJson('main_material_item.image_path')}, 'image', ${uploadedImageJson('main_material_item.image_path')},
'category', ${systemListJsonSql('main_material_item.category_key', itemCategoryOptions, locale)} 'category', ${systemListJsonSql('main_material_item.category_key', itemCategoryOptions, locale)}
@@ -7093,7 +7072,6 @@ function dishCategoryProjection(locale: string): string {
'category', json_build_object('id', dc.id, 'name', ${categoryName}), 'category', json_build_object('id', dc.id, 'name', ${categoryName}),
'item', json_build_object( 'item', json_build_object(
'id', dish_item.id, 'id', dish_item.id,
'displayId', dish_item.display_id,
'name', ${dishItemName}, 'name', ${dishItemName},
'image', ${uploadedImageJson('dish_item.image_path')}, 'image', ${uploadedImageJson('dish_item.image_path')},
'category', ${systemListJsonSql('dish_item.category_key', itemCategoryOptions, locale)} 'category', ${systemListJsonSql('dish_item.category_key', itemCategoryOptions, locale)}
@@ -7102,7 +7080,6 @@ function dishCategoryProjection(locale: string): string {
SELECT json_agg( SELECT json_agg(
json_build_object( json_build_object(
'id', secondary_material_item.id, 'id', secondary_material_item.id,
'displayId', secondary_material_item.display_id,
'name', ${secondaryMaterialName}, 'name', ${secondaryMaterialName},
'image', ${uploadedImageJson('secondary_material_item.image_path')}, 'image', ${uploadedImageJson('secondary_material_item.image_path')},
'category', ${systemListJsonSql('secondary_material_item.category_key', itemCategoryOptions, locale)} 'category', ${systemListJsonSql('secondary_material_item.category_key', itemCategoryOptions, locale)}
@@ -7153,7 +7130,6 @@ function dishProjection(locale: string): string {
json_build_object('id', dc.id, 'name', ${categoryName}) AS category, json_build_object('id', dc.id, 'name', ${categoryName}) AS category,
json_build_object( json_build_object(
'id', dish_item.id, 'id', dish_item.id,
'displayId', dish_item.display_id,
'name', ${dishItemName}, 'name', ${dishItemName},
'image', ${uploadedImageJson('dish_item.image_path')}, 'image', ${uploadedImageJson('dish_item.image_path')},
'category', ${systemListJsonSql('dish_item.category_key', itemCategoryOptions, locale)} 'category', ${systemListJsonSql('dish_item.category_key', itemCategoryOptions, locale)}
@@ -7162,7 +7138,6 @@ function dishProjection(locale: string): string {
SELECT json_agg( SELECT json_agg(
json_build_object( json_build_object(
'id', secondary_material_item.id, 'id', secondary_material_item.id,
'displayId', secondary_material_item.display_id,
'name', ${secondaryMaterialName}, 'name', ${secondaryMaterialName},
'image', ${uploadedImageJson('secondary_material_item.image_path')}, 'image', ${uploadedImageJson('secondary_material_item.image_path')},
'category', ${systemListJsonSql('secondary_material_item.category_key', itemCategoryOptions, locale)} 'category', ${systemListJsonSql('secondary_material_item.category_key', itemCategoryOptions, locale)}
@@ -7568,7 +7543,6 @@ const dataToolColumns = {
habitatPokemon: ['habitat_id', 'pokemon_id', 'map_id', 'time_of_day', 'weather', 'rarity'], habitatPokemon: ['habitat_id', 'pokemon_id', 'map_id', 'time_of_day', 'weather', 'rarity'],
items: [ items: [
'id', 'id',
'display_id',
'name', 'name',
'details', 'details',
'category_key', 'category_key',
@@ -7590,7 +7564,6 @@ const dataToolColumns = {
artifactFavoriteThings: ['ancient_artifact_id', 'favorite_thing_id'], artifactFavoriteThings: ['ancient_artifact_id', 'favorite_thing_id'],
artifacts: [ artifacts: [
'id', 'id',
'display_id',
'name', 'name',
'details', 'details',
'category_key', 'category_key',
@@ -7901,7 +7874,7 @@ async function exportScopeData(client: DbClient, scope: DataToolScope): Promise<
if (scope === 'items') { if (scope === 'items') {
return { return {
items: await tableRows(client, 'SELECT * FROM items ORDER BY display_id, sort_order, id'), items: await tableRows(client, 'SELECT * FROM items ORDER BY sort_order, id'),
itemAcquisitionMethods: await tableRows(client, 'SELECT * FROM item_acquisition_methods ORDER BY item_id, acquisition_method_id'), itemAcquisitionMethods: await tableRows(client, 'SELECT * FROM item_acquisition_methods ORDER BY item_id, acquisition_method_id'),
itemFavoriteThings: await tableRows(client, 'SELECT * FROM item_favorite_things ORDER BY item_id, favorite_thing_id'), itemFavoriteThings: await tableRows(client, 'SELECT * FROM item_favorite_things ORDER BY item_id, favorite_thing_id'),
pokemonSkillItemDrops: await tableRows(client, 'SELECT * FROM pokemon_skill_item_drops ORDER BY pokemon_id, skill_id'), pokemonSkillItemDrops: await tableRows(client, 'SELECT * FROM pokemon_skill_item_drops ORDER BY pokemon_id, skill_id'),
@@ -7912,7 +7885,7 @@ async function exportScopeData(client: DbClient, scope: DataToolScope): Promise<
if (scope === 'artifacts') { if (scope === 'artifacts') {
return { return {
artifacts: await tableRows(client, 'SELECT * FROM ancient_artifacts ORDER BY display_id, sort_order, id'), artifacts: await tableRows(client, 'SELECT * FROM ancient_artifacts ORDER BY sort_order, id'),
artifactFavoriteThings: await tableRows( artifactFavoriteThings: await tableRows(
client, client,
'SELECT * FROM ancient_artifact_favorite_things ORDER BY ancient_artifact_id, favorite_thing_id' 'SELECT * FROM ancient_artifact_favorite_things ORDER BY ancient_artifact_id, favorite_thing_id'

View File

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

View File

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

View File

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

View File

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

View File

@@ -36,7 +36,6 @@ const busy = ref(false);
const message = ref(''); const message = ref('');
const creatingSelect = ref(''); const creatingSelect = ref('');
const artifactForm = ref({ const artifactForm = ref({
displayId: 1,
name: '', name: '',
details: '', details: '',
translations: {} as TranslationMap, translations: {} as TranslationMap,
@@ -98,7 +97,6 @@ async function loadEditor() {
if (isEditing.value) { if (isEditing.value) {
const artifact = await api.ancientArtifactDetail(routeId.value); const artifact = await api.ancientArtifactDetail(routeId.value);
artifactForm.value = { artifactForm.value = {
displayId: artifact.displayId,
name: artifact.baseName ?? artifact.name, name: artifact.baseName ?? artifact.name,
details: artifact.baseDetails ?? artifact.details, details: artifact.baseDetails ?? artifact.details,
translations: artifact.translations ?? {}, translations: artifact.translations ?? {},
@@ -142,7 +140,6 @@ async function saveArtifact() {
try { try {
const payload: AncientArtifactPayload = { const payload: AncientArtifactPayload = {
displayId: artifactForm.value.displayId,
name: artifactNameForSave(), name: artifactNameForSave(),
details: artifactForm.value.details, details: artifactForm.value.details,
translations: artifactForm.value.translations, translations: artifactForm.value.translations,
@@ -190,11 +187,6 @@ onMounted(() => {
required 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 <TranslationFields
id-prefix="artifact-details" id-prefix="artifact-details"
v-model:base-value="artifactForm.details" v-model:base-value="artifactForm.details"

View File

@@ -126,7 +126,7 @@ watch(artifactQuery, loadArtifacts);
<EntityCard <EntityCard
v-for="artifact in artifacts" v-for="artifact in artifacts"
:key="artifact.id" :key="artifact.id"
:title="`#${artifact.displayId} ${artifact.name}`" :title="artifact.name"
:subtitle="artifact.category.name" :subtitle="artifact.category.name"
:to="`/ancient-artifacts/${artifact.id}`" :to="`/ancient-artifacts/${artifact.id}`"
:icon="iconArtifact" :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 dishModalTitle = computed(() => (dishForm.value.id ? t('pages.dish.editDish') : t('pages.dish.newDish')));
const itemSelectOptions = computed<TagsSelectOption[]>(() => 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 optionalItemSelectOptions = computed<TagsSelectOption[]>(() => [{ id: '', name: t('common.none') }, ...itemSelectOptions.value]);
const categorySelectOptions = computed<TagsSelectOption[]>(() => categories.value.map((category) => ({ id: category.id, name: category.name }))); const categorySelectOptions = computed<TagsSelectOption[]>(() => categories.value.map((category) => ({ id: category.id, name: category.name })));
@@ -373,7 +373,7 @@ onMounted(loadPage);
<div> <div>
<dt>{{ t('pages.dish.cookware') }}</dt> <dt>{{ t('pages.dish.cookware') }}</dt>
<dd> <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> </dd>
</div> </div>
<div> <div>
@@ -383,7 +383,7 @@ onMounted(loadPage);
<div> <div>
<dt>{{ t('pages.dish.mainMaterial') }}</dt> <dt>{{ t('pages.dish.mainMaterial') }}</dt>
<dd> <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> </dd>
</div> </div>
</dl> </dl>
@@ -411,7 +411,7 @@ onMounted(loadPage);
<Icon v-else :icon="iconItem" class="entity-card__icon" aria-hidden="true" /> <Icon v-else :icon="iconItem" class="entity-card__icon" aria-hidden="true" />
</RouterLink> </RouterLink>
<div class="dish-card__content"> <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"> <div class="dish-card__meta">
<span>{{ dish.flavor.name }}</span> <span>{{ dish.flavor.name }}</span>
<span v-if="dish.pokemonSkill">{{ dish.pokemonSkill.name }}</span> <span v-if="dish.pokemonSkill">{{ dish.pokemonSkill.name }}</span>

View File

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

View File

@@ -36,7 +36,6 @@ const busy = ref(false);
const message = ref(''); const message = ref('');
const creatingSelect = ref(''); const creatingSelect = ref('');
const itemForm = ref({ const itemForm = ref({
displayId: 1,
name: '', name: '',
details: '', details: '',
translations: {} as TranslationMap, translations: {} as TranslationMap,
@@ -117,7 +116,6 @@ async function loadEditor() {
if (isEditing.value) { if (isEditing.value) {
const item = await api.itemDetail(routeId.value); const item = await api.itemDetail(routeId.value);
itemForm.value = { itemForm.value = {
displayId: item.displayId,
name: item.baseName ?? item.name, name: item.baseName ?? item.name,
details: item.baseDetails ?? item.details, details: item.baseDetails ?? item.details,
translations: item.translations ?? {}, translations: item.translations ?? {},
@@ -173,7 +171,6 @@ async function saveItem() {
try { try {
const payload: ItemPayload = { const payload: ItemPayload = {
displayId: itemForm.value.displayId,
name: itemNameForSave(), name: itemNameForSave(),
details: itemForm.value.details, details: itemForm.value.details,
translations: itemForm.value.translations, translations: itemForm.value.translations,
@@ -226,11 +223,6 @@ onMounted(() => {
required 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 <TranslationFields
id-prefix="item-details" id-prefix="item-details"
v-model:base-value="itemForm.details" v-model:base-value="itemForm.details"

View File

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

View File

@@ -114,7 +114,7 @@ watch(
</div> </div>
</section> </section>
<section v-else class="page-stack"> <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 #kicker>{{ t('pages.recipes.detailKicker') }}</template>
<template #actions> <template #actions>
<RouterLink v-if="canUpdateRecipe" class="ui-button ui-button--primary ui-button--small" :to="`/recipes/${recipe.id}/edit`"> <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" /> <Icon :icon="iconRecipe" class="entity-card__icon" aria-hidden="true" />
</span> </span>
</RouterLink> </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> </div>
</section> </section>

View File

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

View File

@@ -245,7 +245,7 @@ export const systemWordingMessages = {
}, },
eventItems: { eventItems: {
title: 'Event Items', title: 'Event Items',
description: 'Browse limited event items with their own Display IDs and shared item categories.' description: 'Browse limited event items with shared item categories and custom ordering.'
}, },
ancientArtifacts: { ancientArtifacts: {
title: 'Ancient Artifacts', title: 'Ancient Artifacts',
@@ -687,7 +687,6 @@ export const systemWordingMessages = {
loadingList: 'Loading item list', loadingList: 'Loading item list',
loadingDetail: 'Loading item detail', loadingDetail: 'Loading item detail',
loadingEdit: 'Loading item editor', loadingEdit: 'Loading item editor',
displayId: 'Display ID',
description: 'Description', description: 'Description',
category: 'Category', category: 'Category',
usage: 'Usage', usage: 'Usage',
@@ -724,14 +723,13 @@ export const systemWordingMessages = {
detailKicker: 'Ancient Artifact Detail', detailKicker: 'Ancient Artifact Detail',
detailSubtitle: 'Ancient Artifact detail', detailSubtitle: 'Ancient Artifact detail',
editKicker: 'Ancient Artifact Edit', editKicker: 'Ancient Artifact Edit',
editSubtitle: 'Maintain Ancient Artifact Display ID, image, description, category, tags, and translations.', editSubtitle: 'Maintain Ancient Artifact image, description, category, tags, and translations.',
newTitle: 'New Ancient Artifact', newTitle: 'New Ancient Artifact',
editTitle: 'Edit {name}', editTitle: 'Edit {name}',
fallbackName: 'Ancient Artifact', fallbackName: 'Ancient Artifact',
loadingList: 'Loading Ancient Artifact list', loadingList: 'Loading Ancient Artifact list',
loadingDetail: 'Loading Ancient Artifact detail', loadingDetail: 'Loading Ancient Artifact detail',
loadingEdit: 'Loading Ancient Artifact editor', loadingEdit: 'Loading Ancient Artifact editor',
displayId: 'Display ID',
description: 'Description', description: 'Description',
category: 'Category', category: 'Category',
tags: 'Tags', tags: 'Tags',
@@ -1311,11 +1309,9 @@ export const systemWordingMessages = {
environmentRequired: 'Ideal Habitat is required', environmentRequired: 'Ideal Habitat is required',
skillNoDrop: 'This speciality cannot have a drop item', skillNoDrop: 'This speciality cannot have a drop item',
habitatNameRequired: 'Habitat name is required', habitatNameRequired: 'Habitat name is required',
itemDisplayIdRequired: 'Item Display ID is required',
usageRequired: 'Usage is required', usageRequired: 'Usage is required',
itemNameRequired: 'Item name is required', itemNameRequired: 'Item name is required',
categoryRequired: 'Category is required', categoryRequired: 'Category is required',
artifactDisplayIdRequired: 'Ancient Artifact Display ID is required',
artifactNameRequired: 'Ancient Artifact name is required', artifactNameRequired: 'Ancient Artifact name is required',
recipeFreeWithRecipe: 'An item with a recipe cannot be marked as recipe-free', recipeFreeWithRecipe: 'An item with a recipe cannot be marked as recipe-free',
itemRequired: 'Item is required', itemRequired: 'Item is required',
@@ -1600,7 +1596,7 @@ export const systemWordingMessages = {
}, },
eventItems: { eventItems: {
title: 'Event Items', title: 'Event Items',
description: '浏览限时活动物品,并维护独立的 Display ID 与共享分类。' description: '浏览限时活动物品、共享分类与自定义排序。'
}, },
ancientArtifacts: { ancientArtifacts: {
title: 'Ancient Artifacts', title: 'Ancient Artifacts',
@@ -2022,7 +2018,6 @@ export const systemWordingMessages = {
loadingList: '正在加载列表', loadingList: '正在加载列表',
loadingDetail: '正在加载物品详情', loadingDetail: '正在加载物品详情',
loadingEdit: '正在加载物品编辑内容', loadingEdit: '正在加载物品编辑内容',
displayId: 'Display ID',
description: '介绍', description: '介绍',
category: '分类', category: '分类',
usage: '用途', usage: '用途',
@@ -2059,14 +2054,13 @@ export const systemWordingMessages = {
detailKicker: 'Ancient Artifact Detail', detailKicker: 'Ancient Artifact Detail',
detailSubtitle: 'Ancient Artifact 详情', detailSubtitle: 'Ancient Artifact 详情',
editKicker: 'Ancient Artifact Edit', editKicker: 'Ancient Artifact Edit',
editSubtitle: '维护 Ancient Artifact Display ID、图片、介绍、分类、标签和翻译。', editSubtitle: '维护 Ancient Artifact 图片、介绍、分类、标签和翻译。',
newTitle: '新增 Ancient Artifact', newTitle: '新增 Ancient Artifact',
editTitle: '编辑 {name}', editTitle: '编辑 {name}',
fallbackName: 'Ancient Artifact', fallbackName: 'Ancient Artifact',
loadingList: '正在加载 Ancient Artifact 列表', loadingList: '正在加载 Ancient Artifact 列表',
loadingDetail: '正在加载 Ancient Artifact 详情', loadingDetail: '正在加载 Ancient Artifact 详情',
loadingEdit: '正在加载 Ancient Artifact 编辑内容', loadingEdit: '正在加载 Ancient Artifact 编辑内容',
displayId: 'Display ID',
description: '介绍', description: '介绍',
category: '分类', category: '分类',
tags: '标签', tags: '标签',
@@ -2644,14 +2638,12 @@ export const systemWordingMessages = {
heightNonNegative: '身高必须是不小于 0 的数字', heightNonNegative: '身高必须是不小于 0 的数字',
weightNonNegative: '体重必须是不小于 0 的数字', weightNonNegative: '体重必须是不小于 0 的数字',
environmentRequired: '请选择喜欢的环境', environmentRequired: '请选择喜欢的环境',
skillNoDrop: '这个特长不能设置掉落物', skillNoDrop: '这个特长不能设置掉落物',
habitatNameRequired: '请输入栖息地名称', habitatNameRequired: '请输入栖息地名称',
itemDisplayIdRequired: '请输入物品 Display ID', usageRequired: '请选择用途',
usageRequired: '请选择用途', itemNameRequired: '请输入物品名称',
itemNameRequired: '请输入物品名称', categoryRequired: '请选择分类',
categoryRequired: '请选择分类', artifactNameRequired: '请输入 Ancient Artifact 名称',
artifactDisplayIdRequired: '请输入 Ancient Artifact Display ID',
artifactNameRequired: '请输入 Ancient Artifact 名称',
recipeFreeWithRecipe: '已有材料单的物品不能标记为无材料单', recipeFreeWithRecipe: '已有材料单的物品不能标记为无材料单',
itemRequired: '请选择物品', itemRequired: '请选择物品',
recipeFreeItem: '这个物品已标记为无材料单', recipeFreeItem: '这个物品已标记为无材料单',