First test

This commit is contained in:
2023-01-14 15:37:59 +01:00
parent 6ee5c3d6ef
commit b68fdb31a7
14 changed files with 548 additions and 410 deletions

View File

@@ -0,0 +1,26 @@
<script>
export default {
props: {
value: {
type: String,
default: ""
}
},
computed: {
model: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
}
}
}
};
</script>
<template>
<input type="text" v-model="model" placeholder="dummy input">
</template>

View File

@@ -0,0 +1,52 @@
<script setup>
import Multiselect from '@/Components/MultiSelect.vue';
import JsonQueryBuilderGroup from '@/Components/JsonQueryBuilderGroup.vue';
import TextInput from '@/Components/TextInput.vue';
import { FolderPlusIcon, PlusCircleIcon, TrashIcon, ArrowPathRoundedSquareIcon} from '@heroicons/vue/24/solid';
import VueTailwindDatepicker from 'vue-tailwind-datepicker'
import { ref } from 'vue';
import { computed } from 'vue';
defineProps(['query', 'queryOptions', 'i18n']);
defineEmits(['update:query', 'update:queryOptions']);
const level = ref(0);
</script>
<template>
<div class="container mx-auto mt-10">
<!-- Card -->
<div class="h-50 w-full rounded-lg bg-white ">
<!-- Header -->
<div class="flex items-center justify-between border-b bg-red-300">
<div class="p-3 text-gray-700 text-lg font-bold">Query Builder</div>
<div class="p-3 flex">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path> v-if="!isVisible"</svg>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path> v-else </svg>
</div>
</div>
<JsonQueryBuilderGroup :currentQuery="query" :queryOptions="queryOptions" :level="level+1"> </JsonQueryBuilderGroup>
<div class="h-50 w-full rounded-lg bg-white ">
<!-- Header -->
<div class="flex items-center justify-between border-b bg-gray-300">
<div class="p-3 text-gray-700 text-lg font-bold"></div>
<div class="p-3 flex">
<button
@click="$emit('runQuery')"
class="text-slate-800 hover:text-blue-600 text-sm bg-green-200 hover:bg-slate-100 border border-slate-200 rounded-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span>
<ArrowPathRoundedSquareIcon class="w-4 h-4" />
</span>
<span>Spusti vyhľadávanie</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,90 @@
<script setup>
import { FolderPlusIcon, PlusCircleIcon, TrashIcon} from '@heroicons/vue/24/solid';
import JsonQueryBuilderRule from '@/Components/JsonQueryBuilderRule.vue';
import { ref, onMounted } from 'vue';
import { computed } from 'vue';
const props = defineProps(['currentQuery', 'queryOptions', 'i18n', 'level']);
function AddGroup(query) {
console.log(query.rules);
let len = query.rules.push({
condition: 'and',
rules: []
});
AddRule(query.rules[len-1]);
}
function DeleteGroup(query,item) {
console.log(item);
query.rules.splice(query.rules.indexOf(item), 1);
}
function AddRule(query){
console.log(query.rules);
query.rules.push({
id: '',
operator: '=',
value: ''
})
}
function DeleteRule(rules, rule) {
console.log(rule);
rules.splice(rules.indexOf(rule), 1);
}
</script>
<template>
<div :class="level > 1 ? 'border-4 border-blue-300' : ''">
<div class="flex items-center justify-between">
<div class="p-3 text-gray-700 text-lg font-bold">
<button
class="font-bold text-slate-800 hover:text-blue-600 text-sm bg-white hover:bg-slate-100 border border-slate-200 rounded-l-lg font-medium px-4 py-2 inline-flex space-x-1 items-center"
:class="currentQuery.condition == 'and' ? 'bg-blue-500' : 'bg-white'"
@click="currentQuery.condition = 'and'">
<span>AND</span>
</button>
<button
class="text-slate-800 hover:text-blue-600 text-sm bg-white hover:bg-slate-100 border border-slate-200 rounded-r-lg font-medium px-4 py-2 inline-flex space-x-1 items-center"
:class="currentQuery.condition != 'and' ? 'bg-blue-500' : 'bg-white'"
@click="currentQuery.condition = 'or'">
<span>OR</span>
</button>
</div>
<div class="p-3 text-gray-700 text-md font-bold">
<button v-if="level > 1"
@click="$emit('deleteGroup')"
class="text-slate-800 hover:text-blue-600 text-sm bg-red-300 hover:bg-slate-100 border border-slate-200 rounded-l-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span>
<TrashIcon class="w-4 h-4" />
</span>
<span>Zmaž skupinu</span>
</button>
<button
@click="AddGroup(currentQuery)"
class="font-bold text-slate-800 hover:text-blue-600 text-sm bg-white hover:bg-slate-100 border border-slate-200 rounded--lg font-medium px-4 py-2 inline-flex space-x-1 items-center"
:class="level == 1 ? 'rounded-l-lg' : ''">
<span>
<FolderPlusIcon class="w-4 h-4" />
</span>
<span>Pridaj skupinu</span>
</button>
<button
@click="AddRule(currentQuery)"
class="text-slate-800 hover:text-blue-600 text-sm bg-white hover:bg-slate-100 border border-slate-200 rounded-r-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span>
<PlusCircleIcon class="w-4 h-4" />
</span>
<span>Pridaj pravidlo</span>
</button>
</div>
</div>
<template v-for="(item) in currentQuery.rules" :key="item._uuid">
<JsonQueryBuilderGroup v-if="typeof item.condition === 'string'" :currentQuery="item" :queryOptions="queryOptions" @delete-group="DeleteGroup(currentQuery,item)" :level="level + 1"></JsonQueryBuilderGroup>
<JsonQueryBuilderRule v-else :rule="item" :options="queryOptions" @delete-rule="DeleteRule(currentQuery.rules,item)"></JsonQueryBuilderRule>
</template>
</div>
</template>

