vue之实现移动端的复制粘贴功能(两种写法)

实现vue移动端的复制粘贴

第一种使用 vue-clipboard2

1、npm i vue-clipboard2 – save 安装后,在main.js中引入

// vue剪切板功能
import VueClipboard from 'vue-clipboard2'
Vue.use(VueClipboard)

2、在组件中使用

// template中
<div class="flex-center">
   <div class="info-left">订单号</div>
   <div class="fs-24 color-333 font-bold">{{orderDetailData.orderId}}</div>
   <div class="copy ml-20">
        <img src="../../assets/images/order/order-copy.png" alt="" 
             v-clipboard:copy="orderDetailData.orderId" 
             v-clipboard:success="onCopySuccess" 
             v-clipboard:error="onCopyError"
        />
   </div>
</div>


// script的methods中
// 复制粘贴
  onCopySuccess(){
      this.$toast("复制成功");
  },
  onCopyError(){
     this.$toast("复制失败");
  },

第二种使用自己写

封装一个写法:

export function copyText(text) {
  // 动态创建 textarea 标签
  const textarea = document.createElement('textarea')
  // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
  textarea.readOnly = 'readonly'
  textarea.style.position = 'absolute'
  textarea.style.left = '-9999px'
  // 将要 copy 的值赋给 textarea 标签的 value 属性
  textarea.value = text
  // 将 textarea 插入到 body 中
  document.body.appendChild(textarea)
  // 选中值并复制
  textarea.select()
  const result = document.execCommand('Copy')
  document.body.removeChild(textarea)
  return result
}

引入使用

<template>
  <div>
    <div class="c-container">
      <div class="c-title">评论</div>
      <div class="c-item" v-for="(item, index) in comments" :key="index">
        <!-- 评论 -->
        <div
          class="c-info"
          @touchstart="
            touchstart(item.id, item.commentatorId, item.content, 'comment')
          "
          @touchend="touchend"
          @click="handleIptClick(item.id, item.commentatorId)"
        >
          <div class="title">
            <div class="author">{{ item.commentator }}</div>
            <div class="c-time">
              {{ parseTime(item.commentTime, '{m}月{d}日 {h}:{i}') }}
            </div>
          </div>
          <div class="content">{{ item.content }}</div>
        </div>

        <!-- 回复 -->
        <div
          class="r-info"
          v-for="(repItem, repIndex) in item.replyList"
          @click="handleIptClick(item.id, repItem.sentorId)"
          @touchstart="
            touchstart(repItem.id, repItem.sentorId, repItem.content, 'reply')
          "
          @touchend="touchend"
          :key="repIndex"
        >
          <div class="title">
            <div class="author">
              {{ repItem.sentor }}
              <span class="mid-text">回复</span>
              {{ repItem.receivor }}
            </div>
            <div class="c-time">
              {{ parseTime(repItem.replyTime, '{m}月{d}日 {h}:{i}') }}
            </div>
          </div>
          <div class="content">{{ repItem.content }}</div>
        </div>
      </div>

      <div class="c-ipt">
        <input
          type="text"
          v-model="commentContent"
          maxlength="100"
          placeholder="评价内容,文字上限100"
          @keydown.enter="handleSent"
          @click="handleIptClick(null)"
        />
        <img
          class="icon"
          src="@/assets/img/icon-sent.png"
          @click="handleSent"
        />
      </div>

      <div class="inputing" v-show="isInputing" @click="$refs.inputing.focus()">
        <div class="c-title">评论</div>
        <div class="c-ipt">
          <input
            type="text"
            v-model="commentContent"
            maxlength="100"
            ref="inputing"
            placeholder="评价内容,文字上限100"
            @keydown.enter="handleSent"
          />
          <img
            class="icon"
            src="@/assets/img/icon-sent.png"
            @click.stop="handleSent"
          />
        </div>
      </div>
    </div>
    <van-overlay :show="isInputing || isShowCtxBtns" @click="isInputing = isShowCtxBtns = false"/>
    <div class="ctx-btns" v-show="isShowCtxBtns">
      <div class="btn" @click="handleCopy">复制</div>
      <div class="btn" @click="handleRemove" v-show="userAccountId === ownerUserId">删除</div>
      <div class="btn" @click="resetOption">取消</div>
    </div>
  </div>
