编辑单元格和多选的功能
首先是编辑单元格的功能,点击编辑按钮,可以直接在表格中队内容进行编辑,点击保存以后能够同步到数据库。
其次是多选的功能,点击每行前面的多选框按钮,我们可以选中多行。
完整后端代码:
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>
```