vue2文章添加多个标签思路代码及效果展示

14 篇文章 0 订阅
13 篇文章 0 订阅

效果展示

tags

思路

data数据结构

第一个数组,用来存放标签库,供创建文章时选择

第二个数组,用来存放从标签库选中后的标签, 且选中后需在可选的标签库里删除,否则出现同一个标签被多次添加

js代码

点击输入框,可展示所有标签/也可不展示,取决于组件身上属性(激活时展示or不展示)

点击想要的标签,标签库移除,添加到选中数组内

移除标签,标签库添加回去(后续还想选),然后从选中数组内移除

是不是听的云里雾里?这里放一段gif演示

tags1

确定的时候就把选中标签数组提交到后台这没什么好说的,数组转字符串扔过去即可

后端数据库使用String=>varchar类型存储

不过这里设计成弹窗式的,当用户取消之后需要清空选中数组,标签库重置

废话不多说了,上代码

步骤

前端

前端搜索时展示数据

使用element的autocomplete组件

组件

<el-autocomplete
                 v-model="keyWord"
                 class="inline-input"
                 :fetch-suggestions="querySearch"
                 placeholder="请输入内容"
                 style="margin-right: 10px"
                 @select="handleSelect"
                 />

data

image-20240725163016450

method

搜索时回显数据