View File

@@ -0,0 +1,127 @@
<script setup>
import Multiselect from '@/Components/MultiSelect.vue';
import TextInput from '@/Components/TextInput.vue';
import NumberInput from '@/Components/Number.vue';
import { FolderPlusIcon, PlusCircleIcon, TrashIcon} from '@heroicons/vue/24/solid';
import { ref, onMounted } from 'vue';
import { computed } from 'vue';
import VueTailwindDatepicker from 'vue-tailwind-datepicker';
const props = defineProps(['rule', 'options', 'i18n']);
const dateValue = ref([props.rule.value]);
const formatter = ref({
date: 'D.M.YYYY',
month: 'MMM'
});
const fieldValue = ref(selectByValue(props.options,"name",props.rule.id));
const rulesOperatorOptions = computed({
get () {
switch(fieldValue.value.type) {
case 'Number':
return [
{id: '=', name: 'rovná sa'},
{id: '!=', name: 'nerovná sa'},
{id: '>', name: 'je väčšie'},
{id: '<', name: 'je menšie'},
{id: '>=', name: 'je rovné, vačšie'},
{id: '<=', name: 'je menšie, rovné'}
];
case 'Date':
return [
{id: '=', name: 'rovná sa'},
{id: '!=', name: 'nerovný'},
{id: '>', name: 'je väčší'},
{id: '<', name: 'je menší'},
{id: '>=', name: 'je rovný, vačší'},
{id: '<=', name: 'je menši, rovný'}
];
case 'String':
default:
return [
{id: '=', name: 'rovná'},
{id: '!=', name: 'nerovná sa'},
{id: 'in', name: 'je v'},
{id: 'not in', name: 'nieje v'},
{id: '()', name: 'obsahuje'},
{id: '!()', name: 'neobsahuje'},
{id: '(', name: 'začína'},
{id: ')', name: 'končí'},
{id: '!(', name: 'nezačína'},
{id: '!)', name: 'nekončí'}
];
}
},
}
);
const criteria = ref(selectByValue(rulesOperatorOptions.value,"id",props.rule.operator));
const input = ref(props.rule.value);
function selectByValue(arr, id, value, single = true) {
let filtered = arr.filter(a => a[id] == value);
console.log(filtered);
if (single && filtered.length > 0)
return filtered[0];
return filtered;
}
function FieldChange(val) {
console.log(val);
props.rule.id = val.name;
fieldValue.value.type = val.type;
input.value = null;
props.rule.value = null;
}
function CriteriaChange(params) {
console.log(params);
props.rule.operator = params.id;
}
function DateChange(params) {
console.log(params);
props.rule.value = params[0];
}
function InputChange(params) {
console.log(params);
props.rule.value = params;
}
</script>
<template>
<div class="flex justify-between ">
<div class="p-3 grow text-gray-700 text-lg font-bold">
<Multiselect @change-value="FieldChange" v-model="fieldValue" label="desc" trackby="desc" :options="options"></Multiselect>
</div>
<div class="p-3 text-gray-700 text-lg font-bold">
<Multiselect @change-value="CriteriaChange" v-model="criteria" :value="criteria" :allow-empty="false" label="name" trackby="name" :options="rulesOperatorOptions"></Multiselect>
</div>
<div class="p-3 grow text-gray-700 text-lg font-bold">
<vue-tailwind-datepicker as-single :formatter="formatter" i18n="sk" v-if="fieldValue.type == 'Date'"
v-model="dateValue" @update:model-value="DateChange" />
<TextInput v-if="fieldValue.type == 'String'" id="value" type="text" class="block w-full " v-model="input"
required @update:model-value="InputChange" />
<NumberInput v-if="fieldValue.type == 'Number'" id="value" type="number" class="block w-full " v-model="input"
required @update:model-value="InputChange" />
</div>
<div class="p-3 text-gray-700 text-lg font-bold">
<button
@click="$emit('deleteRule')"
class="text-slate-800 hover:text-blue-600 text-sm bg-red-400 hover:bg-slate-100 border border-slate-200 rounded-lg font-medium px-4 py-2 inline-flex space-x-1 items-center">
<span>
<TrashIcon class="w-4 h-4 m-1" />
</span>
</button>
</div>
</div>
</template>

