fix: some
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 56s

This commit is contained in:
luo
2026-10-13 17:21:46 +08:00
parent b2a76adc9a
commit c31183205d
13 changed files with 427 additions and 18 deletions

View File

@@ -226,6 +226,91 @@ test('exchange phone conflicts support HTTP error and business response without
})
})
test('exchange association handoff carries only the new asset and its bound phone as strings', async () => {
const { getExchangePhoneAssociationQuery } = await loadModule(
'src/utils/business/exchangeError.ts'
)
const newAsset = { asset_identifier: '89860624590014594318', associated_phones: ['19232713115'] }
assert.deepEqual(
getExchangePhoneAssociationQuery({ code: 1202, data: { phone: '19232713115' } }, newAsset),
{
asset_identifier: '89860624590014594318',
phone: '19232713115'
}
)
assert.deepEqual(
getExchangePhoneAssociationQuery({ response: { data: { code: 1202 } } }, newAsset),
{
asset_identifier: '89860624590014594318',
phone: '19232713115'
}
)
assert.equal(getExchangePhoneAssociationQuery({ code: 1206 }, newAsset), undefined)
assert.equal(
getExchangePhoneAssociationQuery({ code: 1202, data: { phone: '19232713115' } }, {}),
undefined
)
assert.equal(
getExchangePhoneAssociationQuery(
{ code: 1202 },
{ asset_identifier: '89860624590014594318', associated_phones: [] }
),
undefined
)
assert.deepEqual(
getExchangePhoneAssociationQuery({ code: 1202, data: { phone: '13900000000' } }, newAsset),
{
asset_identifier: '89860624590014594318',
phone: '13900000000'
}
)
})
test('association preselection excludes old assets, different phones and invalid historical rows', async () => {
const {
getPhoneAssetAssociationTarget,
getMatchingPhoneAssetAssociations,
getPhoneAssetUnbindResultName
} = await loadModule('src/utils/business/phoneAssetAssociation.ts')
const target = getPhoneAssetAssociationTarget({
asset_identifier: ' 89860624590014594318 ',
phone: '19232713115'
})
const rows = [
{ id: 1, asset_identifier: target.asset_identifier, phone: target.phone, status: 1 },
{ id: 2, asset_identifier: '89860625770070732445', phone: target.phone, status: 1 },
{ id: 3, asset_identifier: target.asset_identifier, phone: '13900000000', status: 1 },
{ id: 4, asset_identifier: target.asset_identifier, phone: target.phone, status: 0 }
]
assert.deepEqual(
getMatchingPhoneAssetAssociations(rows, target).map((row) => row.id),
[1]
)
assert.deepEqual(getMatchingPhoneAssetAssociations(rows, { ...target, phone: '' }), [])
assert.deepEqual(
getPhoneAssetAssociationTarget({ asset_identifier: ['a', 'b'], phone: ['19232713115'] }),
{ asset_identifier: '', phone: '' }
)
assert.equal(getPhoneAssetUnbindResultName(true), '解绑成功')
assert.equal(getPhoneAssetUnbindResultName(false), '解绑失败')
})
test('association page watches handoff changes, clears stale selections and retains manual unbind confirmation', async () => {
const page = await readFile(
'src/views/asset-management/phone-asset-association/index.vue',
'utf8'
)
assert.match(page, /getPhoneAssetAssociationTarget\(route.query\)/)
assert.match(page, /toggleRowSelection\(row, true\)/)
assert.match(page, /requestId !== listRequestId/)
assert.match(page, /getPhoneAssetUnbindResultName\(row.success\)/)
assert.doesNotMatch(page, /prop="success"/)
assert.match(page, /batchForm.confirmed = false/)
assert.match(page, /batchFormRef.value\?\.validate/)
const table = await readFile('src/components/core/tables/ArtTable.vue', 'utf8')
assert.match(table, /clearSelection:.*clearSelection\(\)/)
})
test('password validation errors accept backend message as well as msg', async () => {
const { normalizeApiError } = await loadModule('src/utils/business/apiError.ts')
for (const field of ['msg', 'message']) {

View File

@@ -101,11 +101,55 @@ const exchange = {
updated_at: time
}
const associations = [
{
id: 101,
asset_id: 1001,
asset_type: 'iot_card',
asset_identifier: exchange.new_asset_identifier,
phone: '13800000000',
status: 1,
status_name: '有效',
established_at: time
},
{
id: 102,
asset_id: 1001,
asset_type: 'iot_card',
asset_identifier: exchange.new_asset_identifier,
phone: '13800000000',
status: 0,
status_name: '已失效',
established_at: time
},
{
id: 103,
asset_id: 1002,
asset_type: 'iot_card',
asset_identifier: exchange.old_asset_identifier,
phone: '13800000000',
status: 1,
status_name: '有效',
established_at: time
},
{
id: 104,
asset_id: 1002,
asset_type: 'iot_card',
asset_identifier: exchange.old_asset_identifier,
phone: '13900000000',
status: 1,
status_name: '有效',
established_at: time
}
]
export const mockApiPlugin = {
name: 'october-local-mock-api',
configureServer(server) {
server.middlewares.use((req, res, next) => {
const path = new URL(req.url, 'http://127.0.0.1').pathname
const url = new URL(req.url, 'http://127.0.0.1')
const path = url.pathname
if (!path.startsWith('/api/')) return next()
if (path === '/api/auth/login' || path === '/api/auth/me') {
const user = {
@@ -134,6 +178,37 @@ export const mockApiPlugin = {
return
}
if (req.method !== 'GET') {
if (path === '/api/admin/phone-asset-associations/batch-unbind') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
const request = JSON.parse(body)
const items = request.assets.map((asset) => {
const matches = associations.filter(
(row) =>
row.asset_id === asset.asset_id &&
row.asset_type === asset.asset_type &&
row.status === 1
)
matches.forEach((row) => {
row.status = 0
row.status_name = '已失效'
})
return { ...asset, success: true, unbound_count: matches.length, reason: '' }
})
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.end(
JSON.stringify({
code: 0,
msg: 'mock',
data: { success_count: items.length, fail_count: 0, items }
})
)
})
return
}
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.end(
JSON.stringify({
@@ -153,6 +228,15 @@ export const mockApiPlugin = {
page_size: 20
})
let data = page([])
if (path === '/api/admin/phone-asset-associations')
data = page(
associations.filter(
(row) =>
(!url.searchParams.get('asset_identifier') ||
row.asset_identifier === url.searchParams.get('asset_identifier')) &&
(!url.searchParams.get('phone') || row.phone === url.searchParams.get('phone'))
)
)
if (path === '/api/admin/orders/1') data = order
if (path === '/api/admin/exchanges') data = page([exchange])
if (path === '/api/admin/exchanges/1') data = exchange