</template>

<script>
import {
  getComments,
  createComment,
  createReply,
  removeComment,
  removeReply
} from '@/api/comment'

import { copyText, parseTime } from '@/utils'
const originResizeFn = window.onresize
export default {
  props: {
    id: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      comments: [],
      commentContent: '',
      selectedCommentId: '',
      selectedType: '',
      selectContent: '', // 用于复制
      receivorId: '',
      ownerUserId: '',
      isInputing: false,
      isShowCtxBtns: false,
      pressTimer: null
    }
  },
  created() {
    this.parseTime = parseTime
  },
  mounted() {
    // 禁用浏览器长按默认事件
    document.oncontextmenu = function (e) {
      e.preventDefault()
    }
    const clientHeight =
      document.documentElement.clientHeight || document.body.clientHeight
    window.onresize = () => {
      // 防止两个事件冲突
      originResizeFn && originResizeFn()
      const nowClientHeight =
        document.documentElement.clientHeight || document.body.clientHeight
      // 键盘收起
      if (clientHeight === nowClientHeight) {
        this.isInputing = false
      }
    }

    this.initComments()
  },
  methods: {
    async initComments() {
      try {
        $loading.show()
        const res = await getComments({ taskDetailId: this.id })
        if (res.code === 0) {
          this.comments = res.item
        }
        $loading.hide()
      } catch (error) {
        $loading.hide()
      }
    },
    handleIptClick(id, receivorId) {
      this.isInputing = true
      this.selectedCommentId = id || ''
      this.receivorId = receivorId || ''
      this.$nextTick(() => {
        this.$refs.inputing.focus()
      })
    },
    async handleSent() {
      if (!this.commentContent) return
      try {
        $loading.show()
        // 有选中的评论,则是回复,否则就是评论
        if (this.selectedCommentId) {
          const res = await createReply({
            commentId: this.selectedCommentId,
            content: this.commentContent,
            receivor: this.receivorId
          })
          if (res.code === 0) {
            this.$toast('回复成功')
            this.resetOption()
            this.initComments()
          } else {
            this.$toast(res.message)
          }
        } else {
          const res = await createComment({
            taskDetailId: this.id,
            content: this.commentContent
          })
          if (res.code === 0) {
            this.$toast('评论成功')
            this.resetOption()
            this.initComments()
          } else {
            this.$toast(res.message)
            this.resetOption()
          }
        }
      } catch (error) {
        this.$toast('操作失败')
        this.resetOption()
        console.error(error)
      }
      $loading.hide()
    },
    handleOpenCtx(id, ownerUserId, content, selectedType) {
      this.isShowCtxBtns = true
      this.selectedType = selectedType || ''
      this.selectedCommentId = id || ''
      this.ownerUserId = ownerUserId || ''
      this.selectContent = content || ''
    },
    handleCopy() {
      const rst = copyText(this.selectContent)
      rst && this.$toast('复制成功')
      this.resetOption()
    },
    async handleRemove() {
      try {
        $loading.show()
        let res
        // 删除回复
        if (this.selectedType === 'reply') {
          res = await removeReply({
            id: this.selectedCommentId
          })
        } else {
          // 删除评论
          res = await removeComment({
            id: this.selectedCommentId
          })
        }
        if (res.code === 0) {
          this.$toast('删除成功')
          this.resetOption()
          this.initComments()
        } else {
          this.resetOption()
          this.$toast(res.message)
        }
      } catch (error) {
        this.$toast('操作失败')
        this.resetOption()
        console.error(error)
      }
      $loading.hide()
    },
    resetOption() {
      this.isInputing = false
      this.isShowCtxBtns = false
      this.commentContent = ''
      this.selectedCommentId = null
      this.receivorId = null
      this.selectedType = ''
      this.ownerUserId = ''
    },
    touchstart(id, ownerUserId, content, selectedType) {
      this.pressTimer && clearTimeout(this.pressTimer)
      this.pressTimer = setTimeout(() => {
        this.handleOpenCtx(id, ownerUserId, content, selectedType)
      }, 600)
    },
    touchend() {
      this.pressTimer && clearTimeout(this.pressTimer)
    }
  },
  computed: {
    userAccountId() {
      return this.$store.getters.userAccountID
    }
  },
  destroyed() {
    window.onresize = originResizeFn
  }
}
</script>

