1.前言


vue3 + ts
支持"查看/编辑"切换表单组件用于展示和修改两个字段,初始化加载模拟数据,点击编辑进入编辑状态,点击保存提交更改并退出编辑,点击取消丢弃更改,恢复原始值;
2.代码
对于表单输入框与文本域样式可根据需求进行调整,编辑的保存和取消关键在于 Object.assign浅拷贝复制,代码如下
javascript
<!-- 编辑数据页面 -->
<template>
<div style="background-color: #AAB0B6;">
<!-- 操作按钮 -->
<div class="edit-btn">
<el-button v-if="!isEditing" @click="handleEdit" type="success">编辑</el-button>
<el-button v-if="isEditing" @click="handleSave" type="success">保存</el-button>
<el-button v-if="isEditing" @click="handCancel" type="info">取消</el-button>
</div>
<!-- 非编辑展示状态 -->
<div v-if="!isEditing" class="content">
<el-row
:gutter="20"
class="content-one"
>
<el-col :span="3">输入框文本:</el-col>
<el-col :span="17">{{ formData.textInputData || '---' }}</el-col>
</el-row>
<el-row
:gutter="20"
class="content-one"
>
<el-col :span="3">文本描述:</el-col>
<el-col :span="17">{{ formData.MutailTextInputData || '---' }}</el-col>
</el-row>
</div>
<!-- 编辑状态 -->
<div v-else class="content">
<el-row
:gutter="20"
class="content-one"
>
<el-col :span="3">输入框文本:</el-col>
<el-col :span="17">
<el-input
v-model="formData.textInputData"
placeholder="请输入文本..."
class="input-text"
/>
</el-col>
</el-row>
<el-row
:gutter="20"
class="content-one"
>
<el-col :span="3">文本描述:</el-col>
<el-col :span="17">
<el-input
v-model="formData.MutailTextInputData"
type="textarea"
:rows="3"
placeholder="文本描述textarea:..."
resize="vertical"
class="content__input"
maxlength="500"
show-word-limit
/>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted} from 'vue'
import { ElMessage } from 'element-plus'
// 模拟数据
const mockInitialData = {
textInputData: '文本输入耶耶耶耶耶耶',
MutailTextInputData: '文本描述性大哥大哥大哥嘟嘟嘟嘟',
}
// 数据类型定义
interface ManagedEntityForm {
textInputData: string
MutailTextInputData: string
}
// formData:当前正在展示/编辑的数据(响应式)
const formData = reactive<ManagedEntityForm>({
textInputData: '',
MutailTextInputData: '',
})
const isEditing = ref(false)
// originalData:保存进入编辑前的"快照",用于取消时回滚
const originalData = ref<ManagedEntityForm>({ ...formData })
onMounted(() => {
loadData()
})
// 初始加载
const loadData = () => {
setTimeout(() => {
// 将 mock 数据赋值给 formData(触发视图更新)
Object.assign(formData, mockInitialData)
// 同时保存一份副本作为"原始值"
Object.assign(originalData.value, formData)
}, 200)
}
// 编辑
const handleEdit = () => {
// 关键:在点击"编辑"时,先保存当前 formData 到 originalData
// 即使用户中途刷新或多次编辑,取消时总能回到最初进入编辑时的状态
Object.assign(originalData.value, formData)
isEditing.value = true
}
// 保存
const handleSave = () => {
// 保存成功后,将当前 formData 视为新的"原始值"
Object.assign(originalData.value, formData)
isEditing.value = false
ElMessage.success('保存成功')
// 调用接口保存formData数据
}
// 取消
const handCancel = () => {
// 将 originalData 的值覆盖回 formData,实现撤销
Object.assign(formData, originalData.value)
isEditing.value = false
}
</script>
<style lang="scss" scoped>
$spacing-md: calc(12 / 1080 * 100vh);
// 编辑按钮
.edit-btn {
display: flex;
gap: 12px;
flex-direction: row-reverse;
padding: 10px;
&-title {
font-size: 20px;
font-weight: bold;
color: rgba(255, 255, 255, 1);
}
}
// 自定义输入框样式
.input-text {
:deep(.el-input__wrapper) {
height: 100%;
padding: 0 $spacing-md;
background: transparent !important;
border: none !important;
box-shadow: none !important;
font-size: 16px;
font-weight: normal;
color: #fff;
}
:deep(.el-input__inner) {
font-size: 16px;
font-weight: normal;
color: #fff;
background: none;
border: 1px solid rgba(255, 255, 255, 0.5);
box-shadow: none;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
}
// 文本域样式
.content {
display: flex;
flex: 1;
flex-direction: column;
gap: 16px;
padding: 16px;
&__input {
width: 100%;
:deep(.el-textarea__inner) {
box-sizing: border-box;
width: 100%;
min-height: calc(80 / 1080 * 100vh);
font-size: 16px;
color: #fff;
resize: vertical;
background: rgba(26, 92, 152, 0.1);
border: none;
border-radius: 4px;
box-shadow: none;
opacity: 0.9;
padding: 8px 12px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
&:focus {
outline: none;
border-color: #1890ff;
}
&::-webkit-scrollbar {
width: 4px;
background: transparent;
}
&::-webkit-scrollbar-track {
background: rgba(54, 148, 255, 0.2);
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: #3694ff;
border-radius: 2px;
}
}
}
.content-one {
font-size: 16px;
font-weight: normal;
color: #fff;
.el-col {
display: flex;
align-items: center;
}
}
}
// 文本输入限制字数 右下角样式
.el-textarea :deep(.el-input__count) {
padding: 2px 6px !important;
font-size: 12px !important;
color: #fff !important;
background-color: rgb(255 255 255 / 0%);
}
</style>