Merge pull request #168 from pinry/feature/tag-auto-complete

close #161 Add tag auto-complete
This commit is contained in:
Ji Qu
2019-12-12 12:41:15 +08:00
committed by GitHub
6 changed files with 91 additions and 21 deletions

View File

@@ -226,3 +226,10 @@ class BoardSerializer(serializers.HyperlinkedModelSerializer):
)
validated_data['submitter'] = user
return super(BoardSerializer, self).create(validated_data)
class TagAutoCompleteSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name', )

View File

@@ -1,7 +1,10 @@
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, mixins, routers
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.viewsets import GenericViewSet
from taggit.models import Tag
from core import serializers as api
from core.models import Image, Pin, Board
@@ -49,8 +52,23 @@ class BoardAutoCompleteViewSet(
pagination_class = None
class TagAutoCompleteViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Tag.objects.all()
serializer_class = api.TagAutoCompleteSerializer
pagination_class = None
@method_decorator(cache_page(60 * 5))
def list(self, request, *args, **kwargs):
return super(TagAutoCompleteViewSet, self).list(
request,
*args,
**kwargs
)
drf_router = routers.DefaultRouter()
drf_router.register(r'pins', PinViewSet)
drf_router.register(r'images', ImageViewSet)
drf_router.register(r'boards', BoardViewSet)
drf_router.register(r'tags-auto-complete', TagAutoCompleteViewSet)
drf_router.register(r'boards-auto-complete', BoardAutoCompleteViewSet)

View File

@@ -258,7 +258,15 @@ const User = {
},
};
const Tag = {
fetchList() {
const url = `${API_PREFIX}tags-auto-complete/`;
return axios.get(url);
},
};
export default {
Tag,
Pin,
Board,
fetchPin,

View File

@@ -38,21 +38,10 @@
<script>
import API from '../api';
import ModelForm from '../utils/ModelForm';
import AutoComplete from '../utils/AutoComplete';
const fields = ['name'];
function getFilteredOptions(options, filterText) {
return options.filter(
(option) => {
const index = option.name
.toString()
.toLowerCase()
.indexOf(filterText.toLowerCase());
return index >= 0;
},
);
}
function getBoardFromResp(boardObject) {
return { name: boardObject.name, value: boardObject.id };
}
@@ -62,7 +51,7 @@ function getAvailableOptions(vm, filter) {
if (filter === '' || filter === null) {
availableOptions = vm.allOptions;
} else {
availableOptions = getFilteredOptions(
availableOptions = AutoComplete.getFilteredOptions(
vm.allOptions, vm.form.name.value,
);
}

View File

@@ -38,6 +38,24 @@
>
</b-input>
</b-field>
<b-field label="Tags">
<b-taginput
v-model="pinModel.form.tags.value"
:data="editorMeta.filteredTagOptions"
autocomplete
ellipsis
icon="label"
:allow-new="true"
placeholder="Add a tag"
@typing="getFilteredTags">
<template slot-scope="props">
<strong>{{ props.option }}</strong>
</template>
<template slot="empty">
There are no items
</template>
</b-taginput>
</b-field>
<b-field label="Descripton"
:type="pinModel.form.description.type"
:message="pinModel.form.description.error">
@@ -49,14 +67,6 @@
>
</b-input>
</b-field>
<b-field label="Tags">
<b-taginput
v-model="pinModel.form.tags.value"
ellipsis
icon="label"
placeholder="Add a tag">
</b-taginput>
</b-field>
</div>
<div class="column" v-if="!isEdit">
<FilterSelect
@@ -91,6 +101,7 @@ import FilterSelect from './FilterSelect.vue';
import bus from '../utils/bus';
import ModelForm from '../utils/ModelForm';
import Loading from '../utils/Loading';
import AutoComplete from '../utils/AutoComplete';
function isURLBlank(url) {
return url !== null && url === '';
@@ -133,13 +144,16 @@ export default {
},
boardId: null,
boardOptions: [],
tagOptions: [],
editorMeta: {
title: 'New Pin',
filteredTagOptions: [],
},
};
},
created() {
this.fetchBoardList();
this.fetchTagList();
if (this.isEdit) {
this.editorMeta.title = 'Edit Pin';
this.pinModel.form.url.value = this.existedPin.url;
@@ -154,6 +168,25 @@ export default {
}
},
methods: {
fetchTagList() {
API.Tag.fetchList().then(
(resp) => {
this.tagOptions = resp.data;
},
);
},
getFilteredTags(text) {
const filteredTagOptions = [];
AutoComplete.getFilteredOptions(
this.tagOptions,
text,
).forEach(
(option) => {
filteredTagOptions.push(option.name);
},
);
this.editorMeta.filteredTagOptions = filteredTagOptions;
},
fetchBoardList() {
API.Board.fetchFullList(this.username).then(
(resp) => {

View File

@@ -0,0 +1,15 @@
function getFilteredOptions(options, filterText) {
return options.filter(
(option) => {
const index = option.name
.toString()
.toLowerCase()
.indexOf(filterText.toLowerCase());
return index >= 0;
},
);
}
export default {
getFilteredOptions,
};