feat(overseas-pda): prefer business identifiers
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<template><view v-if="user" class="page-shell portal-page"><PortalHeader title="作业记录" :user="user" /><view class="portal-content"><view class="summary-card"><view><text class="summary-number">0</text><text class="summary-label">今日完成</text></view><view><text class="summary-number pending-color">0</text><text class="summary-label">进行中</text></view></view><text class="group-title">最近作业</text><view class="history-empty"><text class="history-empty-icon">▤</text><text class="history-empty-title">暂无海外仓作业记录</text><text class="history-empty-desc">接入海外仓真实任务后,完成记录会显示在这里</text></view></view></view></template>
|
||||
<template><view v-if="user" class="page-shell portal-page"><PortalHeader title="作业记录" :user="user" /><view class="portal-content"><view class="summary-card"><view><text class="summary-number">0</text><text class="summary-label">今日完成</text></view><view><text class="summary-number pending-color">0</text><text class="summary-label">进行中</text></view></view><text class="group-title">最近作业</text><view class="history-empty"><text class="history-empty-icon">▤</text><text class="history-empty-title">暂无海外仓作业记录</text><text class="history-empty-desc">当前暂无可查看的作业记录</text></view></view></view></template>
|
||||
<script>
|
||||
import PortalHeader from '@/components/PortalHeader.vue'; import { session, requireSession } from '@/stores/session.js'
|
||||
export default { components: { PortalHeader }, data() { return { user: session.currentUser } }, onShow() { if (!requireSession()) return; this.user = session.currentUser } }
|
||||
|
||||
@@ -47,10 +47,7 @@
|
||||
return name && !/^[a-z0-9._-]+$/i.test(name) ? name : '仓库作业员'
|
||||
},
|
||||
warehouseLabel() {
|
||||
if (this.user?.warehouseName) return this.user.warehouseCode ? `${this.user.warehouseName} · ${this.user.warehouseCode}` : this.user.warehouseName
|
||||
const warehouseId = String(this.user?.warehouseId || '')
|
||||
if (warehouseId) return `当前作业仓 · ${warehouseId.slice(-6)}`
|
||||
return this.user?.warehouse || '仓库由当前账号自动绑定'
|
||||
return String(this.user?.warehouseName || '').trim() || '海外仓未关联'
|
||||
},
|
||||
greeting() {
|
||||
const hour = new Date().getHours()
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
<template v-else>
|
||||
<view class="receiving-heading"><view><text class="receiving-title">{{ operationName }}</text><text class="receiving-subtitle">当前功能暂未开放</text></view></view>
|
||||
<view class="blocked-card"><uni-icons type="locked" color="#8090a3" size="36" /><text class="blocked-title">功能暂未开放</text><text>现有平台、客户或内部接口不能由仓库作业员直接调用。扫码不会提交,也不会产生模拟成功结果。</text></view>
|
||||
<view class="blocked-card"><uni-icons type="locked" color="#8090a3" size="36" /><text class="blocked-title">功能暂未开放</text><text>当前作业暂未开放,扫码不会提交,请返回工作台选择可用功能。</text></view>
|
||||
</template>
|
||||
</view>
|
||||
<view v-if="toast.visible" class="app-toast" :class="toast.type" role="status">{{ toast.message }}</view>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view v-if="user" class="page-shell portal-page">
|
||||
<PortalHeader title="个人中心" :user="user" :show-profile="false" />
|
||||
<view class="portal-content profile-content">
|
||||
<view class="profile-card"><view class="profile-avatar">{{ avatar }}</view><view><text class="profile-name">{{ displayName }}</text><text class="profile-role">{{ user.role }} · {{ user.id }}</text></view></view>
|
||||
<view class="profile-card"><view class="profile-avatar">{{ avatar }}</view><view><text class="profile-name">{{ displayName }}</text><text class="profile-role">{{ roleLabel }}</text></view></view>
|
||||
<view class="profile-list">
|
||||
<view class="profile-row"><text>作业仓库</text><text class="profile-value">{{ warehouseDisplay }}</text></view>
|
||||
<view class="profile-row"><text>当前班次</text><text class="profile-value">以海外仓排班为准</text></view>
|
||||
@@ -30,7 +30,8 @@
|
||||
computed: {
|
||||
displayName() { const name = String(this.user?.name || ''); return name && !/^[a-z0-9._-]+$/i.test(name) ? name : '仓库作业员' },
|
||||
avatar() { return this.displayName.slice(0, 1).toUpperCase() },
|
||||
warehouseDisplay() { return this.user?.warehouseName ? (this.user.warehouseCode ? `${this.user.warehouseName} · ${this.user.warehouseCode}` : this.user.warehouseName) : (this.user?.warehouse || '正在读取仓库主档') },
|
||||
roleLabel() { return '海外仓作业员' },
|
||||
warehouseDisplay() { return String(this.user?.warehouseName || '').trim() || '海外仓未关联' },
|
||||
languageLabel() { return LOCALE_OPTIONS.find((item) => item.value === this.locale)?.label || 'English' }
|
||||
},
|
||||
onShow() { if (!requireSession()) return; this.user = session.currentUser; this.loadAppVersion(); this.loadWarehouseProfile() },
|
||||
|
||||
@@ -74,7 +74,7 @@ function userFromClaims(pair, saved = {}) {
|
||||
name: pair.domestic.username || '仓库作业员',
|
||||
role: PDA_ROLE,
|
||||
roles: [...pair.overseas.roles],
|
||||
warehouse: sameWarehouse && saved.warehouseName ? saved.warehouseName : `海外仓 #${pair.warehouseId}`,
|
||||
warehouse: sameWarehouse && saved.warehouseName ? saved.warehouseName : '',
|
||||
warehouseId: pair.warehouseId,
|
||||
warehouseCode: sameWarehouse ? (saved.warehouseCode || '') : '',
|
||||
warehouseName: sameWarehouse ? (saved.warehouseName || '') : ''
|
||||
@@ -142,7 +142,7 @@ export function applyWarehouseProfile(profile) {
|
||||
if (!warehouseId || warehouseId !== String(session.currentUser.warehouseId)) throw new Error('仓库主档与当前登录仓库不一致')
|
||||
session.currentUser = {
|
||||
...session.currentUser,
|
||||
warehouse: profile.warehouseName || profile.warehouseCode || `海外仓 #${warehouseId}`,
|
||||
warehouse: profile.warehouseName || '',
|
||||
warehouseCode: String(profile.warehouseCode || ''),
|
||||
warehouseName: String(profile.warehouseName || '')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
|
||||
const read = (path) => readFile(new URL(path, import.meta.url), 'utf8')
|
||||
const template = (source) => source.match(/<template>([\s\S]*?)<\/template>/)?.[1] || ''
|
||||
const interpolations = (source) => [...template(source).matchAll(/\{\{([\s\S]*?)\}\}/g)].map((match) => match[1]).join('\n')
|
||||
|
||||
test('all registered user-facing pages avoid rendering internal identifiers', async () => {
|
||||
const paths = [
|
||||
'../src/pages/login/login.vue', '../src/pages/home/home.vue', '../src/pages/module/module.vue',
|
||||
'../src/pages/operation/operation.vue', '../src/pages/pallet/pallet.vue', '../src/pages/profile/profile.vue',
|
||||
'../src/pages/history/history.vue', '../src/pages/scanner-test/scanner-test.vue',
|
||||
'../src/components/PortalHeader.vue', '../src/components/PopupHeader.vue',
|
||||
'../src/components/ReceivingSessionSummary.vue', '../src/components/ScanTargetInput.vue'
|
||||
]
|
||||
const visibleExpressions = (await Promise.all(paths.map(read))).map(interpolations).join('\n')
|
||||
for (const field of [
|
||||
'orderId', 'inboundId', 'planId', 'containerBookingId', 'sourceContainerBookingId',
|
||||
'warehouseId', 'palletId', 'locationId', 'positionId', 'user.id'
|
||||
]) assert.doesNotMatch(visibleExpressions, new RegExp(field.replace('.', '\\.')))
|
||||
})
|
||||
|
||||
test('operator and warehouse cards never fall back to internal IDs', async () => {
|
||||
const [home, profile, session] = await Promise.all([
|
||||
read('../src/pages/home/home.vue'),
|
||||
read('../src/pages/profile/profile.vue'),
|
||||
read('../src/stores/session.js')
|
||||
])
|
||||
|
||||
assert.doesNotMatch(template(home), /warehouseId|warehouseCode|user\.warehouse/)
|
||||
assert.doesNotMatch(home, /warehouseId\.slice|当前作业仓\s*·/)
|
||||
assert.match(home, /warehouseName \|\| ''[\s\S]*海外仓未关联/)
|
||||
assert.doesNotMatch(template(profile), /user\.id|user\.role|warehouseId|warehouseCode|user\.warehouse/)
|
||||
assert.match(profile, /roleLabel\(\) \{ return '海外仓作业员' \}/)
|
||||
assert.match(profile, /warehouseName \|\| ''[\s\S]*海外仓未关联/)
|
||||
assert.doesNotMatch(session, /海外仓 #\$\{|warehouse:\s*profile\.warehouseCode/)
|
||||
})
|
||||
|
||||
test('operation templates expose business references only and keep diagnostic evidence', async () => {
|
||||
const [operation, pallet, summary, apiFiles] = await Promise.all([
|
||||
read('../src/pages/operation/operation.vue'),
|
||||
read('../src/pages/pallet/pallet.vue'),
|
||||
read('../src/components/ReceivingSessionSummary.vue'),
|
||||
Promise.all([
|
||||
read('../src/api/inbound.js'), read('../src/api/pallet.js'),
|
||||
read('../src/api/overseas.js'), read('../src/api/receiving-adapter.js')
|
||||
]).then((values) => values.join('\n'))
|
||||
])
|
||||
|
||||
const forbiddenDisplayFields = [
|
||||
'orderId', 'inboundId', 'planId', 'containerBookingId', 'sourceContainerBookingId',
|
||||
'warehouseId', 'palletId', 'locationId', 'positionId'
|
||||
]
|
||||
for (const field of forbiddenDisplayFields) {
|
||||
assert.doesNotMatch(template(operation), new RegExp(`\\b${field}\\b`))
|
||||
assert.doesNotMatch(template(summary), new RegExp(`\\b${field}\\b`))
|
||||
}
|
||||
for (const field of ['orderId', 'inboundId', 'planId', 'containerBookingId', 'sourceContainerBookingId', 'warehouseId', 'palletId', 'locationId']) {
|
||||
assert.doesNotMatch(template(pallet), new RegExp(`\\b${field}\\b`))
|
||||
}
|
||||
|
||||
for (const businessField of ['containerNo', 'inboundCode', 'cartonMark', 'palletCode']) {
|
||||
assert.match(operation + pallet + summary, new RegExp(businessField))
|
||||
}
|
||||
assert.match(operation, /requestId[\s\S]*traceId/)
|
||||
assert.match(pallet, /requestId[\s\S]*traceId/)
|
||||
assert.doesNotMatch(apiFiles, /\/api\/platform\/v1\/|inbound-code-relationships/)
|
||||
})
|
||||
|
||||
test('missing user-visible relationships use explicit empty labels instead of ID-shaped fallbacks', async () => {
|
||||
const [summary, pallet] = await Promise.all([
|
||||
read('../src/components/ReceivingSessionSummary.vue'),
|
||||
read('../src/pages/pallet/pallet.vue')
|
||||
])
|
||||
assert.match(summary, /overseasWarehouseName \|\| '—'/)
|
||||
assert.match(summary, /unique\.length \? unique : \['—'\]/)
|
||||
assert.match(pallet, /inboundCode \|\| '无入仓码'/)
|
||||
assert.match(pallet, /locationCode \|\| '未绑定库位'/)
|
||||
})
|
||||
@@ -51,7 +51,7 @@ test('valid platform warehouse picker tokens freeze the same operator and wareho
|
||||
const user = signInWithTokens(tokens)
|
||||
assert.deepEqual(user.roles, [PDA_ROLE])
|
||||
assert.equal(user.warehouseId, '900')
|
||||
assert.equal(user.warehouse, '海外仓 #900')
|
||||
assert.equal(user.warehouse, '')
|
||||
assert.equal(session.tenantId, '0')
|
||||
assert.equal(session.token, tokens.overseasAccessToken)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user