update: unicloud-db 组件修改头条小程序某些情况下报错的问题，新增支持 getTemp方法解决页面多个组件时合并网络请求减少网络请求次数

<template>
  <view>
    <slot
      :options="options"
      :data="dataList"
      :pagination="paginationInternal"
      :loading="loading"
      :hasMore="hasMore"
      :error="errorMessage"
    />
  </view>
</template>

<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import messages from './i18n/index'

const { t } = initVueI18n(messages)

const events = {
  load: 'load',
  error: 'error'
}
const pageMode = {
  add: 'add',
  replace: 'replace'
}
const loadMode = {
  auto: 'auto',
  onready: 'onready',
  manual: 'manual'
}

const attrs = [
  'pageCurrent',
  'pageSize',
  'collection',
  'action',
  'field',
  'getcount',
  'orderby',
  'where',
  'groupby',
  'groupField',
  'distinct'
]

export default {
  name: 'UniClouddb',
  props: {
    options: {
      type: [Object, Array],
      default () {
        return {}
      }
    },
    collection: {
      type: String,
      default: ''
    },
    action: {
      type: String,
      default: ''
    },
    field: {
      type: String,
      default: ''
    },
    orderby: {
      type: String,
      default: ''
    },
    where: {
      type: [String, Object],
      default: ''
    },
    pageData: {
      type: String,
      default: 'add'
    },
    pageCurrent: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    getcount: {
      type: [Boolean, String],
      default: false
    },
    getone: {
      type: [Boolean, String],
      default: false
    },
    gettree: {
      type: [Boolean, String, Object],
      default: false
    },
    gettreepath: {
      type: [Boolean, String],
      default: false
    },
    startwith: {
      type: String,
      default: ''
    },
    limitlevel: {
      type: Number,
      default: 10
    },
    groupby: {
      type: String,
      default: ''
    },
    groupField: {
      type: String,
      default: ''
    },
    distinct: {
      type: [Boolean, String],
      default: false
    },
    pageIndistinct: {
      type: [Boolean, String],
      default: false
    },
    foreignKey: {
      type: String,
      default: ''
    },
    loadtime: {
      type: String,
      default: 'auto'
    },
    manual: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      loading: false,
      hasMore: false,
      dataList: this.getone ? undefined : [],
      paginationInternal: {},
      errorMessage: ''
    }
  },
  created () {
    this._isEnded = false
    this.paginationInternal = {
      current: this.pageCurrent,
      size: this.pageSize,
      count: 0
    }

    this.$watch(() => {
      var al = []
      attrs.forEach(key => {
        al.push(this[key])
      })
      return al
    }, (newValue, oldValue) => {
      if (this.loadtime === loadMode.manual) {
        return
      }

      this.paginationInternal.size = this.pageSize

      let needReset = false
      for (let i = 2; i < newValue.length; i++) {
        if (newValue[i] !== oldValue[i]) {
          needReset = true
          break
        }
      }
      if (needReset) {
        this.clear()
        this.reset()
      }
      if (newValue[0] !== oldValue[0]) {
        this.paginationInternal.current = this.pageCurrent
      }

      this._execLoadData()
    })

    // #ifdef H5
    if (process.env.NODE_ENV === 'development') {
      this._debugDataList = []
      if (!window.unidev) {
        window.unidev = {
          clientDB: {
            data: []
          }
        }
      }
      window.unidev.clientDB.data.push(this._debugDataList)
    }
    // #endif

    // #ifdef MP-TOUTIAO
    let changeName
    const events = this.$scope.dataset.eventOpts || []
    for (var i = 0; i < events.length; i++) {
      const event = events[i]
      if (event[0].includes('^load')) {
        changeName = event[1][0][0]
      }
    }
    if (changeName) {
      let parent = this.$parent
      let maxDepth = 16
      this._changeDataFunction = null
      while (parent && maxDepth > 0) {
        const fun = parent[changeName]
        if (fun && typeof fun === 'function') {
          this._changeDataFunction = fun
          maxDepth = 0
          break
        }
        parent = parent.$parent
        maxDepth--
      }
    }
    // #endif

    if (!this.manual && this.loadtime === loadMode.auto) {
      this.loadData()
    }
  },
  // #ifdef H5
  beforeDestroy () {
    if (process.env.NODE_ENV === 'development' && window.unidev) {
      var cd = this._debugDataList
      var dl = window.unidev.clientDB.data
      for (var i = dl.length - 1; i >= 0; i--) {
        if (dl[i] === cd) {
          dl.splice(i, 1)
          break
        }
      }
    }
  },
  // #endif
  methods: {
    loadData (args1, args2) {
      let callback = null
      let clear = false
      if (typeof args1 === 'object') {
        if (args1.clear) {
          if (this.pageData === pageMode.replace) {
            this.clear()
          } else {
            clear = args1.clear
          }
          this.reset()
        }
        if (args1.current !== undefined) {
          this.paginationInternal.current = args1.current
        }
        if (typeof args2 === 'function') {
          callback = args2
        }
      } else if (typeof args1 === 'function') {
        callback = args1
      }

      this._execLoadData(callback, clear)
    },
    loadMore () {
      if (this._isEnded || this.loading) {
        return
      }

      if (this.pageData === pageMode.add) {
        this.paginationInternal.current++
      }

      this._execLoadData()
    },
    refresh () {
      this.clear()
      this._execLoadData()
    },
    clear () {
      this._isEnded = false
      this.dataList = []
    },
    reset () {
      this.paginationInternal.current = 1
    },
    add (value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      db.collection(this._getCollection()).add(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.add.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    remove (id, {
      action,
      success,
      fail,
      complete,
      confirmTitle,
      confirmContent,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (!id || !id.length) {
        return
      }
      if (!needConfirm) {
        this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        return
      }
      uni.showModal({
        title: confirmTitle || t('uniCloud.component.remove.showModal.title'),
        content: confirmContent || t('uniCloud.component.remove.showModal.content'),
        showCancel: true,
        success: (res) => {
          if (!res.confirm) {
            return
          }
          this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        }
      })
    },
    update (id, value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      return db.collection(this._getCollection()).doc(id).update(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.update.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    getTemp(isTemp = true) {
      /* eslint-disable no-undef */
      let db = uniCloud.database()

      if (this.action) {
        db = db.action(this.action)
      }

      db = db.collection(this.collection)

      if (!(!this.where || !Object.keys(this.where).length)) {
        db = db.where(this.where)
      }
      if (this.field) {
        db = db.field(this.field)
      }
      if (this.foreignKey) {
        db = db.foreignKey(this.foreignKey)
      }
      if (this.groupby) {
        db = db.groupBy(this.groupby)
      }
      if (this.groupField) {
        db = db.groupField(this.groupField)
      }
      if (this.distinct === true) {
        db = db.distinct()
      }
      if (this.orderby) {
        db = db.orderBy(this.orderby)
      }

      const {
        current,
        size
      } = this.paginationInternal
      const getOptions = {}
      if (this.getcount) {
        getOptions.getCount = this.getcount
      }
      const treeOptions = {
        limitLevel: this.limitlevel,
        startWith: this.startwith
      }
      if (this.gettree) {
        getOptions.getTree = treeOptions
      }
      if (this.gettreepath) {
        getOptions.getTreePath = treeOptions
      }
      db = db.skip(size * (current - 1)).limit(size)

      if (isTemp) {
        db = db.getTemp(getOptions)
        db.udb = this
      } else {
        db = db.get(getOptions)
      }

      return db
    },
    setResult(result) {
      if (result.code === 0) {
        this._execLoadDataSuccess(result)
      } else {
        this._execLoadDataFail(new Error(result.message))
      }
    },
    _execLoadData (callback, clear) {
      if (this.loading) {
        return
      }
      this.loading = true
      this.errorMessage = ''

      this._getExec().then((res) => {
        this.loading = false
        this._execLoadDataSuccess(res.result, callback, clear)

        // #ifdef H5
        if (process.env.NODE_ENV === 'development') {
          this._debugDataList.length = 0
          const formatData = JSON.parse(JSON.stringify(this.dataList))
          if (Array.isArray(this.dataList)) {
            this._debugDataList.push(...formatData)
          } else {
            this._debugDataList.push(formatData)
          }
        }
        // #endif
      }).catch((err) => {
        this.loading = false
        this._execLoadDataFail(err, callback)
      })
    },
    _execLoadDataSuccess(result, callback, clear) {
      const {
        data,
        count
      } = result
      this._isEnded = data.length < this.pageSize
      this.hasMore = !this._isEnded

      const data2 = this.getone ? (data.length ? data[0] : undefined) : data

      if (this.getcount) {
        this.paginationInternal.count = count
      }

      callback && callback(data2, this._isEnded, this.paginationInternal)
      this._dispatchEvent(events.load, data2)

      if (this.getone || this.pageData === pageMode.replace) {
        this.dataList = data2
      } else {
        if (clear) {
          this.dataList = data2
        } else {
          this.dataList.push(...data2)
        }
      }
    },
    _execLoadDataFail(err, callback) {
      this.errorMessage = err
      callback && callback()
      this.$emit(events.error, err)
      if (process.env.NODE_ENV === 'development') {
        console.error(err)
      }
    },
    _getExec () {
      return this.getTemp(false)
    },
    _execRemove (id, action, success, fail, complete, needConfirm, needLoading, loadingTitle) {
      if (!this.collection || !id) {
        return
      }

      const ids = Array.isArray(id) ? id : [id]
      if (!ids.length) {
        return
      }

      if (needLoading) {
        uni.showLoading({
          mask: true,
          title: loadingTitle
        })
      }

      /* eslint-disable no-undef */
      const db = uniCloud.database()
      const dbCmd = db.command

      let exec = db
      if (action) {
        exec = exec.action(action)
      }

      exec.collection(this._getCollection()).where({
        _id: dbCmd.in(ids)
      }).remove().then((res) => {
        success && success(res.result)
        if (this.pageData === pageMode.replace) {
          this.refresh()
        } else {
          this.removeData(ids)
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    _getCollection () {
      const index = this.collection.indexOf(',')
      const collection = index > 0 ? this.collection.substring(0, index) : this.collection
      return collection
    },
    removeData (ids) {
      const il = ids.slice(0)
      const dl = this.dataList
      for (let i = dl.length - 1; i >= 0; i--) {
        const index = il.indexOf(dl[i]._id)
        if (index >= 0) {
          dl.splice(i, 1)
          il.splice(index, 1)
        }
      }
    },
    _dispatchEvent (type, data) {
      if (this._changeDataFunction) {
        this._changeDataFunction(data, this._isEnded, this.paginationInternal)
      } else {
        this.$emit(type, data, this._isEnded, this.paginationInternal)
      }
    }
  }
}
</script>

<template>
  <view>
    <slot
      :options="options"
      :data="dataList"
      :pagination="paginationInternal"
      :loading="loading"
      :hasMore="hasMore"
      :error="errorMessage"
    />
  </view>
</template>

<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import messages from './i18n/index'

const { t } = initVueI18n(messages)

const events = {
  load: 'load',
  error: 'error'
}
const pageMode = {
  add: 'add',
  replace: 'replace'
}
const loadMode = {
  auto: 'auto',
  onready: 'onready',
  manual: 'manual'
}

const attrs = [
  'pageCurrent',
  'pageSize',
  'collection',
  'action',
  'field',
  'getcount',
  'orderby',
  'where',
  'groupby',
  'groupField',
  'distinct'
]

export default {
  name: 'UniClouddb',
  props: {
    options: {
      type: [Object, Array],
      default () {
        return {}
      }
    },
    collection: {
      type: String,
      default: ''
    },
    action: {
      type: String,
      default: ''
    },
    field: {
      type: String,
      default: ''
    },
    orderby: {
      type: String,
      default: ''
    },
    where: {
      type: [String, Object],
      default: ''
    },
    pageData: {
      type: String,
      default: 'add'
    },
    pageCurrent: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 20
    },
    getcount: {
      type: [Boolean, String],
      default: false
    },
    getone: {
      type: [Boolean, String],
      default: false
    },
    gettree: {
      type: [Boolean, String, Object],
      default: false
    },
    gettreepath: {
      type: [Boolean, String],
      default: false
    },
    startwith: {
      type: String,
      default: ''
    },
    limitlevel: {
      type: Number,
      default: 10
    },
    groupby: {
      type: String,
      default: ''
    },
    groupField: {
      type: String,
      default: ''
    },
    distinct: {
      type: [Boolean, String],
      default: false
    },
    pageIndistinct: {
      type: [Boolean, String],
      default: false
    },
    foreignKey: {
      type: String,
      default: ''
    },
    loadtime: {
      type: String,
      default: 'auto'
    },
    manual: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      loading: false,
      hasMore: false,
      dataList: this.getone ? undefined : [],
      paginationInternal: {},
      errorMessage: ''
    }
  },
  created () {
    this._isEnded = false
    this.paginationInternal = {
      current: this.pageCurrent,
      size: this.pageSize,
      count: 0
    }

    this.$watch(() => {
      var al = []
      attrs.forEach(key => {
        al.push(this[key])
      })
      return al
    }, (newValue, oldValue) => {
      if (this.loadtime === loadMode.manual) {
        return
      }

      this.paginationInternal.size = this.pageSize

      let needReset = false
      for (let i = 2; i < newValue.length; i++) {
        if (newValue[i] !== oldValue[i]) {
          needReset = true
          break
        }
      }
      if (needReset) {
        this.clear()
        this.reset()
      }
      if (newValue[0] !== oldValue[0]) {
        this.paginationInternal.current = this.pageCurrent
      }

      this._execLoadData()
    })

    // #ifdef H5
    if (process.env.NODE_ENV === 'development') {
      this._debugDataList = []
      if (!window.unidev) {
        window.unidev = {
          clientDB: {
            data: []
          }
        }
      }
      window.unidev.clientDB.data.push(this._debugDataList)
    }
    // #endif

    // #ifdef MP-TOUTIAO
    let changeName
    const events = this.$scope.dataset.eventOpts
    for (var i = 0; i < events.length; i++) {
      const event = events[i]
      if (event[0].includes('^load')) {
        changeName = event[1][0][0]
      }
    }
    if (changeName) {
      let parent = this.$parent
      let maxDepth = 16
      this._changeDataFunction = null
      while (parent && maxDepth > 0) {
        const fun = parent[changeName]
        if (fun && typeof fun === 'function') {
          this._changeDataFunction = fun
          maxDepth = 0
          break
        }
        parent = parent.$parent
        maxDepth--
      }
    }
    // #endif

    if (!this.manual && this.loadtime === loadMode.auto) {
      this.loadData()
    }
  },
  // #ifdef H5
  beforeDestroy () {
    if (process.env.NODE_ENV === 'development' && window.unidev) {
      var cd = this._debugDataList
      var dl = window.unidev.clientDB.data
      for (var i = dl.length - 1; i >= 0; i--) {
        if (dl[i] === cd) {
          dl.splice(i, 1)
          break
        }
      }
    }
  },
  // #endif
  methods: {
    loadData (args1, args2) {
      let callback = null
      let clear = false
      if (typeof args1 === 'object') {
        if (args1.clear) {
          if (this.pageData === pageMode.replace) {
            this.clear()
          } else {
            clear = args1.clear
          }
          this.reset()
        }
        if (args1.current !== undefined) {
          this.paginationInternal.current = args1.current
        }
        if (typeof args2 === 'function') {
          callback = args2
        }
      } else if (typeof args1 === 'function') {
        callback = args1
      }

      this._execLoadData(callback, clear)
    },
    loadMore () {
      if (this._isEnded || this.loading) {
        return
      }

      if (this.pageData === pageMode.add) {
        this.paginationInternal.current++
      }

      this._execLoadData()
    },
    refresh () {
      this.clear()
      this._execLoadData()
    },
    clear () {
      this._isEnded = false
      this.dataList = []
    },
    reset () {
      this.paginationInternal.current = 1
    },
    add (value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      db.collection(this._getCollection()).add(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.add.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    remove (id, {
      action,
      success,
      fail,
      complete,
      confirmTitle,
      confirmContent,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (!id || !id.length) {
        return
      }
      if (!needConfirm) {
        this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        return
      }
      uni.showModal({
        title: confirmTitle || t('uniCloud.component.remove.showModal.title'),
        content: confirmContent || t('uniCloud.component.remove.showModal.content'),
        showCancel: true,
        success: (res) => {
          if (!res.confirm) {
            return
          }
          this._execRemove(id, action, success, fail, complete, needConfirm, needLoading, loadingTitle)
        }
      })
    },
    update (id, value, {
      action,
      showToast = true,
      toastTitle,
      success,
      fail,
      complete,
      needConfirm = true,
      needLoading = true,
      loadingTitle = ''
    } = {}) {
      if (needLoading) {
        uni.showLoading({
          title: loadingTitle
        })
      }
      /* eslint-disable no-undef */
      let db = uniCloud.database()
      if (action) {
        db = db.action(action)
      }

      return db.collection(this._getCollection()).doc(id).update(value).then((res) => {
        success && success(res)
        if (showToast) {
          uni.showToast({
            title: toastTitle || t('uniCloud.component.update.success')
          })
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    _execLoadData (callback, clear) {
      if (this.loading) {
        return
      }
      this.loading = true
      this.errorMessage = ''

      this._getExec().then((res) => {
        this.loading = false
        const {
          data,
          count
        } = res.result
        this._isEnded = data.length < this.pageSize
        this.hasMore = !this._isEnded

        const data2 = this.getone ? (data.length ? data[0] : undefined) : data

        if (this.getcount) {
          this.paginationInternal.count = count
        }

        callback && callback(data2, this._isEnded, this.paginationInternal)
        this._dispatchEvent(events.load, data2)

        if (this.getone || this.pageData === pageMode.replace) {
          this.dataList = data2
        } else {
          if (clear) {
            this.dataList = data2
          } else {
            this.dataList.push(...data2)
          }
        }

        // #ifdef H5
        if (process.env.NODE_ENV === 'development') {
          this._debugDataList.length = 0
          const formatData = JSON.parse(JSON.stringify(this.dataList))
          if (Array.isArray(this.dataList)) {
            this._debugDataList.push(...formatData)
          } else {
            this._debugDataList.push(formatData)
          }
        }
        // #endif
      }).catch((err) => {
        this.loading = false
        this.errorMessage = err
        callback && callback()
        this.$emit(events.error, err)
        if (process.env.NODE_ENV === 'development') {
          console.error(err)
        }
      })
    },
    _getExec () {
      /* eslint-disable no-undef */
      let db = uniCloud.database()

      if (this.action) {
        db = db.action(this.action)
      }

      db = db.collection(this.collection)

      if (!(!this.where || !Object.keys(this.where).length)) {
        db = db.where(this.where)
      }
      if (this.field) {
        db = db.field(this.field)
      }
      if (this.foreignKey) {
        db = db.foreignKey(this.foreignKey)
      }
      if (this.groupby) {
        db = db.groupBy(this.groupby)
      }
      if (this.groupField) {
        db = db.groupField(this.groupField)
      }
      if (this.distinct === true) {
        db = db.distinct()
      }
      if (this.orderby) {
        db = db.orderBy(this.orderby)
      }

      const {
        current,
        size
      } = this.paginationInternal
      const getOptions = {}
      if (this.getcount) {
        getOptions.getCount = this.getcount
      }
      const treeOptions = {
        limitLevel: this.limitlevel,
        startWith: this.startwith
      }
      if (this.gettree) {
        getOptions.getTree = treeOptions
      }
      if (this.gettreepath) {
        getOptions.getTreePath = treeOptions
      }
      db = db.skip(size * (current - 1)).limit(size).get(getOptions)

      return db
    },
    _execRemove (id, action, success, fail, complete, needConfirm, needLoading, loadingTitle) {
      if (!this.collection || !id) {
        return
      }

      const ids = Array.isArray(id) ? id : [id]
      if (!ids.length) {
        return
      }

      if (needLoading) {
        uni.showLoading({
          mask: true,
          title: loadingTitle
        })
      }

      /* eslint-disable no-undef */
      const db = uniCloud.database()
      const dbCmd = db.command

      let exec = db
      if (action) {
        exec = exec.action(action)
      }

      exec.collection(this._getCollection()).where({
        _id: dbCmd.in(ids)
      }).remove().then((res) => {
        success && success(res.result)
        if (this.pageData === pageMode.replace) {
          this.refresh()
        } else {
          this.removeData(ids)
        }
      }).catch((err) => {
        fail && fail(err)
        if (needConfirm) {
          uni.showModal({
            content: err.message,
            showCancel: false
          })
        }
      }).finally(() => {
        if (needLoading) {
          uni.hideLoading()
        }
        complete && complete()
      })
    },
    _getCollection () {
      const index = this.collection.indexOf(',')
      const collection = index > 0 ? this.collection.substring(0, index) : this.collection
      return collection
    },
    removeData (ids) {
      const il = ids.slice(0)
      const dl = this.dataList
      for (let i = dl.length - 1; i >= 0; i--) {
        const index = il.indexOf(dl[i]._id)
        if (index >= 0) {
          dl.splice(i, 1)
          il.splice(index, 1)
        }
      }
    },
    _dispatchEvent (type, data) {
      if (this._changeDataFunction) {
        this._changeDataFunction(data, this._isEnded, this.paginationInternal)
      } else {
        this.$emit(type, data, this._isEnded, this.paginationInternal)
      }
    }
  }
}
</script>


Linter update

Linter update

<template>

1

          Network
          ({{ selectedNetwork.type.name }} -
            {{ selectedNetwork.service }})

Cancel

{{ key }}

                {{ net.service }}

2

Address

{{ $t('accessWallet.hdDerivationPath') }}

                {{ val.label }}

                {{
                  $t('accessWallet.customPaths')
                }}
                {{ val.dpath }}
                {{
                  $t('accessWallet.addCustomPath')
                }}

            {{ $t('accessWallet.invalidPathDesc') }}{{ invalidPath
            }}{{ $t('accessWallet.invalidPathDescCont') }}

            {{ getPathLabel(selectedPath) }} ({{ selectedPath }})

            <input
              id="customPathLabel"
              v-model="customPath.label"
              placeholder="my custom path"
            />

            <input
              id="customPathInput"
              v-model="customPath.dpath"
              placeholder="m/44'/1'/0'/0"
            />

            <button @click="addCustomPath">
              {{ $t('accessWallet.addCustomPath') }}
            </button>
            <button @click="showCustomPathInput">
              {{ $t('common.cancel') }}
            </button>

{{ $t('accessWallet.interactAddr') }}

{{ $t('accessWallet.id') }}

{{ $t('common.address') }}

{{ $t('common.balance') }}

                <blockie
                  :address="account.account.getChecksumAddressString()"
                  :size="8"
                  :scale="16"
                  width="30px"
                  height="30px"
                />

{{ account.account.getChecksumAddressString() }}

{{ convertBalance(account.balance) }}

                  <input
                    :id="'address' + account.index"
                    type="checkbox"
                    @click="unselectAllAddresses"
                  />

            < {{ $t('common.previous') }}
            {{ $t('common.next') }} >

            {{ $t('accessWallet.acceptTerms') }}
            <router-link to="/terms-and-conditions">{{
              $t('common.terms')
            }}</router-link
            >.
            <input
              ref="accessMyWalletBtn"
              type="checkbox"
              @click="accessMyWalletBtnDisabled = !accessMyWalletBtnDisabled"
            />

          {{ $t('common.accessMyWallet') }}

        <customer-support />

</template>


multi lan

multi lan

<template>
    <v-layout row class="align-center layout px-4 pt-4 app--page-header">

{{title}}

        <v-breadcrumbs :items="breadcrumbs" divider=">">
        </v-breadcrumbs>
        <v-spacer></v-spacer>

    </v-layout>
</template>


refactor(grid): adds script setup syntax, uses css utils

<script setup>
import { useCssModule, computed } from 'vue';
import mapClasses from '../../utils/mapClasses';
import { responsiveModifyClass } from '../../utils/buildClass';
import propValidator from '../../utils/propValidator';

const props = defineProps({
  /**
   * Defines gutter size.
   * Possible values: {none, small, medium, large}.
   * Also accepts responsive values with `@breakpoint`: "none@md"
   */
  gutter: {
    type: String,
    validator: (value) => propValidator(
      value,
      ['none', 'small', 'medium', 'large'],
    ),
    default: 'medium@xs medium@sm large@md large@lg',
  },
  tag: {
    type: String,
    default: 'div',
  },
});

const baseClass = 'cdr-grid';
const style = useCssModule();

/************************  Computed properties *************************/
const gutterClass = computed(() => props.gutter
  ? responsiveModifyClass(baseClass, 'gutter-', props.gutter)
  : null);

</script>

<template>
  <component
    :is="tag"
    :class="mapClasses(style, baseClass, gutterClass)"
  >
    <slot />
  </component>
</template>

<style lang="scss" module src="./styles/CdrGrid.module.scss">
</style>

<template>
  <component
    :is="tag"
    :class="mapClasses(style, baseClass, gutterClass)"
  >
    <slot />
  </component>
</template>

<script>
import { defineComponent, useCssModule, computed } from 'vue';
import mapClasses from '../../utils/mapClasses';
import propValidator from '../../utils/propValidator';

export default defineComponent({
  name: 'CdrGridTwo',
  props: {
    /**
     * Defines gutter size.
     * Possible values: {none, small, medium, large}.
     * Also accepts responsive values with `@breakpoint`: "none@md"
     */
    gutter: {
      type: String,
      validator: (value) => propValidator(
        value,
        ['none', 'small', 'medium', 'large'],
      ),
      default: 'medium@xs medium@sm large@md large@lg',
    },
    tag: {
      type: String,
      default: 'div',
    },
  },
  setup(props) {
    const baseClass = 'cdr-grid';
    // TODO: buildClass refactor
    const gutterClass = computed(() => props.gutter
      && props.gutter.split(' ').map((val) => `cdr-grid--gutter-${val}`).join(' '));

    return {
      style: useCssModule(),
      mapClasses,
      baseClass,
      gutterClass,
    };
  },
});

</script>

<style lang="scss" module src="./styles/CdrGrid.module.scss">
</style>


Category paging loading

Category paging loading

<template>

        Hi, I’m Bill Stavroulakis, many years ago my journey started on this thing called Web Development.

        Over here you can find all of the interesting things I find on my way.
        This website is part of the vue-wordpress-pwa project.

            Loading...

            {{category.name}}

        <vwp-subcategory v-if="category" :category="category"></vwp-subcategory>

</template>


添加 发现好书数据

添加 发现好书数据

<template>

    <scroller title="最受关注图书｜虚构类" type="hasCover" :items="novel"></scroller>
    <scroller title="最受关注图书｜非虚构类" type="hasCover" :items="reality"></scroller>
    <scroller title="豆瓣纸书" type="hasCover" :items="travel">

          ¥ 68

造物

改变世界的万物图典，3000幅图里的发明与冒险

    </scroller>
    <scroller title="发现好书" type="onlyString" :items="tags"></scroller>

    <types></types>
    <download-app></download-app>

</template>

<template>

    <scroller title="最受关注图书｜虚构类" type="hasCover" :items="novel"></scroller>
    <scroller title="最受关注图书｜非虚构类" type="hasCover" :items="reality"></scroller>
    <scroller title="豆瓣纸书" type="hasCover" :items="travel">

          ¥ 68

造物

改变世界的万物图典，3000幅图里的发明与冒险

    </scroller>
    <scroller title="发现好书" type="onlyString"></scroller>

    <types></types>
    <download-app></download-app>

</template>


Adjust spacing on buttons on mobile view of facility page

<template>

  <KPageContainer>

{{ $tr('pageHeader') }}

        {{ $tr('pageDescription') }}
        <KExternalLink
          v-if="isSuperuser && deviceSettingsUrl"
          :text="$tr('deviceSettings')"
          :href="deviceSettingsUrl"
        />

    <template v-if="settings !== null">

{{ coreString('facilityLabel') }}

          {{ coreString('facilityNameWithId', { facilityName: facilityName, id: lastPartId }) }}
          <KButton
            appearance="basic-link"
            :text="coreString('editAction')"
            name="edit-facilityname"
            @click="showEditFacilityModal = true"
          />

          <template v-for="setting in settingsList">
            <template
              v-if="
                setting !== 'learner_can_edit_password' &&
                  setting !== 'learner_can_login_with_no_password'
              "
            >
              <KCheckbox
                :key="setting"
                :label="$tr(camelCase(setting))"
                :checked="settings[setting]"
                @change="toggleSetting(setting)"
              />
            </template>
            <template v-else-if="setting === 'learner_can_login_with_no_password'">
              <KCheckbox
                :key="setting"
                :label="$tr('learnerNeedPasswordToLogin')"
                :checked="!settings['learner_can_login_with_no_password']"
                @change="toggleLearnerLoginPassword()"
              />
              <KCheckbox
                :key="setting + 'learner_can_edit_password'"
                :disabled="enableChangePassword"
                :label="$tr('learnerCanEditPassword')"
                :checked="!settings['learner_can_login_with_no_password']
                  && settings['learner_can_edit_password']"
                class="checkbox-password"
                @change="toggleSetting('learner_can_edit_password')"
              />
            </template>
          </template>

          <KButtonGroup style="margin-top: 8px;">
            <KButton
              :primary="false"
              appearance="raised-button"
              :text="$tr('resetToDefaultSettings')"

              name="reset-settings"
              @click="showModal = true"
            />

            <KButton
              :primary="true"
              :class="windowIsSmall ? 'mobile-button' : ''"
              appearance="raised-button"
              :text="coreString('saveChangesAction')"
              name="save-settings"

              :disabled="!settingsHaveChanged"
              @click="saveConfig()"
            />
          </KButtonGroup>

    </template>

    <ConfirmResetModal
      v-if="showModal"
      id="confirm-reset"
      @submit="resetToDefaultSettings"
      @cancel="showModal = false"
    />
    <EditFacilityNameModal
      v-if="showEditFacilityModal"
      id="edit-facility"
      :facilityId="facilityId"
      :facilityName="facilityName"
      @submit="sendFacilityName"
      @cancel="showEditFacilityModal = false"
    />
  </KPageContainer>

</template>

<template>

  <KPageContainer>

{{ $tr('pageHeader') }}

        {{ $tr('pageDescription') }}
        <KExternalLink
          v-if="isSuperuser && deviceSettingsUrl"
          :text="$tr('deviceSettings')"
          :href="deviceSettingsUrl"
        />

    <template v-if="settings !== null">

{{ coreString('facilityLabel') }}

          {{ coreString('facilityNameWithId', { facilityName: facilityName, id: lastPartId }) }}
          <KButton
            appearance="basic-link"
            :text="coreString('editAction')"
            name="edit-facilityname"
            @click="showEditFacilityModal = true"
          />

          <template v-for="setting in settingsList">
            <template
              v-if="
                setting !== 'learner_can_edit_password' &&
                  setting !== 'learner_can_login_with_no_password'
              "
            >
              <KCheckbox
                :key="setting"
                :label="$tr(camelCase(setting))"
                :checked="settings[setting]"
                @change="toggleSetting(setting)"
              />
            </template>
            <template v-else-if="setting === 'learner_can_login_with_no_password'">
              <KCheckbox
                :key="setting"
                :label="$tr('learnerNeedPasswordToLogin')"
                :checked="!settings['learner_can_login_with_no_password']"
                @change="toggleLearnerLoginPassword()"
              />
              <KCheckbox
                :key="setting + 'learner_can_edit_password'"
                :disabled="enableChangePassword"
                :label="$tr('learnerCanEditPassword')"
                :checked="!settings['learner_can_login_with_no_password']
                  && settings['learner_can_edit_password']"
                class="checkbox-password"
                @change="toggleSetting('learner_can_edit_password')"
              />
            </template>
          </template>

          <KButtonGroup style="margin-top: 8px;">
            <KButton
              :primary="false"
              appearance="raised-button"
              :text="$tr('resetToDefaultSettings')"

              name="reset-settings"
              @click="showModal = true"
            />

            <KButton
              :primary="true"
              appearance="raised-button"
              :text="coreString('saveChangesAction')"
              name="save-settings"

              :disabled="!settingsHaveChanged"
              @click="saveConfig()"
            />
          </KButtonGroup>

    </template>

    <ConfirmResetModal
      v-if="showModal"
      id="confirm-reset"
      @submit="resetToDefaultSettings"
      @cancel="showModal = false"
    />
    <EditFacilityNameModal
      v-if="showEditFacilityModal"
      id="edit-facility"
      :facilityId="facilityId"
      :facilityName="facilityName"
      @submit="sendFacilityName"
      @cancel="showEditFacilityModal = false"
    />
  </KPageContainer>

</template>


Adding comments for documentation

Adding comments for documentation

<template>

 {{header}}

         {{subheader}}

        <ui-icon-button
        class="previous-button"
        v-show="!isFirstSet"
        :disabled="isFirstSet"
        icon="arrow_back"
        size="large" />

        <ui-icon-button
        class="next-button"
        v-show="!isLastSet"
        :disabled="isLastSet"
        icon="arrow_forward"
        size="large"/>

    <transition-group
      :style="widthOfCarousel"
      class="content-carousel-set"
      tag="div"
      @leave="slide"
      @before-enter="setStartPosition"
      @enter="slide">

          <slot
            :title="content.title"
            :thumbnail="content.thumnail"
            :kind="content.kind"
            :progress="content.progress"
            :id="content.id">

            <content-card
            :title="content.title"
            :thumbnail="content.thumbnail"
            :kind="content.kind"
            :progress="content.progress"
            :link="genLink(content.id, content.kind)"/>
          </slot>

    </transition-group>

</template>


Added Visit to table of data collections when showing a whole proposal

<template>

      {{ heading }}

    <refresh
      v-if="visit !== ''"
      :collection="collection"
      :model="model"
    />

      <toolbar />
      <search
        :collection="collection"
        :value="params.search"
        :url="searchUrl"
      />

    <relion-dialog />

    <pagination
      class="page_wrap"
      :total-records="collection.state.totalRecords"
      :initial-page="1"
      :page-links="3"
      @page-changed="pageChanged"
    />

</template>

<template>

      {{ heading }}

    <refresh
      v-if="$store.state.proposal.visit != ''"
      :collection="collection"
      :model="model"
    />

      <toolbar />
      <search
        :collection="collection"
        :value="params.search"
        :url="searchUrl"
      />

    <relion-dialog />

    <pagination
      class="page_wrap"
      :total-records="collection.state.totalRecords"
      :initial-page="1"
      :page-links="3"
      @page-changed="pageChanged"
    />

</template>


Remove scrollbar

Remove scrollbar

<template>

          <router-link :to="{name: 'home'}" class="brand-logo left" replace>
            book
          </router-link>

            <template v-if="isLoggedIn">

                <router-link :to="{name: 'dnevnik'}" replace>
                  face
                </router-link>

                <router-link :to="{name: 'stats'}" replace>
                  view_list
                </router-link>

                  exit_to_app

            </template>
            <template v-else>

                  settings_power

            </template>

      <v-touch @swipeleft="onswipeLeft" @swiperight="onswipeRight" id="routerArea" class="row">
        <transition :name="transition">
          <keep-alive>
            <router-view></router-view>
          </keep-alive>
        </transition>
      </v-touch>

DnevnikClient

{{ footerText }}

О проекте

Репозиторий

Разработчик

GitHub

VK

Made by @limitedeternity

</template>


Change to local URL since people will be running locally

<template>

                Onset

                A (mostly) linguistically-accurate language evolution simulator

          <button @click="displayModal = false" class="delete"></button>
          {{modalMessage}}

Onset

Onset is a (mostly) linguistically-accurate language evolution simulator.

Evolve: Generate new evolutions for the list of words to the right, according to the settings.

Save: Save the current sound change rules under the given name (to browser storage).

Load: Load sound change rules from the given name and apply them to the current list of words.

                            Evolve

                        <input v-model="filename" class="input" type="text" style="margin: 5px;" placeholder="filename"></input>

                           Save

                           Load

Words

Enter a list of words in IPA, one per line, which will be evolved.

                      <textarea class="textarea" ref="input" rows=20 v-model="wordString"></textarea>

Settings

                <form>

                  Maximum Generations

The maximum number of sound
                      change rules to apply during evolution. If the rule
                      selector goal is achieved early, evolution will
                      end.

                    <input v-model="generations" type="number" class="input">

                  Transcriptions

A list of plain text to IPA
                        transcriptions, separated by colons. The transcriptions
                        will be made from top-to-bottom. For example, the pair
                        sh:ʃ will convert all sh occurences
                        in the input to ʃ, evolve the result, and then
                        convert back before displaying the evolved words.

                    <textarea v-model="transcriptionString" class="textarea" rows=5 placeholder="ng:ŋ"></textarea>

                  Direction

The direction to evolve the language in. If set to forward, the output will be the input with all changes applied. If set to reverse, the output will be the first state of the language, and applying the given changes will produce the input.

                      <select v-model="direction">
                        <option>Forward</option>
                        <option>Reverse</option>
                      </select>

                  Rule selector

The algorithm to use when selecting rules.

                      <select v-model="optimisationFunction">
                        <option>Minimise</option>
                        <option>Maximise</option>
                      </select>

                      <select v-model="metricFunction" class="is-expanded">
                        <option>weighted phonetic product</option>
                        <option>phonetic product</option>
                        <option>Word Complexity Measure</option>
                        <option>number of syllables</option>
                        <option>number of consonant clusters</option>
                        <option>random value</option>
                      </select>

                </form>

Applied Rules

              <rule :rule=rule></rule>

Evolved Words

            {{word}}

Onset is built by Cadel Watson. See the source at kdelwat/Onset!

</template>


Add * to the header for unread messages

Add * to the header for unread messages

Signed-off-by: Joas Schilling <ab43a7c9cb5b2380afc4ddf8b3e2583169b39a02@schilljs.com>

<!--
  - @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @author Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @license GNU AGPL version 3 or any later version
  -
  - This program is free software: you can redistribute it and/or modify
  - it under the terms of the GNU Affero General Public License as
  - published by the Free Software Foundation, either version 3 of the
  - License, or (at your option) any later version.
  -
  - This program is distributed in the hope that it will be useful,
  - but WITHOUT ANY WARRANTY; without even the implied warranty of
  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  - GNU Affero General Public License for more details.
  -
  - You should have received a copy of the GNU Affero General Public License
  - along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
	<Content :class="{'icon-loading': loading}" app-name="Talk">
		<Navigation />
		<AppContent>
			<router-view />
		</AppContent>
		<Sidebar />
	</Content>
</template>

<script>
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
import Content from '@nextcloud/vue/dist/Components/Content'
import Navigation from './components/Navigation/Navigation'
import Router from './router/router'
import Sidebar from './components/Sidebar/Sidebar'
import { EventBus } from './services/EventBus'

export default {
	name: 'App',
	components: {
		Content,
		AppContent,
		Sidebar,
		Navigation,
	},
	data: function() {
		return {
			savedLastMessageMap: {},
			defaultPageTitle: false,
			loading: false,
			windowIsVisible: true,
		}
	},

	computed: {
		conversations() {
			return this.$store.getters.conversations
		},

		/**
		 * Keeps a list for all last message ids
		 * @returns {object} Map with token => lastMessageId
		 */
		lastMessageMap() {
			const conversationList = this.$store.getters.conversationsList
			if (conversationList.length === 0) {
				return {}
			}

			const lastMessage = {}
			conversationList.forEach(conversation => {
				lastMessage[conversation.token] = 0 + (conversation.lastMessage && conversation.lastMessage.id ? conversation.lastMessage.id : 0)
			})
			return lastMessage
		},

		/**
		 * @returns {boolean} Returns true, if
		 * - a conversation is newly added to lastMessageMap
		 * - a conversation has a different last message id then previously
		 */
		atLeastOneLastMessageIdChanged() {
			let modified = false
			Object.keys(this.lastMessageMap).forEach(token => {
				if (!this.savedLastMessageMap[token]
					|| this.savedLastMessageMap[token] !== this.lastMessageMap[token]) {
					modified = true
				}
			})

			return modified
		},

		/**
		 * The current conversation token
		 * @returns {string} The token.
		 */
		token() {
			return this.$route.params.token
		},
	},

	watch: {
		atLeastOneLastMessageIdChanged() {
			if (this.windowIsVisible) {
				return
			}

			this.setPageTitle(this.getConversationName(this.token), this.atLeastOneLastMessageIdChanged)
		},
	},

	beforeDestroy() {
		document.removeEventListener('visibilitychange', this.changeWindowVisibility)
	},

	beforeMount() {
		window.addEventListener('resize', this.onResize)
		document.addEventListener('visibilitychange', this.changeWindowVisibility)

		this.onResize()
		/**
		 * Listens to the conversationsReceived globalevent, emitted by the conversationsList
		 * component each time a new batch of conversations is received and processed in
		 * the store.
		 */
		EventBus.$once('conversationsReceived', () => {
			if (this.$route.name === 'conversation') {
				const CURRENT_CONVERSATION_NAME = this.getConversationName(this.token)
				this.setPageTitle(CURRENT_CONVERSATION_NAME)
			}
		})
		/**
		 * Global before guard, this is called whenever a navigation is triggered.
		*/
		Router.beforeEach((to, from, next) => {
			/**
			 * This runs whenever the new route is a conversation.
			 */
			if (to.name === 'conversation') {
				// Page title
				const NEXT_CONVERSATION_NAME = this.getConversationName(to.params.token)
				this.setPageTitle(NEXT_CONVERSATION_NAME)
			}
			/**
			 * Fires a global event that tells the whole app that the route has changed. The event
			 * carries the from and to objects as payload
			 */
			EventBus.$emit('routeChange', { from, to })

			next()
		})
	},

	methods: {
		changeWindowVisibility() {
			this.windowIsVisible = !document.hidden
			if (this.windowIsVisible) {
				// Remove the potential "*" marker for unread chat messages
				this.setPageTitle(this.getConversationName(this.token), false)
			} else {
				// Copy the last message map to the saved version,
				// this will be our reference to check if any chat got a new
				// message since the last visit
				this.savedLastMessageMap = this.lastMessageMap
			}
		},

		/**
		 * Set the page title to the conversation name
		 * @param {string} title Prefix for the page title e.g. conversation name
		 * @param {boolean} showAsterix Prefix for the page title e.g. conversation name
		 */
		setPageTitle(title, showAsterix) {
			if (this.defaultPageTitle === false) {
				// On the first load we store the current page title "Talk - Nextcloud",
				// so we can append it every time again
				this.defaultPageTitle = window.document.title
				// When a conversation is opened directly, the "Talk - " part is
				// missing from the title
				if (this.defaultPageTitle.indexOf(t('spreed', 'Talk') + ' - ') !== 0) {
					this.defaultPageTitle = t('spreed', 'Talk') + ' - ' + this.defaultPageTitle
				}
			}

			if (title !== '') {
				window.document.title = (showAsterix ? '* ' : '') + `${title} - ${this.defaultPageTitle}`
			} else {
				window.document.title = (showAsterix ? '* ' : '') + this.defaultPageTitle
			}
		},

		onResize() {
			this.windowHeight = window.innerHeight - document.getElementById('header').clientHeight
		},

		/**
		 * Get a conversation's name.
		 * @param {string} token The conversation's token
		 * @returns {string} The conversation's name
		 */
		getConversationName(token) {
			if (!this.$store.getters.conversations[token]) {
				return ''
			}

			return this.$store.getters.conversations[token].displayName
		},
	},
}
</script>

<style lang="scss" scoped>
#content {
	height: 100%;
}
</style>

<!--
  - @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @author Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @license GNU AGPL version 3 or any later version
  -
  - This program is free software: you can redistribute it and/or modify
  - it under the terms of the GNU Affero General Public License as
  - published by the Free Software Foundation, either version 3 of the
  - License, or (at your option) any later version.
  -
  - This program is distributed in the hope that it will be useful,
  - but WITHOUT ANY WARRANTY; without even the implied warranty of
  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  - GNU Affero General Public License for more details.
  -
  - You should have received a copy of the GNU Affero General Public License
  - along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
	<Content :class="{'icon-loading': loading}" app-name="Talk">
		<Navigation />
		<AppContent>
			<router-view />
		</AppContent>
		<Sidebar />
	</Content>
</template>

<script>
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
import Content from '@nextcloud/vue/dist/Components/Content'
import Navigation from './components/Navigation/Navigation'
import Router from './router/router'
import Sidebar from './components/Sidebar/Sidebar'
import { EventBus } from './services/EventBus'

export default {
	name: 'App',
	components: {
		Content,
		AppContent,
		Sidebar,
		Navigation,
	},
	data: function() {
		return {
			defaultPageTitle: false,
			loading: false,
		}
	},

	computed: {
		conversations() {
			return this.$store.getters.conversations
		},
		/**
		 * The current conversation token
		 * @returns {string} The token.
		 */
		token() {
			return this.$route.params.token
		},
	},

	beforeMount() {
		window.addEventListener('resize', this.onResize)
		this.onResize()
		/**
		 * Listens to the conversationsReceived globalevent, emitted by the conversationsList
		 * component each time a new batch of conversations is received and processed in
		 * the store.
		 */
		EventBus.$once('conversationsReceived', () => {
			if (this.$route.name === 'conversation') {
				const CURRENT_CONVERSATION_NAME = this.getConversationName(this.token)
				this.setPageTitle(CURRENT_CONVERSATION_NAME)
			}
		})
		/**
		 * Global before guard, this is called whenever a navigation is triggered.
		*/
		Router.beforeEach((to, from, next) => {
			/**
			 * This runs whenever the new route is a conversation.
			 */
			if (to.name === 'conversation') {
				// Page title
				const NEXT_CONVERSATION_NAME = this.getConversationName(to.params.token)
				this.setPageTitle(NEXT_CONVERSATION_NAME)
			}
			/**
			 * Fires a global event that tells the whole app that the route has changed. The event
			 * carries the from and to objects as payload
			 */
			EventBus.$emit('routeChange', { from, to })

			next()
		})
	},

	methods: {
		/**
		 * Set the page title to the conversation name
		 * @param {string} title Prefix for the page title e.g. conversation name
		 */
		setPageTitle(title) {
			if (this.defaultPageTitle === false) {
				// On the first load we store the current page title "Talk - Nextcloud",
				// so we can append it every time again
				this.defaultPageTitle = window.document.title
				// When a conversation is opened directly, the "Talk - " part is
				// missing from the title
				if (this.defaultPageTitle.indexOf(t('spreed', 'Talk') + ' - ') !== 0) {
					this.defaultPageTitle = t('spreed', 'Talk') + ' - ' + this.defaultPageTitle
				}
			}

			if (title !== '') {
				window.document.title = `${title} - ${this.defaultPageTitle}`
			} else {
				window.document.title = this.defaultPageTitle
			}
		},

		onResize() {
			this.windowHeight = window.innerHeight - document.getElementById('header').clientHeight
		},

		/**
		 * Get a conversation's name.
		 * @param {string} token The conversation's token
		 * @returns {string} The conversation's name
		 */
		getConversationName(token) {
			if (!this.$store.getters.conversations[token]) {
				return ''
			}

			return this.$store.getters.conversations[token].displayName
		},
	},
}
</script>

<style lang="scss" scoped>
#content {
	height: 100%;
}
</style>


Fix error display at RenameItemModal

Fix error display at RenameItemModal

<template>
    <media-modal @close="close()" :size="size" :show="show" v-if="show">

                <button class="close" type="button" @click="close()">×</button>

Rename item

                Loading...

                        Current name

{{ this.getItemName(this.currentFile) }}

                        New name
                        <input type="text" v-model="newItemName" class="form-control" @keyup.enter="renameItem()">

                    <media-errors :errors="errors"></media-errors>

                    <button class="btn btn-primary" type="button" @click="renameItem()">
                        Apply
                    </button>
                    <button class="btn btn-default" type="button" @click="close()">
                        Cancel
                    </button>

    </media-modal>
</template>


Use RichText component for activity

Use RichText component for activity

Signed-off-by: Julius Härtl <bf353fa4999f2f148afcc6d8ee6cb1ee74cc07c3@bitgrid.net>

<template>

			<RichText class="activity--subject" :text="message.subject" :arguments="message.parameters" />

				{{ getTime(activity.datetime) }}

			{{ activity.message }}

</template>

<template>

			{{ getTime(activity.datetime) }}

</template>


Persist ratio on uploaded images

Persist ratio on uploaded images

<template>

      <post-image
        v-for="image in post.images"
        :key="image.shortid"
        :image="new Image(image)"
        :class="{active: isImageActive(image.shortid)}"
        @activate-image="activateImage"
        @remove-image="removeImage"
      ></post-image>
      <image-component
        v-for="image in imageQueue"
        :key="image.shortid"
        :image="image"
        @retry-upload="retryUpload"
      ></image-component>

Drop or click for pics

        <input
          id="image-uploader"
          type="file"
          accept=".jpg,.jpeg,.png"
          multiple
          @change="onFileChange"
        />

</template>


Added missing ImageOverlay options on props

https://leafletjs.com/reference-1.3.4.html#imageoverlay

<script>
import propsBinder from '../utils/propsBinder.js';
import findRealParent from '../utils/findRealParent.js';

const props = {
  url: {
    type: String
  },
  bounds: {
  },
  opacity: {
    type: Number,
    default: 1.0
  },
  alt: {
    type: String,
    default: ''
  },
  interactive: {
    type: Boolean,
    default: false
  },
  crossOrigin: {
    type: Boolean,
    default: false
  },
  visible: {
    type: Boolean,
    custom: true,
    default: true
  },
  errorOverlayUrl: {
    type: String,
    default: '',
  },
  zIndex: {
    type: Number,
    default: 1,
  },
  className: {
    type: String,
    default: ''
  }
};

export default {
  name: 'LImageOverlay',
  props: props,
  mounted () {
    let options = {
      opacity: this.opacity,
      alt: this.alt,
      interactive: this.interactive,
      crossOrigin: this.crossOrigin,
      errorOverlayUrl: this.errorOverlayUrl,
      zIndex: this.zIndex,
      className: this.className
    };
    this.mapObject = L.imageOverlay(this.url, this.bounds, options);
    L.DomEvent.on(this.mapObject, this.$listeners);
    propsBinder(this, this.mapObject, props);
    this.parentContainer = findRealParent(this.$parent);
    this.parentContainer.addLayer(this, !this.visible);
  },
  beforeDestroy () {
    this.parentContainer.removeLayer(this);
  },
  methods: {
    setVisible (newVal, oldVal) {
      if (newVal === oldVal) return;
      if (this.mapObject) {
        if (newVal) {
          this.parentContainer.addLayer(this);
        } else {
          this.parentContainer.removeLayer(this);
        }
      }
    },
    getBounds () {
      return this.mapObject.getBounds();
    }
  },
  render () {
    return null;
  }
};
</script>

<script>
import propsBinder from '../utils/propsBinder.js';
import findRealParent from '../utils/findRealParent.js';

const props = {
  url: {
    type: String
  },
  bounds: {
  },
  opacity: {
    type: Number,
    default: 1.0
  },
  alt: {
    type: String,
    default: ''
  },
  interactive: {
    type: Boolean,
    default: false
  },
  crossOrigin: {
    type: Boolean,
    default: false
  },
  visible: {
    type: Boolean,
    custom: true,
    default: true
  }
};

export default {
  name: 'LImageOverlay',
  props: props,
  mounted () {
    let options = {
      opacity: this.opacity,
      alt: this.alt,
      interactive: this.interactive,
      crossOrigin: this.crossOrigin
    };
    this.mapObject = L.imageOverlay(this.url, this.bounds, options);
    L.DomEvent.on(this.mapObject, this.$listeners);
    propsBinder(this, this.mapObject, props);
    this.parentContainer = findRealParent(this.$parent);
    this.parentContainer.addLayer(this, !this.visible);
  },
  beforeDestroy () {
    this.parentContainer.removeLayer(this);
  },
  methods: {
    setVisible (newVal, oldVal) {
      if (newVal === oldVal) return;
      if (this.mapObject) {
        if (newVal) {
          this.parentContainer.addLayer(this);
        } else {
          this.parentContainer.removeLayer(this);
        }
      }
    },
    getBounds () {
      return this.mapObject.getBounds();
    }
  },
  render () {
    return null;
  }
};
</script>


完成歌词滚动和切换

完成歌词滚动和切换

<template>

 0">
  <transition name="normal" @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave">

        <scroll class="middle-r" :data="currentLyric && currentLyric.lines" ref="lyricWrapper">

{{line.txt}}

        </scroll>

          {{_filterTime(currentTime)}}

            <progress-bar :percent="percent" @progressOnChange="progressOnChange"></progress-bar>

          {{_filterTime(currentSong.duration)}}

  </transition>
  <transition name="mini">

        <progress-circle :radius="radius" :percent="percent">

        </progress-circle>

  </transition>
  <audio :src="currentSong.url" ref="audio" @canplay="ready" @error="error" @timeupdate="getAudioTime" @ended="end"></audio>

</template>


refactor: remove console.log statement

refactor: remove console.log statement

Closes #121

<template lang="html">
  <input
    @change="onChangeInput"
    type="file"
    name="image"
    accept="image/*"
    capture="environment"
    multiple
  />
</template>

<script>
import { scan } from "../misc/scanner.js";
import { imageDataFromFile } from "../misc/image-data.js";
import CommonAPI from "../mixins/CommonAPI.vue";

export default {
  name: "qrcode-capture",

  mixins: [CommonAPI],

  methods: {
    onChangeInput(event) {
      const files = [...event.target.files];
      const resultPromises = files.map(this.processFile);

      resultPromises.forEach(this.onDetect);
    },

    async processFile(file) {
      const imageData = await imageDataFromFile(file);
      const scanResult = await scan(imageData);

      return scanResult;
    }
  }
};
</script>

<template lang="html">
  <input
    @change="onChangeInput"
    type="file"
    name="image"
    accept="image/*"
    capture="environment"
    multiple
  />
</template>

<script>
import { scan } from "../misc/scanner.js";
import { imageDataFromFile } from "../misc/image-data.js";
import CommonAPI from "../mixins/CommonAPI.vue";

export default {
  name: "qrcode-capture",

  mixins: [CommonAPI],

  methods: {
    onChangeInput(event) {
      const files = [...event.target.files];
      const resultPromises = files.map(this.processFile);

      resultPromises.forEach(this.onDetect);

      console.log(Promise.all(resultPromises));
    },

    async processFile(file) {
      const imageData = await imageDataFromFile(file);
      const scanResult = await scan(imageData);

      return scanResult;
    }
  }
};
</script>


Wording and design detail fixes in top controls

Signed-off-by: Jan-Christoph Borchardt <7f550a9f4c44173a37664d938f1355f0f92a47a7@jancborchardt.net>

<template>

		<Popover v-if="isInCall"
			class="top-bar__button"
			trigger="manual"
			:open="showLayoutHint && !hintDismissed"
			@auto-hide="showLayoutHint=false">
			<Actions slot="trigger">
				<ActionButton v-if="isInCall"
					:icon="changeViewIconClass"
					@click="changeView">
					{{ changeViewText }}
				</actionbutton>
			</Actions>

				{{ layoutHintText }}

					<button
						@click="showLayoutHint=false, hintDismissed=true">
						{{ t('spreed', 'Dismiss') }}
					</button>
					<button
						class="primary"
						@click="changeView">
						{{ t('spreed', 'Use promoted view') }}
					</button>

		</Popover>

		<Actions
			v-shortkey="['f']"
			class="top-bar__button"
			@shortkey.native="toggleFullscreen">
			<ActionButton
				:icon="iconFullscreen"
				:aria-label="t('spreed', 'Toggle fullscreen')"
				@click="toggleFullscreen">
				{{ labelFullscreen }}
			</ActionButton>
			<ActionSeparator
				v-if="canFullModerate" />
			<ActionLink
				v-if="isFileConversation"
				icon="icon-text"
				:href="linkToFile">
				{{ t('spreed', 'Go to file') }}
			</ActionLink>
			<template
				v-if="canFullModerate">
				<ActionButton
					v-if="canModerate"
					:close-after-click="true"
					icon="icon-rename"
					@click="handleRenameConversation">
					{{ t('spreed', 'Rename conversation') }}
				</ActionButton>
				<ActionSeparator
					v-if="canFullModerate" />
				<ActionCheckbox
					v-if="canFullModerate"
					:checked="isSharedPublicly"
					@change="toggleGuests">
					{{ t('spreed', 'Share link') }}
				</ActionCheckbox>
			</template>
			<ActionButton
				icon="icon-clippy"
				:close-after-click="true"
				@click="handleCopyLink">
				{{ t('spreed', 'Copy link') }}
			</ActionButton>

			<template
				v-if="canFullModerate">
				<ActionCheckbox
					v-if="isSharedPublicly"
					class="share-link-password-checkbox"
					:checked="isPasswordProtected"
					@check="handlePasswordEnable"
					@uncheck="handlePasswordDisable">
					{{ t('spreed', 'Password protection') }}
				</ActionCheckbox>
				<ActionInput
					v-show="isEditingPassword"
					class="share-link-password"
					icon="icon-password"
					type="password"
					:value.sync="password"
					autocomplete="new-password"
					@submit="handleSetNewPassword">
					{{ t('spreed', 'Enter a password') }}
				</ActionInput>
				<ActionSeparator />
				<ActionCheckbox
					:checked="hasLobbyEnabled"
					@change="toggleLobby">
					{{ t('spreed', 'Enable lobby') }}
				</ActionCheckbox>
				<ActionInput
					v-if="hasLobbyEnabled"
					icon="icon-calendar-dark"
					type="datetime-local"
					v-bind="dateTimePickerAttrs"
					:value="lobbyTimer"
					:disabled="lobbyTimerLoading"
					@change="setLobbyTimer">
					{{ t('spreed', 'Start time (optional)') }}
				</ActionInput>
			</template>
		</Actions>
		<CallButton class="top-bar__button" />
		<Actions v-if="showOpenSidebarButton"
			class="top-bar__button"
			close-after-click="true">
			<ActionButton
				:icon="iconMenuPeople"
				@click="openSidebar" />
		</Actions>

</template>

<template>

		<Popover v-if="isInCall"
			class="top-bar__button"
			trigger="manual"
			:open="showLayoutHint && !hintDismissed"
			@auto-hide="showLayoutHint=false">
			<Actions slot="trigger">
				<ActionButton v-if="isInCall"
					:icon="changeViewIconClass"
					@click="changeView">
					{{ changeViewText }}
				</actionbutton>
			</Actions>

				{{ layoutHintText }}

					<button
						class="error"
						@click="showLayoutHint=false, hintDismissed=true">
						{{ t('spreed', 'Dismiss') }}
					</button>
					<button
						class="primary"
						@click="changeView">
						{{ t('spreed', 'Use promoted-view') }}
					</button>

		</Popover>

		<Actions
			v-shortkey="['f']"
			class="top-bar__button"
			@shortkey.native="toggleFullscreen">
			<ActionButton
				:icon="iconFullscreen"
				:aria-label="t('spreed', 'Toggle fullscreen')"
				@click="toggleFullscreen">
				{{ labelFullscreen }}
			</ActionButton>
			<ActionSeparator
				v-if="canFullModerate" />
			<ActionLink
				v-if="isFileConversation"
				icon="icon-text"
				:href="linkToFile">
				{{ t('spreed', 'Go to file') }}
			</ActionLink>
			<template
				v-if="canFullModerate">
				<ActionButton
					v-if="canModerate"
					:close-after-click="true"
					icon="icon-rename"
					@click="handleRenameConversation">
					{{ t('spreed', 'Rename conversation') }}
				</ActionButton>
				<ActionSeparator
					v-if="canFullModerate" />
				<ActionCheckbox
					v-if="canFullModerate"
					:checked="isSharedPublicly"
					@change="toggleGuests">
					{{ t('spreed', 'Share link') }}
				</ActionCheckbox>
			</template>
			<ActionButton
				icon="icon-clippy"
				:close-after-click="true"
				@click="handleCopyLink">
				{{ t('spreed', 'Copy link') }}
			</ActionButton>

			<template
				v-if="canFullModerate">
				<ActionCheckbox
					v-if="isSharedPublicly"
					class="share-link-password-checkbox"
					:checked="isPasswordProtected"
					@check="handlePasswordEnable"
					@uncheck="handlePasswordDisable">
					{{ t('spreed', 'Password protection') }}
				</ActionCheckbox>
				<ActionInput
					v-show="isEditingPassword"
					class="share-link-password"
					icon="icon-password"
					type="password"
					:value.sync="password"
					autocomplete="new-password"
					@submit="handleSetNewPassword">
					{{ t('spreed', 'Enter a password') }}
				</ActionInput>
				<ActionSeparator />
				<ActionCheckbox
					:checked="hasLobbyEnabled"
					@change="toggleLobby">
					{{ t('spreed', 'Enable lobby') }}
				</ActionCheckbox>
				<ActionInput
					v-if="hasLobbyEnabled"
					icon="icon-calendar-dark"
					type="datetime-local"
					v-bind="dateTimePickerAttrs"
					:value="lobbyTimer"
					:disabled="lobbyTimerLoading"
					@change="setLobbyTimer">
					{{ t('spreed', 'Start time (optional)') }}
				</ActionInput>
			</template>
		</Actions>
		<CallButton class="top-bar__button" />
		<Actions v-if="showOpenSidebarButton"
			class="top-bar__button"
			close-after-click="true">
			<ActionButton
				:icon="iconMenuPeople"
				@click="openSidebar" />
		</Actions>

</template>


export: saving/opening/downloading states works

<template>

          Name:

          <input id="export-name" type="text" v-model="title" />

          Paper size:

          <select v-model="paperSize">
                    <option v-for="size in paperSizes" v-bind:value="$key">ISO {{ $key }} ({{ size[0] }}mm × {{ size[1] }}mm)</option>
                  </select>

          Download:

JPG

PDF

GeoTIFF

          Vector format:

          <select name="select" v-model="vectorFormat">
            <option value="json" selected>GeoJSON (web GIS)</option>
            <option value="kml">KML (Google Earth)</option>
            <option value="geopkg">Geopackage (high performance)</option>
            <option value="shapefile">Shapefile (legacy desktop GIS)</option>
            <option value="csv">CSV (Spreadsheet/Excel)</option>
          </select>

          Export state:

             Download state as file

          Save state:

            <input id="saveStateName" class="input-group-field" type="text" placeholder="Name for saved state"/>

              Save

          Load state:

              ✕

            {{ state }}

            No saved states yet

             Upload state file<input type="file" id="uploadFile" class="show-for-sr" name="statefile" accept="application/json" v-model="statefile" v-el:statefile @change="load()"/>

          Reset:

             Reset SSS

        <gk-legend></gk-legend>

</template>

<template>

          Name:

          <input id="export-name" type="text" v-model="title" />

          Paper size:

          <select v-model="paperSize">
                    <option v-for="size in paperSizes" v-bind:value="$key">ISO {{ $key }} ({{ size[0] }}mm × {{ size[1] }}mm)</option>
                  </select>

          Download:

JPG

PDF

GeoTIFF

          Vector format:

          <select name="select" v-model="vectorFormat">
            <option value="json" selected>GeoJSON (web GIS)</option>
            <option value="kml">KML (Google Earth)</option>
            <option value="geopkg">Geopackage (high performance)</option>
            <option value="shapefile">Shapefile (legacy desktop GIS)</option>
            <option value="csv">CSV (Spreadsheet/Excel)</option>
          </select>

          Export state:

             Download state as file

          Save state:

            <input class="input-group-field" type="text" placeholder="Name for saved state"/>

              Save

          Load state:

              ✕

            Fire ops

            No saved states yet

             Upload state file<input type="file" id="uploadFile" class="show-for-sr" name="statefile" accept="application/json" v-model="statefile" v-el:statefile @change="load()"/>

          Reset:

             Reset SSS

        <gk-legend></gk-legend>

</template>


fix: Search 组件修改事件触发时机

fix: Search 组件修改事件触发时机

<template>

        <input type="text" v-model="inputValue" :placeholder="placeholder" @input="inputTrigger(inputValue)" @keyup.enter="search(inputValue)"/>

      <Button color="primary" v-if="showSearchButton" @click="search(inputValue)">{{searchText}}</Button>

</template>


fix file manager sorting, fixes #981

fix file manager sorting, fixes #981

<template>
  <v-card
    :loading="loading"
    :disabled="loading"
  >
    <v-card-title>

      <v-btn
        v-if="server.permissions.putServerFiles || isAdmin()"
        icon
        @click="createFile = true"
      >
        <v-icon>mdi-file-plus</v-icon>
      </v-btn>
      <ui-overlay
        v-model="createFile"
        :title="$t('files.NewFile')"
        card
      >
        <v-row>
          <v-col>
            <ui-input
              v-model="newFileName"
              hide-details
              autofocus
              :label="$t('common.Name')"
              @keyup.esc="cancelFileCreate()"
              @keyup.enter="confirmFileCreate()"
            />
          </v-col>
        </v-row>
        <v-row>
          <v-col>
            <v-btn
              block
              color="error"
              @click="cancelFileCreate()"
              v-text="$t('common.Cancel')"
            />
          </v-col>
          <v-col>
            <v-btn
              block
              color="success"
              :disabled="newFileName === ''"
              @click="confirmFileCreate()"
              v-text="$t('common.Create')"
            />
          </v-col>
        </v-row>
      </ui-overlay>
      <v-btn
        v-if="server.permissions.putServerFiles || isAdmin()"
        icon
        @click="createFolder = true"
      >
        <v-icon>mdi-folder-plus</v-icon>
      </v-btn>
      <ui-overlay
        v-model="createFolder"
        :title="$t('files.NewFolder')"
        card
      >
        <v-row>
          <v-col>
            <ui-input
              v-model="newFolderName"
              hide-details
              autofocus
              :label="$t('common.Name')"
              @keyup.esc="cancelFolderCreate()"
              @keyup.enter="confirmFolderCreate()"
            />
          </v-col>
        </v-row>
        <v-row>
          <v-col>
            <v-btn
              block
              color="error"
              @click="cancelFolderCreate()"
              v-text="$t('common.Cancel')"
            />
          </v-col>
          <v-col>
            <v-btn
              block
              color="success"
              :disabled="newFolderName === ''"
              @click="confirmFolderCreate()"
              v-text="$t('common.Create')"
            />
          </v-col>
        </v-row>
      </ui-overlay>
      <v-btn
        icon
        @click="fetchItems(currentPath)"
      >
        <v-icon>mdi-refresh</v-icon>
      </v-btn>
    </v-card-title>
    <v-card-subtitle v-text="currentPath" />
    <v-card-text>
      <v-dialog
        v-model="confirmDeleteOpen"
        max-width="600"
      >
        <v-card>
          <v-card-title v-text="$t('files.ConfirmDelete')" />
          <v-card-actions>
            <v-spacer />
            <v-btn
              color="error"
              @click="deleteCancelled()"
              v-text="$t('common.Cancel')"
            />
            <v-btn
              color="success"
              @click="deleteConfirmed()"
              v-text="$t('common.Confirm')"
            />
          </v-card-actions>
        </v-card>
      </v-dialog>

      <v-list>
        <v-list-item
          v-for="file in files"
          :key="file.name"
          @click="itemClicked(file)"
        >
          <v-list-item-content>
            <v-list-item-title v-text="file.name" />
            <v-list-item-subtitle
              v-if="file.isFile"
              v-text="toFileSize(file.size || 0)"
            />
            <v-list-item-subtitle
              v-if="file.modifyTime"
              v-text="$t('files.LastModified') + ': ' + toDate(file.modifyTime)"
            />
          </v-list-item-content>
          <v-list-item-action class="flex-row">
            <v-tooltip
              v-if="file.isFile && !(file.size > maxEditSize)"
              bottom
            >
              <template v-slot:activator="{ on }">
                <v-btn
                  icon
                  v-on="on"
                  @click.stop="itemClicked(file)"
                >
                  <v-icon>mdi-pencil</v-icon>
                </v-btn>
              </template>

            </v-tooltip>
            <v-tooltip
              v-if="file.isFile"
              bottom
            >
              <template v-slot:activator="{ on }">
                <v-btn
                  icon
                  :href="createDownloadLink(file)"
                  target="_blank"
                  v-on="on"
                >
                  <v-icon>mdi-download</v-icon>
                </v-btn>
              </template>

            </v-tooltip>
            <v-tooltip
              v-if="file.name !== '..'"
              bottom
            >
              <template v-slot:activator="{ on }">
                <v-btn
                  icon
                  v-on="on"
                  @click.stop="deleteRequest(file)"
                >
                  <v-icon>mdi-trash-can</v-icon>
                </v-btn>
              </template>

            </v-tooltip>
          </v-list-item-action>
        </v-list-item>
      </v-list>

      <ui-overlay
        v-model="editOpen"
        :title="currentFile"
        card
        closable
        :on-close="cancelEdit"
      >
        <ace
          v-model="fileContents"
          editor-id="fileEditor"
          height="75vh"
          :theme="isDark() ? 'monokai' : 'github'"
          :file="currentFile"
        />
        <template v-slot:actions>

          <v-btn
            color="error"
            @click="cancelEdit()"
            v-text="$t('common.Cancel')"
          />
          <v-btn
            color="success"
            @click="saveEdit()"
            v-text="$t('common.Save')"
          />
        </template>
      </ui-overlay>

        <v-file-input
          v-model="uploadFiles"
          multiple
          chips
          show-size
          counter
          :placeholder="$t('files.Upload')"
          class="mb-4"
        />

          <v-progress-linear
            :value="(uploadCurrent / uploadSize) * 100"
            buffer-value="0"
            stream
            class="mb-4"
          />

        <v-btn
          block
          color="primary"
          :disabled="!(uploadFiles.length > 0) || uploading"
          @click="transmitFiles"
          v-text="$t('files.Upload')"
        />

    </v-card-text>
  </v-card>
</template>


Fixed vue warning

Fixed vue warning

<template>

= 0 || result.level">
      <star-rates :rate="result.star" :width="15" :gutter="4"></star-rates>
      {{ result.level }}

      {{ pron.phsym }} <speaker v-for="(au, i) in pron.audio" :src="au" :key="i"></speaker>

      <highcharts :options="chartOpt"></highcharts>

{{ etym }}

</template>

<template>

= 0 || result.level">
      <star-rates :rate="result.star" :width="15" :gutter="4"></star-rates>
      {{ result.level }}

      {{ pron.phsym }} <speaker v-for="au in pron.audio" :src="au"></speaker>

      <highcharts :options="chartOpt"></highcharts>

{{ etym }}

</template>


Update app/frontend/src/registry/components/people/PersonAdd.vue

Co-Authored-By: AlexZorkin <d2ebbf48847a26e8103d3d9b0e0197272384c654@users.noreply.github.com>

<template>

      <api-error :error="error" resetter="SET_ERROR"></api-error>

Add new applicant

Personal Information

Contact Information at Company

                    Example: (250) 555-1234

                    <div v-for="(error, index) in fieldErrors.contact_email" :key="`emailInput error ${index}`">
                      {{ error }}

ORCS File Number

                  Example: 38000-25/DRI SMIT J

                    example: 38000-25/PUMP SMIT J

                    Well Driller
                    Well Pump Installer

Attachments

 0">

                      {{f.name}}

             x === 'DRILL' || x === 'PUMP')">

                    Company added.

                         Add a company

              <organization-add @newOrgAdded="newOrgHandler"></organization-add>

 x === 'DRILL')" :class="drillerForm.regType.some(x => x === 'PUMP') ? 'mb-5' : 'mb-1' ">

Well Driller Registration

                      <v-select
                        v-model="drillerForm.organizations.drill"
                        :options="companies"
                        placeholder="Begin typing a company name"
                        label="org_verbose_name">
                      </v-select>

Well Driller Classifications

                    <application-add
                      class="mb-3"
                      v-for="item in drillApplications"
                      v-bind:item="item"
                      v-bind:key="item.id"
                      v-on:close="closeApplication (drillApplications, item.id)"
                      v-model="item.data"
                      activity="DRILL"
                      mode="add"/>

                     Add new well driller classification

 x === 'PUMP')" class="my-2">

Well Pump Installer Registration

                      <v-select
                        v-model="drillerForm.organizations.pump"
                        :options="companies"
                        placeholder="Begin typing a company name"
                        label="org_verbose_name">
                      </v-select>

Well Pump Installer Classifications

                    <application-add
                      class="mb-3"
                      v-for="item in pumpApplications"
                      v-bind:item="item"
                      v-bind:key="item.id"
                      v-on:close="closeApplication (pumpApplications, item.id)"
                      v-model="item.data"
                      activity="PUMP"
                      mode="add"/>

                     Add new pump installer classification

                Save
                Reset

                  Successfully created a new person!

                  Error creating a new person.
                  <div v-for="(value, key, index) in submitError.data" :key="`submit error ${index}`">
                    {{ key }}:
                    <span
                      v-for="(msg, msgIndex) in value"
                      :key="`submit error msg ${index} ${msgIndex}`">{{ msg }}

</template>


Small UI tweak

Small UI tweak

<template>

Gerenciamento de Categorias

          ID
          Nome

          {{ category.id }}
          {{ category.name }}

      <cc-pagination
        :pager="pager"
        :current-page="currentPage"
        :max-items="12">
      </cc-pagination>

</template>