zdppy + vue3 + antd 实现一个表格编辑行,批量删除功能

news/2024/7/18 6:36:59 标签: python, 前端

编辑单元格和多选的功能

首先是编辑单元格的功能,点击编辑按钮,可以直接在表格中队内容进行编辑,点击保存以后能够同步到数据库。
在这里插入图片描述

其次是多选的功能,点击每行前面的多选框按钮,我们可以选中多行。
在这里插入图片描述
完整后端代码:

python">import api
import upload
import time
import amcomment
import env
import mcrud
import amuserdetail

save_dir = "uploads"
env.load(".env")
db = mcrud.new_env()

app = api.Api(
    routes=[
        *amcomment.get_routes(db),
        *amuserdetail.get_routes(db),
        upload.download("/download/{filename}", save_dir),
    ],
    middleware=[api.middleware.cors()]
)

if __name__ == "__main__":
    app.run(port=8889)

完整前端代码:

<script setup>
import {cloneDeep} from 'lodash-es';
import {computed, onMounted, reactive, ref} from 'vue';
import axios from "axios";

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

const dataSource = ref([]);

const editableData = reactive({});

const edit = id => {
  editableData[id] = cloneDeep(dataSource.value.filter(item => id === item.id)[0]);
};

const save = id => {

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

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

const cancel = id => {
  delete editableData[id];
};

// handle multi selected
const state = reactive({
  selectedRowKeys: [],
  // Check here to configure the default column
  loading: false,
});

const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const start = () => {
  state.loading = true;
  // ajax request after empty completing
  setTimeout(() => {
    state.loading = false;
    state.selectedRowKeys = [];
  }, 1000);
};

const onSelectChange = selectedRowKeys => {
  console.log('selectedRowKeys changed: ', selectedRowKeys);
  state.selectedRowKeys = selectedRowKeys;
};

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

<template>
  <div style="margin-bottom: 16px">
    <a-button type="primary" :disabled="!hasSelected" :loading="state.loading" @click="start">
      Reload
    </a-button>
    <span style="margin-left: 8px">
        <template v-if="hasSelected">
          {{ `Selected ${state.selectedRowKeys.length} items` }}
        </template>
      </span>
  </div>


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

    <template #bodyCell="{ column, text, record }">
      <template v-if="['name', 'age', 'address'].includes(column.dataIndex)">
        <div>
          <a-input
              v-if="editableData[record.id]"
              v-model:value="editableData[record.id][column.dataIndex]"
              style="margin: -5px 0"
          />
          <template v-else>
            {{ text }}
          </template>
        </div>
      </template>

      <template v-else-if="column.id === 'action'">
        <div class="editable-row-operations">
          <span v-if="editableData[record.id]">
            <a-typography-link @click="save(record.id)">保存</a-typography-link>
            <a-popconfirm title="Sure to cancel?" @confirm="cancel(record.id)">
              <a>取消</a>
            </a-popconfirm>
          </span>

          <span v-else>
            <a @click="edit(record.id)">编辑</a>
          </span>
        </div>
      </template>
    </template>
  </a-table>
</template>

<style scoped>
.editable-row-operations a {
  margin-right: 8px;
}
</style>

不过目前有个问题,那就是没有实现批量删除的功能。我们可以在多选以后,点击批量删除按钮,同时删除被选中的数据。

实现前端批量删除的功能

如图:
在这里插入图片描述

选中任意两条数据以后,点击批量删除按钮,这时会弹出确认信息。
点击确认以后会触发一个方法,会在控制台输出需要被删除的数据的ID,是一个数组,用于执行批量删除的操作。

此时的完整前端代码如下:

<script setup>
import {cloneDeep} from 'lodash-es';
import {computed, onMounted, reactive, ref} from 'vue';
import axios from "axios";

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

const dataSource = ref([]);

const editableData = reactive({});

const edit = id => {
  editableData[id] = cloneDeep(dataSource.value.filter(item => id === item.id)[0]);
};

const save = id => {

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

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

const cancel = id => {
  delete editableData[id];
};

// handle multi selected
const state = reactive({
  selectedRowKeys: [],
  // Check here to configure the default column
  loading: false,
});

const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const start = () => {
  state.loading = true;
  // ajax request after empty completing
  setTimeout(() => {
    state.loading = false;
    state.selectedRowKeys = [];
  }, 1000);
};

const onSelectChange = selectedRowKeys => {
  console.log('selectedRowKeys changed: ', selectedRowKeys);
  state.selectedRowKeys = selectedRowKeys;
};

// delete selected data
const onDeleteSelectedClick = () => {
  console.log("onDeleteSelectedClick", state.selectedRowKeys);
}
onMounted(() => {
  axios({
    method: "get",
    url: "http://127.0.0.1:8889/zdppy_amuserdetail",
  }).then((response) => {
    console.log("response.data", response.data);
    const responseData = response.data.data
    console.log("responseData=", responseData)
    dataSource.value = responseData.results;
  })
})
</script>

<template>
  <div class="flex space-x-3 py-3">
    <a-button type="primary" :disabled="!hasSelected" :loading="state.loading" @click="start">
      Reload
    </a-button>
    <a-popconfirm title="您确认要删除选中的数据吗?" @confirm="onDeleteSelectedClick">
      <a-button type="primary" :disabled="!hasSelected" danger>
        批量删除
      </a-button>
    </a-popconfirm>
  </div>


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

    <template #bodyCell="{ column, text, record }">
      <template v-if="['name', 'age', 'address'].includes(column.dataIndex)">
        <div>
          <a-input
              v-if="editableData[record.id]"
              v-model:value="editableData[record.id][column.dataIndex]"
              style="margin: -5px 0"
          />
          <template v-else>
            {{ text }}
          </template>
        </div>
      </template>

      <template v-else-if="column.id === 'action'">
        <div class="editable-row-operations">
          <span v-if="editableData[record.id]">
            <a-typography-link @click="save(record.id)">保存</a-typography-link>
            <a-popconfirm title="Sure to cancel?" @confirm="cancel(record.id)">
              <a>取消</a>
            </a-popconfirm>
          </span>

          <span v-else>
            <a @click="edit(record.id)">编辑</a>
          </span>
        </div>
      </template>
    </template>
  </a-table>
</template>

<style scoped>
.editable-row-operations a {
  margin-right: 8px;
}
</style>

修改确认提示文本

修改之前:是OK和Cancel
在这里插入图片描述

修改之后:是确认和取消
在这里插入图片描述

核心代码如下:

<a-popconfirm
        title="您确认要删除选中的数据吗?"
        ok-text="确认"
        cancel-text="取消"
        @confirm="onDeleteSelectedClick">
      <a-button type="primary" :disabled="!hasSelected" danger>
        批量删除
      </a-button>
    </a-popconfirm>
```

此时前端的完整代码如下:
```html
<script setup>
import {cloneDeep} from 'lodash-es';
import {computed, onMounted, reactive, ref} from 'vue';
import axios from "axios";

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

const dataSource = ref([]);

const editableData = reactive({});

const edit = id => {
  editableData[id] = cloneDeep(dataSource.value.filter(item => id === item.id)[0]);
};

const save = id => {

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

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

const cancel = id => {
  delete editableData[id];
};

// handle multi selected
const state = reactive({
  selectedRowKeys: [],
  // Check here to configure the default column
  loading: false,
});

const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const start = () => {
  state.loading = true;
  // ajax request after empty completing
  setTimeout(() => {
    state.loading = false;
    state.selectedRowKeys = [];
  }, 1000);
};

const onSelectChange = selectedRowKeys => {
  console.log('selectedRowKeys changed: ', selectedRowKeys);
  state.selectedRowKeys = selectedRowKeys;
};

// delete selected data
const onDeleteSelectedClick = () => {
  console.log("onDeleteSelectedClick", state.selectedRowKeys);
}
onMounted(() => {
  axios({
    method: "get",
    url: "http://127.0.0.1:8889/zdppy_amuserdetail",
  }).then((response) => {
    console.log("response.data", response.data);
    const responseData = response.data.data
    console.log("responseData=", responseData)
    dataSource.value = responseData.results;
  })
})
</script>

<template>
  <div class="flex space-x-3 py-3">
    <a-button type="primary" :disabled="!hasSelected" :loading="state.loading" @click="start">
      Reload
    </a-button>
    <a-popconfirm
        title="您确认要删除选中的数据吗?"
        ok-text="确认"
        cancel-text="取消"
        @confirm="onDeleteSelectedClick">
      <a-button type="primary" :disabled="!hasSelected" danger>
        批量删除
      </a-button>
    </a-popconfirm>
  </div>


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

    <template #bodyCell="{ column, text, record }">
      <template v-if="['name', 'age', 'address'].includes(column.dataIndex)">
        <div>
          <a-input
              v-if="editableData[record.id]"
              v-model:value="editableData[record.id][column.dataIndex]"
              style="margin: -5px 0"
          />
          <template v-else>
            {{ text }}
          </template>
        </div>
      </template>

      <template v-else-if="column.id === 'action'">
        <div class="editable-row-operations">
          <span v-if="editableData[record.id]">
            <a-typography-link @click="save(record.id)">保存</a-typography-link>
            <a-popconfirm title="Sure to cancel?" @confirm="cancel(record.id)">
              <a>取消</a>
            </a-popconfirm>
          </span>

          <span v-else>
            <a @click="edit(record.id)">编辑</a>
          </span>
        </div>
      </template>
    </template>
  </a-table>
</template>

<style scoped>
.editable-row-operations a {
  margin-right: 8px;
}
</style>

```


## 实现批量删除的功能
后端本身已经有这个接口了,我们只需要在前端调用即可。

核心代码如下:
```js

// delete selected data
const onDeleteSelectedClick = () => {
  console.log("onDeleteSelectedClick", state.selectedRowKeys);
  state.loading = true
  axios({
    method: "delete",
    url: "http://127.0.0.1:8889/zdppy_amuserdetail_ids",
    contentType: "application/json",
    data: {
      ids: state.selectedRowKeys,
    }
  }).finally(() => {
    state.loading = false;
    loadTableData()
  })
}

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


此时完整的前端代码如下:
```html
<script setup>
import {cloneDeep} from 'lodash-es';
import {computed, onMounted, reactive, ref} from 'vue';
import axios from "axios";

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

const dataSource = ref([]);

const editableData = reactive({});

const edit = id => {
  editableData[id] = cloneDeep(dataSource.value.filter(item => id === item.id)[0]);
};

const save = id => {

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

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

const cancel = id => {
  delete editableData[id];
};

// handle multi selected
const state = reactive({
  selectedRowKeys: [],
  // Check here to configure the default column
  loading: false,
});

const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const start = () => {
  state.loading = true;
  // ajax request after empty completing
  setTimeout(() => {
    state.loading = false;
    state.selectedRowKeys = [];
  }, 1000);
};

const onSelectChange = selectedRowKeys => {
  console.log('selectedRowKeys changed: ', selectedRowKeys);
  state.selectedRowKeys = selectedRowKeys;
};

// delete selected data
const onDeleteSelectedClick = () => {
  console.log("onDeleteSelectedClick", state.selectedRowKeys);
  state.loading = true
  axios({
    method: "delete",
    url: "http://127.0.0.1:8889/zdppy_amuserdetail_ids",
    contentType: "application/json",
    data: {
      ids: state.selectedRowKeys,
    }
  }).finally(() => {
    state.loading = false;
    loadTableData()
  })
}

const loadTableData = () => {
  axios({
    method: "get",
    url: "http://127.0.0.1:8889/zdppy_amuserdetail",
  }).then((response) => {
    console.log("response.data", response.data);
    const responseData = response.data.data
    console.log("responseData=", responseData)
    dataSource.value = responseData.results;
  })
}
onMounted(() => {
  loadTableData()
})
</script>

<template>
  <div class="flex space-x-3 py-3">
    <a-button type="primary" :disabled="!hasSelected" :loading="state.loading" @click="start">
      Reload
    </a-button>
    <a-popconfirm
        title="您确认要删除选中的数据吗?"
        ok-text="确认"
        cancel-text="取消"
        @confirm="onDeleteSelectedClick">
      <a-button type="primary" :disabled="!hasSelected" danger>
        批量删除
      </a-button>
    </a-popconfirm>
  </div>


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

    <template #bodyCell="{ column, text, record }">
      <template v-if="['name', 'age', 'address'].includes(column.dataIndex)">
        <div>
          <a-input
              v-if="editableData[record.id]"
              v-model:value="editableData[record.id][column.dataIndex]"
              style="margin: -5px 0"
          />
          <template v-else>
            {{ text }}
          </template>
        </div>
      </template>

      <template v-else-if="column.id === 'action'">
        <div class="editable-row-operations">
          <span v-if="editableData[record.id]">
            <a-typography-link @click="save(record.id)">保存</a-typography-link>
            <a-popconfirm title="Sure to cancel?" @confirm="cancel(record.id)">
              <a>取消</a>
            </a-popconfirm>
          </span>

          <span v-else>
            <a @click="edit(record.id)">编辑</a>
          </span>
        </div>
      </template>
    </template>
  </a-table>
</template>

<style scoped>
.editable-row-operations a {
  margin-right: 8px;
}
</style>

```


http://www.niftyadmin.cn/n/5543967.html

相关文章

51单片机-第一节-LED和独立按键

一、点亮LED&#xff1a; 首先包含头文件 <REGX52.H> 随后令P2为0xFE。(此时二进制对应1111 1110&#xff0c;为0 的LED亮&#xff0c;故八个灯中的最后一个亮起)。 注&#xff1a;P2为控制LED的8位寄存器。 void main() {P2 0xFE;//1111 1110while(1){} } 二、L…

CAN总线(下)

位时序 为了灵活调整每个采样点的位置&#xff0c;使采样点对齐数据位中心附近&#xff0c;CAN总线对每一个数据位的时长进行了更细的划分&#xff0c; 分为同步段&#xff08;SS&#xff09;、传播时间段&#xff08;PTS&#xff09;、相位缓冲段1&#xff08;PBS1&#xff0…

ES7210高性能四通道音频ADC转换模拟麦克风为IIS数字咪头

特征 高性能多位 Delta-Σ 音频 ADC 102 dB 信噪比 -85 分贝 THDN 24 位&#xff0c;8 至 100 kHz 采样频率 I2S/PCM 主串行数据端口或从串行数据端口 支持TDM 256/384Fs、USB 12/24 MHz 和其他非标准音频系统时钟 低功耗待机模式 应用 麦克风阵列 智能音箱 远场语音捕获 订购…

Git注释规范

主打一个有用 代码的提交规范参考如下&#xff1a; init:初始化项目feat:新功能&#xff08;feature&#xff09;fix:修补bugdocs:文档&#xff08;documentation&#xff09;style:格式&#xff08;不影响代码运行的变动&#xff09;refactor:重构&#xff08;即不是新增功能…

Double 4 VR虚拟仿真情景实训教学系统在商务洽谈课堂上的应用

随着科技的飞速发展&#xff0c;VR&#xff08;虚拟现实&#xff09;技术已经逐渐渗透到教育领域&#xff0c;为传统的教学模式带来了革命性的变革。在商务洽谈课堂上&#xff0c;Double 4 VR虚拟仿真情景实训教学系统的应用&#xff0c;不仅为学生提供了更加真实、生动的实践环…

根据RTL图编写Verilog程序

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 根据以下RTL图&#xff0c;使用 Verilog HDL语言编写代码&#xff0c;实现相同的功能&#xff0c;并编写testbench验证功能。 输入描述&#xff1a; clk&#xff1a;系统时钟信号 rst_n&#xff1a;…

shark云原生-日志体系-ECK

文章目录 0. ECK 介绍1. 部署 CRDS & Opereator2. 部署 Elasticsearch 集群3. 配置存储4. 部署示例 0. ECK 介绍 ECK&#xff08;Elastic Cloud on Kubernetes&#xff09;是Elasticsearch官方提供的一种方式&#xff0c;用于在Kubernetes上部署、管理和扩展Elasticsearch…

【MySQL】4.MySQL 的数据类型

MySQL 的数据类型 一.数据类型分类在这里插入图片描述二.注意点1.char VS varchar2.datetime VS timestamp3.enum 和 set 的使用方法 一.数据类型分类 二.注意点 1.char VS varchar char 的意义是直接开辟固定大小的空间&#xff0c;浪费磁盘空间&#xff0c;但是效率高varcha…