278 lines
11 KiB
Vue
278 lines
11 KiB
Vue
<template>
|
|
<!-- <q-toolbar class="bg-grey-3" flat dense round icon="menu" aria-label="Menu" @click.stop>
|
|
<q-toolbar-title>条件エディタ</q-toolbar-title>
|
|
<q-space></q-space>
|
|
<q-btn flat round dense icon="info" color="blue" @click="showingCondition=!showingCondition"></q-btn>
|
|
</q-toolbar> -->
|
|
<div class="q-pa-md">
|
|
<q-tree :nodes="[tree.root]" node-key="index" children-key="children"
|
|
tick-strategy="strict" v-model:ticked="ticked" :expanded="expanded" default-expand-all dense color="primary" >
|
|
<template v-slot:header-root="prop">
|
|
<!-- root -->
|
|
<div class="row items-center" @click.stop>
|
|
<q-select v-model="prop.node.logicalOperator" :options="logicalOperators" filled outlined dense></q-select>
|
|
<q-btn flat round dense icon="more_horiz" size="sm" >
|
|
<q-menu auto-close anchor="top right">
|
|
<q-list>
|
|
<q-item clickable @click="addGroup(prop.node, LogicalOperator.AND)">
|
|
<q-item-section avatar><q-icon name="playlist_add" ></q-icon></q-item-section>
|
|
<q-item-section>グループの追加</q-item-section>
|
|
</q-item>
|
|
<q-item clickable @click="addCondition(prop.node)">
|
|
<q-item-section avatar><q-icon name="add_circle_outline" ></q-icon></q-item-section>
|
|
<q-item-section >条件式の追加</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
</div>
|
|
</template>
|
|
<template v-slot:header-generic="prop">
|
|
<!-- logic group -->
|
|
<div v-if="prop.node.type !== NodeType.Condition" class="row items-center" @click.stop>
|
|
<q-select v-model="prop.node.logicalOperator" :options="logicalOperators" :outlined="true" :filled="true" :dense="true"></q-select>
|
|
<q-btn flat round dense icon="more_horiz" size="sm" >
|
|
<q-menu auto-close anchor="top right">
|
|
<q-list>
|
|
<q-item clickable @click="moveUp(prop.node)">
|
|
<q-item-section avatar><q-icon name="arrow_upward" ></q-icon></q-item-section>
|
|
<q-item-section >一つ上に移動</q-item-section>
|
|
</q-item>
|
|
<q-item clickable @click="moveDown(prop.node)">
|
|
<q-item-section avatar><q-icon name="arrow_downward" ></q-icon></q-item-section>
|
|
<q-item-section >一つ下に移動</q-item-section>
|
|
</q-item>
|
|
<q-separator inset/>
|
|
<q-item clickable @click="addGroup(prop.node, LogicalOperator.AND)">
|
|
<q-item-section avatar><q-icon name="playlist_add" ></q-icon></q-item-section>
|
|
<q-item-section >グループ追加</q-item-section>
|
|
</q-item>
|
|
<q-item clickable @click="addCondition(prop.node)">
|
|
<q-item-section avatar><q-icon name="add_circle_outline" ></q-icon></q-item-section>
|
|
<q-item-section >条件式追加</q-item-section>
|
|
</q-item>
|
|
<q-separator inset/>
|
|
<q-item clickable @click="splitGroup(prop.node)">
|
|
<q-item-section avatar><q-icon name="playlist_remove" color="negative"></q-icon></q-item-section>
|
|
<q-item-section >グループ化解除</q-item-section>
|
|
</q-item>
|
|
<q-item clickable @click="removeNode(prop.node)">
|
|
<q-item-section avatar><q-icon name="delete" color="negative"></q-icon></q-item-section>
|
|
<q-item-section >削除</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
</div>
|
|
<!-- condition -->
|
|
<div @click.stop @keypress.stop v-else >
|
|
<div class="row no-wrap items-center q-my-xs">
|
|
<ConditionObject v-bind="prop.node" v-model="prop.node.object" :config="leftDynamicItemConfig" class="col-4"/>
|
|
<q-select v-model="prop.node.operator" :options="operators" class="operator" :outlined="true" :dense="true"></q-select>
|
|
<ConditionObject v-bind="prop.node" v-model="prop.node.value" :config="rightDynamicItemConfig" class="col-4"
|
|
:options="objectValueOptions(prop.node?.object?.options)"
|
|
/>
|
|
<!-- <ConditionObject v-bind="prop.node" v-model="prop.node.object" class="col-4"/> -->
|
|
<!-- <q-input v-if="!prop.node.object || !('options' in prop.node.object)"
|
|
v-model="prop.node.value"
|
|
class="condition-value" :outlined="true" :dense="true" ></q-input> -->
|
|
<!-- <q-select v-if="prop.node.object && ('options' in prop.node.object)"
|
|
v-model="prop.node.value"
|
|
:options="objectValueOptions(prop.node.object.options)"
|
|
clearable
|
|
value-key="index"
|
|
class="condition-value" :outlined="true" :dense="true" ></q-select> -->
|
|
<q-btn flat round dense icon="more_horiz" size="sm" >
|
|
<q-menu auto-close anchor="top right">
|
|
<q-list>
|
|
<q-item clickable @click="moveUp(prop.node)">
|
|
<q-item-section avatar><q-icon name="arrow_upward" ></q-icon></q-item-section>
|
|
<q-item-section >一つ上に移動</q-item-section>
|
|
</q-item>
|
|
<q-item clickable @click="moveDown(prop.node)">
|
|
<q-item-section avatar><q-icon name="arrow_downward" ></q-icon></q-item-section>
|
|
<q-item-section >一つ下に移動</q-item-section>
|
|
</q-item>
|
|
<q-separator inset/>
|
|
<q-item clickable @click="groupMerge(prop.node)" v-if="canMerge(prop.node)">
|
|
<q-item-section avatar><q-icon name="playlist_add"></q-icon></q-item-section>
|
|
<q-item-section >グループ化</q-item-section>
|
|
</q-item>
|
|
<q-separator inset/>
|
|
<q-item clickable @click="removeNode(prop.node)">
|
|
<q-item-section avatar><q-icon name="delete" color="negative"></q-icon></q-item-section>
|
|
<q-item-section>削除</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</q-tree>
|
|
<q-tooltip anchor="center middle" v-model="showingCondition" no-parent-event>
|
|
import { finished } from 'stream';
|
|
{{ conditionString }}
|
|
</q-tooltip>
|
|
</div>
|
|
</template>
|
|
<script lang="ts">
|
|
import { defineComponent,ref,reactive, computed, inject } from 'vue';
|
|
import { INode,ConditionTree,GroupNode,ConditionNode, LogicalOperator,Operator,NodeType } from '../../types/Conditions';
|
|
import ConditionObject from './ConditionObject.vue';
|
|
import { IDynamicInputConfig } from 'src/types/ComponentTypes';
|
|
export default defineComponent( {
|
|
name: 'NodeCondition',
|
|
components: {
|
|
ConditionObject
|
|
},
|
|
props:{
|
|
conditionTree: {
|
|
type: ConditionTree,
|
|
default: null
|
|
},
|
|
show:{
|
|
type:Boolean,
|
|
default:false
|
|
}
|
|
},
|
|
setup(props) {
|
|
const ticked= ref([]);
|
|
const showingCondition=ref(false);
|
|
|
|
const logicalOperators = computed(()=>{
|
|
const opts=[];
|
|
for(const op in LogicalOperator){
|
|
opts.push(LogicalOperator[op as keyof typeof LogicalOperator]);
|
|
}
|
|
return opts;
|
|
});
|
|
|
|
const operatorSet = inject<Array<any>>('Operator')
|
|
const operators = ref(operatorSet ? operatorSet : Object.values(Operator));
|
|
const tree = reactive(props.conditionTree);
|
|
|
|
const conditionString = computed(()=>{
|
|
return tree.buildConditionString(tree.root);
|
|
});
|
|
|
|
const objectValueOptions=(options:any):any[]|null=>{
|
|
if(!options){
|
|
return null;
|
|
}
|
|
const opts:any[] =[];
|
|
Object.keys(options).forEach((key) =>
|
|
{
|
|
const opt=options[key];
|
|
opts.push(opt);
|
|
});
|
|
return opts;
|
|
};
|
|
|
|
const addGroup = (parent:GroupNode, logicOp:LogicalOperator) => {
|
|
if(!parent){
|
|
parent=tree.root;
|
|
}
|
|
tree.addNode(parent,new GroupNode(logicOp,parent));
|
|
};
|
|
|
|
const addCondition = (parent:GroupNode) => {
|
|
const newNode = new ConditionNode({},Operator.Equal,'',parent);
|
|
tree.addNode(parent,newNode);
|
|
};
|
|
|
|
const removeNode = (node:INode) => {
|
|
tree.removeNode(node);
|
|
};
|
|
|
|
const moveUp =(node:INode)=>{
|
|
tree.moveNode(node,'up');
|
|
}
|
|
|
|
const moveDown =(node:INode)=>{
|
|
tree.moveNode(node,'down');
|
|
}
|
|
|
|
const getConditionJson=()=>{
|
|
return tree.toJson();
|
|
}
|
|
//JsonからConditionTreeのインスタンスを作成
|
|
const LoadCondition=()=>{
|
|
tree.fromJson(conditionString.value);
|
|
}
|
|
//グループ化
|
|
const groupMerge=(node:INode)=>{
|
|
const checkedNodes:INode[]=[];
|
|
const checkedIndexs:number[] = ticked.value;
|
|
checkedIndexs.forEach(index => {
|
|
const node = tree.findByIndex(index);
|
|
if(node){
|
|
checkedNodes.push(node);
|
|
}
|
|
});
|
|
tree.createGroupNode(node,checkedNodes,LogicalOperator.AND);
|
|
ticked.value=[];
|
|
}
|
|
//グループ化可能かをチェックする
|
|
const canMerge =(node:INode)=>{
|
|
const checkedIndexs:number[] = ticked.value;
|
|
const findNode = checkedIndexs.find(index=>node.index===index);
|
|
console.log("findNode=>",findNode!==undefined,findNode);
|
|
return findNode!==undefined;
|
|
}
|
|
//グループ化解散
|
|
const splitGroup=(node:INode)=>{
|
|
tree.dissolveGroupNode(node as GroupNode);
|
|
ticked.value=[];
|
|
}
|
|
|
|
const expanded=computed(()=>tree.getGroups(tree.root));
|
|
// addCondition(tree.root);
|
|
const leftDynamicItemConfig = inject<IDynamicInputConfig>('leftDynamicItemConfig');
|
|
const rightDynamicItemConfig = inject<IDynamicInputConfig>('rightDynamicItemConfig');
|
|
|
|
return {
|
|
leftDynamicItemConfig,
|
|
rightDynamicItemConfig,
|
|
showingCondition,
|
|
conditionString,
|
|
tree,
|
|
ticked,
|
|
logicalOperators,
|
|
operators,
|
|
addGroup,
|
|
addCondition,
|
|
removeNode,
|
|
moveUp,
|
|
moveDown,
|
|
LogicalOperator,
|
|
Operator,
|
|
NodeType,
|
|
getConditionJson,
|
|
LoadCondition,
|
|
objectValueOptions,
|
|
expanded,
|
|
canMerge,
|
|
groupMerge,
|
|
splitGroup
|
|
};
|
|
},
|
|
});
|
|
</script>
|
|
<style lang="scss">
|
|
.condition-value{
|
|
min-width: 200px;
|
|
max-height: 40px;
|
|
margin: 0 2px;
|
|
}
|
|
|
|
.operator{
|
|
min-width: 150px;
|
|
max-height: 40px;
|
|
margin: 0 2px;
|
|
|
|
text-align: center;
|
|
font-size: 12pt;
|
|
}
|
|
</style>
|