mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-02 12:49:02 -05:00
models, messages and multiselects
This commit is contained in:
@@ -2,39 +2,35 @@
|
||||
<v-dialog activator="parent" v-model="dialog">
|
||||
<template v-slot:default="{ isActive }">
|
||||
<v-card style="overflow: auto">
|
||||
<v-card-title>Meal Plan Edit <i class="mt-2 float-right fas fa-times" @click="isActive.value = false"></i></v-card-title>
|
||||
<v-card-title>Meal Plan Edit
|
||||
<v-btn icon="fas fa-times" variant="flat" size="x-small" class="mt-2 float-right " @click="isActive.value = false"></v-btn>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-form>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field label="Title"></v-text-field>
|
||||
<v-text-field label="From Date" type="date"></v-text-field>
|
||||
<v-text-field label="Meal Type"></v-text-field>
|
||||
<v-number-input control-variant="split" :min="0"></v-number-input>
|
||||
<v-text-field label="Share"></v-text-field>
|
||||
<v-text-field label="Title" v-model="mutableMealPlan.title"></v-text-field>
|
||||
<v-date-input
|
||||
v-model="dateRangeValue"
|
||||
label="Plan Date"
|
||||
multiple="range"
|
||||
prepend-icon=""
|
||||
prepend-inner-icon="$calendar"
|
||||
></v-date-input>
|
||||
<ModelSelect model="MealType" v-model="mutableMealPlan.mealType"></ModelSelect>
|
||||
<v-number-input control-variant="split" :min="0" v-model="mutableMealPlan.servings"></v-number-input>
|
||||
<v-text-field label="Share" v-model="mutableMealPlan.shared"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<Multiselect
|
||||
name="recipe"
|
||||
:columns="{ sm: 12, md : 6}"
|
||||
label="Recipe"
|
||||
label-prop="name"
|
||||
value-prop="id"
|
||||
:object="true"
|
||||
:strict="false"
|
||||
:search="true"
|
||||
:items="recipeSearch"
|
||||
:delay="300"
|
||||
rules="required"
|
||||
></Multiselect>
|
||||
<v-text-field label="To Date" type="date"></v-text-field>
|
||||
<ModelSelect model="recipe" v-model="mutableMealPlan.recipe"></ModelSelect>
|
||||
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>--> <!--TODO create days input with +/- snyced to date -->
|
||||
<recipe-card :recipe="mutableMealPlan.recipe" v-if="mutableMealPlan && mutableMealPlan.recipe"></recipe-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-textarea label="Note"></v-textarea>
|
||||
<v-textarea label="Note" v-model="mutableMealPlan.note"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
@@ -60,7 +56,10 @@ import {DateTime} from "luxon";
|
||||
import RecipeCard from "@/components/display/RecipeCard.vue";
|
||||
import {useMealPlanStore} from "@/stores/MealPlanStore";
|
||||
import {VNumberInput} from 'vuetify/labs/VNumberInput' //TODO remove once component is out of labs
|
||||
import {VDateInput} from 'vuetify/labs/VDateInput' //TODO remove once component is out of labs
|
||||
import Multiselect from '@vueform/multiselect'
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import {useMessageStore} from "@/stores/MessageStore";
|
||||
|
||||
const props = defineProps(
|
||||
{
|
||||
@@ -69,12 +68,16 @@ const props = defineProps(
|
||||
)
|
||||
|
||||
const dialog = ref(false)
|
||||
let mutableMealPlan = ref(props.mealPlan)
|
||||
let mutableMealPlan = ref(newMealPlan())
|
||||
const dateRangeValue = ref([] as Date[])
|
||||
|
||||
if (props.mealPlan != undefined) {
|
||||
mutableMealPlan.value = props.mealPlan
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.mealPlan != undefined) {
|
||||
mutableMealPlan.value = props.mealPlan
|
||||
|
||||
} else {
|
||||
mutableMealPlan.value = newMealPlan()
|
||||
}
|
||||
@@ -84,6 +87,14 @@ function saveMealPlan() {
|
||||
|
||||
if (mutableMealPlan.value != undefined) {
|
||||
mutableMealPlan.value.recipe = mutableMealPlan.value.recipe as RecipeOverview
|
||||
if (dateRangeValue.value != null) {
|
||||
mutableMealPlan.value.fromDate = dateRangeValue.value[0]
|
||||
mutableMealPlan.value.toDate = dateRangeValue.value[dateRangeValue.value.length-1]
|
||||
} else {
|
||||
useMessageStore().addError('Missing Dates')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('calling save method')
|
||||
useMealPlanStore().createOrUpdate(mutableMealPlan.value).catch(err => {
|
||||
// TODO handle error
|
||||
@@ -100,23 +111,6 @@ function newMealPlan() {
|
||||
} as MealPlan
|
||||
}
|
||||
|
||||
async function mealTypeSearch(searchQuery: string) {
|
||||
console.log('called search')
|
||||
const api = new ApiApi()
|
||||
return await api.apiMealTypeList()
|
||||
}
|
||||
|
||||
async function shareUserSearch(searchQuery: string) {
|
||||
console.log('called su search')
|
||||
const api = new ApiApi()
|
||||
return await api.apiUserList()
|
||||
}
|
||||
|
||||
async function recipeSearch(searchQuery: string) {
|
||||
console.log('called recipe search')
|
||||
const api = new ApiApi()
|
||||
return (await api.apiRecipeList({query: searchQuery})).results
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
156
vue3/src/components/dialogs/MessageListDialog.vue
Normal file
156
vue3/src/components/dialogs/MessageListDialog.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<v-dialog max-width="70vw" min-height="80vh" activator="parent">
|
||||
<template v-slot:default="{ isActive }">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
Nachrichten <v-btn icon="fas fa-times" variant="flat" size="x-small" class="mt-2 float-right " @click="isActive.value = false"></v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<h4>Filter</h4>
|
||||
|
||||
<v-text-field
|
||||
class="mt-2"
|
||||
v-model="search"
|
||||
label="Search"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
variant="outlined"
|
||||
clearable
|
||||
hide-details
|
||||
single-line
|
||||
></v-text-field>
|
||||
|
||||
<v-btn-toggle
|
||||
class="mt-2"
|
||||
v-model="typeFilter"
|
||||
variant="outlined"
|
||||
divided
|
||||
multiple>
|
||||
<v-btn :value="MessageType.SUCCESS">
|
||||
<v-icon icon="fas fa-eye" class="me-2" color="success"></v-icon>
|
||||
Success
|
||||
</v-btn>
|
||||
<v-btn :value="MessageType.INFO">
|
||||
<v-icon icon="fas fa-eye" class="me-2" color="info"></v-icon>
|
||||
Info
|
||||
</v-btn>
|
||||
<v-btn :value="MessageType.WARNING">
|
||||
<v-icon icon="fas fa-eye" class="me-2" color="warning"></v-icon>
|
||||
Warning
|
||||
</v-btn>
|
||||
<v-btn :value="MessageType.ERROR">
|
||||
<v-icon icon="fas fa-eye" class="me-2" color="error"></v-icon>
|
||||
Error
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
|
||||
<v-data-table
|
||||
:headers="tableHeaders"
|
||||
:items="displayItems"
|
||||
:sort-by="sortBy"
|
||||
:search="search"
|
||||
>
|
||||
|
||||
<template v-slot:item.type="{ value }">
|
||||
<v-chip :color="value">
|
||||
{{ value }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-icon icon="fas fa-search" @click="showDetailDialog = true; detailItem = item"></v-icon>
|
||||
<v-icon class="ms-1" icon="fas fa-copy" @click="copy(JSON.stringify({'type': item.type, 'createdAt': item.createdAt, 'msg': item.msg, 'data': item.data}));"></v-icon>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="useMessageStore().deleteAllMessages()" color="error">Alle Löschen</v-btn>
|
||||
<v-btn @click="addTestMessage()" color="warning">Test Nachricht</v-btn>
|
||||
<v-btn @click="isActive.value = false">Close</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showDetailDialog" max-width="50vw">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
Nachricht Details <small>{{ DateTime.fromSeconds(detailItem.createdAt).toLocaleString(DateTime.DATETIME_MED) }}</small>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-label>Typ</v-label>
|
||||
<br/>
|
||||
<v-chip :color="detailItem.type">{{ detailItem.type }}</v-chip>
|
||||
<br/>
|
||||
|
||||
<v-label class="mt-2">Nachricht</v-label>
|
||||
<p>{{ detailItem.msg }}</p>
|
||||
|
||||
<v-label class="mt-2">Data</v-label>
|
||||
<pre style="white-space: pre-wrap;" v-if="detailItem.data != null">{{ detailItem.data }}</pre>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-btn
|
||||
text="Close Dialog"
|
||||
@click="showDetailDialog = false"
|
||||
></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import {Message, MessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import {DateTime} from "luxon";
|
||||
import {useClipboard} from "@vueuse/core";
|
||||
|
||||
const {copy} = useClipboard()
|
||||
|
||||
const displayItems = computed(() => {
|
||||
let items = [] as Message[]
|
||||
useMessageStore().messages.forEach(m => {
|
||||
if (typeFilter.value.includes(m.type)) {
|
||||
items.push(m)
|
||||
}
|
||||
})
|
||||
return items
|
||||
})
|
||||
|
||||
const sortBy = ref([{key: 'createdAt', order: 'desc'}])
|
||||
const search = ref('')
|
||||
const tableHeaders = ref([
|
||||
{title: 'Type', key: 'type'},
|
||||
{
|
||||
title: 'Created',
|
||||
key: 'createdAt',
|
||||
value: (item: Message) => `${DateTime.fromSeconds(item.createdAt).toLocaleString(DateTime.DATETIME_MED)}`,
|
||||
},
|
||||
{title: 'Message', key: 'msg'},
|
||||
{title: 'Actions', key: 'actions', align: 'end'},
|
||||
])
|
||||
const typeFilter = ref([MessageType.SUCCESS, MessageType.INFO, MessageType.WARNING, MessageType.ERROR])
|
||||
const detailItem = ref({} as Message)
|
||||
const showDetailDialog = ref(false)
|
||||
|
||||
function addTestMessage() {
|
||||
let types = [MessageType.SUCCESS, MessageType.ERROR, MessageType.INFO, MessageType.WARNING]
|
||||
useMessageStore().addMessage(types[Math.floor(Math.random() * types.length)], `Lorem Ipsum ${Math.random() * 1000}`, 5000, {json: "data", 'msg': 'whatever', data: 1})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -61,7 +61,7 @@ const calendarItemHeight = computed(() => {
|
||||
onMounted(() => {
|
||||
let api = new ApiApi()
|
||||
api.apiMealPlanList().then(r => {
|
||||
mealPlans.value = r
|
||||
mealPlans.value = r.results
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
87
vue3/src/components/display/VSnackbarQueued.vue
Normal file
87
vue3/src/components/display/VSnackbarQueued.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<v-snackbar
|
||||
v-model="showSnackbar"
|
||||
:timer="true"
|
||||
:timeout="visibleMessage.showTimeout"
|
||||
:color="visibleMessage.type"
|
||||
:vertical="vertical"
|
||||
:location="location"
|
||||
multi-line
|
||||
>
|
||||
<small>{{ DateTime.fromSeconds(visibleMessage.createdAt).toLocaleString(DateTime.DATETIME_MED) }}</small> <br/>
|
||||
{{ visibleMessage.msg }}
|
||||
|
||||
<template v-slot:actions>
|
||||
|
||||
<v-btn variant="text">View <message-list-dialog></message-list-dialog> </v-btn>
|
||||
<v-btn variant="text" @click="removeItem()">
|
||||
<span v-if="useMessageStore().snackbarQueue.length > 1">Next ({{ useMessageStore().snackbarQueue.length - 1 }})</span>
|
||||
<span v-else>Close</span>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue'
|
||||
import {Message, useMessageStore} from "@/stores/MessageStore";
|
||||
import {DateTime} from "luxon";
|
||||
import MessageListDialog from "@/components/dialogs/MessageListDialog.vue";
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* passed to VSnackbar location prop https://vuetifyjs.com/en/api/v-snackbar/#props-location
|
||||
* Specifies the anchor point for positioning the component, using directional cues to align it either horizontally, vertically, or both…
|
||||
*/
|
||||
location: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
/**
|
||||
* passed to VSnackbar vertical prop https://vuetifyjs.com/en/api/v-snackbar/#props-vertical
|
||||
* Stacks snackbar content on top of the actions (button).
|
||||
*/
|
||||
vertical: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const timeoutId = ref(-1)
|
||||
const visibleMessage = ref({} as Message)
|
||||
const showSnackbar = ref(false)
|
||||
|
||||
useMessageStore().$subscribe((mutation, state) => {
|
||||
if ('snackbarQueue' in state && mutation.events.type == 'add') {
|
||||
processQueue()
|
||||
}
|
||||
})
|
||||
|
||||
function processQueue() {
|
||||
if (timeoutId.value == -1 && useMessageStore().snackbarQueue.length > 0) {
|
||||
visibleMessage.value = useMessageStore().snackbarQueue[0]
|
||||
showSnackbar.value = true
|
||||
timeoutId.value = setTimeout(() => {
|
||||
useMessageStore().snackbarQueue.shift()
|
||||
timeoutId.value = -1
|
||||
processQueue()
|
||||
}, visibleMessage.value.showTimeout + 50)
|
||||
}
|
||||
}
|
||||
|
||||
function removeItem() {
|
||||
showSnackbar.value = false
|
||||
clearTimeout(timeoutId.value)
|
||||
timeoutId.value = -1
|
||||
useMessageStore().snackbarQueue.shift()
|
||||
processQueue()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,98 +1,117 @@
|
||||
<template>
|
||||
<v-input>
|
||||
<!--TODO Problems: 1. behind other cards when those are underneath the element, making card overflow visible breaks cards -->
|
||||
<VueMultiselect
|
||||
:id="id"
|
||||
v-model="selected_items"
|
||||
:options="items"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:hide-selected="multiple"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="limit"
|
||||
:placeholder="model"
|
||||
:label="label"
|
||||
track-by="id"
|
||||
:multiple="multiple"
|
||||
:taggable="allowCreate"
|
||||
tag-placeholder="TODO CREATE PLACEHOLDER"
|
||||
:loading="search_loading"
|
||||
@search-change="debouncedSearchFunction"
|
||||
@input="selectionChanged"
|
||||
@tag="addItem"
|
||||
@open="search('')"
|
||||
:disabled="disabled"
|
||||
>
|
||||
</VueMultiselect>
|
||||
</v-input>
|
||||
<!-- <!–TODO Problems: 1. behind other cards when those are underneath the element, making card overflow visible breaks cards –>-->
|
||||
<!-- <VueMultiselect-->
|
||||
<!-- :id="id"-->
|
||||
<!-- v-model="selected_items"-->
|
||||
<!-- :options="items"-->
|
||||
<!-- :close-on-select="true"-->
|
||||
<!-- :clear-on-select="true"-->
|
||||
<!-- :hide-selected="multiple"-->
|
||||
<!-- :preserve-search="true"-->
|
||||
<!-- :internal-search="false"-->
|
||||
<!-- :limit="limit"-->
|
||||
<!-- :placeholder="model"-->
|
||||
<!-- :label="label"-->
|
||||
<!-- track-by="id"-->
|
||||
<!-- :multiple="multiple"-->
|
||||
<!-- :taggable="allowCreate"-->
|
||||
<!-- tag-placeholder="TODO CREATE PLACEHOLDER"-->
|
||||
<!-- :loading="search_loading"-->
|
||||
<!-- @search-change="debouncedSearchFunction"-->
|
||||
<!-- @input="selectionChanged"-->
|
||||
<!-- @tag="addItem"-->
|
||||
<!-- @open="search('')"-->
|
||||
<!-- :disabled="disabled"-->
|
||||
<!-- class="material-multiselect"-->
|
||||
|
||||
<!-- >-->
|
||||
<!-- </VueMultiselect>-->
|
||||
|
||||
<Multiselect
|
||||
|
||||
class="material-multiselect z-max"
|
||||
v-model="model"
|
||||
:options="search"
|
||||
:delay="300"
|
||||
:object="true"
|
||||
valueProp="id"
|
||||
:label="label"
|
||||
:searchable="true"
|
||||
:strict="false"
|
||||
:disabled="disabled"
|
||||
|
||||
|
||||
/>
|
||||
|
||||
</v-input>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, ref, Ref,} from 'vue'
|
||||
import {ApiApi} from "@/openapi/index.js";
|
||||
import {useDebounceFn} from "@vueuse/core";
|
||||
import {GenericModel, getModelFromStr} from "@/types/Models";
|
||||
import VueMultiselect from 'vue-multiselect'
|
||||
import {computed, onMounted, PropType, ref, Ref} from "vue"
|
||||
import {ApiApi} from "@/openapi/index.js"
|
||||
import {useDebounceFn} from "@vueuse/core"
|
||||
import {GenericModel, getModelFromStr} from "@/types/Models"
|
||||
import Multiselect from '@vueform/multiselect'
|
||||
|
||||
const props = defineProps(
|
||||
{
|
||||
model: {type: String, required: true},
|
||||
multiple: {type: Boolean, default: true},
|
||||
limit: {type: Number, default: 25},
|
||||
allowCreate: {type: Boolean, default: false},
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
id: {type: String, required: false, default: Math.random().toString()},
|
||||
const props = defineProps({
|
||||
model: {type: String, required: true},
|
||||
|
||||
// not verified
|
||||
search_on_load: {type: Boolean, default: false},
|
||||
id: {type: String, required: false, default: Math.random().toString()},
|
||||
|
||||
// not verified
|
||||
|
||||
clearable: {type: Boolean, default: false,},
|
||||
chips: {type: Boolean, default: undefined,},
|
||||
multiple: {type: Boolean, default: true},
|
||||
limit: {type: Number, default: 25},
|
||||
allowCreate: {type: Boolean, default: false},
|
||||
|
||||
itemName: {type: String, default: 'name'},
|
||||
itemValue: {type: String, default: 'id'},
|
||||
search_on_load: {type: Boolean, default: false},
|
||||
|
||||
clearable: {type: Boolean, default: false},
|
||||
chips: {type: Boolean, default: undefined},
|
||||
|
||||
placeholder: {type: String, default: undefined},
|
||||
label: {type: String, default: "name"},
|
||||
parent_variable: {type: String, default: undefined},
|
||||
itemName: {type: String, default: "name"},
|
||||
itemValue: {type: String, default: "id"},
|
||||
|
||||
sticky_options: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
placeholder: {type: String, default: undefined},
|
||||
label: {type: String, default: "name"},
|
||||
parent_variable: {type: String, default: undefined},
|
||||
|
||||
sticky_options: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
initial_selection: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
initial_single_selection: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
initial_selection: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
initial_single_selection: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
disabled: {type: Boolean, default: false},
|
||||
})
|
||||
|
||||
disabled: {type: Boolean, default: false,},
|
||||
}
|
||||
)
|
||||
|
||||
const model = defineModel()
|
||||
const model_class = ref({} as GenericModel<any>)
|
||||
const items: Ref<Array<any>> = ref([])
|
||||
const selected_items: Ref<Array<any> | any> = ref(undefined)
|
||||
const search_query = ref('')
|
||||
const search_query = ref("")
|
||||
const search_loading = ref(false)
|
||||
|
||||
const elementId = ref((Math.random() * 100000).toString())
|
||||
|
||||
onMounted(() => {
|
||||
model_class.value = getModelFromStr(props.model)
|
||||
if (props.search_on_load) {
|
||||
debouncedSearchFunction('')
|
||||
debouncedSearchFunction("")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -108,16 +127,9 @@ const debouncedSearchFunction = useDebounceFn((query: string) => {
|
||||
* @param query input to search for on the API
|
||||
*/
|
||||
function search(query: string) {
|
||||
|
||||
search_loading.value = true
|
||||
model_class.value.list(query).then(r => {
|
||||
items.value = r
|
||||
if (props.allowCreate && search_query.value != '') {
|
||||
// TODO check if search_query is already in items
|
||||
items.value.unshift({id: null, name: `Create "${search_query.value}"`})
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
return model_class.value.list(query).then((r) => {
|
||||
return r
|
||||
}).catch((err) => {
|
||||
//useMessageStore().addMessage(MessageType.ERROR, err, 8000)
|
||||
}).finally(() => {
|
||||
search_loading.value = false
|
||||
@@ -129,7 +141,7 @@ function addItem(item: string) {
|
||||
const api = new ApiApi()
|
||||
api.apiKeywordList()
|
||||
|
||||
model_class.value.create(item).then(createdObj => {
|
||||
model_class.value.create(item).then((createdObj) => {
|
||||
//StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE)
|
||||
if (selected_items.value instanceof Array) {
|
||||
selected_items.value.push(createdObj)
|
||||
@@ -146,12 +158,19 @@ function addItem(item: string) {
|
||||
}
|
||||
|
||||
function selectionChanged() {
|
||||
//this.$emit("change", { var: this.parent_variable, val: this.selected_objects })
|
||||
emit('update:modelValue', selected_items)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||
<style src="@vueform/multiselect/themes/default.css"></style>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
.material-multiselect {
|
||||
--ms-line-height: 2.5;
|
||||
--ms-bg: rgba(235, 235, 235, 0.75);
|
||||
--ms-border-color: 0;
|
||||
--ms-border-color-active: 0;
|
||||
border-bottom: 4px #0f0f0f;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user