task-95(店铺中心): 实现店铺删除确认
新增 shop-manage-delete.ts(删除确认文案 + 删除后目标页回退) 并在
shop-manage-api.ts 增加 deleteShopManage(DELETE /api/admin/shop-manages/{id})。
TDD: task-95.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
@@ -73,3 +73,8 @@ export async function fetchShopCredential(id: number, shopName: string): Promise
|
|||||||
}
|
}
|
||||||
return credential
|
return credential
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除店铺:DELETE /api/admin/shop-manages/{id};成功后由调用方按目标页刷新。 */
|
||||||
|
export async function deleteShopManage(id: number): Promise<void> {
|
||||||
|
await http.delete<unknown>(`${SHOP_MANAGE_ENDPOINT}/${id}`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/** 店铺删除确认与刷新(任务 95):删除前确认文案与删除后目标页计算,纯逻辑。 */
|
||||||
|
import { SHOP_PAGE_DEFAULT_SIZE, type ShopSummary } from './shop-dto.ts'
|
||||||
|
|
||||||
|
function nameOrFallback(item: ShopSummary | null | undefined): string {
|
||||||
|
if (!item || typeof item !== 'object') return ''
|
||||||
|
if (typeof item.shopName === 'string' && item.shopName.trim()) {
|
||||||
|
return item.shopName.trim()
|
||||||
|
}
|
||||||
|
if (typeof item.id === 'number' && Number.isFinite(item.id) && item.id >= 1) {
|
||||||
|
return `#${Math.floor(item.id)}`
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认文案用到的店铺名(为空时回退 #id)。 */
|
||||||
|
export function shopManageDeleteTargetName(item: ShopSummary | null | undefined): string {
|
||||||
|
return nameOrFallback(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除确认文案;空记录返回 null。 */
|
||||||
|
export function shopManageDeleteConfirmText(item: ShopSummary | null | undefined): string | null {
|
||||||
|
const name = nameOrFallback(item)
|
||||||
|
return name ? `确定删除店铺“${name}”吗?` : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function sizeOr(size: unknown, fallback: number): number {
|
||||||
|
return typeof size === 'number' && Number.isFinite(size) && size >= 1 ? Math.floor(size) : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除一行后应请求的目标页:若被删的是末页最后一条则回退,否则保持原页。 */
|
||||||
|
export function nextShopManagePageAfterDelete(page: unknown, pageSize: unknown, totalBefore: unknown): number {
|
||||||
|
const size = sizeOr(pageSize, SHOP_PAGE_DEFAULT_SIZE)
|
||||||
|
const before = typeof totalBefore === 'number' && Number.isFinite(totalBefore) ? Math.max(Math.floor(totalBefore), 0) : 0
|
||||||
|
const after = Math.max(before - 1, 0)
|
||||||
|
const pagesAfter = after === 0 ? 1 : Math.max(1, Math.ceil(after / size))
|
||||||
|
const requested = typeof page === 'number' && Number.isFinite(page) ? Math.floor(page) : 1
|
||||||
|
return Math.max(1, Math.min(requested, pagesAfter))
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import test from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { readSource } from './helpers.ts'
|
||||||
|
import {
|
||||||
|
nextShopManagePageAfterDelete,
|
||||||
|
shopManageDeleteConfirmText,
|
||||||
|
shopManageDeleteTargetName,
|
||||||
|
} from '../src/pages/shop/shop-manage-delete.ts'
|
||||||
|
import type { ShopSummary } from '../src/pages/shop/shop-dto.ts'
|
||||||
|
|
||||||
|
function row(id: number, shopName = ''): ShopSummary {
|
||||||
|
return { id, groupId: 1, groupName: '', shopName, mallName: '', znUsername: '', account: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_normal_primary_path', () => {
|
||||||
|
// 正常主路径:删除确认以店铺名落文案,随成功删除刷新到有效页。
|
||||||
|
assert.equal(shopManageDeleteTargetName(row(1, 'BlueWave')), 'BlueWave')
|
||||||
|
assert.equal(shopManageDeleteConfirmText(row(1, 'BlueWave')), '确定删除店铺“BlueWave”吗?')
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(2, 15, 31), 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_normal_variant_input', () => {
|
||||||
|
// 正常变体:店铺名为空用 #id 兜底;末页删除后回退一页。
|
||||||
|
assert.equal(shopManageDeleteTargetName(row(12)), '#12')
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(2, 15, 16), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_repeated_is_idempotent', () => {
|
||||||
|
// 正常重复:文案/翻页稳定、不改入参。
|
||||||
|
assert.equal(shopManageDeleteConfirmText(row(2, 'A店')), shopManageDeleteConfirmText(row(2, 'A店')))
|
||||||
|
const input = { page: 3, pageSize: 15, totalBefore: 40 }
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(input.page, input.pageSize, input.totalBefore), 3)
|
||||||
|
assert.deepEqual(input, { page: 3, pageSize: 15, totalBefore: 40 })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_boundary_empty_input', () => {
|
||||||
|
// 边界空值:删除后恰为空时回到第 1 页。
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(2, 15, 0), 1)
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(1, 15, 1), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_boundary_single_item', () => {
|
||||||
|
// 边界单元素:首页单条删除后仍为第 1 页。
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(1, 15, 1), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_boundary_limit_or_missing_field', () => {
|
||||||
|
// 边界上限/缺字段:页大小无效按默认参与末页判断。
|
||||||
|
assert.equal(nextShopManagePageAfterDelete(2, 0, 16), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_invalid_input_rejected', () => {
|
||||||
|
// 异常输入:空记录不产出可误删的确认文案。
|
||||||
|
assert.equal(shopManageDeleteTargetName(null as unknown as ShopSummary), '')
|
||||||
|
assert.equal(shopManageDeleteConfirmText(null as unknown as ShopSummary), null)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_task_095_shop_manage_delete_dependency_failure_returns_actionable_message', () => {
|
||||||
|
// 依赖失败/删除走 adapter:删除模型纯逻辑;adapter DELETE /api/admin/shop-manages/{id}。
|
||||||
|
const model = readSource('src/pages/shop/shop-manage-delete.ts')
|
||||||
|
assert.equal(/axios|http\.|vue/.test(model), false, '删除模型保持纯逻辑')
|
||||||
|
const api = readSource('src/pages/shop/shop-manage-api.ts')
|
||||||
|
assert.match(api, /deleteShopManage/)
|
||||||
|
assert.match(api, /http\.delete/)
|
||||||
|
assert.match(api, /\/api\/admin\/shop-manages/)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user