Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tribute 实现@(艾特)功能 #66

Open
xiaotiandada opened this issue Jan 22, 2021 · 0 comments
Open

tribute 实现@(艾特)功能 #66

xiaotiandada opened this issue Jan 22, 2021 · 0 comments
Labels

Comments

@xiaotiandada
Copy link
Owner

xiaotiandada commented Jan 22, 2021

利用 https://github.com/zurb/tribute 实现一个前端@的功能 大概长这样。

image

因为前端是Vue(Nuxt)所以用了一个别人封装好的库 https://github.com/syropian/vue-tribute

在Nuxt里面用?

如果你不需要在Nuxt里面用就可以跳过

// import Tribute from 'tributejs'
let Tribute

if (process.client) {
  Tribute = require('tributejs')
}

const VueTribute = {
  name: 'vue-tribute',
  props: {
    options: {
      type: Object,
      required: true
    }
  },
  watch: {
    options: {
      immediate: false,
      deep: true,
      handler() {
        if (this.tribute) {
          setTimeout(() => {
            var $el = this.$slots.default[0].elm
            this.tribute.detach($el)

            setTimeout(() => {
              $el = this.$slots.default[0].elm
              this.tribute = new Tribute(this.options)
              this.tribute.attach($el)
              $el.tributeInstance = this.tribute
            }, 0)
          }, 0)
        }
      }
    }
  },
  mounted() {
    if (process.client) {
      if (typeof Tribute === 'undefined') {
        throw new Error('[vue-tribute] cannot locate tributejs!')
      }
  
      const $el = this.$slots.default[0].elm
  
      this.tribute = new Tribute(this.options)
  
      this.tribute.attach($el)
  
      $el.tributeInstance = this.tribute
  
      $el.addEventListener('tribute-replaced', e => {
        e.target.dispatchEvent(new Event('input', { bubbles: true }))
      })
    }
  },
  beforeDestroy() {
    const $el = this.$slots.default[0].elm

    if (this.tribute) {
      this.tribute.detach($el)
    }
  },
  render(h) {
    return h(
      'div',
      {
        staticClass: 'v-tribute'
      },
      this.$slots.default
    )
  }
}
if (process.client) {
  if (typeof window !== 'undefined' && window.Vue) {
    window.Vue.component(VueTribute.name, VueTribute)
  }
}

export default VueTribute

因为Nuxt的关系 我基于源码又做了一层封装(加了一些判断而已)。

使用

<client-only>
      <vue-tribute
        :options="tributeOptions"
        @tribute-no-match="noMatchFound"
        @tribute-replaced="tributeReplaced"
      >
        <div
          id="tributeShare"
          class="content-editable"
          contenteditable="true"
          placeholder="谈谈感想"
        />
      </vue-tribute>
</client-only>

搜索用户

最主要的是这个options配置,然后 tribute 有几种输入框,具体看Demo就知道了 这里说一下怎么搜索

      tributeOptions: {
        collection: [
          {
            trigger: '@',
            values: (query, cb) => {
              console.log('query', query)
              if (!query) {
                return cb([])
              }

              return this.searchUser(query, cb)
            },
            loadingItemTemplate: '<div style="padding: 16px">Loading</div>',
            lookup: 'key',
            fillAttr: 'key',
            selectTemplate: function (item) {
              console.log('item', item)
              return `<a
                  class="tribute-mention"
                  contenteditable="false"
                  href="javascript:;"
                  title="${item.original.value}"
                  data-user="${item.original.id}">@${item.original.value}</a>`
            },
          },
        ],
      },

// ...

    searchUser: debounce(async function (val, cb) {
      const list = await search(params)
        return cb(list)
      } else {
        return cb([])
      }
    }, 300),

搜索通过 values 定义的Func来执行 query 是输入的内容 利用cb设置数据

设置用户显示模版

可以自定义Temp,我这里返回了一个 a Tag,默认是 span 我为了方便改成了 a。
并且设置了 data-user 自定义数据 方便后续做渲染操作。

return `<a
class="tribute-mention"
contenteditable="false"
href="javascript:;"
title="${item.original.value}"
data-user="${item.original.id}">@${item.original.value}</a>`

Xss过滤

为了避免用户插入HTML等提交,这里用 https://github.com/leizongmin/js-xss 过滤,显示的时候也需要过滤一次 避免用户通过接口提交(也可以用其他的办法)

这里只通过a标签 因为a标签需要做用户跳转的

// 过滤分享内容
const whiteListShare = {
  a: [ 'class', 'contenteditable', 'href', 'title', 'data-user', 'target' ]
}
export const Fn = (html, whiteList = whiteListShare) => {
  return xss(html, {
    whiteList: whiteList,
    stripIgnoreTag: true,
    stripIgnoreTagBody: ['script']
  })
}

渲染

因为搜索用户的时候已经做了a标签处理只需要设置属性就可以

    ele.setAttribute('href', 'xxx')

Emoji

https://github.com/jm-david/emoji-mart-vue 可以考虑用这个 选择很多

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant