This commit is contained in:
2026-09-22 13:51:58 +08:00
parent aefcd2565f
commit fed257a720
8 changed files with 174 additions and 45 deletions
+3
View File
@@ -267,6 +267,7 @@ export interface PlatformManagedUser {
lastLoginAt: string | null
protectedAccount: boolean
overseasWarehouseId: number | string | null
domesticWarehouseId?: number | string | null
}
export interface PlatformManagedUserPage {
@@ -309,6 +310,7 @@ export interface CreatePlatformManagedUserRequest {
tenantId: number | string
roleIds: Array<number | string>
overseasWarehouseId: number | string | null
domesticWarehouseId?: number | string | null
password: string
status: 'ACTIVE' | 'DISABLED'
}
@@ -320,6 +322,7 @@ export interface UpdatePlatformManagedUserRequest {
tenantId: number | string
roleIds: Array<number | string>
overseasWarehouseId: number | string | null
domesticWarehouseId?: number | string | null
}
async function getData<T>(path: string): Promise<T> {
@@ -29,6 +29,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useAuthStore } from '@/core/auth/auth'
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
const access = useAccessPreviewStore()
const auth = useAuthStore()
@@ -37,6 +38,9 @@ const options = ref<PlatformUserOptions>({ tenants: [], roles: [] })
const overseasWarehouses = ref<SharedReceivingOverseasWarehouseOption[]>([])
const overseasWarehousesLoading = ref(false)
const overseasWarehousesError = ref<ApiErrorDetails | null>(null)
const domesticWarehouses = ref<PlatformWarehouseSummaryResource[]>([])
const domesticWarehousesLoading = ref(false)
const domesticWarehousesError = ref<ApiErrorDetails | null>(null)
const loading = ref(false)
const saving = ref(false)
const loadError = ref<ApiErrorDetails | null>(null)
@@ -81,6 +85,7 @@ const emptyForm = () => ({
tenantId: '0',
roleIds: [] as string[],
overseasWarehouseId: '',
domesticWarehouseId: '',
password: '',
confirmPassword: '',
status: 'ACTIVE' as 'ACTIVE' | 'DISABLED',
@@ -111,6 +116,10 @@ const overseasPdaRoleIds = computed(() => new Set(
availableRoles.value.filter((role) => role.roleCode === 'OVERSEAS_WAREHOUSE_PDA_OPERATOR').map((role) => String(role.id)),
))
const hasOverseasPdaRole = computed(() => form.roleIds.some((roleId) => overseasPdaRoleIds.value.has(roleId)))
const domesticPdaRoleIds = computed(() => new Set(
availableRoles.value.filter((role) => role.roleCode === 'CN_FIRSTLEG_PDA_OPERATOR').map((role) => String(role.id)),
))
const hasDomesticPdaRole = computed(() => form.roleIds.some((roleId) => domesticPdaRoleIds.value.has(roleId)))
const formRules = computed<FormRules>(() => ({
username: editingUser.value ? [] : [
@@ -127,6 +136,9 @@ const formRules = computed<FormRules>(() => ({
overseasWarehouseId: !hasOverseasPdaRole.value || (editingUser.value && !canEdit.value) ? [] : [
{ required: true, message: '请选择实际海外收货仓', trigger: 'change' },
],
domesticWarehouseId: !hasDomesticPdaRole.value || (editingUser.value && !canEdit.value) ? [] : [
{ required: true, message: '请选择国内作业仓', trigger: 'change' },
],
password: editingUser.value && !form.resetPassword ? [] : [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ pattern: /^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, message: '密码为 8-64 位,且必须包含字母和数字', trigger: 'blur' },
@@ -217,6 +229,7 @@ function openEdit(row: PlatformManagedUser) {
tenantId: '0',
roleIds: row.roleIds.map(String),
overseasWarehouseId: row.overseasWarehouseId == null ? '' : String(row.overseasWarehouseId),
domesticWarehouseId: row.domesticWarehouseId == null ? '' : String(row.domesticWarehouseId),
password: '',
confirmPassword: '',
status: row.status,
@@ -243,6 +256,19 @@ async function loadOverseasWarehouses() {
}
}
async function loadDomesticWarehouses() {
domesticWarehousesLoading.value = true
domesticWarehousesError.value = null
try {
domesticWarehouses.value = await listAllPlatformWarehouses({ warehouseType: 'DOMESTIC', status: 'ACTIVE' })
} catch (error) {
domesticWarehouses.value = []
domesticWarehousesError.value = getApiErrorDetails(error, '国内作业仓加载失败,请重试')
} finally {
domesticWarehousesLoading.value = false
}
}
function isCurrentUser(row: PlatformManagedUser | null) {
if (!row) return false
return String(row.userId) === auth.user?.id || String(row.authUserId) === auth.user?.id
@@ -266,6 +292,7 @@ async function saveUser() {
tenantId: '0',
roleIds: form.roleIds,
overseasWarehouseId: hasOverseasPdaRole.value ? form.overseasWarehouseId : null,
...(hasDomesticPdaRole.value ? { domesticWarehouseId: form.domesticWarehouseId } : {}),
password: form.password,
status: form.status,
})
@@ -280,6 +307,7 @@ async function saveUser() {
tenantId: '0',
roleIds: form.roleIds,
overseasWarehouseId: hasOverseasPdaRole.value ? form.overseasWarehouseId : null,
...(hasDomesticPdaRole.value ? { domesticWarehouseId: form.domesticWarehouseId } : {}),
})
}
if (canChangeStatus.value && form.status !== row.status) {
@@ -340,6 +368,14 @@ watch(hasOverseasPdaRole, (selected) => {
}
void loadOverseasWarehouses()
})
watch(hasDomesticPdaRole, (selected) => {
if (!selected) {
form.domesticWarehouseId = ''
domesticWarehousesError.value = null
return
}
void loadDomesticWarehouses()
})
onMounted(loadInitialData)
</script>
@@ -435,6 +471,20 @@ onMounted(loadInitialData)
</p>
<p v-else class="field-help">仓库将写入登录 Token,海外 PDA 不提供仓库切换。</p>
</el-form-item>
<el-form-item v-if="hasDomesticPdaRole" label="国内作业仓" prop="domesticWarehouseId" required>
<el-select v-model="form.domesticWarehouseId" filterable
:loading="domesticWarehousesLoading" :disabled="Boolean(editingUser) && !canEdit"
placeholder="请选择国内作业仓">
<el-option v-for="warehouse in domesticWarehouses" :key="String(warehouse.id)"
:label="`${warehouse.warehouseName}(${warehouse.warehouseCode})`"
:value="String(warehouse.id)" />
</el-select>
<p v-if="domesticWarehousesError" class="field-error">
{{ domesticWarehousesError.message }}
<el-button link type="primary" :loading="domesticWarehousesLoading" @click="loadDomesticWarehouses">重新加载</el-button>
</p>
<p v-else class="field-help">该人员只会出现在此仓库的国内头程 PDA 任务候选中。</p>
</el-form-item>
<el-form-item v-if="editingUser" label="最后登录">
<el-input :model-value="formatLogin(editingUser.lastLoginAt)" disabled />
</el-form-item>