Files
im_manager/web/src/views/imOrg/model.ts
T
Austin 0596599db9 feat: add tenant bound restriction for IM dashboard and management pages
1. extract isTenantBound utility from user store logic
2. add disabled state to tenant select components across all IM pages
3. update policy tenant options logic to hide global option for bound tenants
4. add backend scope validation for IM admin operations
2026-09-22 11:34:40 +08:00

201 lines
5.5 KiB
TypeScript

import { computed, ref } from 'vue';
import { cloneDeep } from 'lodash-es';
import { FormSchema } from '@/components/Form';
import { useDictStore } from '@/store/modules/dict';
import { isTenantBound, loadImTenantOptions } from '@/views/imTenantOptions';
const dict = useDictStore();
export const tenantOptions = ref<any[]>([]);
export const departmentOptions = ref<any[]>([]);
export class DepartmentState {
public tenantId = 0;
public tenantName = '';
public id = 0;
public departmentId = '';
public name = '';
public parentId = 'root';
public sortOrder = 0;
public version = 1;
public state = 'active';
public childCount = 0;
public memberCount = 0;
public createdAt = '';
public updatedAt = '';
constructor(state?: Partial<DepartmentState>) {
if (state) Object.assign(this, state);
}
}
export class MemberState {
public tenantId = 0;
public tenantName = '';
public id = 0;
public departmentId = '';
public departmentName = '';
public userId = '';
public displayName = '';
public position = '';
public avatarFileId = '';
public mobile = '';
public email = '';
public employmentState = 'active';
public contactable = 1;
public sortOrder = 0;
public version = 1;
public createdAt = '';
public updatedAt = '';
constructor(state?: Partial<MemberState>) {
if (state) Object.assign(this, state);
}
}
export function newDepartmentState(state: DepartmentState | Record<string, any> | null) {
if (state !== null) {
if (state instanceof DepartmentState) return cloneDeep(state);
return new DepartmentState(state);
}
return new DepartmentState();
}
export function newMemberState(state: MemberState | Record<string, any> | null) {
if (state !== null) {
if (state instanceof MemberState) return cloneDeep(state);
return new MemberState(state);
}
return new MemberState();
}
export const departmentSchemas = ref<FormSchema[]>([
{
field: 'tenantId',
component: 'NSelect',
label: '商户',
componentProps: {
clearable: false,
options: computed(() => tenantOptions.value),
disabled: isTenantBound(),
placeholder: '请选择商户',
},
},
{
field: 'name',
component: 'NInput',
label: '部门名称',
componentProps: { placeholder: '请输入部门名称' },
},
{
field: 'state',
component: 'NSelect',
label: '状态',
componentProps: {
clearable: true,
options: dict.getOption('ImOrgDepartmentStateOptions'),
placeholder: '请选择状态',
},
},
]);
export const memberSchemas = ref<FormSchema[]>([
{
field: 'query',
component: 'NInput',
label: '关键词',
componentProps: { placeholder: '姓名/手机号/邮箱/用户ID' },
},
{
field: 'employmentState',
component: 'NSelect',
label: '在职状态',
componentProps: {
clearable: true,
options: dict.getOption('ImOrgEmploymentStateOptions'),
placeholder: '请选择在职状态',
},
},
]);
export const departmentColumns = [
{ title: '部门ID', key: 'departmentId', align: 'left', width: 170 },
{ title: '部门名称', key: 'name', align: 'left', width: 160 },
{ title: '上级部门', key: 'parentId', align: 'left', width: 130 },
{
title: '状态',
key: 'state',
align: 'left',
width: 90,
render(row: DepartmentState) {
return dict.getLabel('ImOrgDepartmentStateOptions', row.state);
},
},
{ title: '子部门', key: 'childCount', align: 'left', width: 80 },
{ title: '成员', key: 'memberCount', align: 'left', width: 80 },
{ title: '排序', key: 'sortOrder', align: 'left', width: 80 },
{ title: '更新时间', key: 'updatedAt', align: 'left', width: 180 },
];
export const memberColumns = [
{ title: '用户ID', key: 'userId', align: 'left', width: 110 },
{ title: '姓名', key: 'displayName', align: 'left', width: 130 },
{ title: '部门', key: 'departmentName', align: 'left', width: 130 },
{ title: '岗位', key: 'position', align: 'left', width: 130 },
{ title: '手机号', key: 'mobile', align: 'left', width: 140 },
{ title: '邮箱', key: 'email', align: 'left', width: 180 },
{
title: '在职状态',
key: 'employmentState',
align: 'left',
width: 100,
render(row: MemberState) {
return dict.getLabel('ImOrgEmploymentStateOptions', row.employmentState);
},
},
{
title: '可联系',
key: 'contactable',
align: 'left',
width: 90,
render(row: MemberState) {
return dict.getLabel('ImOrgContactableOptions', row.contactable);
},
},
{ title: '排序', key: 'sortOrder', align: 'left', width: 80 },
{ title: '更新时间', key: 'updatedAt', align: 'left', width: 180 },
];
export const departmentRules = {
name: { required: true, trigger: ['blur', 'input'], type: 'string', message: '请输入部门名称' },
state: { required: true, trigger: ['blur', 'change'], type: 'string', message: '请选择状态' },
};
export const memberRules = {
departmentId: {
required: true,
trigger: ['blur', 'change'],
type: 'string',
message: '请选择部门',
},
userId: { required: true, trigger: ['blur', 'input'], type: 'string', message: '请输入用户ID' },
employmentState: {
required: true,
trigger: ['blur', 'change'],
type: 'string',
message: '请选择在职状态',
},
};
export function loadOptions() {
dict.loadOptions([
'ImOrgDepartmentStateOptions',
'ImOrgEmploymentStateOptions',
'ImOrgContactableOptions',
]);
}
export async function loadTenantOptions() {
return loadImTenantOptions(tenantOptions, { page: 1, perPage: 100 });
}