This commit is contained in:
2025-09-08 16:38:12 +08:00
parent 0730f73b01
commit 627c678e11

View File

@@ -0,0 +1,313 @@
<script lang="ts" setup>
import { ref, computed } from 'vue'
import AuthTree from './AuthTree.vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const errorMessage = ref('')
const logic = ref<'or' | 'and'>('or')
const relationList = ref<any[]>([])
const svgRealinePath = computed(() => {
const lg = relationList.value.length
let a = { x: 0, y: 0, child: relationList.value }
a.y = Math.floor(dfsXY(a, 0) / 2)
if (!lg) return ''
let path = calculateDepth(a)
return path
})
const svgDashinePath = computed(() => {
const lg = relationList.value.length
let a = { x: 0, y: 0, child: relationList.value }
a.y = Math.floor(dfsXY(a, 0) / 2)
if (!lg) return `M48 20 L68 20`
let path = calculateDepthDash(a)
return path
})
const init = (expressionTree: any) => {
const { logic: lg = 'or', items = [] } = expressionTree
logic.value = lg
relationList.value = dfsInit(items)
}
const submit = () => {
errorMessage.value = ''
emits('save', {
logic: logic.value,
items: dfsSubmit(relationList.value),
errorMessage: errorMessage.value,
})
}
const errorDetected = ({ filter_type, field_id, term, value }: any) => {
if (!field_id) {
errorMessage.value = t('permission.cannot_be_empty_')
return
}
if (filter_type === 'logic') {
if (!term) {
errorMessage.value = t('permission.cannot_be_empty_de_ruler')
return
}
if (!term.includes('null') && !term.includes('empty') && value === '') {
errorMessage.value = t('permission.filter_value_can_null')
return
}
}
}
const dfsInit = (arr: any[]) => {
const elementList: any[] = []
arr.forEach((ele: any) => {
const { sub_tree } = ele
if (sub_tree) {
const { items, logic } = sub_tree
const child = dfsInit(items)
elementList.push({ logic, child })
} else {
const { enum_value, field_id, filter_type, term, value, field } = ele
const { name } = field || {}
elementList.push({
enum_value: enum_value.join(','),
field_id,
filter_type,
term,
value,
name,
})
}
})
return elementList
}
const dfsSubmit = (arr: any[]) => {
const items: any[] = []
arr.forEach((ele) => {
const { child = [] } = ele
if (child.length) {
const { logic } = ele
const sub_tree = dfsSubmit(child)
items.push({
enum_value: [],
field_id: '',
filter_type: '',
term: '',
type: 'tree',
value: '',
sub_tree: { logic, items: sub_tree },
})
} else {
const { enum_value, field_id, filter_type, term, value, name } = ele
errorDetected({ enum_value, field_id, filter_type, term, value, name })
if (field_id) {
items.push({
enum_value: enum_value ? enum_value.split(',') : [],
field_id,
filter_type,
term,
value,
type: 'item',
sub_tree: null,
})
}
}
})
return items
}
const removeRelationList = () => {
relationList.value = []
}
const getY: any = (arr: any[]) => {
const [a] = arr
if (a.child?.length) {
return getY(a.child)
}
return a.y
}
const calculateDepthDash = (obj: any) => {
const lg = obj.child?.length
let path = ''
if (!lg && Array.isArray(obj.child)) {
const { x, y } = obj
path += `M${48 + x * 68} ${y * 41.4 + 20} L${88 + x * 68} ${y * 41.4 + 20}`
} else if (obj.child?.length) {
let y = Math.max(dfsY(obj, 0), dfs(obj.child, 0) + getY(obj.child) - 1)
let parent = (dfs(obj.child, 0) * 41.4) / 2 + (getY(obj.child) || 0) * 41.4
const { x } = obj
path += `M${24 + x * 68} ${parent} L${24 + x * 68} ${y * 41.4 + 20} L${
64 + x * 68
} ${y * 41.4 + 20}`
obj.child.forEach((item: any) => {
path += calculateDepthDash(item)
})
}
return path
}
const calculateDepth = (obj: any) => {
const lg = obj.child.length
if (!lg) return ''
let path = ''
const { x: depth, y } = obj
obj.child.forEach((item: any, index: any) => {
const { y: sibingLg, z } = item
if (item.child?.length) {
let parent = (dfs(obj.child, 0) * 41.4) / 2 + (getY(obj.child) || 0) * 41.4
let children = (dfs(item.child, 0) * 41.4) / 2 + getY(item.child) * 41.4
let path1 = 0
let path2 = 0
if (parent < children) {
path1 = parent
path2 = children
} else {
;[path1, path2] = [children, parent]
}
if (y >= sibingLg) {
path1 = parent
path2 = children
}
path += `M${24 + depth * 68} ${path1} L${24 + depth * 68} ${path2} L${
68 + depth * 68
} ${path2}`
// path += a;
path += calculateDepth(item)
}
if (!item.child?.length) {
if (sibingLg >= y) {
path += `M${24 + depth * 68} ${y * 40} L${24 + depth * 68} ${
(sibingLg + 1) * 41.4 - 20.69921875
} L${68 + depth * 68} ${(sibingLg + 1) * 41.4 - 20.69921875}`
} else {
path += `M${24 + depth * 68} ${
(sibingLg +
(lg === 1 && index === 0 ? 0 : 1) +
(obj.child[index + 1]?.child?.length ? y - sibingLg - 1 : 0)) *
41.4 +
20 +
(lg === 1 && index === 0 ? 26 : 0)
} L${24 + depth * 68} ${
(sibingLg + 1) * 41.4 - 20.69921875 - (lg === 1 && index === 0 ? (z || 0) * 1.4 : 0)
} L${68 + depth * 68} ${
(sibingLg + 1) * 41.4 - 20.69921875 - (lg === 1 && index === 0 ? (z || 0) * 1.4 : 0)
}`
}
}
})
return path
}
const changeAndOrDfs = (arr: any, logic: any) => {
arr.forEach((ele: any) => {
if (ele.child) {
ele.logic = logic === 'and' ? 'or' : 'and'
changeAndOrDfs(ele.child, ele.logic)
}
})
}
const dfs = (arr: any[], count: any) => {
arr.forEach((ele) => {
if (ele.child?.length) {
count = dfs(ele.child, count)
} else {
count += 1
}
})
count += 1
return count
}
const dfsY = (obj: any, count: any) => {
obj.child.forEach((ele: any) => {
if (ele.child?.length) {
count = dfsY(ele, count)
} else {
count = Math.max(count, ele.y, obj.y)
}
})
return count
}
const dfsXY = (obj: any, count: any) => {
obj.child.forEach((ele: any) => {
ele.x = obj.x + 1
if (ele.child?.length) {
let l = dfs(ele.child, 0)
ele.y = Math.floor(l / 2) + count
count = dfsXY(ele, count)
} else {
count += 1
ele.y = count - 1
}
})
count += 1
return count
}
const addCondReal = (type: any, logic: any) => {
relationList.value.push(
type === 'condition'
? {
field_id: '',
value: '',
enum_value: '',
term: '',
filter_type: 'logic',
name: '',
}
: { child: [], logic }
)
}
const del = (index: any) => {
relationList.value.splice(index, 1)
}
defineExpose({
init,
submit,
})
const emits = defineEmits(['save'])
</script>
<template>
<div class="rowAuth">
<auth-tree
v-model:logic="logic"
:relation-list="relationList"
@del="(idx) => del(idx)"
@add-cond-real="addCondReal"
@remove-relation-list="removeRelationList"
@change-and-or-dfs="(type) => changeAndOrDfs(relationList, type)"
/>
<svg width="388" height="100%" class="real-line">
<path
stroke-linejoin="round"
stroke-linecap="round"
:d="svgRealinePath"
fill="none"
stroke="#D9DCDF"
stroke-width="0.5"
></path>
</svg>
<svg width="388" height="100%" class="dash-line">
<path
stroke-linejoin="round"
stroke-linecap="round"
:d="svgDashinePath"
fill="none"
stroke="#D9DCDF"
stroke-width="0.5"
stroke-dasharray="4,4"
></path>
</svg>
</div>
</template>
<style lang="less" scoped>
.rowAuth {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
position: relative;
}
.real-line,
.dash-line {
position: absolute;
top: 0;
left: 0;
user-select: none;
}
</style>