feat(stocktake): 欢迎页识别跨域角色,完善场景 A 提示
- 公司选择器原先只对 SUPER_ADMIN 解锁,漏掉了同样持有 crossDomain
权限的 WAREHOUSE_MGR —— 它同样不受后端公司隔离约束,盘点范围完全
由所选公司决定,必须一并要求显式选择。
注意 SUPER_ADMIN 的权限是通配符 ['*'],permissions.includes('crossDomain')
对其恒为 false,故角色与权限码两个条件都要判。
- 场景 A 提示改为「当前公司已有盘点在进行中,已扫 XXX 件,发起人:XXX」,
使用后端新增的 scanned / initiator 字段(scanned 已排除系统自动漏盘记录)。
按钮文案统一为「加入当前盘点」。
- 清理 serverDraftCount:提示语改用 activeSession.scanned 后它只写不读,
属死状态,一并移除。
- 确认弹窗文案原先指向已不存在的「继续上次盘点」入口,改为说明开新会话后
「加入当前盘点」只会进入最新会话。
This commit is contained in:
@ -16,7 +16,7 @@
|
||||
placeholder="请选择盘点公司"
|
||||
size="large"
|
||||
class="company-select"
|
||||
:disabled="!isSuperAdmin"
|
||||
:disabled="!hasCrossDomain"
|
||||
:loading="companyLoading"
|
||||
@change="handleCompanyChange"
|
||||
>
|
||||
@ -55,10 +55,10 @@
|
||||
@click="joinActiveSession"
|
||||
:loading="btnLoading"
|
||||
>
|
||||
加入正在进行的盘点
|
||||
加入当前盘点
|
||||
</el-button>
|
||||
<div class="active-session-tip">
|
||||
当前已有未完成的盘点进度({{ serverDraftCount }} 项)
|
||||
当前公司已有盘点在进行中,已扫 {{ activeSession.scanned }} 件<template v-if="activeSession.initiator">,发起人:{{ activeSession.initiator }}</template>
|
||||
</div>
|
||||
<!-- 次要入口:缺了它,本司一旦有过草稿就永远开不了新一轮 -->
|
||||
<el-button
|
||||
@ -187,7 +187,7 @@
|
||||
>
|
||||
<div class="confirm-content">
|
||||
<el-icon :size="48" color="#E6A23C"><WarningFilled /></el-icon>
|
||||
<p class="confirm-text">存在未完成的盘点记录。开启新一轮盘点会生成新的会话,<strong>原有数据将保留</strong>,之后仍可通过「继续上次盘点」回到之前的会话。确定开始吗?</p>
|
||||
<p class="confirm-text">当前公司已有盘点在进行中。开启新一轮会生成新的会话,<strong>原有数据将保留</strong>,但之后「加入当前盘点」只会进入最新的这个会话。确定开始吗?</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="cancelConfirm">取消</el-button>
|
||||
@ -509,7 +509,6 @@ const loading = ref(false)
|
||||
const btnLoading = ref(false)
|
||||
const printing = ref(false)
|
||||
const isSessionActive = ref(false)
|
||||
const serverDraftCount = ref(0)
|
||||
const syncStatus = ref<'success' | 'syncing' | 'failed'>('success')
|
||||
|
||||
// 新增:扫码相关状态
|
||||
@ -562,17 +561,30 @@ const COMPANY_STORAGE_KEY = 'stocktake_company'
|
||||
const selectedCompany = ref<string>('')
|
||||
const companyOptions = ref<string[]>([])
|
||||
const companyLoading = ref(false)
|
||||
// 该公司的活跃会话:{ session_id, total, last_scan_time };无进行中的盘点则为 null
|
||||
const activeSession = ref<{ session_id: string; total: number; last_scan_time: string | null } | null>(null)
|
||||
// 该公司的活跃会话;无进行中的盘点则为 null
|
||||
const activeSession = ref<{
|
||||
session_id: string
|
||||
total: number // 该会话草稿行数
|
||||
scanned: number // 实际扫到的件数(按物料去重,不含系统自动漏盘)
|
||||
last_scan_time: string | null
|
||||
initiator: string | null
|
||||
} | null>(null)
|
||||
// ★ 探测失败标记:网络抖动时绝不能把「没查到」当成「没有盘点」——
|
||||
// 否则界面会退回「开始新盘点」,库管一点就新建会话,把多人协同拆开
|
||||
const probeFailed = ref(false)
|
||||
|
||||
const isSuperAdmin = computed(() => userStore.role === 'SUPER_ADMIN')
|
||||
// ★ 跨域角色:超管,或持有 crossDomain 权限的角色(当前为 WAREHOUSE_MGR)。
|
||||
// 后端 get_current_company_filter() 对它们返回 None(不隔离),所以盘点范围
|
||||
// 完全取决于用户选哪个公司 —— 这类用户必须显式选择,不能有默认值。
|
||||
// 注意:SUPER_ADMIN 的权限是通配符 ['*'],permissions.includes('crossDomain')
|
||||
// 对它恒为 false,因此角色与权限码两个条件都要判。
|
||||
const hasCrossDomain = computed(() =>
|
||||
userStore.role === 'SUPER_ADMIN' || userStore.permissions.includes('crossDomain')
|
||||
)
|
||||
|
||||
// 选择器下方的说明文字
|
||||
const companyHint = computed(() => {
|
||||
if (isSuperAdmin.value) {
|
||||
if (hasCrossDomain.value) {
|
||||
return selectedCompany.value ? `当前盘点范围:${selectedCompany.value}` : '请先选择要盘点的公司'
|
||||
}
|
||||
if (!selectedCompany.value) return '您的账号未绑定公司,请联系管理员'
|
||||
@ -658,7 +670,7 @@ const api = {
|
||||
method: 'get',
|
||||
params: withCompany({ session_id: sessionId })
|
||||
}),
|
||||
// ★ 该公司最近一次活跃的盘点会话(多人协同:用于「加入正在进行的盘点」)
|
||||
// ★ 该公司最近一次活跃的盘点会话(多人协同:用于「加入当前盘点」)
|
||||
activeSession: () => request({
|
||||
url: '/v1/inbound/stock/draft/active-session',
|
||||
method: 'get',
|
||||
@ -723,8 +735,8 @@ onMounted(async () => {
|
||||
const initEntry = async () => {
|
||||
await loadCompanies()
|
||||
|
||||
if (!isSuperAdmin.value) {
|
||||
// 普通用户锁定本公司(后端也会强制隔离,这里只是让选择器显示正确)
|
||||
if (!hasCrossDomain.value) {
|
||||
// 非跨域用户:后端会强制隔离到本公司,这里只是让选择器显示正确
|
||||
const myCompany = userStore.companyName
|
||||
if (myCompany) {
|
||||
// 本公司可能尚无物料、不在列表中,补进去否则 el-select 显示为空
|
||||
@ -732,7 +744,7 @@ const initEntry = async () => {
|
||||
persistCompany(myCompany)
|
||||
}
|
||||
} else {
|
||||
// 超管沿用上次选择;必须显式选一个公司,否则无法确定盘点范围
|
||||
// 跨域角色沿用上次选择;必须显式选一个公司,否则盘点范围无从确定
|
||||
const saved = localStorage.getItem(COMPANY_STORAGE_KEY)
|
||||
if (saved && companyOptions.value.includes(saved)) persistCompany(saved)
|
||||
}
|
||||
@ -757,27 +769,30 @@ const loadCompanies = async () => {
|
||||
const handleCompanyChange = async (val: string) => {
|
||||
clearPersistedSession()
|
||||
activeSession.value = null
|
||||
serverDraftCount.value = 0
|
||||
probeFailed.value = false
|
||||
persistCompany(val)
|
||||
await checkServerDraft()
|
||||
}
|
||||
|
||||
// ★ 探测该公司是否有进行中的盘点 —— 服务端是唯一事实来源,
|
||||
// 这样第二台 PDA 选同一公司后就能看到「加入正在进行的盘点」。
|
||||
// 这样第二台 PDA 选同一公司后就能看到「加入当前盘点」。
|
||||
const checkServerDraft = async () => {
|
||||
if (!selectedCompany.value) {
|
||||
activeSession.value = null
|
||||
serverDraftCount.value = 0
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res: any = await api.activeSession()
|
||||
const data = res?.data || {}
|
||||
activeSession.value = data.session_id
|
||||
? { session_id: data.session_id, total: data.total || 0, last_scan_time: data.last_scan_time || null }
|
||||
? {
|
||||
session_id: data.session_id,
|
||||
total: data.total || 0,
|
||||
scanned: data.scanned || 0,
|
||||
last_scan_time: data.last_scan_time || null,
|
||||
initiator: data.initiator || null
|
||||
}
|
||||
: null
|
||||
serverDraftCount.value = data.total || 0
|
||||
probeFailed.value = false
|
||||
} catch (e) {
|
||||
// ★ 探测失败 ≠ 没有盘点。保持 activeSession 不变并把界面切到「重试」,
|
||||
@ -845,7 +860,7 @@ const joinActiveSession = async () => {
|
||||
btnLoading.value = true
|
||||
try {
|
||||
persistSession(activeSession.value.session_id)
|
||||
await enterScanning('已加入正在进行的盘点')
|
||||
await enterScanning('已加入当前盘点')
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || '加入失败')
|
||||
} finally { btnLoading.value = false }
|
||||
|
||||
Reference in New Issue
Block a user