vue之实现移动端的复制粘贴功能(两种写法)
实现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()
},
}
}
更多推荐
已为社区贡献21条内容
所有评论(0)