zdppy+vue3+antd 实现表格单元格编辑功能

初步实现

html 复制代码
<template>
  <a-button class="editable-add-btn" style="margin-bottom: 8px" @click="handleAdd">Add</a-button>

  <a-table bordered :data-source="dataSource" :columns="columns">
    <template #bodyCell="{ column, text, record }">
      <!--make the name column editable-->
      <template v-if="column.dataIndex === 'name'">
        <div class="editable-cell">
          <div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
            <a-input v-model:value="editableData[record.key].name" @pressEnter="save(record.key)"/>
            <check-outlined class="editable-cell-icon-check" @click="save(record.key)"/>
          </div>
          <div v-else class="editable-cell-text-wrapper">
            {{ text || ' ' }}
            <edit-outlined class="editable-cell-icon" @click="edit(record.key)"/>
          </div>
        </div>
      </template>

      <!--Action column-->
      <template v-else-if="column.dataIndex === 'operation'">
        <a-popconfirm
            v-if="dataSource.length"
            title="Sure to delete?"
            @confirm="onDelete(record.key)"
        >
          <a>Delete</a>
        </a-popconfirm>
      </template>
    </template>
  </a-table>
</template>
<script setup>
import {computed, reactive, ref} from 'vue';
import {CheckOutlined, EditOutlined} from "@ant-design/icons-vue";
import {cloneDeep} from 'lodash-es';

const columns = [
  {
    title: 'name',
    dataIndex: 'name',
    width: '30%',
  },
  {
    title: 'age',
    dataIndex: 'age',
  },
  {
    title: 'address',
    dataIndex: 'address',
  },
  {
    title: 'operation',
    dataIndex: 'operation',
  },
];
const dataSource = ref([
  {
    key: '0',
    name: 'Edward King 0',
    age: 32,
    address: 'London, Park Lane no. 0',
  },
  {
    key: '1',
    name: 'Edward King 1',
    age: 32,
    address: 'London, Park Lane no. 1',
  },
]);
const count = computed(() => dataSource.value.length + 1);
const editableData = reactive({});

// make the row editable
const edit = key => {
  editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
};

// save the edit result
const save = key => {
  Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
  delete editableData[key];
};

// delete the row
const onDelete = key => {
  dataSource.value = dataSource.value.filter(item => item.key !== key);
};

// add a row to data source
const handleAdd = () => {
  const newData = {
    key: `${count.value}`,
    name: `Edward King ${count.value}`,
    age: 32,
    address: `London, Park Lane no. ${count.value}`,
  };
  dataSource.value.push(newData);
};
</script>
<style lang="less" scoped>
.editable-cell {
  position: relative;

  .editable-cell-input-wrapper,
  .editable-cell-text-wrapper {
    padding-right: 24px;
  }

  .editable-cell-text-wrapper {
    padding: 5px 24px 5px 5px;
  }

  .editable-cell-icon,
  .editable-cell-icon-check {
    position: absolute;
    right: 0;
    width: 20px;
    cursor: pointer;
  }

  .editable-cell-icon {
    margin-top: 4px;
    display: none;
  }

  .editable-cell-icon-check {
    line-height: 28px;
  }

  .editable-cell-icon:hover,
  .editable-cell-icon-check:hover {
    color: #108ee9;
  }

  .editable-add-btn {
    margin-bottom: 8px;
  }
}

.editable-cell:hover .editable-cell-icon {
  display: inline-block;
}
</style>

渲染后端接口数据

html 复制代码
<script setup>
import {computed, onMounted, reactive, ref} from 'vue';
import {CheckOutlined, EditOutlined} from "@ant-design/icons-vue";
import {cloneDeep} from 'lodash-es';
import axios from "axios";

const columns = [
  {
    name: '姓名',
    dataIndex: 'name',
    key: 'name',
  },
  {
    name: '性别',
    dataIndex: 'gender',
    key: 'gender',
  },
  {
    title: '年龄',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: '电话',
    dataIndex: 'phone',
    key: 'phone',
  },
  {
    title: '邮箱',
    key: 'email',
    dataIndex: 'email',
  },
  {
    title: '薪资',
    key: 'salary',
    dataIndex: 'salary',
  },
  {
    title: '操作',
    key: 'action',
  },
];

