vue实现双击编辑
实现双击编辑功能的基本思路
在Vue中实现双击编辑功能,通常需要结合v-model数据绑定和v-on事件监听。核心逻辑是:当用户双击元素时,显示可编辑的输入框;完成编辑后,保存数据并隐藏输入框。
基础实现方案
创建两个状态:一个用于控制显示模式(文本或输入框),另一个用于存储编辑内容。
<template>
<div>
<span v-if="!isEditing" @dblclick="startEditing">{{ content }}</span>
<input
v-else
ref="input"
v-model="content"
@blur="stopEditing"
@keyup.enter="stopEditing"
/>
</div>
</template>
<script>
export default {
data() {
return {
content: '双击编辑内容',
isEditing: false
}
},
methods: {
startEditing() {
this.isEditing = true
this.$nextTick(() => {
this.$refs.input.focus()
})
},
stopEditing() {
this.isEditing = false
}
}
}
</script>
使用自定义指令优化
可以封装为可复用的自定义指令,简化组件中的代码。
Vue.directive('editable', {
bind(el, binding, vnode) {
let value = binding.value
const input = document.createElement('input')
el.addEventListener('dblclick', () => {
input.value = value
el.style.display = 'none'
el.parentNode.insertBefore(input, el)
input.focus()
})
input.addEventListener('blur', () => {
value = input.value
vnode.context[binding.expression] = value
el.textContent = value
el.style.display = ''
input.remove()
})
},
update(el, binding) {
el.textContent = binding.value
}
})
使用方式:
<span v-editable="content"></span>
结合Vuex状态管理
当需要跨组件共享编辑状态时,可以结合Vuex管理编辑状态。
// store.js
export default new Vuex.Store({
state: {
editableContent: '初始内容'
},
mutations: {
updateContent(state, payload) {
state.editableContent = payload
}
}
})
组件中使用:
<template>
<div>
<span
v-if="!$store.state.isEditing"
@dblclick="$store.commit('setEditing', true)"
>
{{ $store.state.editableContent }}
</span>
<input
v-else
:value="$store.state.editableContent"
@input="e => $store.commit('updateContent', e.target.value)"
@blur="$store.commit('setEditing', false)"
/>
</div>
</template>
添加样式和过渡效果
为编辑状态切换添加平滑的过渡效果,提升用户体验。
<template>
<div>
<transition name="fade">
<span v-if="!isEditing" @dblclick="startEditing" class="editable-text">
{{ content }}
</span>
<input
v-else
ref="input"
v-model="content"
@blur="stopEditing"
class="edit-input"
/>
</transition>
</div>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
.editable-text {
cursor: pointer;
padding: 4px;
}
.edit-input {
border: 1px solid #ddd;
padding: 4px;
}
</style>
完整组件封装示例
将功能封装为可复用的组件,支持更多自定义选项。
<template>
<div class="editable-container">
<component
:is="tag"
v-show="!isEditing"
@dblclick="handleDblClick"
:class="textClass"
>
{{ displayValue }}
</component>
<component
:is="editTag"
v-show="isEditing"
ref="editComponent"
v-model="localValue"
v-bind="editProps"
@blur="handleBlur"
@keyup.enter="handleBlur"
:class="editClass"
/>
</div>
</template>
<script>
export default {
props: {
value: { type: [String, Number], default: '' },
tag: { type: String, default: 'span' },
editTag: { type: String, default: 'input' },
textClass: { type: String, default: '' },
editClass: { type: String, default: '' },
editProps: { type: Object, default: () => ({}) }
},
data() {
return {
isEditing: false,
localValue: this.value
}
},
computed: {
displayValue() {
return this.localValue || this.placeholder
}
},
watch: {
value(newVal) {
this.localValue = newVal
}
},
methods: {
handleDblClick() {
this.isEditing = true
this.$nextTick(() => {
const el = this.$refs.editComponent
if (el && el.focus) el.focus()
if (el && el.select) el.select()
})
},
handleBlur() {
this.isEditing = false
this.$emit('input', this.localValue)
this.$emit('change', this.localValue)
}
}
}
</script>
使用方式:

<editable-text v-model="content" tag="div" edit-tag="textarea" />






