task-74(ASIN 数据中心): 实现查询 ASIN 详情抽屉
抽屉副标题/5 国家草稿与变更差异(空值→删除)纯逻辑, 保存走 PUT/DELETE
/api/admin/query-asins/{id}/countries/{country}, 9 个契约测试。
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/** 查询 ASIN 详情抽屉保存适配(任务 74):PUT/DELETE /api/admin/query-asins/{id}/countries/{country}。 */
|
||||
import { http } from '@/api/http'
|
||||
|
||||
export const QUERY_ASIN_DETAIL_ENDPOINT = '/api/admin/query-asins'
|
||||
|
||||
function countryUrl(id: number, country: string): string {
|
||||
return `${QUERY_ASIN_DETAIL_ENDPOINT}/${id}/countries/${country}`
|
||||
}
|
||||
|
||||
/** 保存某国家 ASIN(body { asin });返回后由列表刷新回读最新行。 */
|
||||
export async function updateQueryAsinCountryAsin(id: number, country: string, asin: string): Promise<void> {
|
||||
await http.put<unknown>(countryUrl(id, country), { asin })
|
||||
}
|
||||
|
||||
/** 删除某国家 ASIN(清空该站点 ASIN)。 */
|
||||
export async function deleteQueryAsinCountryAsin(id: number, country: string): Promise<void> {
|
||||
await http.delete<unknown>(countryUrl(id, country))
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/** 查询 ASIN 详情抽屉模型(任务 74):副标题、5 国家草稿与变更差异(空→删除),纯逻辑。 */
|
||||
import { QUERY_ASIN_COUNTRIES, asinForCountry, type QueryAsinCountry, type QueryAsinItem } from './query-asin-model.ts'
|
||||
|
||||
export const QUERY_ASIN_COUNTRY_LABELS: Record<QueryAsinCountry, string> = {
|
||||
DE: '德国',
|
||||
UK: '英国',
|
||||
FR: '法国',
|
||||
IT: '意大利',
|
||||
ES: '西班牙',
|
||||
}
|
||||
|
||||
/** 国家码 → 中文标签;不属白名单原样返回。 */
|
||||
export function queryAsinCountryLabel(country: string): string {
|
||||
return QUERY_ASIN_COUNTRY_LABELS[country as QueryAsinCountry] || country
|
||||
}
|
||||
|
||||
/** 抽屉副标题:`分组 / 店铺名`;无分组时仅店铺名。 */
|
||||
export function queryAsinDetailSubtitle(item: Pick<QueryAsinItem, 'groupId' | 'groupName' | 'shopName'>): string {
|
||||
const group = (item.groupName || '').trim()
|
||||
const shop = (item.shopName || '').trim()
|
||||
return group ? `${group} / ${shop}` : shop
|
||||
}
|
||||
|
||||
/** 抽屉单国家输入草稿(编辑前的初值)。 */
|
||||
export interface QueryAsinCountryDraft {
|
||||
code: string
|
||||
value: string
|
||||
}
|
||||
|
||||
/** 由列表行生成 5 国家草稿(当前值 trimmed,保持支持国家序)。 */
|
||||
export function queryAsinCountryDraftsOf(item: QueryAsinItem): QueryAsinCountryDraft[] {
|
||||
return QUERY_ASIN_COUNTRIES.map((code) => ({
|
||||
code,
|
||||
value: asinForCountry(item, code).trim(),
|
||||
}))
|
||||
}
|
||||
|
||||
export type QueryAsinCountryChangeKind = 'update' | 'delete'
|
||||
|
||||
export interface QueryAsinCountryChange {
|
||||
code: string
|
||||
label: string
|
||||
kind: QueryAsinCountryChangeKind
|
||||
asin?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 与当前行比较草稿产出变更:
|
||||
* - 归一(trim + 大写)后与当前相同 → 跳过;
|
||||
* - 草稿留空(当前有值) → delete;
|
||||
* - 新值非空 → update。
|
||||
* 未知国家码或缺失草稿不产生变更,避免误删;结果按支持国家序稳定输出。
|
||||
*/
|
||||
export function diffQueryAsinCountryChanges(
|
||||
item: QueryAsinItem,
|
||||
drafts: QueryAsinCountryDraft[],
|
||||
): QueryAsinCountryChange[] {
|
||||
const byCode = new Map<string, string>()
|
||||
for (const draft of drafts || []) {
|
||||
if (!draft || typeof draft.code !== 'string') continue
|
||||
byCode.set(draft.code.trim().toUpperCase(), typeof draft.value === 'string' ? draft.value : '')
|
||||
}
|
||||
const changes: QueryAsinCountryChange[] = []
|
||||
for (const code of QUERY_ASIN_COUNTRIES) {
|
||||
if (!byCode.has(code)) continue
|
||||
const next = (byCode.get(code) || '').trim().toUpperCase()
|
||||
const current = asinForCountry(item, code).trim().toUpperCase()
|
||||
if (next === current) continue
|
||||
if (!next) {
|
||||
changes.push({ code, label: queryAsinCountryLabel(code), kind: 'delete' })
|
||||
} else {
|
||||
changes.push({ code, label: queryAsinCountryLabel(code), kind: 'update', asin: next })
|
||||
}
|
||||
}
|
||||
return changes
|
||||
}
|
||||
Reference in New Issue
Block a user