feat(oa): add owner person/dept fields with initiator defaults
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
3b46b10604
commit
4ebc2dbe82
18 files changed
+791
-15
No files matched your search
@@ -0,0 +1,19 @@
|
||||
import FcDesigner from '@form-create/designer'
|
||||
import OaOwnerDeptSelect from '@/components/oa-approval/OaOwnerDeptSelect.vue'
|
||||
import { ownerDeptSelectDragRule } from '@/utils/oa-owner-dept-select-rule'
|
||||
|
||||
const FLAG = '__oaOwnerDeptSelectConfigured'
|
||||
|
||||
export function setupOaOwnerDeptSelect(formCreate?: {
|
||||
component?: (name: string, c: unknown) => void
|
||||
}) {
|
||||
if ((FcDesigner as any)[FLAG]) return
|
||||
FcDesigner.component('ownerDept', OaOwnerDeptSelect)
|
||||
if (typeof formCreate?.component === 'function') {
|
||||
formCreate.component('ownerDept', OaOwnerDeptSelect)
|
||||
}
|
||||
if (typeof FcDesigner.addDragRule === 'function') {
|
||||
FcDesigner.addDragRule(ownerDeptSelectDragRule)
|
||||
}
|
||||
;(FcDesigner as any)[FLAG] = true
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import FcDesigner from '@form-create/designer'
|
||||
import OaOwnerPersonSelect from '@/components/oa-approval/OaOwnerPersonSelect.vue'
|
||||
import { ownerPersonSelectDragRule } from '@/utils/oa-owner-person-select-rule'
|
||||
|
||||
const FLAG = '__oaOwnerPersonSelectConfigured'
|
||||
|
||||
export function setupOaOwnerPersonSelect(formCreate?: {
|
||||
component?: (name: string, c: unknown) => void
|
||||
}) {
|
||||
if ((FcDesigner as any)[FLAG]) return
|
||||
FcDesigner.component('ownerPerson', OaOwnerPersonSelect)
|
||||
if (typeof formCreate?.component === 'function') {
|
||||
formCreate.component('ownerPerson', OaOwnerPersonSelect)
|
||||
}
|
||||
if (typeof FcDesigner.addDragRule === 'function') {
|
||||
FcDesigner.addDragRule(ownerPersonSelectDragRule)
|
||||
}
|
||||
;(FcDesigner as any)[FLAG] = true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/** 与 FormCreate 官方 unique 同类:生成不重复 field */
|
||||
let _uidSeq = 0
|
||||
function uniqueId() {
|
||||
const num = 370 + ++_uidSeq
|
||||
return (
|
||||
'F' +
|
||||
Math.random().toString(36).slice(3, 6) +
|
||||
Number(`${Date.now()}`).toString(36) +
|
||||
num.toString(36) +
|
||||
'od'
|
||||
)
|
||||
}
|
||||
|
||||
/** FormCreate Designer DragRule:归属部门(默认当前登录人主部门) */
|
||||
export const ownerDeptSelectDragRule = {
|
||||
menu: 'main' as const,
|
||||
icon: 'icon-tree',
|
||||
label: '归属部门',
|
||||
name: 'ownerDept',
|
||||
input: true,
|
||||
mask: true,
|
||||
validate: ['string', 'array', 'object'],
|
||||
event: ['change'],
|
||||
rule() {
|
||||
return {
|
||||
type: 'ownerDept',
|
||||
field: uniqueId(),
|
||||
title: '归属部门',
|
||||
info: '',
|
||||
$required: false,
|
||||
props: {
|
||||
allowModify: false
|
||||
}
|
||||
}
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'allowModify',
|
||||
title: '允许修改',
|
||||
value: false,
|
||||
props: { activeText: '是', inactiveText: '否' }
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'formCreateInfo',
|
||||
title: '提示信息'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import { fetchDeptTree } from '@/api/admin'
|
||||
import { isDeptValueItem, type OaDeptValueItem } from '@/utils/oa-dept-value'
|
||||
import { isPersonValueItem, type OaPersonValueItem } from '@/utils/oa-person-value'
|
||||
import { buildPathLabelMapFromTreeRoots } from '@/utils/tree-path'
|
||||
|
||||
export type OwnerUserSnapshot = {
|
||||
id?: string
|
||||
userId?: string
|
||||
realName?: string
|
||||
userName?: string
|
||||
username?: string
|
||||
regionId?: string
|
||||
deptId?: string
|
||||
regionName?: string
|
||||
deptName?: string
|
||||
}
|
||||
|
||||
function walkRules(rules: unknown[], out: Record<string, any>[]) {
|
||||
for (const r of rules) {
|
||||
if (!r || typeof r !== 'object') continue
|
||||
const rule = r as Record<string, any>
|
||||
out.push(rule)
|
||||
const children = rule.children || rule.props?.rule || []
|
||||
if (Array.isArray(children)) walkRules(children, out)
|
||||
}
|
||||
}
|
||||
|
||||
function flattenRules(schemaOrRules: unknown): Record<string, any>[] {
|
||||
if (Array.isArray(schemaOrRules)) {
|
||||
const out: Record<string, any>[] = []
|
||||
walkRules(schemaOrRules, out)
|
||||
return out
|
||||
}
|
||||
if (schemaOrRules && typeof schemaOrRules === 'object') {
|
||||
const root = schemaOrRules as any
|
||||
const rules = root.rule || root.rules || []
|
||||
if (Array.isArray(rules)) {
|
||||
const out: Record<string, any>[] = []
|
||||
walkRules(rules, out)
|
||||
return out
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function isEmptyOwnerValue(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 buildCurrentOwnerPerson(user: OwnerUserSnapshot | null | undefined): OaPersonValueItem | null {
|
||||
if (!user) return null
|
||||
const id = String(user.id || user.userId || '').trim()
|
||||
if (!id) return null
|
||||
const name = String(user.realName || user.userName || user.username || '').trim() || id
|
||||
return { id, name }
|
||||
}
|
||||
|
||||
export function buildCurrentOwnerDeptSync(
|
||||
user: OwnerUserSnapshot | null | undefined
|
||||
): OaDeptValueItem | null {
|
||||
if (!user) return null
|
||||
const id = String(user.regionId || user.deptId || '').trim()
|
||||
if (!id) return null
|
||||
const name = String(user.regionName || user.deptName || '').trim() || id
|
||||
return { id, name }
|
||||
}
|
||||
|
||||
/** 用部门树解析全路径名称;失败则回退 id / 登录态名称 */
|
||||
export async function resolveCurrentOwnerDept(
|
||||
user: OwnerUserSnapshot | null | undefined
|
||||
): Promise<OaDeptValueItem | null> {
|
||||
const base = buildCurrentOwnerDeptSync(user)
|
||||
if (!base) return null
|
||||
try {
|
||||
const tree = (await fetchDeptTree()) || []
|
||||
const map = buildPathLabelMapFromTreeRoots(tree as any, { nameKey: 'deptName' })
|
||||
const path = map.get(base.id)
|
||||
if (path) return { id: base.id, name: path }
|
||||
} catch {
|
||||
/* 无部门树权限时仍用 id/名称回退 */
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起页:写入当前登录人 / 主部门。
|
||||
* - allowModify 为假:始终覆盖为当前用户(防展示旧值 / 复制发起)
|
||||
* - allowModify 为真:仅空字段补默认
|
||||
*/
|
||||
export async function applyOwnerFieldDefaults(
|
||||
schemaOrRules: unknown,
|
||||
formData: Record<string, any>,
|
||||
user: OwnerUserSnapshot | null | undefined
|
||||
): Promise<Record<string, any>> {
|
||||
const rules = flattenRules(schemaOrRules)
|
||||
const hasPerson = rules.some((r) => String(r.type || '') === 'ownerPerson')
|
||||
const hasDept = rules.some((r) => String(r.type || '') === 'ownerDept')
|
||||
if (!hasPerson && !hasDept) return { ...formData }
|
||||
|
||||
const person = hasPerson ? buildCurrentOwnerPerson(user) : null
|
||||
const dept = hasDept ? await resolveCurrentOwnerDept(user) : null
|
||||
const next = { ...formData }
|
||||
|
||||
for (const rule of rules) {
|
||||
const type = String(rule.type || '')
|
||||
const field = String(rule.field || rule.name || '').trim()
|
||||
if (!field) continue
|
||||
const allowModify = rule.props?.allowModify === true
|
||||
if (type === 'ownerPerson' && person) {
|
||||
if (!allowModify || isEmptyOwnerValue(next[field])) {
|
||||
next[field] = person
|
||||
}
|
||||
}
|
||||
if (type === 'ownerDept' && dept) {
|
||||
if (!allowModify || isEmptyOwnerValue(next[field])) {
|
||||
next[field] = dept
|
||||
}
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
/** 确保 props.allowModify 有明确布尔(缺省 false) */
|
||||
export function ensureOwnerFieldRuleProps(rules: unknown): any[] {
|
||||
if (!Array.isArray(rules)) return []
|
||||
return rules.map((raw) => {
|
||||
if (!raw || typeof raw !== 'object') return raw
|
||||
const rule = { ...(raw as any) }
|
||||
const type = String(rule.type || '')
|
||||
if (type === 'ownerPerson' || type === 'ownerDept') {
|
||||
const props = { ...(rule.props || {}) }
|
||||
if (props.allowModify !== true) props.allowModify = false
|
||||
rule.props = props
|
||||
}
|
||||
if (Array.isArray(rule.children)) {
|
||||
rule.children = ensureOwnerFieldRuleProps(rule.children)
|
||||
}
|
||||
if (rule.props && Array.isArray(rule.props.rule)) {
|
||||
rule.props = {
|
||||
...rule.props,
|
||||
rule: ensureOwnerFieldRuleProps(rule.props.rule)
|
||||
}
|
||||
}
|
||||
return rule
|
||||
})
|
||||
}
|
||||
|
||||
export function isOwnerPersonValue(v: unknown): v is OaPersonValueItem {
|
||||
return isPersonValueItem(v)
|
||||
}
|
||||
|
||||
export function isOwnerDeptValue(v: unknown): v is OaDeptValueItem {
|
||||
return isDeptValueItem(v)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/** 与 FormCreate 官方 unique 同类:生成不重复 field */
|
||||
let _uidSeq = 0
|
||||
function uniqueId() {
|
||||
const num = 370 + ++_uidSeq
|
||||
return (
|
||||
'F' +
|
||||
Math.random().toString(36).slice(3, 6) +
|
||||
Number(`${Date.now()}`).toString(36) +
|
||||
num.toString(36) +
|
||||
'op'
|
||||
)
|
||||
}
|
||||
|
||||
/** FormCreate Designer DragRule:归属人(默认当前登录人) */
|
||||
export const ownerPersonSelectDragRule = {
|
||||
menu: 'main' as const,
|
||||
icon: 'icon-input',
|
||||
label: '归属人',
|
||||
name: 'ownerPerson',
|
||||
input: true,
|
||||
mask: true,
|
||||
validate: ['string', 'array', 'object'],
|
||||
event: ['change'],
|
||||
rule() {
|
||||
return {
|
||||
type: 'ownerPerson',
|
||||
field: uniqueId(),
|
||||
title: '归属人',
|
||||
info: '',
|
||||
$required: false,
|
||||
props: {
|
||||
allowModify: false
|
||||
}
|
||||
}
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'allowModify',
|
||||
title: '允许修改',
|
||||
value: false,
|
||||
props: { activeText: '是', inactiveText: '否' }
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'formCreateInfo',
|
||||
title: '提示信息'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user