450 lines
12 KiB
Vue
450 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<q-drawer
|
|
v-model="rightDrawerOpen"
|
|
content-class="bg-grey-2"
|
|
side="right"
|
|
elevated>
|
|
<div class="q-pa-md">
|
|
<div class="q-mb-xs text-caption text-grey-7">
|
|
{{$t('category')}}
|
|
<q-icon
|
|
v-if="category"
|
|
style="font-size: 1.3em;"
|
|
name="cancel"
|
|
class="cursor-pointer"
|
|
:title="$t('deleteFilter')"
|
|
:to="{params: {category: null}}"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<q-btn
|
|
flat
|
|
class="category q-pa-sm"
|
|
v-for="cat in categories"
|
|
:class="{active: category == cat.id}"
|
|
:key="cat.id"
|
|
:title="cat.name"
|
|
:to="{params: {category: cat.id}}">
|
|
<img
|
|
class="category-img"
|
|
:src="`statics/category/${cat.id}.svg`">
|
|
</q-btn>
|
|
</div>
|
|
<q-select
|
|
v-model="type"
|
|
option-value="id"
|
|
option-label="name"
|
|
:options="types"
|
|
:disable="!category"
|
|
clearable
|
|
:label="$t('family')"
|
|
@filter="filterType"
|
|
/>
|
|
</div>
|
|
<q-separator />
|
|
<div class="q-pa-md">
|
|
<q-select
|
|
v-model="order"
|
|
input-debounce="0"
|
|
:options="orderOptions"
|
|
:label="$t('orderBy')"
|
|
/>
|
|
<q-input
|
|
v-model="date"
|
|
mask="date"
|
|
:rules="['date']"
|
|
:label="$t('deliveryDate')">
|
|
<template v-slot:append>
|
|
<q-icon name="event" class="cursor-pointer">
|
|
<q-popup-proxy ref="qDateProxy" anchor="bottom left" self="top middle">
|
|
<q-date v-model="date" @input="() => $refs.qDateProxy.hide()" />
|
|
</q-popup-proxy>
|
|
</q-icon>
|
|
</template>
|
|
</q-input>
|
|
</div>
|
|
<q-separator />
|
|
<div class="q-pa-md"
|
|
v-if="type || search">
|
|
<div class="q-mb-md"
|
|
v-for="tag in tags"
|
|
:key="tag.uid">
|
|
<div class="q-mb-xs text-caption text-grey-7">
|
|
{{tag.name}}
|
|
<q-icon
|
|
v-if="tag.filter.length || tag.filter.min || tag.filter.max"
|
|
style="font-size: 1.3em;"
|
|
name="cancel"
|
|
:title="$t('deleteFilter')"
|
|
class="cursor-pointer"
|
|
@click="onResetTagFilterClick(tag)"
|
|
/>
|
|
</div>
|
|
<div v-if="!tag.useRange">
|
|
<div
|
|
v-for="value in tag.values.slice(0, tag.showCount)"
|
|
:key="value">
|
|
<q-checkbox
|
|
v-model="tag.filter"
|
|
:dense="true"
|
|
:val="value"
|
|
:label="value"
|
|
@input="loadItems"
|
|
/>
|
|
</div>
|
|
<div v-if="tag.values.length > tag.showCount">
|
|
<span
|
|
class="cursor-pointer text-blue"
|
|
@click="tag.showCount = Infinity">
|
|
<q-icon name="keyboard_arrow_down" />
|
|
{{$t('viewMore')}}
|
|
</span>
|
|
</div>
|
|
<div v-if="tag.showCount == Infinity">
|
|
<span
|
|
class="cursor-pointer text-blue"
|
|
@click="tag.showCount = tag.initialCount">
|
|
<q-icon name="keyboard_arrow_up" />
|
|
{{$t('viewLess')}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="q-mx-md">
|
|
<q-range
|
|
class="q-mt-lg"
|
|
v-if="tag.useRange"
|
|
v-model="tag.filter"
|
|
:min="tag.min"
|
|
:max="tag.max"
|
|
:step="tag.step"
|
|
@input="loadItemsDelayed"
|
|
@change="loadItems"
|
|
label-always
|
|
markers
|
|
snap
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</q-drawer>
|
|
<q-infinite-scroll
|
|
@load="onLoad"
|
|
scroll-taget="html"
|
|
:offset="800"
|
|
:disable="disableScroll">
|
|
<div class="q-pa-md row justify-center q-gutter-md">
|
|
<q-spinner
|
|
v-if="isLoading"
|
|
color="primary"
|
|
size="50px">
|
|
</q-spinner>
|
|
<q-card
|
|
class="my-card"
|
|
v-for="item in items"
|
|
:key="item.id">
|
|
<img :src="`${$imageBase}/catalog/200x200/${item.image}`" />
|
|
<q-card-section>
|
|
<div class="name text-subtitle1">{{item.longName}}</div>
|
|
<div class="text-uppercase text-subtitle1 text-grey-7">
|
|
{{item.subName}}
|
|
</div>
|
|
</q-card-section>
|
|
<q-card-section class="tags">
|
|
<div v-for="tag in item.tags" :key="tag.tagFk">
|
|
<span class="text-grey-7">{{tag.tag.name}}</span> {{tag.value}}
|
|
</div>
|
|
</q-card-section>
|
|
<q-card-actions class="actions justify-between">
|
|
<div class="q-pl-sm">
|
|
<span class="available bg-green text-white">{{item.available}}</span>
|
|
{{$t('from')}}
|
|
<span class="price">{{item.inbounds[0].buy && item.inbounds[0].buy.price3 | currency}}</span>
|
|
</div>
|
|
<q-btn flat>{{$t('buy')}}</q-btn>
|
|
</q-card-actions>
|
|
</q-card>
|
|
</div>
|
|
<template slot="loading">
|
|
<div class="row justify-center q-my-md">
|
|
<q-spinner color="primary" name="dots" size="40px" />
|
|
</div>
|
|
</template>
|
|
</q-infinite-scroll>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="stylus" scoped>
|
|
.my-card
|
|
width 100%
|
|
max-width 21em
|
|
height 38em
|
|
overflow hidden
|
|
.name
|
|
white-space nowrap
|
|
overflow hidden
|
|
text-overflow ellipsis
|
|
.description
|
|
height 40px
|
|
overflow hidden
|
|
.category
|
|
width 25%
|
|
&.active
|
|
background: rgba(0, 0, 0, .08)
|
|
.category-img
|
|
height 3.5em
|
|
.tags
|
|
max-height 6.2em
|
|
overflow hidden
|
|
.available
|
|
padding .15em
|
|
border-radius .2em
|
|
font-size 1.3em
|
|
.price
|
|
font-size 1.3em
|
|
.actions
|
|
position absolute
|
|
bottom 0
|
|
width 100%
|
|
</style>
|
|
|
|
<script>
|
|
import { date } from 'quasar'
|
|
import axios from 'axios'
|
|
|
|
const CancelToken = axios.CancelToken
|
|
|
|
export default {
|
|
name: 'Catalog',
|
|
data () {
|
|
return {
|
|
uid: 0,
|
|
search: null,
|
|
date: date.formatDate(new Date(), 'YYYY/MM/DD'),
|
|
category: null,
|
|
categories: [],
|
|
tags: [],
|
|
type: null,
|
|
types: [],
|
|
orgTypes: [],
|
|
isLoading: false,
|
|
items: null,
|
|
rightDrawerOpen: this.$q.platform.is.desktop,
|
|
pageSize: 24,
|
|
limit: null,
|
|
maxTags: 5,
|
|
disableScroll: true,
|
|
order: {
|
|
label: this.$t('name'),
|
|
value: 'name'
|
|
},
|
|
orderOptions: [
|
|
{
|
|
label: this.$t('name'),
|
|
value: 'longName'
|
|
}, /* {
|
|
label: this.$t('priceAsc'),
|
|
value: 'price ASC'
|
|
}, {
|
|
label: this.$t('priceDesc'),
|
|
value: 'price DESC'
|
|
}, {
|
|
label: this.$t('available'),
|
|
value: 'available'
|
|
}, */ {
|
|
label: this.$t('siceAsc'),
|
|
value: 'size ASC'
|
|
}, {
|
|
label: this.$t('sizeDesc'),
|
|
value: 'size DESC'
|
|
}
|
|
]
|
|
}
|
|
},
|
|
mounted () {
|
|
this.$axios.get('ItemCategories')
|
|
.then(res => (this.categories = res.data))
|
|
},
|
|
beforeDestroy () {
|
|
this.clearTimeoutAndRequest()
|
|
},
|
|
watch: {
|
|
order () {
|
|
this.loadItems()
|
|
},
|
|
date () {
|
|
this.loadItems()
|
|
},
|
|
type (type) {
|
|
this.$router.push({
|
|
params: {
|
|
category: this.category,
|
|
type: type && type.id
|
|
}
|
|
})
|
|
this.$state.subtitle = type && type.name
|
|
this.$state.search = ''
|
|
this.loadItems()
|
|
},
|
|
'$state.search': function (value) {
|
|
if (!value) this.loadItems()
|
|
else this.loadItemsDelayed()
|
|
},
|
|
'$route.params.category': function (categoryId) {
|
|
let category = this.categories.find(i => i.id === categoryId)
|
|
|
|
if (category) {
|
|
this.$state.title = category.name
|
|
this.$state.titleColor = `#${category.color}`
|
|
} else {
|
|
this.$state.title = this.$t(this.$router.currentRoute.name)
|
|
this.$state.titleColor = null
|
|
}
|
|
|
|
this.category = categoryId
|
|
this.type = null
|
|
|
|
let params = { filter: { where: { categoryFk: categoryId } } }
|
|
this.$axios.get('ItemTypes', { params })
|
|
.then(res => (this.orgTypes = res.data))
|
|
},
|
|
'$route.params.type': function (type) {
|
|
if (!this.type) this.type = { id: type }
|
|
}
|
|
},
|
|
methods: {
|
|
loadItemsDelayed () {
|
|
this.clearTimeoutAndRequest()
|
|
this.timeout = setTimeout(() => this.loadItems(), 500)
|
|
},
|
|
clearTimeoutAndRequest () {
|
|
if (this.timeout) {
|
|
clearTimeout(this.timeout)
|
|
this.timeout = null
|
|
}
|
|
if (this.source) {
|
|
this.source.cancel()
|
|
this.source = null
|
|
}
|
|
},
|
|
loadItems () {
|
|
this.items = []
|
|
this.isLoading = true
|
|
this.limit = this.pageSize
|
|
this.disableScroll = false
|
|
this.loadItemsBase()
|
|
.finally(() => {
|
|
this.isLoading = false
|
|
})
|
|
},
|
|
onLoad (index, done) {
|
|
if (this.isLoading) return done()
|
|
this.limit += this.pageSize
|
|
this.loadItemsBase()
|
|
.finally(() => done())
|
|
},
|
|
loadItemsBase () {
|
|
this.clearTimeoutAndRequest()
|
|
|
|
if (!this.type) {
|
|
return Promise.resolve(true)
|
|
}
|
|
|
|
let typeFk
|
|
let tagFilter = []
|
|
|
|
for (let tag of this.tags) {
|
|
if (this.hasFilters(tag)) {
|
|
tagFilter.push({
|
|
tagFk: tag.id,
|
|
values: tag.filter
|
|
})
|
|
}
|
|
}
|
|
|
|
if (this.type) {
|
|
typeFk = this.type.id
|
|
}
|
|
|
|
this.source = CancelToken.source()
|
|
|
|
let params = {
|
|
dated: this.date,
|
|
typeFk: typeFk,
|
|
search: this.$state.search,
|
|
order: this.order.value,
|
|
tagFilter: tagFilter,
|
|
limit: this.limit
|
|
}
|
|
let config = {
|
|
params,
|
|
cancelToken: this.source.token
|
|
}
|
|
return this.$axios.get('Items/catalog', config)
|
|
.then(res => this.onItemsGet(res))
|
|
.catch(err => { if (!err.__CANCEL__) throw err })
|
|
.finally(() => (this.cancel = null))
|
|
},
|
|
onItemsGet (res) {
|
|
for (let tag of res.data.tags) {
|
|
tag.uid = this.uid++
|
|
|
|
if (tag.filter) {
|
|
tag.useRange =
|
|
tag.filter.max ||
|
|
tag.filter.min
|
|
} else {
|
|
tag.useRange = tag.isQuantitative &&
|
|
tag.values.length > this.maxTags
|
|
this.resetTagFilter(tag)
|
|
}
|
|
|
|
if (tag.values) {
|
|
tag.initialCount = this.maxTags
|
|
if (Array.isArray(tag.filter)) {
|
|
tag.initialCount = Math.max(tag.initialCount, tag.filter.length)
|
|
}
|
|
tag.showCount = tag.initialCount
|
|
}
|
|
}
|
|
|
|
this.items = res.data.items
|
|
this.tags = res.data.tags
|
|
this.disableScroll = this.items.length < this.limit
|
|
},
|
|
resetTagFilter (tag) {
|
|
if (tag.useRange) {
|
|
tag.filter = {
|
|
min: null,
|
|
max: null
|
|
}
|
|
} else {
|
|
tag.filter = []
|
|
}
|
|
},
|
|
onResetTagFilterClick (tag) {
|
|
this.resetTagFilter(tag)
|
|
this.loadItems()
|
|
},
|
|
hasFilters (tag) {
|
|
let filter = tag.filter
|
|
return filter.length || filter.min || filter.max
|
|
},
|
|
filterType (val, update) {
|
|
if (val === '') {
|
|
update(() => { this.types = this.orgTypes })
|
|
} else {
|
|
update(() => {
|
|
const needle = val.toLowerCase()
|
|
this.types = this.orgTypes.filter(type =>
|
|
type.name.toLowerCase().indexOf(needle) > -1)
|
|
})
|
|
}
|
|
}
|
|
},
|
|
filters: {
|
|
currency: i => i ? i.toFixed(2) + '€' : i
|
|
}
|
|
}
|
|
</script>
|