<style lang="scss" scoped>
.c-container {
  width: 100%;
  background: #f5f6fb;
  padding: 0.3rem;
  .c-title {
    font-size: 0.28rem;
    color: #36395c;
    font-weight: 600;
    padding-bottom: 0.2rem;
  }

  .c-item {
    padding: 0.2rem 0;
    ~ .c-item {
      border-top: 1px solid #eee;
    }
    .title {
      display: flex;
      height: 0.4rem;
      line-height: 0.4rem;

      .author {
        color: #4646e6;
        font-weight: 600;
        font-size: 0.28rem;

        margin-right: 0.26rem;
        .mid-text {
          color: #65687f;
          font-size: 0.24rem;
          font-weight: 300;
          margin: 0 0.14rem;
        }
      }
      .c-time {
        font-size: 0.22rem;
        font-weight: 300;
        color: #cccdd8;
      }
    }
    .content {
      color: #65687f;
      font-size: 0.26rem;
      font-weight: 400;
      line-height: 0.36rem;

      margin-top: 0.1rem;
    }
    .r-info {
      margin-top: 0.4rem;
    }
  }

  .c-ipt {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 0.72rem;
    margin-top: 0.4rem;
    input {
      height: 100%;
      width: 6rem;
      padding-left: 0.24rem;

      font-weight: 300;
      font-size: 0.26rem;
      border-radius: 0.16rem;
      border: 0.01rem solid #efeff6;
      background: #ffffff;
      &::placeholder {
        color: #cccdd8;
      }
      /* IE 10–11 */
      &:-ms-input-placeholder {
        color: #cccdd8;
      }
      /* Firefox 19-50 */
      &::-moz-placeholder {
        color: #cccdd8;
      }
      ::-webkit-input-placeholder {
        color: #cccdd8;
      }
    }
    .icon {
      height: 0.56rem;
    }
  }

  .inputing {
    background: #f5f6fb;
    z-index: 999;
    position: fixed;
    width: 100%;
    max-width: 450px;
    bottom: 0;
    left: 0;
    right: 0;
    margin: 0 auto;
    padding: 0.3rem;

    width: 100%;
    .c-ipt {
      margin: 0;
    }
    input {
      background-color: #fff;
    }
  }
}

.ctx-btns {
  width: 100%;
  background-color: #fff;
  border-radius: 0.24rem 0.24rem 0rem 0rem;

  position: fixed;
  width: 100%;
  max-width: 450px;
  bottom: 0;
  left: 0;
  right: 0;
  margin: 0 auto;

  z-index: 999;
  .btn {
    color: #36395c;
    font-size: 0.34rem;
    font-weight: 300;
    text-align: center;
    line-height: 1.14rem;
    transition: all 0.3s;
    cursor: pointer;
    &:first-of-type {
      border-bottom: 0.02rem solid #f5f6fb;
      border-radius: 0.24rem 0.24rem 0rem 0rem;
    }
    &:last-of-type {
      border-top: 0.12rem solid #f5f6fb;
    }
    &:active,
    &:hover {
      background-color: #f5f6fb;
    }
  }
}

.c-info,
.r-info,
.ctx-btns {
  -webkit-touch-callout: default;
  -moz-user-select: -moz-none;
  -moz-user-select: none;
  -o-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
</style>

主要代码如下:

<div class="btn" @click="handleCopy">复制</div>


import { copyText } from '@/utils'

export default {
 data() {
  return {
	selectContent: '', // 用于复制
  }
  methods: {
    handleCopy() {
       const rst = copyText(this.selectContent)
       rst && this.$toast('复制成功')
       this.resetOption()
     },
   }
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值