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 @@