View File

@@ -4,15 +4,13 @@ import { onMounted, ref } from 'vue';
import { defineEmits } from 'vue'
defineProps(['modelValue', 'options', 'trackby', 'label']);
defineEmits(['update:modelValue']);
const props = defineProps(['modelValue', 'options', 'trackby', 'selectby', 'label', 'value', 'allowEmpty']);
defineEmits(['update:modelValue', 'changeValue']);
const input = ref(null);
const value = ref(props.modelValue);
function update(t) {
console.log(t);
context.emit("update:modelValue",t);
}
console.log('props=',value.value);
</script>
@@ -20,7 +18,7 @@ function update(t) {
<template>
<multiselect :value="modelValue" v-model="value" @select='$emit("update:modelValue", $event)' :track-by="trackby" :label="label" placeholder="Vyber jednu" :options="options" :clear-on-select="false" :searchable="true" ref="input">
<multiselect :value="modelValue" v-model="value" @select='$emit("changeValue", $event)' :track-by="trackby" :label="label" placeholder="Vyber jednu" :options="options" :clear-on-select="false" :searchable="true" ref="input">
</multiselect>

View File

@@ -0,0 +1,27 @@
<script setup>
import { onMounted, ref } from 'vue';
defineProps(['modelValue']);
defineEmits(['update:modelValue']);
const input = ref(null);
onMounted(() => {
if (input.value.hasAttribute('autofocus')) {
input.value.focus();
}
});
defineExpose({ focus: () => input.value.focus() });
</script>
<template>
<input
class="h-10 border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
ref="input"
/>
</template>

View File

@@ -1,11 +1,12 @@
<script setup>
import { onMounted, ref } from 'vue';
defineProps(['modelValue']);
const props = defineProps(['modelValue']);
defineEmits(['update:modelValue']);
const input = ref(null);
const value = ref(props.modelValue);
onMounted(() => {
if (input.value.hasAttribute('autofocus')) {
@@ -18,7 +19,7 @@ defineExpose({ focus: () => input.value.focus() });
<template>
<input
class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
class="h-10 border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
ref="input"