浏览代码

feat: tags autocomplete in page properties

NGPixel 5 年之前
父节点
当前提交
f4e3fd0954

+ 54 - 6
client/components/editor/editor-modal-properties.vue

@@ -67,18 +67,32 @@
           v-divider
           v-card-text.grey.pt-5(:class='darkMode ? `darken-3-d5` : `lighten-4`')
             .overline.pb-5 {{$t('editor:props.categorization')}}
+            v-chip-group.radius-5.mb-5(column, v-if='tags && tags.length > 0')
+              v-chip(
+                v-for='tag of tags'
+                :key='`tag-` + tag'
+                close
+                label
+                color='teal'
+                text-color='teal lighten-5'
+                @click:close='removeTag(tag)'
+                ) {{tag}}
             v-combobox(
-              chips
-              deletable-chips
               :label='$t(`editor:props.tags`)'
               outlined
-              multiple
               v-model='tags'
               :hint='$t(`editor:props.tagsHint`)'
+              :items='newTagSuggestions'
+              :loading='$apollo.queries.newTagSuggestions.loading'
               persistent-hint
-              clearable
-              height='130'
+              deletable-chips
+              hide-no-data
+              hide-selected
+              :search-input.sync='newTagSearch'
+              multiple
               )
+              template(v-slot:selection='{ attrs, item, parent, selected }')
+                span
         v-tab-item
           v-card-text
             .overline.pb-5 {{$t('editor:props.publishState')}} #[v-chip.ml-3(label, color='grey', small, outlined).white--text coming soon]
@@ -243,6 +257,7 @@
 <script>
 import _ from 'lodash'
 import { sync, get } from 'vuex-pathify'
+import gql from 'graphql-tag'
 
 /* global siteLangs, siteConfig */
 
@@ -258,7 +273,10 @@ export default {
       isPublishStartShown: false,
       isPublishEndShown: false,
       pageSelectorShown: false,
-      namespaces: siteLangs.length ? siteLangs.map(ns => ns.code) : [siteConfig.lang]
+      namespaces: siteLangs.length ? siteLangs.map(ns => ns.code) : [siteConfig.lang],
+      newTag: '',
+      newTagSuggestions: [],
+      newTagSearch: ''
     }
   },
   computed: {
@@ -291,6 +309,14 @@ export default {
     }
   },
   methods: {
+    addTag () {
+      this.$nextTick(() => {
+        this.tags.push(this.newTag)
+      })
+    },
+    removeTag (tag) {
+      this.tags = _.without(this.tags, tag)
+    },
     close() {
       this.isShown = false
     },
@@ -301,6 +327,28 @@ export default {
       this.locale = locale
       this.path = path
     }
+  },
+  apollo: {
+    newTagSuggestions: {
+      query: gql`
+        query ($query: String!) {
+          pages {
+            searchTags (query: $query)
+          }
+        }
+      `,
+      variables () {
+        return {
+          query: this.newTagSearch
+        }
+      },
+      fetchPolicy: 'cache-first',
+      update: (data) => _.get(data, 'pages.searchTags', []),
+      skip () {
+        return !this.value || _.isEmpty(this.newTagSearch)
+      },
+      throttle: 500
+    }
   }
 }
 </script>

+ 18 - 0
server/graph/resolvers/page.js

@@ -129,6 +129,24 @@ module.exports = {
     async tags (obj, args, context, info) {
       return WIKI.models.tags.query().orderBy('tag', 'asc')
     },
+    /**
+     * SEARCH TAGS
+     */
+    async searchTags (obj, args, context, info) {
+      const results = await WIKI.models.tags.query()
+        .column('tag')
+        .where(builder => {
+          builder.andWhere(builderSub => {
+            if (WIKI.config.db.type === 'postgres') {
+              builderSub.where('tag', 'ILIKE', `%${args.query}%`)
+            } else {
+              builderSub.where('tag', 'LIKE', `%${args.query}%`)
+            }
+          })
+        })
+        .limit(5)
+      return results.map(r => r.tag)
+    },
     /**
      * FETCH PAGE TREE
      */

+ 4 - 0
server/graph/schemas/page.graphql

@@ -41,6 +41,10 @@ type PageQuery {
 
   tags: [PageTag]! @auth(requires: ["manage:system", "read:pages"])
 
+  searchTags(
+    query: String!
+  ): [String]! @auth(requires: ["manage:system", "read:pages"])
+
   tree(
     path: String
     parent: Int

+ 4 - 0
server/models/tags.js

@@ -53,6 +53,10 @@ module.exports = class Tag extends Model {
   static async associateTags ({ tags, page }) {
     let existingTags = await WIKI.models.tags.query().column('id', 'tag')
 
+    // Format tags
+
+    tags = _.uniq(tags.map(t => t.toLowerCase()))
+
     // Create missing tags
 
     const newTags = _.filter(tags, t => !_.some(existingTags, ['tag', t])).map(t => ({

+ 0 - 1
server/modules/search/db/engine.js

@@ -30,7 +30,6 @@ module.exports = {
         if (opts.path) {
           builder.andWhere('path', 'like', `${opts.path}%`)
         }
-        // TODO: Add user permissions filtering
         builder.andWhere(builderSub => {
           if (WIKI.config.db.type === 'postgres') {
             builderSub.where('title', 'ILIKE', `%${q}%`)