const dataSource = ref([]);

onMounted(() => {
  axios.get("http://127.0.0.1:8889/zdppy_amuserdetail").then((response) => {
    console.log("response.data", response.data);
    dataSource.value = response.data.data.results;
  })
})

const count = computed(() => dataSource.value.length + 1);
const editableData = reactive({});

// make the row editable
const edit = id => {
  console.log("edit", editableData, id)
  editableData[id] = cloneDeep(dataSource.value.filter(item => id === item.id)[0]);
};

// save the edit result
const save = id => {
  Object.assign(dataSource.value.filter(item => id === item.id)[0], editableData[id]);
  delete editableData[id];
};

// add a row to data source
const handleAdd = () => {
  const newData = {
    key: `${count.value}`,
    name: `Edward King ${count.value}`,
    age: 32,
    address: `London, Park Lane no. ${count.value}`,
  };
  dataSource.value.push(newData);
};
</script>

<template>
  <a-button class="editable-add-btn" style="margin-bottom: 8px" @click="handleAdd">Add</a-button>

  <a-table
      bordered
      :data-source="dataSource"
      :columns="columns"
      :row-key="record => record.id"
  >
    <template #headerCell="{ column }">
      <template v-if="column.key === 'name'">
        <span>
          {{ column.name }}
        </span>
      </template>
      <template v-else-if="column.key === 'gender'">
        <span>
          {{ column.name }}
        </span>
      </template>
    </template>

    <template #bodyCell="{ column, text, record }">
      <!--make the name column editable-->
      <template v-if="column.dataIndex === 'name'">
        <div class="editable-cell">
          <div v-if="editableData[record.id]" class="editable-cell-input-wrapper">
            <a-input v-model:value="editableData[record.id].name" @pressEnter="save(record.id)"/>
            <check-outlined class="editable-cell-icon-check" @click="save(record.id)"/>
          </div>
          <div v-else class="editable-cell-text-wrapper">
            {{ text || ' ' }}
            <edit-outlined class="editable-cell-icon" @click="edit(record.id)"/>
          </div>
        </div>
      </template>

      <!--Action column-->
      <template v-else-if="column.key === 'action'">
        <a-space wrap>
          <a-button size="small" type="primary" block>编辑</a-button>
          <a-button size="small" block>详情</a-button>
          <a-button size="small" danger block>删除</a-button>
        </a-space>
      </template>
    </template>
  </a-table>
</template>
<style lang="less" scoped>
.editable-cell {
  position: relative;

  .editable-cell-input-wrapper,
  .editable-cell-text-wrapper {
    padding-right: 24px;
  }

  .editable-cell-text-wrapper {
    padding: 5px 24px 5px 5px;
  }

  .editable-cell-icon,
  .editable-cell-icon-check {
    position: absolute;
    right: 0;
    width: 20px;
    cursor: pointer;
  }

  .editable-cell-icon {
    margin-top: 4px;
    display: none;
  }

  .editable-cell-icon-check {
    line-height: 28px;
  }

  .editable-cell-icon:hover,
  .editable-cell-icon-check:hover {
    color: #108ee9;
  }

  .editable-add-btn {
    margin-bottom: 8px;
  }
}

.editable-cell:hover .editable-cell-icon {
  display: inline-block;
}
</style>

将修改的数据保存到数据库

html 复制代码
<script setup>
import {computed, onMounted, reactive, ref} from 'vue';
import {CheckOutlined, EditOutlined} from "@ant-design/icons-vue";
import {cloneDeep} from 'lodash-es';
import axios from "axios";

const columns = [
  {
    name: '姓名',
    dataIndex: 'name',
    key: 'name',
  },
  {
    name: '性别',
    dataIndex: 'gender',
    key: 'gender',
  },
  {
    title: '年龄',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: '电话',
    dataIndex: 'phone',
    key: 'phone',
  },
  {
    title: '邮箱',
    key: 'email',
    dataIndex: 'email',
  },
  {
    title: '薪资',
    key: 'salary',
    dataIndex: 'salary',
  },
  {
    title: '操作',
    key: 'action',
  },
];