//用于like查询 
querySearch(queryString, cb) {
      var tags = this.tags
      var results = queryString ? tags.filter((tag) => {
        return (tag.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
      }) : tags
      // 调用 callback 返回建议列表的数据
      cb(results)
    },

点击时加入已选中标签数组

    handleSelect(item) {
      this.keyWord = ''
      // 加入选中组
      this.selectedTags.push(item)
      // 选中后该标签不可被再选中,从标签库移除
      this.tags = this.tags.filter((i) => i.value !== item.value)
    },

关闭标签时的移除&加入各自的数组

handleCloseTag(tag) {
      // 还给标签库
      this.tags.push(tag)
      // 移除取消的标签
      this.selectedTags = this.selectedTags.filter((item) => item.value !== tag.value)
    },

挂载时回调获取数据

 async selectAllTags() {
      const res = await selectAllTag(1, 99)
      if (res.code === 20000) {
        this.tags = res.data.records
        // 由于element的autocomplete 搜索时回显的数据字段必须叫value,这里给他重新包装一下
        this.tags = this.tags.map((tag) => {
          return {
            value: tag.tagName
          }
        })
        //   .filter((item) => {
        // 由于后续编辑回显selectedTag也有值,产生关闭后,unselect加值,重复
        //   // 与已选中数组一致的不会进  可选列表
        //   // 这里的some在返回值为true时直接跳出, 唯一为真,如果是every则要么找到一个false要么全为true才结束
        //   return !this.selectedTags.some(selected => selected.value === item.value)
        // })
        this.unselectedTags = this.tags
      } else {
        console.log('服务器废了')
      }
    },

这里注释掉的是为了解决编辑回显时 选中数组和标签库产生重复问题,你可以自行研究完添加后在将其注释解开慢慢看

保存提交数据

  this.tags = this.selectedTags.map((item) => {
        return item.value
      })
      this.form.tags = this.tags.join(',')

页面完整代码

<template>
  <div v-if="initSuccess" class="main">

    <div class="operate" style="display: flex;justify-content: space-between">
      <div style="display: flex;">
        <el-input v-model="form.title" placeholder="标题" />
        <el-select v-model="form.categoryName" placeholder="请选择" style="margin:0 20px;min-width: 150px">
          <el-option
            v-for="category in categoryData"
            :key="category.categoryName"
            :label="category.categoryName"
            :value="category.categoryName"
          />
        </el-select>
        <el-input v-model="form.summary" placeholder="摘要" style="margin-right: 20px" />
        <el-button style="margin-right: 10px;" @click=" handleAdd">添加标签 +</el-button>
        <el-dialog title="添加标签" :visible.sync="dialogVis" style="margin-top: -100px">
          <!--          <el-input v-model="keyWord" @input="searchTag" />-->
          <el-autocomplete
            v-model="keyWord"
            class="inline-input"
            :fetch-suggestions="querySearch"
            placeholder="请输入内容"
            style="margin-right: 10px"
            @select="handleSelect"
          />
          <el-tag
            v-for="(tag ,index) in selectedTags"
            :key="index"
            class="singleTag"
            style="margin-right: 10px"
            closable
            @close="handleCloseTag(tag)"
          >
            {{ tag.value }}
          </el-tag>
          <div slot="footer" class="dialog-footer">
            <el-button @click="cancelAddTag">重 置</el-button>
            <el-button type="primary" @click="dialogVis=false">确 定</el-button>
          </div>
        </el-dialog>
        <el-tag
          v-for="(tag ,index) in selectedTags"
          :key="index"
          class="singleTag"
          style="margin-right: 10px"
          closable
          @close="handleCloseTag(tag)"
        >
          {{ tag.value }}
        </el-tag>
      </div>
      <div style="display: flex">
        <div class="publishBox" @click="save">保存</div>
        <div class="cancelBox" @click="$router.push('/article/list')">取消</div>
      </div>
    </div>
    <MdEditor :content="form.content" @update:content="getEditorContent" />
  </div>
</template>

<script>

import MdEditor from '@/components/MdEditor/index.vue'
import { add, update } from '@/api/article/list'
import { selectList as selectCategoryList } from '@/api/article/category'
import { selectList as selectAllTag } from '@/api/tag'

export default {
  name: 'Index',
  components: { MdEditor },
  data() {
    return {
      articleId: this.$route.params.id,
      article: {},
      content: '',
      form: {},
      categoryData: [],
      initSuccess: false,
      dialogVis: false,
      keyWord: '',
      selectedTags: [],
      unselectedTags: [],
      tags: []
    }
  },
  mounted() {
    if (this.$route.params.id !== '0') {
      this.form = this.$store.state.currArticle
      this.selectedTags = this.form.tags.split(',').map(item => {
        return {
          value: item
        }
      })
    }
    selectCategoryList(1, 999, this.searchObj).then(res => {
      this.categoryData = res.data.records
      this.listLoading = false
      this.initSuccess = true
    })
    this.selectAllTags()
  },
  methods: {
    cancelAddTag() {
      this.keyWord = ''
      this.selectedTags = []
      this.tags = this.unselectedTags
      this.dialogVis = false
    },

    handleCloseTag(tag) {
      // 还给标签库
      this.tags.push(tag)
      // 移除取消的标签
      this.selectedTags = this.selectedTags.filter((item) => item.value !== tag.value)
    },
    querySearch(queryString, cb) {
      var tags = this.tags
      var results = queryString ? tags.filter((tag) => {
        return (tag.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
      }) : tags
      // 调用 callback 返回建议列表的数据
      cb(results)
    },
    handleSelect(item) {
      this.keyWord = ''
      // 加入选中组
      this.selectedTags.push(item)
      // 选中后该标签不可被再选中,从标签库移除
      this.tags = this.tags.filter((i) => i.value !== item.value)
    },

    async selectAllTags() {
      const res = await selectAllTag(1, 99)
      if (res.code === 20000) {
        this.tags = res.data.records
        // 由于element的autocomplete 搜索时回显的数据字段必须叫value,这里给他重新包装一下
        this.tags = this.tags.map((tag) => {
          return {
            value: tag.tagName
          }
        })
        //   .filter((item) => {
        // 由于后续编辑回显selectedTag也有值,产生关闭后,unselect加值,重复
        //   // 与已选中数组一致的不会进  可选列表
        //   // 这里的some在返回值为true时直接跳出, 唯一为真,如果是every则要么找到一个false要么全为true才结束
        //   return !this.selectedTags.some(selected => selected.value === item.value)
        // })
        this.unselectedTags = this.tags
      } else {
        console.log('服务器废了')
      }
    },
    handleAdd() {
      this.dialogVis = true
    },

    getEditorContent(content) {
      this.form.content = content
    },
    save() {
      // 将原来的对象数组转为数组,提取出对象的value值
      this.tags = this.selectedTags.map((item) => {
        return item.value
      })
      this.form.tags = this.tags.join(',')
      if (this.articleId === '0') {
        add(this.form).then(res => {
          if (res.code === 20000) {
            this.$message.success('保存成功')
            this.$router.push('/article/list')
          } else {
            this.$message.error(res.msg)
          }
        })
      } else {
        update(this.form).then(res => {
          if (res.code === 20000) {
            this.$message.success('保存成功')
            this.$router.push('/article/list')
          } else {
            this.$message.error(res.msg)
          }
        })
      }
    }
  }
}
</script>

<style scoped>
.singleTag {
  position: relative;
}

.del {
  position: absolute;
  font-size: 12px;
  color: red;
  cursor: pointer;
  display: block;
  /*background: red;*/
  bottom: 0;
  right: -10px;
  width: 20px;
  text-align: center;
  transition: 1s;
}

.del:hover {
  color: #2f4d03;
  cursor: pointer;
  transform: rotateZ(360deg);
}

.publishBox {
  cursor: pointer;
  width: 100px;
  text-align: center;
  margin-right: 20px;
  padding: 10px;
  background: #6ce8ff;
  color: #000000;
  box-shadow: 0 0 4px black;
  border-radius: 10px;
}

.cancelBox {
  cursor: pointer;
  width: 100px;
  text-align: center;
  padding: 10px;
  background: #ff8383;
  color: #000000;
  box-shadow: 0 0 4px black;
  border-radius: 10px;
}
</style>

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值