diff --git a/frontend/src/views/Users.test.js b/frontend/src/views/Users.test.js
index 1cfd8ac1..b62b10f4 100644
--- a/frontend/src/views/Users.test.js
+++ b/frontend/src/views/Users.test.js
@@ -7,7 +7,7 @@ import { organizationsApi } from '../api/organizations'
const RoleSelectStub = {
props: {
- modelValue: Array,
+ modelValue: [Array, Number],
multiple: Boolean
},
emits: ['update:modelValue'],
@@ -33,6 +33,7 @@ vi.mock('../stores/users', () => ({ useUsersStore: () => usersStore }))
vi.mock('../stores/auth', () => ({ useAuthStore: () => ({ isAdmin: true }) }))
vi.mock('../api/organizations', () => ({
organizationsApi: {
+ list: vi.fn().mockResolvedValue({ data: [{ id: 1, name: '甲公司', status: 'active' }, { id: 2, name: '乙公司', status: 'inactive' }] }),
listRoles: vi.fn()
}
}))
@@ -147,6 +148,8 @@ describe('新增用户后列表与搜索框保持一致', () => {
state.form.username = 'lisi'
state.form.password = '123456'
state.form.role_ids = [2]
+ state.form.organization_ids = [1]
+ state.form.default_organization_id = 1
await state.handleSave()
await flushPromises()
@@ -176,6 +179,86 @@ describe('新增用户后列表与搜索框保持一致', () => {
})
})
+describe('用户公司授权', () => {
+ test('编辑普通用户非空授权时必须选择默认公司', async () => {
+ organizationsApi.listRoles.mockResolvedValue({ data: [{ id: 2, code: 'warehouse_admin', enabled: true, name: '仓库管理员' }] })
+ usersStore.updateUser.mockClear()
+ const wrapper = mountUsers()
+ await flushPromises()
+ const state = wrapper.vm.$.setupState
+ state.showEditModal({ id: 8, username: 'user', roles: [{ id: 2 }], organization_ids: [1], default_organization_id: null })
+ await state.handleSave()
+ expect(usersStore.updateUser).not.toHaveBeenCalled()
+ wrapper.unmount()
+ })
+
+ test('超级管理员无需逐项授权,可以选择启用公司作为默认公司', async () => {
+ organizationsApi.listRoles.mockResolvedValue({ data: [{ id: 1, code: 'super_admin', enabled: true, name: '超级管理员' }] })
+ const wrapper = mountUsers()
+ await flushPromises()
+ const state = wrapper.vm.$.setupState
+ state.showAddModal()
+ Object.assign(state.form, { username: 'admin2', password: 'secret', role_ids: [1], default_organization_id: 1 })
+ await state.handleSave()
+ expect(state.defaultOrganizations.map(item => item.id)).toEqual([1])
+ expect(usersStore.createUser).toHaveBeenLastCalledWith(expect.objectContaining({ organization_ids: [], default_organization_id: 1 }))
+ wrapper.unmount()
+ })
+
+ test('停用授权可以移除但不能重新分配给未持有的用户', async () => {
+ const wrapper = mountUsers()
+ await flushPromises()
+ const state = wrapper.vm.$.setupState
+ state.showEditModal({ id: 8, username: 'user', roles: [{ id: 2 }], organization_ids: [2], default_organization_id: null })
+ await flushPromises()
+ const options = () => wrapper.findAllComponents(RoleOptionStub).filter(option => option.props('label') === '乙公司(已停用)')
+ expect(options()[0].props('disabled')).toBe(false)
+ state.form.organization_ids = []
+ await flushPromises()
+ expect(options()[0].props('disabled')).toBe(true)
+ wrapper.unmount()
+ })
+
+ test('编辑展示历史停用授权,默认公司只提供已选启用公司', async () => {
+ const wrapper = mountUsers()
+ await flushPromises()
+ const state = wrapper.vm.$.setupState
+ state.showEditModal({ id: 8, username: 'user', roles: [{ id: 2 }], organization_ids: [1, 2], default_organization_id: 1 })
+ await flushPromises()
+ expect(state.form.organization_ids).toEqual([1, 2])
+ expect(wrapper.text()).toContain('乙公司(已停用)')
+ expect(state.defaultOrganizations.map(item => item.id)).toEqual([1])
+ state.form.organization_ids = [2]
+ await flushPromises()
+ expect(state.form.default_organization_id).toBeNull()
+ wrapper.unmount()
+ })
+
+ test('新增普通用户缺少公司授权时禁止保存', async () => {
+ usersStore.createUser.mockClear()
+ const wrapper = mountUsers()
+ await flushPromises()
+ const state = wrapper.vm.$.setupState
+ state.showAddModal()
+ Object.assign(state.form, { username: 'new', password: 'secret', role_ids: [2] })
+ await state.handleSave()
+ expect(usersStore.createUser).not.toHaveBeenCalled()
+ wrapper.unmount()
+ })
+
+ test('显式清空授权发送空集合和空默认公司', async () => {
+ const wrapper = mountUsers()
+ await flushPromises()
+ const state = wrapper.vm.$.setupState
+ state.showEditModal({ id: 8, username: 'user', roles: [{ id: 2 }], organization_ids: [1], default_organization_id: 1 })
+ state.form.organization_ids = []
+ await flushPromises()
+ await state.handleSave()
+ expect(usersStore.updateUser).toHaveBeenLastCalledWith(8, expect.objectContaining({ organization_ids: [], default_organization_id: null }))
+ wrapper.unmount()
+ })
+})
+
// 已停用角色在下拉里怎么呈现,直接决定"编辑用户"这条路走不走得通:保存是全量替换
// 角色,前端若摘不掉那个停用角色、后端又要求角色全部启用,这个用户就永远存不回去。
// 现在后端已放行"本来就持有的停用角色",前端这两条是配套:既让人摘得掉,也不让人
diff --git a/frontend/src/views/Users.vue b/frontend/src/views/Users.vue
index 1704f655..2146273f 100644
--- a/frontend/src/views/Users.vue
+++ b/frontend/src/views/Users.vue
@@ -75,6 +75,22 @@
/>
+
+
+
+
+
+
+
+
+
+
@@ -90,7 +106,7 @@