const dataSource = ref([]);

onMounted(() => {
  axios.get("http://127.0.0.1:8889/zdppy_amuserdetail").then((response) => {
    console.log("response.data", response.data);
    dataSource.value = response.data.data.results;
  })
})

const count = computed(() => dataSource.value.length + 1);
const editableData = reactive({});

// make the row editable
const edit = id => {
  console.log("edit", editableData, id)
  editableData[id] = cloneDeep(dataSource.value.filter(item => id === item.id)[0]);
};

// save the edit result
const save = id => {
  const data = editableData[id]
  console.log("updated data", data)

  // backend update
  axios({
    method: "put",
    url: "http://127.0.0.1:8889/zdppy_amuserdetail/" + id,
    contentType: "application/json",
    data,
  }).then(resp => {
    console.log("update", resp.data);
  })

  // frontedn update
  Object.assign(dataSource.value.filter(item => id === item.id)[0], editableData[id]);
  delete editableData[id];
};

// add a row to data source
const handleAdd = () => {
  const newData = {
    key: `${count.value}`,
    name: `Edward King ${count.value}`,
    age: 32,
    address: `London, Park Lane no. ${count.value}`,
  };
  dataSource.value.push(newData);
};
</script>

<template>
  <a-button class="editable-add-btn" style="margin-bottom: 8px" @click="handleAdd">Add</a-button>

  <a-table
      bordered
      :data-source="dataSource"
      :columns="columns"
      :row-key="record => record.id"
  >
    <template #headerCell="{ column }">
      <template v-if="column.key === 'name'">
        <span>
          {{ column.name }}
        </span>
      </template>
      <template v-else-if="column.key === 'gender'">
        <span>
          {{ column.name }}
        </span>
      </template>
    </template>

    <template #bodyCell="{ column, text, record }">
      <!--make the name column editable-->
      <template v-if="column.dataIndex === 'name'">
        <div class="editable-cell">
          <div v-if="editableData[record.id]" class="editable-cell-input-wrapper">
            <a-input v-model:value="editableData[record.id].name" @pressEnter="save(record.id)"/>
            <check-outlined class="editable-cell-icon-check" @click="save(record.id)"/>
          </div>
          <div v-else class="editable-cell-text-wrapper">
            {{ text || ' ' }}
            <edit-outlined class="editable-cell-icon" @click="edit(record.id)"/>
          </div>
        </div>
      </template>

      <!--Action column-->
      <template v-else-if="column.key === 'action'">
        <a-space wrap>
          <a-button size="small" type="primary" block>编辑</a-button>
          <a-button size="small" block>详情</a-button>
          <a-button size="small" danger block>删除</a-button>
        </a-space>
      </template>
    </template>
  </a-table>
</template>
<style lang="less" scoped>
.editable-cell {
  position: relative;

  .editable-cell-input-wrapper,
  .editable-cell-text-wrapper {
    padding-right: 24px;
  }

  .editable-cell-text-wrapper {
    padding: 5px 24px 5px 5px;
  }

  .editable-cell-icon,
  .editable-cell-icon-check {
    position: absolute;
    right: 0;
    width: 20px;
    cursor: pointer;
  }

  .editable-cell-icon {
    margin-top: 4px;
    display: none;
  }

  .editable-cell-icon-check {
    line-height: 28px;
  }

  .editable-cell-icon:hover,
  .editable-cell-icon-check:hover {
    color: #108ee9;
  }

  .editable-add-btn {
    margin-bottom: 8px;
  }
}

.editable-cell:hover .editable-cell-icon {
  display: inline-block;
}
</style>
相关推荐
子兮曰19 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰20 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万20 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝20 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A20 小时前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋20 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁21 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄1 天前
JavaScript 隐式全局变量解析
javascript
honkun61 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19911 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net