feat(permission): replace OA roles with post-bound roles and assignee posts

Unify effective roles via post-role bindings, remove dedicated OA role UI, and update OA assignee resolution plus related SQL/docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-04 17:41:04 +08:00
1 parent a3c73d76bb
commit 06db3bc966
66 files changed
+55337 -38647

No files matched your search

@@ -161,7 +161,7 @@ function buildConfirmationKitChildren() {
export const confirmationKitDragRule = {
menu: 'kit' as const,
icon: 'icon-user',
icon: 'icon-idcard',
label: '转正',
name: 'confirmationKit',
input: false,
@@ -1,5 +1,6 @@
import FcDesigner from '@form-create/designer'
import OaConfirmationKit from '@/components/oa-approval/OaConfirmationKit.vue'
import { ensureOaKitMenu } from '@/utils/oa-form-create-kit-menu'
import { confirmationKitDragRule } from '@/utils/oa-confirmation-kit-rule'
const FLAG = '__oaConfirmationKitConfigured'
@@ -9,14 +10,7 @@ export function setupOaConfirmationKit(formCreate?: {
}) {
if ((FcDesigner as any)[FLAG]) return
// kit 菜单可能已由请假套件注册;幂等再调一次无妨
if (typeof FcDesigner.addMenu === 'function') {
try {
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
} catch {
/* already */
}
}
ensureOaKitMenu()
FcDesigner.component('confirmationKit', OaConfirmationKit)
if (typeof formCreate?.component === 'function') {
@@ -0,0 +1,16 @@
import FcDesigner from '@form-create/designer'
const FLAG = '__oaKitMenuConfigured'
/**
* 确保「套件」左侧菜单只注册一次。
* 全局 FcDesigner.addMenu 会无条件 push,多套件各自 addMenu 会导致同名分组重复,
* 进而 appendMenuItem 把每个拖拽规则挂到所有同名分组上(N 套件 × N 分组)。
*/
export function ensureOaKitMenu() {
if ((FcDesigner as any)[FLAG]) return
if (typeof FcDesigner.addMenu === 'function') {
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
}
;(FcDesigner as any)[FLAG] = true
}
@@ -1,5 +1,6 @@
import FcDesigner from '@form-create/designer'
import OaLeaveKit from '@/components/oa-approval/OaLeaveKit.vue'
import { ensureOaKitMenu } from '@/utils/oa-form-create-kit-menu'
import { leaveKitDragRule } from '@/utils/oa-leave-kit-rule'
const FLAG = '__oaLeaveKitConfigured'
@@ -7,9 +8,7 @@ const FLAG = '__oaLeaveKitConfigured'
export function setupOaLeaveKit(formCreate?: { component?: (name: string, c: unknown) => void }) {
if ((FcDesigner as any)[FLAG]) return
if (typeof FcDesigner.addMenu === 'function') {
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
}
ensureOaKitMenu()
FcDesigner.component('leaveKit', OaLeaveKit)
if (typeof formCreate?.component === 'function') {
@@ -1,5 +1,6 @@
import FcDesigner from '@form-create/designer'
import OaOvertimeKit from '@/components/oa-approval/OaOvertimeKit.vue'
import { ensureOaKitMenu } from '@/utils/oa-form-create-kit-menu'
import { overtimeKitDragRule } from '@/utils/oa-overtime-kit-rule'
const FLAG = '__oaOvertimeKitConfigured'
@@ -7,13 +8,7 @@ const FLAG = '__oaOvertimeKitConfigured'
export function setupOaOvertimeKit(formCreate?: { component?: (name: string, c: unknown) => void }) {
if ((FcDesigner as any)[FLAG]) return
if (typeof FcDesigner.addMenu === 'function') {
try {
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
} catch {
/* kit menu may already exist */
}
}
ensureOaKitMenu()
FcDesigner.component('overtimeKit', OaOvertimeKit)
if (typeof formCreate?.component === 'function') {
@@ -1,5 +1,6 @@
import FcDesigner from '@form-create/designer'
import OaResignationKit from '@/components/oa-approval/OaResignationKit.vue'
import { ensureOaKitMenu } from '@/utils/oa-form-create-kit-menu'
import { resignationKitDragRule } from '@/utils/oa-resignation-kit-rule'
const FLAG = '__oaResignationKitConfigured'
@@ -9,13 +10,7 @@ export function setupOaResignationKit(formCreate?: {
}) {
if ((FcDesigner as any)[FLAG]) return
if (typeof FcDesigner.addMenu === 'function') {
try {
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
} catch {
/* already */
}
}
ensureOaKitMenu()
FcDesigner.component('resignationKit', OaResignationKit)
if (typeof formCreate?.component === 'function') {
@@ -1,5 +1,6 @@
import FcDesigner from '@form-create/designer'
import OaTripKit from '@/components/oa-approval/OaTripKit.vue'
import { ensureOaKitMenu } from '@/utils/oa-form-create-kit-menu'
import { tripKitDragRule } from '@/utils/oa-trip-kit-rule'
const FLAG = '__oaTripKitConfigured'
@@ -7,13 +8,7 @@ const FLAG = '__oaTripKitConfigured'
export function setupOaTripKit(formCreate?: { component?: (name: string, c: unknown) => void }) {
if ((FcDesigner as any)[FLAG]) return
if (typeof FcDesigner.addMenu === 'function') {
try {
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
} catch {
/* kit menu may already exist */
}
}
ensureOaKitMenu()
FcDesigner.component('tripKit', OaTripKit)
if (typeof formCreate?.component === 'function') {
+1 -1
View File
@@ -96,7 +96,7 @@ function buildLeaveKitChildren() {
/** FormCreate Designer DragRule:请假套件 */
export const leaveKitDragRule = {
menu: 'kit' as const,
icon: 'icon-date',
icon: 'icon-askforleave',
label: '请假',
name: 'leaveKit',
input: false,
+1 -1
View File
@@ -84,7 +84,7 @@ function buildOvertimeKitChildren() {
/** FormCreate Designer DragRule:加班套件 */
export const overtimeKitDragRule = {
menu: 'kit' as const,
icon: 'icon-date',
icon: 'icon-time',
label: '加班',
name: 'overtimeKit',
input: false,
+83 -3
View File
@@ -1,4 +1,9 @@
/** 加班套件运行时:时长仅手动填写,补齐数字框左对齐 */
/** 加班套件运行时:时长仅手动填写,补齐数字框左对齐;加班人默认当前登录人 */
import {
buildCurrentOwnerPerson,
type OwnerUserSnapshot
} from '@/utils/oa-owner-field-defaults'
function asRules(schema: unknown): Record<string, unknown>[] {
if (Array.isArray(schema)) return schema.filter((r) => r && typeof r === 'object') as any
@@ -11,12 +16,88 @@ function asRules(schema: unknown): Record<string, unknown>[] {
return []
}
function walk(rules: Record<string, unknown>[], out: Record<string, unknown>[]) {
for (const r of rules) {
out.push(r)
if (Array.isArray(r.children)) {
walk(r.children as any, out)
}
const props = r.props as any
if (props && Array.isArray(props.rule)) walk(props.rule, out)
}
}
export type OvertimeKitBinding = {
personField?: string
durationField?: string
}
function resolveBinding(kit: Record<string, unknown>): OvertimeKitBinding {
const props = (kit.props && typeof kit.props === 'object' ? kit.props : {}) as Record<
string,
any
>
let personField = props.personField ? String(props.personField) : undefined
let durationField = props.durationField ? String(props.durationField) : undefined
const children = (
Array.isArray(kit.children)
? kit.children
: Array.isArray(props.rule)
? props.rule
: []
) as Record<string, any>[]
for (const c of children) {
if (!c?.field) continue
const sem = String(c.props?._fc_kit_field || '')
if (sem === 'overtimePerson' && !personField) personField = String(c.field)
if (sem === 'overtimeDuration' && !durationField) durationField = String(c.field)
}
return { personField, durationField }
}
export function listOvertimeKitBindings(schema: unknown): OvertimeKitBinding[] {
const flat: Record<string, unknown>[] = []
walk(asRules(schema), flat)
return flat.filter((r) => String(r.type || '') === 'overtimeKit').map(resolveBinding)
}
function isEmptyPersonValue(raw: unknown): boolean {
if (raw == null || raw === '') return true
if (Array.isArray(raw)) return raw.length === 0
if (typeof raw === 'object') {
const o = raw as Record<string, unknown>
return !String(o.id || '').trim()
}
return false
}
/**
* 发起页:加班人字段为空时写入当前登录人(人员组件为多选,值为数组)。
*/
export function applyOvertimeKitDefaults(
schema: unknown,
formData: Record<string, unknown>,
user: OwnerUserSnapshot | null | undefined
): Record<string, unknown> {
const data = { ...formData }
const person = buildCurrentOwnerPerson(user)
if (!person) return data
for (const kit of listOvertimeKitBindings(schema)) {
const f = kit.personField
if (f && isEmptyPersonValue(data[f])) {
data[f] = [person]
}
}
return data
}
function resolveDurationField(kit: Record<string, unknown>): string | undefined {
const binding = resolveBinding(kit)
if (binding.durationField) return binding.durationField
const props = (kit.props && typeof kit.props === 'object' ? kit.props : {}) as Record<
string,
any
>
if (props.durationField) return String(props.durationField)
const children = (
Array.isArray(kit.children)
? kit.children
@@ -26,7 +107,6 @@ function resolveDurationField(kit: Record<string, unknown>): string | undefined
) as Record<string, any>[]
for (const c of children) {
if (!c?.field) continue
if (String(c.props?._fc_kit_field || '') === 'overtimeDuration') return String(c.field)
const t = String(c.type || '')
if ((t === 'inputNumber' || t === 'number') && String(c.title || '').includes('时长')) {
return String(c.field)
@@ -90,7 +90,7 @@ function buildResignationKitChildren() {
export const resignationKitDragRule = {
menu: 'kit' as const,
icon: 'icon-user',
icon: 'icon-transfer',
label: '离职',
name: 'resignationKit',
input: false,
+1 -1
View File
@@ -124,7 +124,7 @@ function buildTripKitChildren() {
/** FormCreate Designer DragRule:出差套件 */
export const tripKitDragRule = {
menu: 'kit' as const,
icon: 'icon-date',
icon: 'icon-businesstrip',
label: '出差',
name: 'tripKit',
input: false,