forked from github/dataease
fix(仪表板): 图表过滤器日期类型字段支持动态日期
This commit is contained in:
parent
48592ccccb
commit
d54328e821
@ -0,0 +1,135 @@
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, PropType, ref, onBeforeMount, watch, computed } from 'vue'
|
||||
import { type DatePickType } from 'element-plus-secondary'
|
||||
import {
|
||||
getThisYear,
|
||||
getLastYear,
|
||||
getThisMonth,
|
||||
getLastMonth,
|
||||
getToday,
|
||||
getYesterday,
|
||||
getMonthBeginning,
|
||||
getYearBeginning,
|
||||
getCustomTime
|
||||
} from './time-format'
|
||||
interface SelectConfig {
|
||||
relativeToCurrent: string
|
||||
timeNum: number
|
||||
relativeToCurrentType: string
|
||||
around: string
|
||||
arbitraryTime: Date
|
||||
timeGranularity: DatePickType
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object as PropType<SelectConfig>,
|
||||
default: () => {
|
||||
return {
|
||||
relativeToCurrent: 'custom',
|
||||
timeNum: 0,
|
||||
relativeToCurrentType: 'year',
|
||||
around: 'f',
|
||||
arbitraryTime: new Date(),
|
||||
timeGranularity: 'year'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
const selectValue = ref()
|
||||
|
||||
const { config } = toRefs(props)
|
||||
|
||||
const timeConfig = computed(() => {
|
||||
const {
|
||||
relativeToCurrent,
|
||||
timeNum,
|
||||
relativeToCurrentType,
|
||||
around,
|
||||
arbitraryTime,
|
||||
timeGranularity
|
||||
} = config.value
|
||||
return {
|
||||
relativeToCurrent,
|
||||
timeNum,
|
||||
relativeToCurrentType,
|
||||
around,
|
||||
arbitraryTime,
|
||||
timeGranularity
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => timeConfig.value,
|
||||
() => {
|
||||
init()
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
const init = () => {
|
||||
const {
|
||||
relativeToCurrent,
|
||||
timeNum,
|
||||
relativeToCurrentType,
|
||||
around,
|
||||
arbitraryTime,
|
||||
timeGranularity
|
||||
} = timeConfig.value
|
||||
if (relativeToCurrent === 'custom') {
|
||||
selectValue.value = getCustomTime(
|
||||
timeNum,
|
||||
relativeToCurrentType,
|
||||
timeGranularity,
|
||||
around,
|
||||
timeGranularity === 'datetime' ? arbitraryTime : null
|
||||
)
|
||||
} else {
|
||||
switch (relativeToCurrent) {
|
||||
case 'thisYear':
|
||||
selectValue.value = getThisYear()
|
||||
break
|
||||
case 'lastYear':
|
||||
selectValue.value = getLastYear()
|
||||
break
|
||||
case 'thisMonth':
|
||||
selectValue.value = getThisMonth()
|
||||
break
|
||||
case 'lastMonth':
|
||||
selectValue.value = getLastMonth()
|
||||
break
|
||||
case 'today':
|
||||
selectValue.value = getToday()
|
||||
break
|
||||
case 'yesterday':
|
||||
selectValue.value = getYesterday()
|
||||
break
|
||||
case 'monthBeginning':
|
||||
selectValue.value = getMonthBeginning()
|
||||
break
|
||||
case 'yearBeginning':
|
||||
selectValue.value = getYearBeginning()
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-date-picker
|
||||
disabled
|
||||
:key="config.timeGranularity"
|
||||
v-model="selectValue"
|
||||
:type="config.timeGranularity"
|
||||
:placeholder="$t('commons.date.select_date_time')"
|
||||
/>
|
||||
</template>
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from 'element-plus-secondary'
|
||||
import { inject, computed, ref, nextTick, provide } from 'vue'
|
||||
import RowAuth from '@/views/visualized/data/dataset/auth-tree/RowAuth.vue'
|
||||
import RowAuth from '@/views/chart/components/editor/filter/auth-tree/RowAuth.vue'
|
||||
|
||||
const emits = defineEmits(['filter-data'])
|
||||
const filedList = inject('filedList', () => [])
|
||||
|
@ -0,0 +1,308 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import DynamicTime from '@/custom-component/v-query/DynamicTimeForViewFilter.vue'
|
||||
import { type DatePickType } from 'element-plus-secondary'
|
||||
export interface SelectConfig {
|
||||
relativeToCurrent: string
|
||||
timeNum: number
|
||||
relativeToCurrentType: string
|
||||
around: string
|
||||
arbitraryTime: Date
|
||||
timeGranularity: DatePickType
|
||||
}
|
||||
|
||||
const defaultValue: SelectConfig = {
|
||||
relativeToCurrent: 'custom', //相对当前 thisYear | lastYear | thisMonth | lastMonth | today | yesterday | monthBeginning | yearBeginning
|
||||
timeGranularity: 'year', //时间粒度 year | month | date | datetime
|
||||
timeNum: 0, // 数值
|
||||
relativeToCurrentType: 'year', // year | month | date
|
||||
around: 'b', // 前 b | 后 f
|
||||
arbitraryTime: new Date() //timeGranularity = datetime时 取时分秒
|
||||
}
|
||||
const curComponent = ref<SelectConfig>({ ...defaultValue })
|
||||
|
||||
const init = (val: SelectConfig) => {
|
||||
curComponent.value = {
|
||||
...defaultValue,
|
||||
...val
|
||||
}
|
||||
}
|
||||
|
||||
const aroundList = [
|
||||
{
|
||||
label: '前',
|
||||
value: 'f'
|
||||
},
|
||||
{
|
||||
label: '后',
|
||||
value: 'b'
|
||||
}
|
||||
]
|
||||
const relativeToCurrentList = computed(() => {
|
||||
let list = []
|
||||
if (!curComponent.value) return list
|
||||
switch (curComponent.value.timeGranularity) {
|
||||
case 'year':
|
||||
list = [
|
||||
{
|
||||
label: '今年',
|
||||
value: 'thisYear'
|
||||
},
|
||||
{
|
||||
label: '去年',
|
||||
value: 'lastYear'
|
||||
}
|
||||
]
|
||||
break
|
||||
case 'month':
|
||||
list = [
|
||||
{
|
||||
label: '本月',
|
||||
value: 'thisMonth'
|
||||
},
|
||||
{
|
||||
label: '上月',
|
||||
value: 'lastMonth'
|
||||
}
|
||||
]
|
||||
break
|
||||
case 'date':
|
||||
list = [
|
||||
{
|
||||
label: '今天',
|
||||
value: 'today'
|
||||
},
|
||||
{
|
||||
label: '昨天',
|
||||
value: 'yesterday'
|
||||
},
|
||||
{
|
||||
label: '月初',
|
||||
value: 'monthBeginning'
|
||||
},
|
||||
{
|
||||
label: '年初',
|
||||
value: 'yearBeginning'
|
||||
}
|
||||
]
|
||||
break
|
||||
case 'datetime':
|
||||
list = [
|
||||
{
|
||||
label: '今天',
|
||||
value: 'today'
|
||||
},
|
||||
{
|
||||
label: '昨天',
|
||||
value: 'yesterday'
|
||||
},
|
||||
{
|
||||
label: '月初',
|
||||
value: 'monthBeginning'
|
||||
},
|
||||
{
|
||||
label: '年初',
|
||||
value: 'yearBeginning'
|
||||
}
|
||||
]
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return [
|
||||
...list,
|
||||
{
|
||||
label: '自定义',
|
||||
value: 'custom'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const relativeToCurrentTypeList = computed(() => {
|
||||
if (!curComponent.value) return []
|
||||
let index = ['year', 'month', 'date', 'datetime'].indexOf(curComponent.value.timeGranularity) + 1
|
||||
return [
|
||||
{
|
||||
label: '年',
|
||||
value: 'year'
|
||||
},
|
||||
{
|
||||
label: '月',
|
||||
value: 'month'
|
||||
},
|
||||
{
|
||||
label: '日',
|
||||
value: 'date'
|
||||
}
|
||||
].slice(0, index)
|
||||
})
|
||||
|
||||
const timeGranularityChange = (val: string) => {
|
||||
if (
|
||||
['year', 'month', 'date', 'datetime'].indexOf(val) <
|
||||
['year', 'month', 'date'].indexOf(curComponent.value.relativeToCurrentType)
|
||||
) {
|
||||
curComponent.value.relativeToCurrentType = 'year'
|
||||
}
|
||||
if (curComponent.value.relativeToCurrent !== 'custom') {
|
||||
curComponent.value.relativeToCurrent = relativeToCurrentList.value[0]?.value
|
||||
}
|
||||
}
|
||||
|
||||
const timeList = [
|
||||
{
|
||||
label: '年',
|
||||
value: 'year'
|
||||
},
|
||||
{
|
||||
label: '年月',
|
||||
value: 'month'
|
||||
},
|
||||
{
|
||||
label: '年月日',
|
||||
value: 'date'
|
||||
},
|
||||
{
|
||||
label: '年月日时分秒',
|
||||
value: 'datetime'
|
||||
}
|
||||
]
|
||||
|
||||
defineExpose({
|
||||
init,
|
||||
curComponent
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="time-dialog">
|
||||
<div class="setting">
|
||||
<div class="setting-label">时间粒度</div>
|
||||
<div class="setting-value select">
|
||||
<el-select
|
||||
@change="timeGranularityChange"
|
||||
placeholder="请选择时间粒度"
|
||||
v-model="curComponent.timeGranularity"
|
||||
>
|
||||
<el-option
|
||||
v-for="ele in timeList"
|
||||
:key="ele.value"
|
||||
:label="ele.label"
|
||||
:value="ele.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting">
|
||||
<div class="setting-label">相对当前</div>
|
||||
<div class="setting-value select">
|
||||
<el-select v-model="curComponent.relativeToCurrent">
|
||||
<el-option
|
||||
v-for="item in relativeToCurrentList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting" v-if="curComponent.relativeToCurrent === 'custom'">
|
||||
<div
|
||||
class="setting-input"
|
||||
:class="curComponent.timeGranularity === 'datetime' && 'with-date'"
|
||||
>
|
||||
<el-input-number v-model="curComponent.timeNum" :min="0" controls-position="right" />
|
||||
<el-select v-model="curComponent.relativeToCurrentType">
|
||||
<el-option
|
||||
v-for="item in relativeToCurrentTypeList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select v-model="curComponent.around">
|
||||
<el-option
|
||||
v-for="item in aroundList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-time-picker
|
||||
style="width: 146px; margin-left: 8px"
|
||||
v-if="curComponent.timeGranularity === 'datetime'"
|
||||
v-model="curComponent.arbitraryTime"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting">
|
||||
<div class="setting-label">预览</div>
|
||||
<div class="setting-value">
|
||||
<component :config="curComponent" isConfig ref="inputCom" :is="DynamicTime"></component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.time-dialog {
|
||||
.setting {
|
||||
&.setting {
|
||||
margin-top: 8px;
|
||||
}
|
||||
&.parameters {
|
||||
width: 100%;
|
||||
padding-left: 24px;
|
||||
.ed-date-editor {
|
||||
width: 325px !important;
|
||||
}
|
||||
}
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.setting-label {
|
||||
width: 80px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.setting-value {
|
||||
margin: 8px 0;
|
||||
&.select {
|
||||
margin-top: 0;
|
||||
.ed-select {
|
||||
width: 325px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-input {
|
||||
display: flex;
|
||||
padding-left: 86px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
&.range {
|
||||
padding-left: 0px;
|
||||
}
|
||||
& > div + div {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&.with-date {
|
||||
.ed-input-number {
|
||||
width: 71px;
|
||||
}
|
||||
.ed-select {
|
||||
width: 62px;
|
||||
}
|
||||
|
||||
.ed-date-editor.ed-input {
|
||||
width: 106px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,244 @@
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'logic-relation'
|
||||
}
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { PropType, computed, toRefs } from 'vue'
|
||||
import FilterFiled from './FilterFiled.vue'
|
||||
import type { Item } from './FilterFiled.vue'
|
||||
export type Logic = 'or' | 'and'
|
||||
export type Relation = {
|
||||
child?: Relation[]
|
||||
logic: Logic
|
||||
x: number
|
||||
} & Item
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
relationList: {
|
||||
type: Array as PropType<Relation[]>,
|
||||
default: () => []
|
||||
},
|
||||
x: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
logic: {
|
||||
type: String as PropType<Logic>,
|
||||
default: 'or'
|
||||
}
|
||||
})
|
||||
|
||||
const marginLeft = computed(() => {
|
||||
return {
|
||||
marginLeft: props.x ? '20px' : 0
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits([
|
||||
'addCondReal',
|
||||
'changeAndOrDfs',
|
||||
'update:logic',
|
||||
'removeRelationList',
|
||||
'del'
|
||||
])
|
||||
|
||||
const { relationList } = toRefs(props)
|
||||
|
||||
const handleCommand = type => {
|
||||
emits('update:logic', type)
|
||||
emits('changeAndOrDfs', type)
|
||||
}
|
||||
|
||||
const removeRelationList = index => {
|
||||
relationList.value.splice(index, 1)
|
||||
}
|
||||
const addCondReal = type => {
|
||||
emits('addCondReal', type, props.logic === 'or' ? 'and' : 'or')
|
||||
}
|
||||
const add = (type, child, logic) => {
|
||||
child.push(
|
||||
type === 'condition'
|
||||
? {
|
||||
fieldId: '',
|
||||
value: '',
|
||||
enumValue: '',
|
||||
term: '',
|
||||
filterType: 'logic',
|
||||
name: '',
|
||||
filterTypeTime: 'dateValue',
|
||||
timeValue: '',
|
||||
dynamicTimeSetting: {},
|
||||
deType: ''
|
||||
}
|
||||
: { child: [], logic }
|
||||
)
|
||||
}
|
||||
const del = (index, child) => {
|
||||
child.splice(index, 1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="logic" :style="marginLeft">
|
||||
<div class="logic-left">
|
||||
<div class="operate-title">
|
||||
<span style="color: #bfbfbf" class="mrg-title" v-if="x">
|
||||
{{ logic === 'or' ? 'OR' : 'AND' }}
|
||||
</span>
|
||||
<el-dropdown @command="handleCommand" trigger="click" v-else>
|
||||
<span style="color: rgba(0 0 0 / 65%)" class="mrg-title fir">
|
||||
{{ logic === 'or' ? 'OR' : 'AND' }}
|
||||
<el-icon>
|
||||
<Icon name="icon_down_outlined"></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="and">AND</el-dropdown-item>
|
||||
<el-dropdown-item command="or">OR</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<span class="operate-icon" v-if="x">
|
||||
<el-icon @click="emits('removeRelationList')">
|
||||
<Icon name="icon_delete-trash_outlined"></Icon>
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="logic-right">
|
||||
<template :key="index" v-for="(item, index) in relationList">
|
||||
<logic-relation
|
||||
v-if="item.child"
|
||||
:x="item.x"
|
||||
@del="idx => del(idx, item.child)"
|
||||
@addCondReal="(type, logic) => add(type, item.child, logic)"
|
||||
:logic="item.logic"
|
||||
@removeRelationList="removeRelationList(index)"
|
||||
:relationList="item.child"
|
||||
>
|
||||
</logic-relation>
|
||||
<filter-filed v-else :item="item" @del="emits('del', index)" :index="index"></filter-filed>
|
||||
</template>
|
||||
<div class="logic-right-add">
|
||||
<button @click="addCondReal('condition')" class="operand-btn">
|
||||
+ {{ t('auth.add_condition') }}
|
||||
</button>
|
||||
<button v-if="x < 2" @click="addCondReal('relation')" class="operand-btn">
|
||||
+ {{ t('auth.add_relationship') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.logic {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
|
||||
.logic-left {
|
||||
box-sizing: border-box;
|
||||
width: 48px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
|
||||
.operate-title {
|
||||
font-family: '阿里巴巴普惠体 3.0 55 Regular L3', Hiragino Sans GB, Microsoft YaHei, sans-serif;
|
||||
word-wrap: break-word;
|
||||
box-sizing: border-box;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 65px;
|
||||
background-color: #f8f8fa;
|
||||
line-height: 28px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 28px;
|
||||
|
||||
.mrg-title {
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-left: 11px;
|
||||
margin-right: 11px;
|
||||
line-height: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.operate-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.operate-title {
|
||||
.mrg-title:not(.fir) {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operate-icon {
|
||||
width: 40px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
background-color: #f8f8fa;
|
||||
z-index: 1;
|
||||
display: none;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
display: unset;
|
||||
padding: 5px 3px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logic-right-add {
|
||||
display: flex;
|
||||
height: 41.4px;
|
||||
align-items: center;
|
||||
padding-left: 26px;
|
||||
|
||||
.operand-btn {
|
||||
box-sizing: border-box;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
outline: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
color: #246dff;
|
||||
background: #fff;
|
||||
border: 1px solid #246dff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,375 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import AuthTree from './AuthTree.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const errorMessage = ref('')
|
||||
const logic = ref<'or' | 'and'>('or')
|
||||
const relationList = ref([])
|
||||
|
||||
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 => {
|
||||
const { items } = expressionTree
|
||||
logic.value = expressionTree.logic || 'or'
|
||||
relationList.value = dfsInit(items || [])
|
||||
}
|
||||
const submit = () => {
|
||||
errorMessage.value = ''
|
||||
emits('save', {
|
||||
logic: logic.value,
|
||||
items: dfsSubmit(relationList.value),
|
||||
errorMessage: errorMessage.value
|
||||
})
|
||||
}
|
||||
const errorDetected = ({ enumValue, deType, filterType, term, value, name, timeValue }) => {
|
||||
if (!name) {
|
||||
errorMessage.value = t('data_set.cannot_be_empty_')
|
||||
return
|
||||
}
|
||||
if (filterType === 'logic') {
|
||||
if (!term) {
|
||||
errorMessage.value = t('data_set.cannot_be_empty_de_ruler')
|
||||
return
|
||||
}
|
||||
if (
|
||||
!term.includes('null') &&
|
||||
!term.includes('empty') &&
|
||||
['', null, undefined].includes(value) &&
|
||||
deType !== 1
|
||||
) {
|
||||
errorMessage.value = t('chart.filter_value_can_null')
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
!term.includes('null') &&
|
||||
!term.includes('empty') &&
|
||||
['', null, undefined].includes(timeValue) &&
|
||||
deType === 1
|
||||
) {
|
||||
errorMessage.value = t('chart.filter_value_can_null')
|
||||
return
|
||||
}
|
||||
if ([2, 3].includes(deType)) {
|
||||
if (parseFloat(value).toString() === 'NaN') {
|
||||
errorMessage.value = t('chart.filter_value_can_not_str')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filterType === 'enum') {
|
||||
if (enumValue.length < 1) {
|
||||
errorMessage.value = t('chart.enum_value_can_not_null')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
const dfsInit = arr => {
|
||||
const elementList = []
|
||||
arr.forEach(ele => {
|
||||
const { subTree } = ele
|
||||
if (subTree) {
|
||||
const { items = [], logic } = subTree
|
||||
const child = dfsInit(items)
|
||||
elementList.push({ logic, child })
|
||||
} else {
|
||||
const {
|
||||
enumValue,
|
||||
timeValue,
|
||||
filterTypeTime,
|
||||
dynamicTimeSetting,
|
||||
fieldId,
|
||||
filterType,
|
||||
term,
|
||||
value,
|
||||
field
|
||||
} = ele
|
||||
const { name, deType } = field || {}
|
||||
elementList.push({
|
||||
enumValue: enumValue.join(','),
|
||||
fieldId,
|
||||
filterType,
|
||||
term,
|
||||
timeValue,
|
||||
filterTypeTime,
|
||||
dynamicTimeSetting,
|
||||
value,
|
||||
name,
|
||||
deType
|
||||
})
|
||||
}
|
||||
})
|
||||
return elementList
|
||||
}
|
||||
const dfsSubmit = arr => {
|
||||
const items = []
|
||||
arr.forEach(ele => {
|
||||
const { child = [] } = ele
|
||||
if (child.length) {
|
||||
const { logic } = ele
|
||||
const subTree = dfsSubmit(child)
|
||||
items.push({
|
||||
enumValue: [],
|
||||
fieldId: '',
|
||||
filterType: '',
|
||||
term: '',
|
||||
type: 'tree',
|
||||
value: '',
|
||||
filterTypeTime: 'dateValue',
|
||||
timeValue: '',
|
||||
dynamicTimeSetting: {},
|
||||
subTree: { logic, items: subTree }
|
||||
})
|
||||
} else {
|
||||
const {
|
||||
enumValue,
|
||||
filterTypeTime,
|
||||
dynamicTimeSetting,
|
||||
fieldId,
|
||||
filterType,
|
||||
deType,
|
||||
term,
|
||||
value,
|
||||
name,
|
||||
timeValue
|
||||
} = ele
|
||||
errorDetected({ deType, enumValue, filterType, term, value, name, timeValue })
|
||||
if (fieldId) {
|
||||
items.push({
|
||||
enumValue: enumValue ? enumValue.split(',') : [],
|
||||
fieldId,
|
||||
timeValue,
|
||||
filterType,
|
||||
filterTypeTime,
|
||||
dynamicTimeSetting,
|
||||
term,
|
||||
value,
|
||||
type: 'item',
|
||||
subTree: null
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return items
|
||||
}
|
||||
const removeRelationList = () => {
|
||||
relationList.value = []
|
||||
}
|
||||
const getY = arr => {
|
||||
const [a] = arr
|
||||
if (a.child?.length) {
|
||||
return getY(a.child)
|
||||
}
|
||||
return a.y
|
||||
}
|
||||
const calculateDepthDash = obj => {
|
||||
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 => {
|
||||
path += calculateDepthDash(item)
|
||||
})
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
const calculateDepth = obj => {
|
||||
const lg = obj.child.length
|
||||
if (!lg) return ''
|
||||
let path = ''
|
||||
const { x: depth, y } = obj
|
||||
obj.child.forEach((item, index) => {
|
||||
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 += 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, logic) => {
|
||||
arr.forEach(ele => {
|
||||
if (ele.child) {
|
||||
ele.logic = logic === 'and' ? 'or' : 'and'
|
||||
changeAndOrDfs(ele.child, ele.logic)
|
||||
}
|
||||
})
|
||||
}
|
||||
const dfs = (arr, count) => {
|
||||
arr.forEach(ele => {
|
||||
if (ele.child?.length) {
|
||||
count = dfs(ele.child, count)
|
||||
} else {
|
||||
count += 1
|
||||
}
|
||||
})
|
||||
count += 1
|
||||
return count
|
||||
}
|
||||
const dfsY = (obj, count) => {
|
||||
obj.child.forEach(ele => {
|
||||
if (ele.child?.length) {
|
||||
count = dfsY(ele, count)
|
||||
} else {
|
||||
count = Math.max(count, ele.y, obj.y)
|
||||
}
|
||||
})
|
||||
return count
|
||||
}
|
||||
const dfsXY = (obj, count) => {
|
||||
obj.child.forEach(ele => {
|
||||
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, logic) => {
|
||||
relationList.value.push(
|
||||
type === 'condition'
|
||||
? {
|
||||
fieldId: '',
|
||||
value: '',
|
||||
enumValue: '',
|
||||
term: '',
|
||||
filterType: 'logic',
|
||||
name: '',
|
||||
timeValue: '',
|
||||
filterTypeTime: 'dateValue',
|
||||
dynamicTimeSetting: {},
|
||||
deType: ''
|
||||
}
|
||||
: { child: [], logic }
|
||||
)
|
||||
}
|
||||
const del = index => {
|
||||
relationList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
init,
|
||||
submit
|
||||
})
|
||||
const emits = defineEmits(['save'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rowAuth">
|
||||
<auth-tree
|
||||
@del="idx => del(idx)"
|
||||
@addCondReal="addCondReal"
|
||||
@removeRelationList="removeRelationList"
|
||||
@changeAndOrDfs="type => changeAndOrDfs(relationList, type)"
|
||||
:relationList="relationList"
|
||||
v-model:logic="logic"
|
||||
/>
|
||||
<svg width="388" height="100%" class="real-line">
|
||||
<path
|
||||
stroke-linejoin="round"
|
||||
stroke-linecap="round"
|
||||
:d="svgRealinePath"
|
||||
fill="none"
|
||||
stroke="#CCCCCC"
|
||||
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="#CCCCCC"
|
||||
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>
|
2
de-xpack
2
de-xpack
@ -1 +1 @@
|
||||
Subproject commit 11b651cfa73e22195c36887672539a6a7ee0dddc
|
||||
Subproject commit 11240f0a32fc1916409c82580e79fb17bb7988eb
|
Loading…
Reference in New Issue
Block a user