component-编辑数据页面(操作按钮-编辑,保存,取消) Object.assign浅拷贝复制

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>
相关推荐
倾颜17 分钟前
React 19 源码主线拆解 04:Fiber 到底是什么,React 为什么需要 Fiber?
前端·react.js·源码阅读
AI攻城狮23 分钟前
国产大模型能力大比拼,社区有话说
前端
IT_陈寒1 小时前
Vite的public文件夹放静态资源?这坑我替你踩了
前端·人工智能·后端
涵涵(互关)1 小时前
GoView各项目文件中的相关语法2
前端·javascript·vue.js
子兮曰1 小时前
别让爬虫白嫖你的导航站了:纯免费,手把手实现加密字体防爬
前端·javascript·后端
小村儿2 小时前
连载06 - Hooks 源码深度解析:Claude Code 的确定性自动化体系
前端·后端·ai编程
心中无石马2 小时前
uniapp引入tailwindcss4.x
前端·css·uni-app
焰火19992 小时前
[Vue]可重置的响应式状态reactive
前端·vue.js
陆枫Larry2 小时前
CSS transform scale:图片放大效果背后的原理
前端
源码宝2 小时前
基于 SpringBoot + Vue 的医院随访系统:技术架构与功能实现
java·vue.js·spring boot·架构·源码·随访系统·随访管理