Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
B
binfast-admin-view
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
binfast
binfast-admin-view
Commits
8d4896cf
Commit
8d4896cf
authored
Nov 04, 2025
by
刘斌
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 基础版本更新
parent
a336722d
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1009 additions
and
702 deletions
+1009
-702
sysTenant.ts
apps/web-antd/src/api/system/sysTenant.ts
+10
-0
index.ts
apps/web-antd/src/api/workflow/instance/index.ts
+21
-3
model.d.ts
apps/web-antd/src/api/workflow/instance/model.d.ts
+13
-0
slot.ts
apps/web-antd/src/components/global/slot.ts
+27
-0
editor.vue
apps/web-antd/src/components/tinymce/src/editor.vue
+5
-0
tree-select-panel.vue
apps/web-antd/src/components/tree/src/tree-select-panel.vue
+104
-132
data.tsx
apps/web-antd/src/views/auth/role/data.tsx
+32
-10
deptDataScopeModal.vue
apps/web-antd/src/views/auth/role/deptDataScopeModal.vue
+33
-43
list.vue
apps/web-antd/src/views/system/dict/type/list.vue
+3
-3
list.vue
apps/web-antd/src/views/system/tenant/list.vue
+18
-0
flow-actions.vue
...td/src/views/workflow/components/actions/flow-actions.vue
+418
-0
index.ts
apps/web-antd/src/views/workflow/components/actions/index.ts
+1
-0
apply-modal.vue
apps/web-antd/src/views/workflow/components/apply-modal.vue
+9
-0
approval-details.vue
...b-antd/src/views/workflow/components/approval-details.vue
+0
-2
approval-panel.vue
...web-antd/src/views/workflow/components/approval-panel.vue
+73
-447
approval-timeline-item.vue
.../src/views/workflow/components/approval-timeline-item.vue
+2
-4
approval-timeline.vue
...-antd/src/views/workflow/components/approval-timeline.vue
+8
-9
flow-preview.vue
apps/web-antd/src/views/workflow/components/flow-preview.vue
+16
-2
hook.ts
apps/web-antd/src/views/workflow/components/hook.ts
+14
-8
type.d.ts
apps/web-antd/src/views/workflow/components/type.d.ts
+8
-0
index.vue
apps/web-antd/src/views/workflow/processInstance/index.vue
+2
-1
instance-invalid-modal.vue
...views/workflow/processInstance/instance-invalid-modal.vue
+180
-33
menu.vue
packages/@core/ui-kit/menu-ui/src/components/menu.vue
+1
-1
directive.ts
packages/effects/access/src/directive.ts
+2
-4
use-access.ts
packages/effects/access/src/use-access.ts
+4
-0
style.css
packages/effects/plugins/src/vxe-table/style.css
+5
-0
No files found.
apps/web-antd/src/api/system/sysTenant.ts
View file @
8d4896cf
...
...
@@ -146,3 +146,13 @@ export function tenantSyncDict(tenantId?: string) {
successMessageMode
:
'message'
,
});
}
/**
* 同步租户配置
* @returns void
*/
export
function
tenantSyncConfig
()
{
return
requestClient
.
get
(
'/system/tenant/syncTenantConfig'
,
{
successMessageMode
:
'message'
,
});
}
apps/web-antd/src/api/workflow/instance/index.ts
View file @
8d4896cf
import
type
{
TaskInfo
}
from
'../task/model'
;
import
type
{
FlowInfoResponse
}
from
'./model'
;
import
type
{
FlowInfoResponse
,
FlowInstanceVariableResp
}
from
'./model'
;
import
type
{
ID
,
IDS
,
PageQuery
,
PageResult
}
from
'#/api/baseModel'
;
...
...
@@ -106,8 +106,8 @@ export function flowInfo(businessId: string) {
* @returns Map<string,any>
*/
export
function
instanceVariable
(
instanceId
:
string
)
{
return
requestClient
.
get
<
Record
<
string
,
any
>
>
(
`/workflow/instance/
v
ariable/
${
instanceId
}
`
,
return
requestClient
.
get
<
FlowInstanceVariableResp
>
(
`/workflow/instance/
instanceV
ariable/
${
instanceId
}
`
,
);
}
...
...
@@ -122,3 +122,21 @@ export function workflowInstanceInvalid(data: {
successMessageMode
:
'message'
,
});
}
/**
* 修改流程参数
* @param data 参数
* @param data.instanceId 实例ID
* @param data.key 参数key
* @param data.value 值
* @returns void
*/
export
function
updateFlowVariable
(
data
:
{
instanceId
:
string
;
key
:
string
;
value
:
any
;
})
{
return
requestClient
.
put
(
'/workflow/instance/updateVariable'
,
data
,
{
successMessageMode
:
'message'
,
});
}
apps/web-antd/src/api/workflow/instance/model.d.ts
View file @
8d4896cf
export
{};
export
interface
Flow
{
id
:
string
;
createTime
:
string
;
...
...
@@ -39,3 +41,14 @@ export interface FlowInfoResponse {
instanceId
:
string
;
list
:
Flow
[];
}
export
interface
FlowInstanceVariableResp
{
/**
* json字符串 流程变量
*/
variable
:
string
;
variableList
:
{
key
:
string
;
value
:
any
;
}[];
}
apps/web-antd/src/components/global/slot.ts
0 → 100644
View file @
8d4896cf
import
{
defineComponent
,
h
}
from
'vue'
;
/**
* 使用默认插槽来自定义组件
* 给vbenForm的components使用
*/
export
const
DefaultSlot
=
defineComponent
({
name
:
'DefaultSlot'
,
inheritAttrs
:
false
,
props
:
{
/**
* 绑定到根节点的div上的属性
*/
rootDivAttrs
:
{
type
:
Object
,
default
:
()
=>
({}),
},
},
render
()
{
/**
* 获取属性 传递给作用域插槽供外部使用
*/
const
attrs
=
this
.
$attrs
;
return
h
(
'div'
,
{
...
this
.
rootDivAttrs
},
this
.
$slots
.
default
?.(
attrs
));
},
});
apps/web-antd/src/components/tinymce/src/editor.vue
View file @
8d4896cf
...
...
@@ -133,6 +133,11 @@ const initOptions = computed((): InitOptions => {
toolbar_mode
:
'sliding'
,
// 隐藏下面的 按xxx获取帮助
help_accessibility
:
false
,
// https://blog.csdn.net/qq_46380656/article/details/122171418
// 避免图片地址和链接地址转换成相对路径
relative_urls
:
false
,
remove_script_host
:
false
,
convert_urls
:
false
,
...
options
,
/**
* 覆盖默认的base64行为
...
...
apps/web-antd/src/components/tree/src/tree-select-panel.vue
View file @
8d4896cf
<
script
setup
lang=
"ts"
>
import
type
{
CheckboxChangeEvent
}
from
'ant-design-vue/es/checkbox/interface'
;
import
type
{
DataNode
}
from
'ant-design-vue/es/tree'
;
import
type
{
CheckInfo
}
from
'ant-design-vue/es/vc-tree/props'
;
import
type
{
PropType
,
SetupContext
}
from
'vue'
;
import
{
computed
,
nextTick
,
onMounted
,
ref
}
from
'vue'
;
import
{
computed
,
nextTick
,
onMounted
,
ref
,
useSlots
,
watch
}
from
'vue
'
;
import
{
treeToList
}
from
'@vben/utils
'
;
import
{
findGroupParentIds
,
treeToList
}
from
'@vben/utils'
;
import
{
Checkbox
,
Tree
}
from
'ant-design-vue'
;
import
{
uniq
}
from
'lodash-es'
;
import
{
Button
,
Checkbox
,
Tree
}
from
'ant-design-vue'
;
/** 需要禁止透传 */
defineOptions
({
inheritAttrs
:
false
});
const
props
=
defineProps
({
checkStrictly
:
{
default
:
true
,
type
:
Boolean
,
},
expandAllOnInit
:
{
default
:
false
,
type
:
Boolean
,
},
fieldNames
:
{
default
:
()
=>
({
key
:
'id'
,
title
:
'name'
}),
type
:
Object
as
PropType
<
{
key
:
string
;
title
:
string
}
>
,
},
/** 点击节点关联/独立时 清空已勾选的节点 */
resetOnStrictlyChange
:
{
default
:
true
,
type
:
Boolean
,
},
treeData
:
{
default
:
()
=>
[],
type
:
Array
as
PropType
<
DataNode
[]
>
,
},
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{
expandAllOnInit
:
false
,
fieldNames
:
()
=>
({
key
:
'id'
,
title
:
'label'
}),
resetOnStrictlyChange
:
true
,
treeData
:
()
=>
[],
});
const
emit
=
defineEmits
<
{
checkStrictlyChange
:
[
boolean
]
}
>
();
const
expandStatus
=
ref
(
false
);
const
selectAllStatus
=
ref
(
false
);
interface
Props
{
/**
* 是否展开所有节点 mount
*/
expandAllOnInit
?:
boolean
;
/**
* 自定义字段
*/
fieldNames
?:
{
key
:
string
;
title
:
string
};
/**
* 点击节点关联/独立时 清空已勾选的节点
*/
resetOnStrictlyChange
?:
boolean
;
/**
* 树结构数据
*/
treeData
?:
DataNode
[];
}
/**
* 后台的这个字段跟antd/ele是反的
* 组件库这个字段代表不关联
* 后台这个代表关联
* 展开的状态
*/
const
innerCheckedStrictly
=
computed
(()
=>
{
return
!
props
.
checkStrictly
;
});
const
expandStatus
=
ref
(
false
);
/**
* 全选状态
*/
const
selectAllStatus
=
ref
(
false
);
const
associationText
=
computed
(()
=>
{
return
props
.
checkStrictly
?
'父子节点关联'
:
'父子节点独立'
;
return
checkStrictly
.
value
?
'父子节点关联'
:
'父子节点独立'
;
});
/**
* 这个只用于界面显示
* 关联情况下 只会有最末尾的节点被选中
*/
const
checkedKeys
=
defineModel
(
'value'
,
{
const
checkedKeys
=
defineModel
<
(
number
|
string
)[]
>
(
'value'
,
{
default
:
()
=>
[],
type
:
Array
as
PropType
<
(
number
|
string
)[]
>
,
});
// 所有节点的ID
const
allKeys
=
computed
(()
=>
{
const
idField
=
props
.
fieldNames
.
key
;
return
treeToList
(
props
.
treeData
).
map
((
item
:
any
)
=>
item
[
idField
]);
});
/** 已经选择的所有节点 包括子/父节点 用于提交 */
const
checkedRealKeys
=
ref
<
(
number
|
string
)[]
>
([]);
/**
* 取第一次的menuTree id 设置到checkedMenuKeys
* 主要为了解决没有任何修改 直接点击保存的情况
*
* length为0情况(即新增时候没有勾选节点) 勾选这里会延迟触发 节点会拼接上父节点 导致ID重复
* 是否节点关联 后端字段跟前端字段是反的
*/
const
stop
=
watch
([
checkedKeys
,
()
=>
props
.
treeData
],
()
=>
{
if
(
props
.
checkStrictly
&&
checkedKeys
.
value
.
length
>
0
&&
props
.
treeData
.
length
>
0
)
{
/** 找到父节点 添加上 */
const
parentIds
=
findGroupParentIds
(
props
.
treeData
,
checkedKeys
.
value
as
any
,
{
id
:
props
.
fieldNames
.
key
},
);
const
checkStrictly
=
defineModel
<
boolean
>
(
'checkStrictly'
,
{
default
:
()
=>
true
,
});
const
computedCheckedKeys
=
computed
<
any
>
({
get
()
{
/**
* uniq 解决上面的id重复问题
* 严格模式(节点不关联) 需要返回{checked: string[] | number[], halfChecked: string[]}
* @see https://www.antdv.com/components/tree-cn#tree-props
*/
checkedRealKeys
.
value
=
uniq
([...
parentIds
,
...
checkedKeys
.
value
]);
stop
();
}
if
(
!
props
.
checkStrictly
&&
checkedKeys
.
value
.
length
>
0
)
{
/** 节点独立 这里是全部的节点 */
checkedRealKeys
.
value
=
checkedKeys
.
value
;
stop
();
}
if
(
!
checkStrictly
.
value
)
{
return
{
checked
:
[...
checkedKeys
.
value
],
halfChecked
:
[],
};
}
return
checkedKeys
.
value
;
},
set
(
v
)
{
if
(
!
checkStrictly
.
value
)
{
checkedKeys
.
value
=
[...
v
.
checked
,
...
v
.
halfChecked
];
return
;
}
checkedKeys
.
value
=
v
;
},
});
/**
*
* @param checkedStateKeys 已经选中的子节点的ID
* @param info info.halfCheckedKeys为父节点的ID
*/
type
CheckedState
<
T
=
number
|
string
>
=
|
T
[]
|
{
checked
:
T
[];
halfChecked
:
T
[]
};
function
handleChecked
(
checkedStateKeys
:
CheckedState
,
info
:
CheckInfo
)
{
// 数组的话为节点关联
if
(
Array
.
isArray
(
checkedStateKeys
))
{
const
halfCheckedKeys
:
number
[]
=
(
info
.
halfCheckedKeys
||
[])
as
number
[];
checkedRealKeys
.
value
=
[...
halfCheckedKeys
,
...
checkedStateKeys
];
}
else
{
checkedRealKeys
.
value
=
[...
checkedStateKeys
.
checked
];
// fix: Invalid prop: type check failed for prop "value". Expected Array, got Object
checkedKeys
.
value
=
[...
checkedStateKeys
.
checked
];
}
}
// 所有节点的ID
const
allKeys
=
computed
(()
=>
{
const
idField
=
props
.
fieldNames
.
key
;
return
treeToList
(
props
.
treeData
).
map
((
item
:
any
)
=>
item
[
idField
]);
});
function
handle
Expand
Change
(
e
:
CheckboxChangeEvent
)
{
function
handle
CheckedAll
Change
(
e
:
CheckboxChangeEvent
)
{
// 这个用于展示
checkedKeys
.
value
=
e
.
target
.
checked
?
allKeys
.
value
:
[];
// 这个用于提交
checkedRealKeys
.
value
=
e
.
target
.
checked
?
allKeys
.
value
:
[];
}
const
expandedKeys
=
ref
<
string
[]
>
([]);
function
handleExpandOrCollapseAll
(
e
:
CheckboxChangeEvent
)
{
const
expand
=
e
.
target
.
checked
;
expandedKeys
.
value
=
expand
?
allKeys
.
value
:
[];
function
handleExpandOrCollapseAll
()
{
expandStatus
.
value
=
!
expandStatus
.
value
;
expandedKeys
.
value
=
expand
Status
.
value
?
allKeys
.
value
:
[];
}
function
handleCheckStrictlyChange
(
e
:
CheckboxChangeEvent
)
{
emit
(
'checkStrictlyChange'
,
e
.
target
.
checked
);
function
handleCheckStrictlyChange
()
{
if
(
props
.
resetOnStrictlyChange
)
{
checkedKeys
.
value
=
[];
checkedRealKeys
.
value
=
[];
}
}
/**
* 暴露方法来获取用于提交的全部节点
* uniq去重(保险方案)
*/
defineExpose
({
getCheckedKeys
:
()
=>
uniq
(
checkedRealKeys
.
value
),
});
onMounted
(
async
()
=>
{
if
(
props
.
expandAllOnInit
)
{
await
nextTick
();
expandedKeys
.
value
=
allKeys
.
value
;
}
});
const
slots
=
useSlots
()
as
SetupContext
[
'slots'
];
</
script
>
<
template
>
<div
class=
"bg-background w-full rounded-lg border-[1px] p-[12px]"
>
<!--
<div
class=
"flex flex-col gap-6 text-[13px]"
>
<div>
computedCheckedKeys
{{
computedCheckedKeys
}}
</div>
<div>
checkedKeys
{{
checkedKeys
}}
</div>
</div>
-->
<div
class=
"flex items-center justify-between gap-2 border-b-[1px] pb-2"
>
<div>
<div
class=
"opacity-75"
>
<span>
节点状态:
</span>
<span
:class=
"[
props.
checkStrictly ? 'text-primary' : 'text-red-500']"
>
<span
:class=
"[checkStrictly ? 'text-primary' : 'text-red-500']"
>
{{
associationText
}}
</span>
</div>
<div>
已选中
<span
class=
"text-primary mx-1 font-semibold"
>
{{
checkedRealKeys
.
length
}}
</span>
个节点
</div>
</div>
<div
class=
"flex flex-wrap items-center justify-between border-b-[1px] py-2"
>
<Button
size=
"small"
@
click=
"handleExpandOrCollapseAll"
>
展开/折叠全部
</Button>
<Checkbox
v-model:checked=
"
expand
Status"
@
change=
"handle
ExpandOrCollapseAll
"
v-model:checked=
"
selectAll
Status"
@
change=
"handle
CheckedAllChange
"
>
展开/折叠全部
</Checkbox>
<Checkbox
v-model:checked=
"selectAllStatus"
@
change=
"handleExpandChange"
>
全选/取消全选
</Checkbox>
<Checkbox
:checked=
"checkStrictly"
@
change=
"handleCheckStrictlyChange"
>
<Checkbox
v-model:checked=
"checkStrictly"
@
change=
"handleCheckStrictlyChange"
>
父子节点关联
</Checkbox>
</div>
<div
class=
"py-2"
>
<Tree
v-if=
"treeData.length > 0"
v-model:check-strictly=
"innerCheckedStrictly"
v-model:checked-keys=
"checkedKeys"
:check-strictly=
"!checkStrictly"
v-model:checked-keys=
"computedCheckedKeys"
v-model:expanded-keys=
"expandedKeys"
:checkable=
"true"
:field-names=
"fieldNames"
:selectable=
"false"
:tree-data=
"treeData"
@
check=
"handleChecked"
>
<template
v-for=
"slotName in Object.keys(slots)"
v-for=
"slotName in Object.keys(
$
slots)"
:key=
"slotName"
#[
slotName
]="
data
"
>
...
...
@@ -219,3 +174,20 @@ const slots = useSlots() as SetupContext['slots'];
</div>
</div>
</template>
<
style
lang=
"scss"
scoped
>
:deep
(
.ant-tree
)
{
// 勾选框居中
&
.ant-tree-checkbox
{
margin
:
0
;
margin-right
:
6px
;
}
// 展开图标居中
&
.ant-tree-switcher
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
}
</
style
>
apps/web-antd/src/views/auth/role/data.tsx
View file @
8d4896cf
import
type
{
VbenFormSchema
}
from
'#/adapter/form'
;
import
type
{
VxeTableGridOptions
}
from
'#/adapter/vxe-table'
;
import
{
markRaw
}
from
'vue'
;
import
{
useAccess
}
from
'@vben/access'
;
import
{
DictEnum
}
from
'@vben/constants'
;
import
{
getVxePopupContainer
}
from
'@vben/utils'
;
...
...
@@ -8,6 +10,8 @@ import { getVxePopupContainer } from '@vben/utils';
import
{
Tag
}
from
'ant-design-vue'
;
import
{
changeRoleStatus
}
from
'#/api/auth/role'
;
import
{
DefaultSlot
}
from
'#/components/global/slot'
;
import
{
TreeSelectPanel
}
from
'#/components/tree'
;
import
{
getDictOptions
}
from
'#/utils/dict'
;
/**
...
...
@@ -134,15 +138,6 @@ export const dataScopeModalSchemas: VbenFormSchema[] = [
fieldName
:
'id'
,
label
:
'角色ID'
,
},
{
component
:
'Radio'
,
dependencies
:
{
show
:
()
=>
false
,
triggerFields
:
[
''
],
},
fieldName
:
'deptCheckStrictly'
,
label
:
'deptCheckStrictly'
,
},
{
component
:
'Input'
,
componentProps
:
{
...
...
@@ -171,12 +166,39 @@ export const dataScopeModalSchemas: VbenFormSchema[] = [
label
:
'权限范围'
,
},
{
component
:
'TreeSelect'
,
component
:
'Radio'
,
dependencies
:
{
show
:
()
=>
false
,
triggerFields
:
[
''
],
},
fieldName
:
'deptCheckStrictly'
,
label
:
'deptCheckStrictly'
,
},
{
// 这种的场景基本上是一个组件需要绑定两个或以上的场景
component
:
markRaw
(
DefaultSlot
),
defaultValue
:
[],
componentProps
:
{
rootDivAttrs
:
{
class
:
'w-full'
,
},
},
dependencies
:
{
show
:
(
values
)
=>
values
.
dataScope
===
5
,
triggerFields
:
[
'dataScope'
],
},
renderComponentContent
:
(
model
)
=>
({
default
:
(
attrs
:
any
)
=>
{
return
(
<
TreeSelectPanel
expand
-
all
-
on
-
init=
{
true
}
treeData=
{
attrs
.
treeData
}
v
-
model
:
checkStrictly=
{
model
.
deptCheckStrictly
}
v
-
model
:
value=
{
model
.
deptIds
}
/>
);
},
}),
fieldName
:
'deptIds'
,
help
:
'更改后立即生效'
,
label
:
'部门权限'
,
...
...
apps/web-antd/src/views/auth/role/deptDataScopeModal.vue
View file @
8d4896cf
...
...
@@ -2,14 +2,14 @@
import
type
{
SysDeptApi
}
from
'#/api/auth/dept'
;
import
type
{
SysRoleApi
}
from
'#/api/auth/role'
;
import
{
ref
}
from
'vue'
;
import
{
useVbenModal
}
from
'@vben/common-ui'
;
import
{
findGroupParentIds
}
from
'@vben/utils'
;
import
{
uniq
}
from
'lodash-es'
;
import
{
useVbenForm
}
from
'#/adapter/form'
;
import
{
treeList
}
from
'#/api/auth/dept'
;
import
{
authDataScope
,
getRoleDeptIds
}
from
'#/api/auth/role'
;
import
{
TreeSelectPanel
}
from
'#/components/tree'
;
import
{
defaultFormValueGetter
,
useBeforeCloseDiff
}
from
'#/utils/popup'
;
import
{
dataScopeModalSchemas
}
from
'./data'
;
...
...
@@ -29,30 +29,36 @@ const [BasicForm, formApi] = useVbenForm({
showDefaultActions
:
false
,
});
const
deptTree
=
ref
<
DeptTreeOption
[]
>
([]);
// const deptTree = ref
<
DeptTreeOption
[]
>
([]);
/**
* 保存部门数据 用于获取祖先节点
*/
let
treeData
:
DeptTreeOption
[]
=
[];
async
function
setupDeptTree
(
id
:
number
)
{
const
[
roleDeptIds
,
deptTreeList
]
=
await
Promise
.
all
([
getRoleDeptIds
(
id
),
treeList
(),
]);
// const resp = await getRoleDeptIds(id);
formApi
.
setFieldValue
(
'deptIds'
,
roleDeptIds
);
/**
* 设置部门树数据
*/
formApi
.
updateSchema
([
{
fieldName
:
'deptIds'
,
componentProps
:
{
treeData
:
deptTreeList
}
},
]);
/**
* 设置选中 必须先传递treeData
* Note: Tree missing follow keys: '1981565541727186945'
*/
await
formApi
.
setFieldValue
(
'deptIds'
,
roleDeptIds
);
// 设置菜单信息
deptTree
.
value
=
deptTreeList
;
}
async
function
customFormValueGetter
()
{
const
v
=
await
defaultFormValueGetter
(
formApi
)();
// 获取勾选信息
const
menuIds
=
deptSelectRef
.
value
?.[
0
]?.
getCheckedKeys
()
??
[];
const
mixStr
=
v
+
menuIds
.
join
(
','
);
return
mixStr
;
treeData
=
deptTreeList
;
}
const
{
onBeforeClose
,
markInitialized
,
resetInitialized
}
=
useBeforeCloseDiff
(
{
initializedGetter
:
customFormValueGetter
,
currentGetter
:
customFormValueGetter
,
initializedGetter
:
defaultFormValueGetter
(
formApi
)
,
currentGetter
:
defaultFormValueGetter
(
formApi
)
,
},
);
...
...
@@ -63,6 +69,7 @@ const [BasicModal, modalApi] = useVbenModal({
onConfirm
:
handleConfirm
,
onOpenChange
:
async
(
isOpen
)
=>
{
if
(
!
isOpen
)
{
treeData
=
[];
return
null
;
}
modalApi
.
modalLoading
(
true
);
...
...
@@ -78,11 +85,6 @@ const [BasicModal, modalApi] = useVbenModal({
},
});
/**
* 这里拿到的是一个数组ref
*/
const
deptSelectRef
=
ref
();
async
function
handleConfirm
()
{
try
{
modalApi
.
lock
(
true
);
...
...
@@ -93,9 +95,16 @@ async function handleConfirm() {
// formApi.getValues拿到的是一个readonly对象,不能直接修改,需要cloneDeep
const
data
=
await
formApi
.
getValues
();
// 不为自定义权限的话 删除部门id
let
deptIds
:
number
[]
=
[];
if
(
data
.
dataScope
===
5
)
{
deptIds
=
deptSelectRef
.
value
?.[
0
]?.
getCheckedKeys
()
??
[];
let
{
deptIds
,
deptCheckStrictly
}
=
data
;
// 节点关联 需要拼接上祖级ID(获取的是不带的)
if
(
deptCheckStrictly
)
{
// 找到所有父级ID
const
parentIds
=
findGroupParentIds
(
treeData
,
deptIds
,
{
id
:
'id'
});
// 去重
deptIds
=
uniq
([...
parentIds
,
...
deptIds
]);
}
// 赋值
data
.
deptIds
=
deptIds
;
}
else
{
data
.
deptIds
=
[];
...
...
@@ -115,29 +124,10 @@ async function handleClosed() {
await
formApi
.
resetForm
();
resetInitialized
();
}
/**
* 通过回调更新 无法通过v-model
* @param value 菜单选择是否严格模式
*/
function
handleCheckStrictlyChange
(
value
:
boolean
)
{
formApi
.
setFieldValue
(
'deptCheckStrictly'
,
value
);
}
</
script
>
<
template
>
<BasicModal
class=
"min-h-[600px] w-[550px]"
title=
"分配权限"
>
<BasicForm>
<template
#
deptIds=
"slotProps"
>
<TreeSelectPanel
ref=
"deptSelectRef"
v-bind=
"slotProps"
:check-strictly=
"formApi.form.values.deptCheckStrictly"
:expand-all-on-init=
"true"
:tree-data=
"deptTree"
@
check-strictly-change=
"handleCheckStrictlyChange"
/>
</
template
>
</BasicForm>
<BasicForm
/>
</BasicModal>
</
template
>
apps/web-antd/src/views/system/dict/type/list.vue
View file @
8d4896cf
...
...
@@ -137,7 +137,7 @@ function onDelete(row: DictTypeApi.DictType) {
async
function
handleRefreshCache
()
{
await
refreshDictTypeCache
();
await
table
Api
.
query
();
await
grid
Api
.
query
();
}
</
script
>
<
template
>
...
...
@@ -145,12 +145,12 @@ async function handleRefreshCache() {
<DictTypeModal
@
success=
"onRefresh"
/>
<Grid
table-title=
"字典类型列表"
>
<template
#
toolbar-tools
>
<
a-b
utton
<
B
utton
v-access:code=
"['system:dict:edit']"
@
click=
"handleRefreshCache"
>
刷新缓存
</
a-b
utton>
</
B
utton>
<Button
type=
"primary"
v-access:code=
"['system:dict:add']"
...
...
apps/web-antd/src/views/system/tenant/list.vue
View file @
8d4896cf
...
...
@@ -139,6 +139,18 @@ function handleSyncTenantDict() {
},
});
}
function
handleSyncTenantConfig
()
{
Modal
.
confirm
({
title
:
'提示'
,
iconType
:
'warning'
,
content
:
'确认同步租户参数配置?'
,
onOk
:
async
()
=>
{
await
syncTenantConfig
();
await
tableApi
.
query
();
},
});
}
</
script
>
<
template
>
<Page
auto-content-height
>
...
...
@@ -152,6 +164,12 @@ function handleSyncTenantDict() {
>
同步租户字典
</Button>
<Button
v-access:code=
"['system:tenant:edit']"
@
click=
"handleSyncTenantConfig"
>
同步租户参数配置
</Button>
<Button
v-access:code=
"['system:tenant:export']"
@
click=
"handleDownloadExcel"
...
...
apps/web-antd/src/views/workflow/components/actions/flow-actions.vue
0 → 100644
View file @
8d4896cf
This diff is collapsed.
Click to expand it.
apps/web-antd/src/views/workflow/components/actions/index.ts
0 → 100644
View file @
8d4896cf
export
{
default
as
FlowActions
}
from
'./flow-actions.vue'
;
apps/web-antd/src/views/workflow/components/apply-modal.vue
View file @
8d4896cf
...
...
@@ -43,6 +43,8 @@ const [BasicModal, modalApi] = useVbenModal({
if
(
!
isOpen
)
{
return
null
;
}
modalApi
.
modalLoading
(
true
);
const
{
taskId
}
=
modalApi
.
getData
()
as
ModalProps
;
// 查询是否有按钮权限
...
...
@@ -63,6 +65,8 @@ const [BasicModal, modalApi] = useVbenModal({
},
},
]);
modalApi
.
modalLoading
(
false
);
},
});
...
...
@@ -108,6 +112,11 @@ const [BasicForm, formApi] = useVbenForm({
component
:
'Input'
,
defaultValue
:
[],
label
:
'抄送人'
,
// 默认不显示
dependencies
:
{
if
:
false
,
triggerFields
:
[
''
],
},
},
],
showDefaultActions
:
false
,
...
...
apps/web-antd/src/views/workflow/components/approval-details.vue
View file @
8d4896cf
...
...
@@ -21,8 +21,6 @@ defineOptions({
defineProps
<
{
currentFlowInfo
:
FlowInfoResponse
;
iframeHeight
:
number
;
iframeLoaded
:
boolean
;
task
:
TaskInfo
;
}
>
();
</
script
>
...
...
apps/web-antd/src/views/workflow/components/approval-panel.vue
View file @
8d4896cf
This diff is collapsed.
Click to expand it.
apps/web-antd/src/views/workflow/components/approval-timeline-item.vue
View file @
8d4896cf
...
...
@@ -45,11 +45,8 @@ onMounted(async () => {
}));
});
/**
* 这里无法处理昵称中带,的情况
*/
const
isMultiplePerson
=
computed
(
()
=>
props
.
item
.
approve
Name
?.
split
(
','
).
length
>
1
,
()
=>
props
.
item
.
approve
r
?.
split
(
','
).
length
>
1
,
);
</
script
>
...
...
@@ -87,6 +84,7 @@ const isMultiplePerson = computed(
</div>
<div
:class=
"cn('mt-2 flex flex-wrap gap-2')"
v-if=
"isMultiplePerson"
>
<!-- 如果昵称中带, 这里的处理是不准确的 -->
<div
:class=
"cn('bg-foreground/5 flex items-center rounded-full', 'p-1')"
v-for=
"(name, index) in item.approveName.split(',')"
...
...
apps/web-antd/src/views/workflow/components/approval-timeline.vue
View file @
8d4896cf
<
script
setup
lang=
"ts"
>
import
type
{
Flow
}
from
'#/api/workflow/instance/model'
;
import
{
Timeline
}
from
'ant-design-vue'
;
import
{
Empty
,
Timeline
}
from
'ant-design-vue'
;
import
ApprovalTimelineItem
from
'./approval-timeline-item.vue'
;
const
props
=
defineProps
<
{
interface
Props
{
list
:
Flow
[];
}
>
();
}
defineProps
<
Props
>
();
</
script
>
<
template
>
<Timeline
v-if=
"props.list.length > 0"
>
<ApprovalTimelineItem
v-for=
"item in props.list"
:key=
"item.id"
:item=
"item"
/>
<Timeline
v-if=
"list.length > 0"
>
<ApprovalTimelineItem
v-for=
"item in list"
:key=
"item.id"
:item=
"item"
/>
</Timeline>
<Empty
v-else
/>
</
template
>
apps/web-antd/src/views/workflow/components/flow-preview.vue
View file @
8d4896cf
<!-- 流程图预览组件 -->
<
script
setup
lang=
"ts"
>
import
{
useAppConfig
}
from
'@vben/hooks'
;
import
{
stringify
}
from
'@vben/request'
;
...
...
@@ -7,7 +9,14 @@ import { useWarmflowIframe } from './hook';
defineOptions
({
name
:
'FlowPreview'
});
const
props
=
defineProps
<
{
instanceId
:
number
}
>
();
const
props
=
defineProps
<
Props
>
();
interface
Props
{
/**
* 流程实例ID
*/
instanceId
:
number
;
}
const
{
clientId
}
=
useAppConfig
(
import
.
meta
.
env
,
import
.
meta
.
env
.
PROD
);
...
...
@@ -21,6 +30,7 @@ const params = {
/**
* iframe地址
* 后端地址 + 固定flow地址拼接
*/
const
url
=
`
${
import
.
meta
.
env
.
VITE_GLOB_API_URL
}
/warm-flow-ui/index.html?
${
stringify
(
params
)}
`
;
...
...
@@ -28,5 +38,9 @@ const { iframeRef } = useWarmflowIframe();
</
script
>
<
template
>
<iframe
ref=
"iframeRef"
:src=
"url"
class=
"h-[500px] w-full border"
></iframe>
<iframe
ref=
"iframeRef"
:src=
"url"
class=
"h-[600px] w-full rounded-[6px] border"
></iframe>
</
template
>
apps/web-antd/src/views/workflow/components/hook.ts
View file @
8d4896cf
...
...
@@ -10,20 +10,26 @@ export function useWarmflowIframe() {
const
iframeRef
=
useTemplateRef
<
HTMLIFrameElement
>
(
'iframeRef'
);
const
{
isDark
}
=
usePreferences
();
async
function
iframeLoadEvent
()
{
/**
* TODO: 这里可以优化 因为拿不到内部vue的mount状态
*/
await
new
Promise
((
resolve
)
=>
setTimeout
(
resolve
,
500
));
const
theme
=
isDark
.
value
?
'theme-dark'
:
'theme-light'
;
iframeRef
.
value
?.
contentWindow
?.
postMessage
({
type
:
theme
});
}
onMounted
(()
=>
{
/**
* load只是iframe加载完 而非vue加载完
*/
iframeRef
.
value
?.
addEventListener
(
'load'
,
async
()
=>
{
/**
* TODO: 这里可以优化 因为拿不到内部vue的mount状态
*/
await
new
Promise
((
resolve
)
=>
setTimeout
(
resolve
,
500
));
const
theme
=
isDark
.
value
?
'theme-dark'
:
'theme-light'
;
iframeRef
.
value
?.
contentWindow
?.
postMessage
({
type
:
theme
});
});
iframeRef
.
value
?.
addEventListener
(
'load'
,
iframeLoadEvent
);
});
// onBeforeUnmount(() => {
// iframeRef.value?.removeEventListener('load', iframeLoadEvent);
// });
// 监听主题切换 通知iframe切换
watch
(
isDark
,
(
dark
)
=>
{
if
(
!
iframeRef
.
value
)
{
...
...
apps/web-antd/src/views/workflow/components/type.d.ts
0 → 100644
View file @
8d4896cf
export
{};
/**
* myself 我发起的
* readonly 只读 只用于查看
* approve 审批(我的待办)
* admin 流程监控 - 待办任务使用
*/
export
type
ApprovalType
=
'admin'
|
'approve'
|
'myself'
|
'readonly'
;
apps/web-antd/src/views/workflow/processInstance/index.vue
View file @
8d4896cf
...
...
@@ -149,9 +149,10 @@ const [InstanceVariableModal, instanceVariableModalApi] = useVbenModal({
connectedComponent
:
instanceVariableModal
,
});
function
handleVariable
(
row
:
Recordable
<
any
>
)
{
instanceVariableModalApi
.
setData
({
record
:
row
.
variable
});
instanceVariableModalApi
.
setData
({
instanceId
:
row
.
id
});
instanceVariableModalApi
.
open
();
}
const
[
FlowInfoModal
,
flowInfoModalApi
]
=
useVbenModal
({
connectedComponent
:
flowInfoModal
,
});
...
...
apps/web-antd/src/views/workflow/processInstance/instance-invalid-modal.vue
View file @
8d4896cf
<
script
setup
lang=
"ts"
>
import
{
useVbenModal
}
from
'@vben/common-ui
'
;
<
script
setup
lang=
"ts
x
"
>
import
{
ref
}
from
'vue
'
;
import
{
cloneDeep
}
from
'lodash-es'
;
import
{
JsonPreview
,
useVbenModal
}
from
'@vben/common-ui'
;
import
{
cn
,
getPopupContainer
}
from
'@vben/utils'
;
import
{
message
,
Modal
,
Tag
}
from
'ant-design-vue'
;
import
{
useVbenForm
}
from
'#/adapter/form'
;
import
{
workflowInstanceInvalid
}
from
'#/api/workflow/instance/index
'
;
import
{
instanceVariable
,
updateFlowVariable
}
from
'#/api/workflow/instance
'
;
const
emit
=
defineEmits
<
{
reload
:
[]
}
>
();
interface
ModalData
{
/**
* 变量 json字符串
*/
record
:
string
;
instanceId
:
string
;
}
const
data
=
ref
({});
const
[
BasicModal
,
modalApi
]
=
useVbenModal
({
onConfirm
:
handleSubmit
,
onCancel
:
handleCancel
,
title
:
'流程变量'
,
fullscreenButton
:
false
,
title
:
'作废原因'
,
footer
:
false
,
onOpenChange
:
async
(
visible
)
=>
{
if
(
!
visible
)
{
data
.
value
=
{};
return
null
;
}
modalApi
.
modalLoading
(
true
);
await
loadData
();
modalApi
.
modalLoading
(
false
);
},
});
const
[
BasicForm
,
formApi
]
=
useVbenForm
({
const
fieldTypeColors
=
{
string
:
'cyan'
,
number
:
'blue'
,
boolean
:
'orange'
,
object
:
'purple'
,
};
function
getFieldTypeColor
(
fieldType
:
string
)
{
return
(
fieldTypeColors
[
fieldType
as
keyof
typeof
fieldTypeColors
]
??
'default'
);
}
async
function
loadData
()
{
const
{
instanceId
}
=
modalApi
.
getData
()
as
ModalData
;
const
resp
=
await
instanceVariable
(
instanceId
);
const
jsonObj
=
JSON
.
parse
(
resp
.
variable
);
data
.
value
=
jsonObj
;
// 表单
const
objEntry
=
Object
.
entries
(
jsonObj
);
interface
OptionsType
{
label
:
string
;
value
:
string
;
fieldType
:
string
;
}
formApi
.
updateSchema
([
{
fieldName
:
'key'
,
componentProps
:
{
options
:
objEntry
.
map
(
([
key
,
value
])
=>
({
label
:
key
,
value
:
key
,
fieldType
:
typeof
value
,
})
as
OptionsType
,
),
},
renderComponentContent
:
()
=>
({
option
:
(
option
:
OptionsType
)
=>
(
<
div
>
{
option
.
label
}
<
Tag
class
=
"ml-1"
color
=
{
getFieldTypeColor
(
option
.
fieldType
)}
>
{
option
.
fieldType
}
<
/Tag
>
<
/div
>
),
}),
},
]);
}
const
[
Form
,
formApi
]
=
useVbenForm
({
commonConfig
:
{
formItemClass
:
'col-span-2'
,
componentProps
:
{
class
:
'w-full'
,
allowClear
:
true
,
},
labelWidth
:
80
,
},
layout
:
'vertical'
,
schema
:
[
{
fieldName
:
'comment'
,
label
:
'作废原因'
,
component
:
'Textarea'
,
fieldName
:
'key'
,
component
:
'Select'
,
label
:
'变量名称'
,
rules
:
'selectRequired'
,
componentProps
:
{
getPopupContainer
,
},
},
{
fieldName
:
'valueType'
,
component
:
'Select'
,
label
:
'变量类型'
,
rules
:
'selectRequired'
,
componentProps
:
{
getPopupContainer
,
options
:
[
{
label
:
'string'
,
value
:
'string'
,
},
{
label
:
'boolean | number | object (使用JSON.parse)'
,
value
:
'object'
,
},
],
},
},
{
fieldName
:
'value'
,
component
:
'Input'
,
label
:
'变量值'
,
rules
:
'required'
,
},
],
showDefaultActions
:
false
,
wrapperClass
:
'grid-cols-2'
,
resetButtonOptions
:
{
show
:
false
,
},
submitButtonOptions
:
{
content
:
'修改'
,
},
handleSubmit
:
async
(
values
)
=>
{
console
.
log
(
values
);
Modal
.
confirm
({
title
:
'修改流程变量'
,
content
:
'确认修改流程变量吗?'
,
centered
:
true
,
okButtonProps
:
{
danger
:
true
,
},
onOk
:
async
()
=>
{
await
handleSubmit
(
values
);
},
});
},
});
async
function
handleCancel
()
{
modalApi
.
close
();
await
formApi
.
resetForm
();
}
async
function
handleSubmit
()
{
async
function
handleSubmit
(
values
:
any
)
{
try
{
modalApi
.
modalLoading
(
true
);
const
{
valid
}
=
await
formApi
.
validate
();
if
(
!
valid
)
{
return
;
modalApi
.
lock
(
true
);
const
{
instanceId
}
=
modalApi
.
getData
()
as
ModalData
;
let
transformValue
=
values
.
value
;
if
(
values
.
valueType
!==
'string'
)
{
try
{
transformValue
=
JSON
.
parse
(
values
.
value
);
}
catch
(
error
)
{
console
.
error
(
error
);
if
(
error
instanceof
Error
)
{
message
.
error
(
error
.
message
);
}
throw
error
;
}
}
const
data
=
cloneDeep
(
await
formApi
.
getValues
());
data
.
id
=
modalApi
.
getData
().
id
;
await
workflowInstanceInvalid
(
data
as
any
);
emit
(
'reload'
);
handleCancel
();
// 修改
const
requestData
=
{
instanceId
,
key
:
values
.
key
,
value
:
transformValue
,
};
await
updateFlowVariable
(
requestData
);
await
formApi
.
resetForm
();
// 查询修改后的
const
resp
=
await
instanceVariable
(
instanceId
);
const
jsonObj
=
JSON
.
parse
(
resp
.
variable
);
data
.
value
=
jsonObj
;
}
catch
(
error
)
{
console
.
error
(
error
);
}
finally
{
modalApi
.
modalLoading
(
false
);
modalApi
.
lock
(
false
);
}
}
</
script
>
<
template
>
<BasicModal>
<BasicForm
/>
<div
:class=
"cn('min-h-[400px] overflow-y-auto border', 'rounded-[4px] p-2')"
>
<JsonPreview
:data=
"data"
/>
</div>
<!--
<div
class=
"mt-2 break-all text-sm font-medium text-orange-500"
>
需要支持变量类型需要更改后端代码(原版只支持string类型)
<div>
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java
</div>
将value的类型改为Object才能使用
</div>
-->
<Form
class=
"mt-2"
/>
</BasicModal>
</
template
>
packages/@core/ui-kit/menu-ui/src/components/menu.vue
View file @
8d4896cf
...
...
@@ -495,7 +495,7 @@ $namespace: vben;
&
.is-rounded
{
--menu-item-margin-x
:
8px
;
--menu-item-collapse-margin-x
:
6px
;
--menu-item-radius
:
8
px
;
--menu-item-radius
:
6
px
;
}
&
.is-horizontal
:not
(
.is-rounded
)
{
...
...
packages/effects/access/src/directive.ts
View file @
8d4896cf
...
...
@@ -12,15 +12,13 @@ function isAccessible(
el
:
Element
,
binding
:
DirectiveBinding
<
string
|
string
[]
>
,
)
{
const
{
accessMode
,
hasAccessByCodes
,
hasAccessByRoles
}
=
useAccess
();
const
{
hasAccessByCodes
,
hasAccessByRoles
}
=
useAccess
();
const
value
=
binding
.
value
;
if
(
!
value
)
return
;
const
authMethod
=
accessMode
.
value
===
'frontend'
&&
binding
.
arg
===
'role'
?
hasAccessByRoles
:
hasAccessByCodes
;
binding
.
arg
===
'role'
?
hasAccessByRoles
:
hasAccessByCodes
;
const
values
=
Array
.
isArray
(
value
)
?
value
:
[
value
];
...
...
packages/effects/access/src/use-access.ts
View file @
8d4896cf
...
...
@@ -17,6 +17,10 @@ function useAccess() {
*/
function
hasAccessByRoles
(
roles
:
string
[])
{
const
userRoleSet
=
new
Set
(
userStore
.
userRoles
);
// 超管的角色
if
(
userRoleSet
.
has
(
'superadmin'
))
{
return
true
;
}
const
intersection
=
roles
.
filter
((
item
)
=>
userRoleSet
.
has
(
item
));
return
intersection
.
length
>
0
;
}
...
...
packages/effects/plugins/src/vxe-table/style.css
View file @
8d4896cf
...
...
@@ -133,3 +133,8 @@ TODO: 最后一条数据hover/check仍会显示边框
border-radius
:
var
(
--vxe-ui-table-border-radius
)
var
(
--vxe-ui-table-border-radius
)
0
0
;
}
/* modal/drawer里使用列配置 重置列弹窗被遮挡 */
.vxe-dynamics--modal
>
.vxe-modal--wrapper
{
z-index
:
calc
(
var
(
--popup-z-index
)
+
1
)
!important
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment