import { Ref } from 'vue'; import { List as TenantList } from '@/api/imTenant'; import { useUserStore } from '@/store/modules/user'; import type { FormSchema } from '@/components/Form'; export interface ImTenantOption { label: string; value: number; code?: string; name?: string; } export function formatImTenantLabel(item: any): string { const code = item?.code || item?.tenantCode || ''; const name = item?.name || item?.tenantName || '当前商户'; return code ? `${name} (${code})` : name; } export function getDefaultTenantValue(options: any[]) { const defaultTenant = options.find( (item) => item.code === 'default' || String(item.label || '').includes('(default)') ); return defaultTenant?.value || options.find((item) => Number(item.value) > 0)?.value || 0; } export async function loadImTenantOptions( target: Ref, params: Record = { page: 1, perPage: 100 } ) { const boundTenantId = getBoundTenantId(); // 登录用户已绑定商户租户:仅返回自己商户选项(与后端 resolveImAdminScope 强制范围一致) if (boundTenantId > 0) { const info = readCurrentUserInfo(); try { const res = await TenantList(params); target.value = (res?.list || []).map(toTenantOption); } catch { const tenant = { id: boundTenantId, name: info?.tenantName, code: info?.tenantCode }; target.value = [toTenantOption(tenant)]; } return target.value; } // 平台账号:加载全部商户;失败时保留旧选项,避免页面初始化被单个请求失败打断 try { const res = await TenantList(params); target.value = (res?.list || []).map(toTenantOption); } catch (err) { console.error('loadImTenantOptions failed:', err); } return target.value; } // 登录用户绑定的商户租户ID(按绑定租户判定,与后端 resolveImAdminScope 的范围逻辑一致) export function getBoundTenantId(): number { const userInfo = readCurrentUserInfo(); return Number(userInfo?.tenantId || userInfo?.tenantID || 0); } // 登录用户是否为商户绑定账号(true=下拉框只显示并锁定自己商户) export function isTenantBound(): boolean { return getBoundTenantId() > 0; } // 商户绑定账号:锁定搜索表单中的商户下拉项(仅一项、不可清除、不可更改) export function lockTenantSchema(schema?: FormSchema) { if (!schema || !isTenantBound()) return; const props: any = (schema.componentProps = schema.componentProps || {}); props.disabled = true; props.clearable = false; } /** * 统一搜索重置回调:BasicForm 的 resetFields 会把包括 tenantId 在内的全部搜索字段清空, * 随后的自动查询因缺商户条件命中前端空列表 guard(或被后端以「请选择IM租户」拒绝)。 * 此工厂返回的回调在 reset 之后把商户下拉回填为当前选项列表的第一项 * (商户绑定账号=自己商户;平台账号=全部商户列表第一项),再触发查询—— * 与各页面初始加载(onMounted 默认选中第一项)的行为保持一致。 * * setFieldsValueFn 必须传页面 useForm() 解构出的 setFieldsValue(与 onMounted 同一通道), * 不要用模板 ref 上的实例方法(可靠性不足)。 */ export function imSearchReset( setFieldsValueFn: (values: Record) => Promise | void, tenantOptions: Ref, reload: () => unknown, ) { return async () => { const first = Number(tenantOptions.value[0]?.value || 0); if (first > 0) { await setFieldsValueFn({ tenantId: first }); } await reload(); }; } function toTenantOption(item: any): ImTenantOption { return { label: formatImTenantLabel(item), value: Number(item.id), code: item.code, name: item.name, }; } function readCurrentUserInfo() { const userStore = useUserStore(); return { ...readTokenUserInfo(userStore.getToken), ...(userStore.getUserInfo as any) }; } function readTokenUserInfo(token: string) { try { const payload = token.split('.')[1]; if (!payload) return {}; const normalized = payload.replace(/-/g, '+').replace(/_/g, '/'); const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '='); const binary = window.atob(padded); const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); return JSON.parse(new TextDecoder().decode(bytes)); } catch { return {}; } }