import {
|
computed,
|
defineComponent,
|
ref,
|
onMounted,
|
watch,
|
SetupContext,
|
PropType,
|
nextTick,
|
} from 'vue'
|
import BaseDrawer from '@/components/BaseDrawer/BaseDrawer'
|
import DyForm from '@/components/DyForm/DyForm'
|
import { getFlowDetail, getFlowData } from '@/api/logic-flow'
|
import { FormItemPropType } from '@/components/DyForm/DyForm.d'
|
import ElInputNumber from 'element-plus/es/components/input-number/index'
|
import { cloneDeep, debounce, isFunction, sortBy, throttle } from 'lodash'
|
import styles from './EdgeDrawer.module.scss'
|
import { fittingString } from '../../core/transformHelp'
|
import { width, fontSize } from '../Nodes'
|
import ConditionDialog from '../ConditionDialog/ConditionDialog'
|
import { ConditionType, ValueText, CompositeCondition } from '../../core/enum'
|
import { Condition } from '../../type'
|
import { injectStore } from '../../core/store'
|
import Icon from '@/components/Icon/Icon'
|
import FlowContextDialog from '@/components/FlowContextDialog/FlowContextDialog'
|
import { WidgetNameType } from '../Models/WidgetTypeByEnum'
|
import { _t } from '@/libs/Language/Language'
|
|
export default defineComponent({
|
name: 'NodeDrawer',
|
props: {
|
title: {
|
type: String,
|
},
|
model: {
|
type: Object,
|
default: () => ({}),
|
},
|
modelValue: {
|
type: Boolean,
|
},
|
graph: {
|
type: Object,
|
default: null,
|
},
|
type: {
|
type: String,
|
default: '',
|
},
|
edgeData: {
|
type: Array,
|
default: [],
|
},
|
isEdit: {
|
type: Boolean,
|
default: false,
|
},
|
},
|
emits: ['update:modelValue', 'close', 'confirm', 'update:title'],
|
setup(props, { slots, emit }) {
|
const { flowMap, flowNodeMap, edgeMap } = injectStore()
|
const isOpen = ref(false)
|
const edgeType = ref('')
|
const title = ref(props.title)
|
const formRef = ref<any>(null)
|
const formConditionRef = ref<any>(null)
|
const conditionFormItemProps = ref<FormItemPropType>([])
|
const formDataCondition = ref({})
|
const conditionConfig = ref<Record<string, any>>({
|
has: false,
|
item: {},
|
})
|
|
const conditionOptions = ref<
|
{
|
label: string
|
value: string
|
}[]
|
>([])
|
|
const visible = computed({
|
get: () => props.modelValue,
|
set: (value) => {
|
emit('update:modelValue', value)
|
},
|
})
|
const model = computed(() => props.model)
|
|
const currentCondition = computed(() => {
|
const data = formData.value?.Condition || {}
|
|
return getCondition(edgeType.value, data)
|
})
|
|
const currentTip = computed(() => {
|
const type = model.value?.properties?.[ConditionType]
|
return edgeMap.get(type)?.description
|
})
|
|
const formData: Record<string, any> = ref()
|
|
const formItemProps = ref<FormItemPropType>([])
|
|
const customWidgetMap = {
|
inputNumber: ElInputNumber,
|
flowItemKey: FlowContextDialog,
|
|
condition: (props: any, { attrs }: SetupContext) => (
|
<ConditionDialog
|
formItemProps={conditionFormItemProps.value}
|
conditionOptions={conditionOptions.value}
|
onConditionChange={onConditionChange}
|
onUpdateFormData={onUpdateFormData}
|
getCondition={getCondition}
|
v-model:formData={formData.value.Condition}
|
{...props}
|
{...attrs}
|
/>
|
),
|
}
|
|
const onClose = () => {
|
visible.value = false
|
isOpen.value = false
|
emit('close')
|
}
|
|
const onUpdateFormData = (data: Condition) => {
|
formData.value.Condition = data
|
}
|
|
const onConfirm = () => {
|
const graph = props.graph
|
const item = graph.findById(model.value.id)
|
const modelData = cloneDeep(model.value)
|
modelData.properties = formData.value
|
modelData.label = formData.value?.Condition?.Label || formData.value.Label
|
|
item.update(modelData)
|
visible.value = false
|
emit('confirm')
|
}
|
|
const formItemSort = (formItems: FormItemPropType[]) => {
|
return sortBy(formItems, ['sort'])
|
}
|
|
const getConditionsItems = async (type: string) => {
|
const data = await getFlowDetail(type)
|
return generateFormItems(data.attributes || [])
|
}
|
|
const getConditions = async () => {
|
const data = await getFlowData('Conditions')
|
const conditions: Record<string, any> = data.Conditions || []
|
conditionOptions.value = conditions.map((item: any) => {
|
let name = item.type === CompositeCondition ? _t('复合条件') : item.name
|
return {
|
...item,
|
label: name,
|
value: item.type,
|
tip: item.description,
|
}
|
})
|
}
|
|
const onClearCondition = (val: string) => {
|
if (!val) {
|
formData.value.Condition = {}
|
}
|
}
|
|
const onConditionChange = async (
|
val: string,
|
isNative: boolean = true,
|
isSelectChange?: boolean
|
) => {
|
const data = await getConditionsItems(val)
|
if (isSelectChange) {
|
formData.value.Condition.Operator = ''
|
}
|
if (isNative) {
|
edgeType.value = val
|
conditionFormItemProps.value = data
|
}
|
return data
|
}
|
const getRules = (item: any) => {
|
return item.required
|
? [
|
{
|
required: true,
|
message: item.name,
|
trigger: 'blur',
|
},
|
]
|
: []
|
}
|
const generateFormItems = (attributes: any[]) => {
|
const formItems: any[] = []
|
|
attributes?.forEach((item: FormItemPropType) => {
|
const placeholder = WidgetNameType[item.propertyType]?.placeholder
|
const el =
|
WidgetNameType[item.propertyType]?.el ||
|
WidgetNameType[item.propertyType]?.type
|
const type = WidgetNameType[item.propertyType]?.el
|
? WidgetNameType[item.propertyType]?.type
|
: undefined
|
if (item.propertyType !== 'Condition') {
|
const formItem: FormItemPropType = {
|
label: item.name,
|
// prop:
|
// item.propertyType === 'Object'
|
// ? `${item.propertyKey}.${ValueText}`
|
// : item.propertyKey,
|
prop: item.propertyKey,
|
readonly: item.readonly,
|
el,
|
rules: getRules(item),
|
tip: item.description || item.name,
|
icon: 'wen',
|
options: item.propertyData?.map((item: any) => {
|
return {
|
label: item.name,
|
tip: item.description,
|
value: item.value,
|
}
|
}),
|
disabled: ['Source', 'Sink'].includes(item.propertyKey),
|
defaultValue: item.propertyValue,
|
clearable: !item.nullable ? false : true,
|
controlsPosition: 'right',
|
step: 1,
|
min: WidgetNameType[item.propertyType]?.type?.min ?? undefined,
|
nullable: item.nullable ? undefined : item.propertyValue,
|
valueOnClear: item.nullable ? undefined : item.propertyValue,
|
type,
|
rows: WidgetNameType[item.propertyType]?.rows || 10,
|
}
|
|
if (item.pattern) {
|
formItem.rules.push({
|
pattern: new RegExp(item.pattern),
|
message: item.ruleMessage || _t('格式错误'),
|
trigger: 'blur',
|
})
|
}
|
if (placeholder) {
|
formItem.placeholder = placeholder + item.name
|
}
|
if (item.visible) {
|
formItems.push(formItem)
|
}
|
} else {
|
conditionConfig.value.has = true
|
conditionConfig.value.item = item
|
}
|
})
|
|
return formItems
|
}
|
|
const onOpen = async () => {
|
const type =
|
model.value?.properties?.[ConditionType] || 'BusinessTransition'
|
if (!props.model && type) return
|
isOpen.value = true
|
getConditions()
|
const data = await getFlowDetail(type)
|
const attributes = data.attributes || []
|
const formItems: FormItemPropType[] = generateFormItems(attributes)
|
if (conditionConfig.value.has) {
|
formItems.push({
|
label: conditionConfig.value.item.name,
|
clearable: conditionConfig.value.item.nullable ? true : false,
|
prop: `Condition.${ConditionType}`,
|
el: 'select',
|
placeholder: _t('请选择'),
|
icon: 'wen',
|
defaultValue: '',
|
rules: getRules(conditionConfig.value.item),
|
options: conditionOptions.value,
|
tip: conditionConfig.value.item.description,
|
onClear: onClearCondition,
|
onChange: (val: string, isNative: boolean) =>
|
onConditionChange(val, isNative, true),
|
})
|
}
|
title.value = data.name
|
formItemProps.value = formItemSort(formItems)
|
nextTick(() => {
|
formRef.value?.initFormData()
|
initEdgeFormData()
|
})
|
}
|
const initEdgeFormData = () => {
|
formData.value = {}
|
nextTick(() => {
|
const properties = cloneDeep(model.value?.properties)
|
const Condition = properties?.Condition || {}
|
formData.value = {
|
...formData.value,
|
...properties,
|
}
|
onConditionChange(Condition[ConditionType])
|
const { oldSourceId, oldTargetId } = formData.value
|
|
if (oldSourceId && oldTargetId) {
|
const sourceName = flowMap.get(oldSourceId).name
|
const sinkName = flowMap.get(oldTargetId).name
|
formData.value.Source = sourceName
|
formData.value.Sink = sinkName
|
} else {
|
const sourceName = flowNodeMap.get(model.value?.source)?.name
|
const sinkName = flowNodeMap.get(model.value?.target)?.name
|
formData.value.Source = sourceName
|
formData.value.Sink = sinkName
|
}
|
})
|
}
|
const OperatorType: Record<string, string> = {
|
RelOpEqual: '=',
|
RelOpLess: '<',
|
RelOpLarge: '>',
|
RelOpLessEq: '<=',
|
RelOpLargeEq: '>=',
|
RelOpNotEqual: '!=',
|
RelOpContain: 'Contains',
|
}
|
/**
|
* 获取条件
|
* @returns
|
*/
|
const getCondition = (type: string, data: Record<string, any>) => {
|
const not = data.NOT ? '!' : ''
|
const typeMap: Record<string, Function> = {
|
PropertyCondition() {
|
// PropertyToCompare需要比较的值
|
// Property 属性
|
// Operator 比较符
|
// Value 常量
|
// Parameter 参数名
|
const result = data.PropertyToCompare || data.Value
|
const property =
|
data.Parameter && data.Property
|
? `${data.Parameter}.${data.Property}`
|
: data.Property || data.Parameter
|
if (property && OperatorType[data.Operator] && result) {
|
return `${not} ${property} ${OperatorType[data.Operator]} ${result}`
|
}
|
},
|
CompositeCondition() {
|
const treeToString: any = (tree: any[]) => {
|
if (!Array.isArray(tree) || tree.length === 0) {
|
return ''
|
}
|
|
return tree
|
.map((node) => {
|
if (node.children && node.children.length > 0) {
|
// 递归处理子节点
|
return `(${treeToString(node.children)})`
|
} else {
|
// 处理当前节点
|
return node.condition?.trim()
|
}
|
})
|
.join(` ${tree[0].operator?.trim()} `)
|
}
|
const fn = (data: any[], root: boolean = false) => {
|
let sumArray: any[] = []
|
data.forEach((item) => {
|
const operator = item.Operator
|
if (Array.isArray(item.children)) {
|
item.children.forEach((composition: any) => {
|
const type = composition[ConditionType]
|
|
if (type === CompositeCondition) {
|
if (composition.children) {
|
sumArray.push({
|
children: fn([composition]),
|
operator,
|
})
|
}
|
} else {
|
const condition = getCondition(type, composition)
|
sumArray.push({
|
condition,
|
operator,
|
})
|
}
|
})
|
}
|
})
|
return sumArray
|
}
|
const condition = treeToString(fn([data]))
|
return `${not} ${condition}`
|
},
|
ChoiceCondition() {
|
const name = data.ActivityRowName
|
const choice = data.Choice
|
if (name && choice) {
|
return `${not} ${name}.Choice = ${choice}`
|
}
|
},
|
TableCondition() {
|
const name = data.PropertyName
|
const operator = OperatorType[data.Operator]
|
const value = data.Value
|
if (name && operator && value) {
|
return `${not} ${name} ${operator} ${value}`
|
}
|
},
|
XMLCondition() {
|
const parameter = data.Parameter
|
const prefix = data.Prefix
|
const xPath = data.XPath
|
if (parameter && prefix && xPath) {
|
return `${not} ${parameter}[${prefix}:${xPath}].Result>0`
|
}
|
},
|
}
|
const fn = typeMap[type]
|
return isFunction(fn) && fn()
|
}
|
|
onMounted(() => {
|
if (!isOpen.value) {
|
onOpen()
|
}
|
})
|
|
return () => {
|
return (
|
<BaseDrawer
|
onClose={onClose}
|
onConfirm={onConfirm}
|
onOpen={onOpen}
|
width="700px"
|
v-model={visible.value}
|
submitDisabled={!formItemProps.value.length || !props.isEdit}
|
destroy-on-close
|
v-slots={{
|
title: () => {
|
return (
|
<div class={styles.edgeDialogTitle}>
|
{title.value || model.value.label}
|
<el-tooltip
|
effect="dark"
|
content={currentTip.value}
|
placement="top"
|
>
|
<Icon icon="wen" />
|
</el-tooltip>
|
</div>
|
)
|
},
|
}}
|
>
|
<div class={styles.drawContent}>
|
{formItemProps.value.length ? (
|
<DyForm
|
ref={formRef}
|
customWidgetMap={customWidgetMap}
|
formItemProps={formItemProps.value}
|
v-model:formData={formData.value}
|
labelWidth="140px"
|
></DyForm>
|
) : (
|
<el-empty image-size={200} description={_t('暂无数据')} />
|
)}
|
{conditionConfig.value.has ? (
|
<div class={styles.formDrawer}>
|
<DyForm
|
ref={formConditionRef}
|
customWidgetMap={customWidgetMap}
|
formItemProps={conditionFormItemProps.value}
|
v-model:formData={formData.value.Condition}
|
labelWidth="140px"
|
></DyForm>
|
</div>
|
) : null}
|
|
<div class={styles.condition}>{currentCondition.value}</div>
|
</div>
|
</BaseDrawer>
|
)
|
}
|
},
|
})
|