!79 增加批量删除

增加表格多选
运行跨页多选
显示多选数据
移除多选数据
增加批量删除
示例代码在菜单管理里面
This commit is contained in:
dvadmin
2024-08-29 00:13:00 +00:00
committed by Gitee
8 changed files with 312 additions and 104 deletions

View File

@@ -22,7 +22,7 @@ export const handleColumnPermission = async (func: Function, crudOptions: any,ex
} }
} }
const columns = crudOptions.columns; const columns = crudOptions.columns;
const excludeColumns = ['_index','id', 'create_datetime', 'update_datetime'].concat(excludeColumn) const excludeColumns = ['checked','_index','id', 'create_datetime', 'update_datetime'].concat(excludeColumn)
for (let col in columns) { for (let col in columns) {
for (let item of res.data) { for (let item of res.data) {
if (excludeColumns.includes(item.field_name)) { if (excludeColumns.includes(item.field_name)) {

View File

@@ -48,3 +48,10 @@ export function BatchAdd(obj: AddReq) {
}); });
} }
export function BatchDelete(keys: any) {
return request({
url: apiPrefix + 'multiple_delete/',
method: 'delete',
data: { keys },
});
}

View File

@@ -4,6 +4,8 @@ import {auth} from '/@/utils/authFunction'
import {request} from '/@/utils/service'; import {request} from '/@/utils/service';
import { successNotification } from '/@/utils/message'; import { successNotification } from '/@/utils/message';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { nextTick, ref } from 'vue';
import XEUtils from 'xe-utils';
//此处为crudOptions配置 //此处为crudOptions配置
export const createCrudOptions = function ({crudExpose, context}: CreateCrudOptionsProps): CreateCrudOptionsRet { export const createCrudOptions = function ({crudExpose, context}: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async () => { const pageRequest = async () => {
@@ -22,7 +24,42 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti
const addRequest = async ({form}: AddReq) => { const addRequest = async ({form}: AddReq) => {
return await api.AddObj({...form, ...{menu: context!.selectOptions.value.id}}); return await api.AddObj({...form, ...{menu: context!.selectOptions.value.id}});
}; };
// 记录选中的行
const selectedRows = ref<any>([]);
const onSelectionChange = (changed: any) => {
const tableData = crudExpose.getTableData();
const unChanged = tableData.filter((row: any) => !changed.includes(row));
// 添加已选择的行
XEUtils.arrayEach(changed, (item: any) => {
const ids = XEUtils.pluck(selectedRows.value, 'id');
if (!ids.includes(item.id)) {
selectedRows.value = XEUtils.union(selectedRows.value, [item]);
}
});
// 剔除未选择的行
XEUtils.arrayEach(unChanged, (unItem: any) => {
selectedRows.value = XEUtils.remove(selectedRows.value, (item: any) => item.id !== unItem.id);
});
};
const toggleRowSelection = () => {
// 多选后,回显默认勾选
const tableRef = crudExpose.getBaseTableRef();
const tableData = crudExpose.getTableData();
const selected = XEUtils.filter(tableData, (item: any) => {
const ids = XEUtils.pluck(selectedRows.value, 'id');
return ids.includes(item.id);
});
nextTick(() => {
XEUtils.arrayEach(selected, (item) => {
tableRef.toggleRowSelection(item, true);
});
});
};
return { return {
selectedRows,
crudOptions: { crudOptions: {
pagination:{ pagination:{
show:false show:false
@@ -84,6 +121,11 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti
editRequest, editRequest,
delRequest, delRequest,
}, },
table: {
rowKey: 'id', //设置你的主键id 默认rowKey=id
onSelectionChange,
onRefreshed: () => toggleRowSelection(),
},
form: { form: {
col: {span: 24}, col: {span: 24},
labelWidth: '100px', labelWidth: '100px',
@@ -93,6 +135,16 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti
}, },
}, },
columns: { columns: {
$checked: {
title: '选择',
form: { show: false },
column: {
type: 'selection',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
},
},
_index: { _index: {
title: '序号', title: '序号',
form: {show: false}, form: {show: false},

View File

@@ -1,19 +1,72 @@
<template> <template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud> <fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>
<el-tooltip content="批量删除">
<el-button text type="danger" :disabled="selectedRowsCount === 0" :icon="Delete" circle @click="handleBatchDelete" />
</el-tooltip>
</template>
<template #pagination-right>
<el-popover placement="top" :width="400" trigger="click">
<template #reference>
<el-button text :type="selectedRowsCount > 0 ? 'primary' : ''">已选中{{ selectedRowsCount }}条数据</el-button>
</template>
<el-table :data="selectedRows" size="small">
<el-table-column width="150" property="id" label="id" />
<el-table-column fixed="right" label="操作" min-width="60">
<template #default="scope">
<el-button text type="info" :icon="Close" @click="removeSelectedRows(scope.row)" circle />
</template>
</el-table-column>
</el-table>
</el-popover>
</template>
</fs-crud>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { computed, ref } from 'vue';
import { useFs } from '@fast-crud/fast-crud'; import { useFs } from '@fast-crud/fast-crud';
import { createCrudOptions } from './crud'; import { createCrudOptions } from './crud';
import { MenuTreeItemType } from '../../types'; import { MenuTreeItemType } from '../../types';
import { ElMessage, ElMessageBox } from 'element-plus';
import XEUtils from 'xe-utils';
import { BatchDelete } from './api';
import { Close, Delete } from '@element-plus/icons-vue';
// 当前选择的菜单信息 // 当前选择的菜单信息
let selectOptions: any = ref({ name: null }); let selectOptions: any = ref({ name: null });
const { crudRef, crudBinding, crudExpose, context } = useFs({ createCrudOptions, context: { selectOptions } }); const { crudRef, crudBinding, crudExpose, context,selectedRows } = useFs({ createCrudOptions, context: { selectOptions } });
const { doRefresh, setTableData } = crudExpose; const { doRefresh, setTableData } = crudExpose;
// 选中行的条数
const selectedRowsCount = computed(() => {
return selectedRows.value.length;
});
// 批量删除
const handleBatchDelete = async () => {
await ElMessageBox.confirm(`确定要批量删除这${selectedRows.value.length}条记录吗`, '确认', {
distinguishCancelAndClose: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
});
await BatchDelete(XEUtils.pluck(selectedRows.value, 'id'));
ElMessage.info('删除成功');
selectedRows.value = [];
await crudExpose.doRefresh();
};
// 移除已选中的行
const removeSelectedRows = (row: any) => {
const tableRef = crudExpose.getBaseTableRef();
const tableData = crudExpose.getTableData();
if (XEUtils.pluck(tableData, 'id').includes(row.id)) {
tableRef.toggleRowSelection(row, false);
} else {
selectedRows.value = XEUtils.remove(selectedRows.value, (item: any) => item.id !== row.id);
}
};
const handleRefreshTable = (record: MenuTreeItemType) => { const handleRefreshTable = (record: MenuTreeItemType) => {
if (!record.is_catalog && record.id) { if (!record.is_catalog && record.id) {
selectOptions.value = record; selectOptions.value = record;

View File

@@ -42,6 +42,13 @@ export function DelObj(id: DelReq) {
}); });
} }
export function BatchDelete(keys: any) {
return request({
url: apiPrefix + 'multiple_delete/',
method: 'delete',
data: { keys },
});
}
/** /**
* 获取所有model * 获取所有model
*/ */

View File

@@ -2,8 +2,9 @@ import * as api from './api';
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'; import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
import { request } from '/@/utils/service'; import { request } from '/@/utils/service';
import { dictionary } from '/@/utils/dictionary'; import { dictionary } from '/@/utils/dictionary';
import { inject } from 'vue'; import { inject, nextTick, ref } from 'vue';
import {auth} from "/@/utils/authFunction"; import {auth} from "/@/utils/authFunction";
import XEUtils from 'xe-utils';
@@ -27,8 +28,41 @@ export const createCrudOptions = function ({ crudExpose, props,modelDialog,selec
form.menu = selectOptions.value.id; form.menu = selectOptions.value.id;
return await api.AddObj(form); return await api.AddObj(form);
}; };
// 记录选中的行
const selectedRows = ref<any>([]);
const onSelectionChange = (changed: any) => {
const tableData = crudExpose.getTableData();
const unChanged = tableData.filter((row: any) => !changed.includes(row));
// 添加已选择的行
XEUtils.arrayEach(changed, (item: any) => {
const ids = XEUtils.pluck(selectedRows.value, 'id');
if (!ids.includes(item.id)) {
selectedRows.value = XEUtils.union(selectedRows.value, [item]);
}
});
// 剔除未选择的行
XEUtils.arrayEach(unChanged, (unItem: any) => {
selectedRows.value = XEUtils.remove(selectedRows.value, (item: any) => item.id !== unItem.id);
});
};
const toggleRowSelection = () => {
// 多选后,回显默认勾选
const tableRef = crudExpose.getBaseTableRef();
const tableData = crudExpose.getTableData();
const selected = XEUtils.filter(tableData, (item: any) => {
const ids = XEUtils.pluck(selectedRows.value, 'id');
return ids.includes(item.id);
});
nextTick(() => {
XEUtils.arrayEach(selected, (item) => {
tableRef.toggleRowSelection(item, true);
});
});
};
return { return {
selectedRows,
crudOptions: { crudOptions: {
request: { request: {
pageRequest, pageRequest,
@@ -77,7 +111,22 @@ export const createCrudOptions = function ({ crudExpose, props,modelDialog,selec
width: '600px', width: '600px',
}, },
}, },
table: {
rowKey: 'id', //设置你的主键id 默认rowKey=id
onSelectionChange,
onRefreshed: () => toggleRowSelection(),
},
columns: { columns: {
$checked: {
title: '选择',
form: { show: false },
column: {
type: 'selection',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
},
},
_index: { _index: {
title: '序号', title: '序号',
form: { show: false }, form: { show: false },

View File

@@ -1,137 +1,177 @@
<template> <template>
<div> <div>
<el-dialog ref="modelRef" v-model="modelDialog" title="选择model"> <el-dialog ref="modelRef" v-model="modelDialog" title="选择model">
<div v-show="props.model"> <div v-show="props.model">
<el-tag>已选择:{{ props.model }}</el-tag> <el-tag>已选择:{{ props.model }}</el-tag>
</div> </div>
<!-- 搜索输入框 --> <!-- 搜索输入框 -->
<el-input <el-input v-model="searchQuery" placeholder="搜索模型..." style="margin-bottom: 10px"></el-input>
v-model="searchQuery" <div class="model-card">
placeholder="搜索模型..." <!--注释编号:django-vue3-admin-index483211: 对请求回来的allModelData进行computed计算返加搜索框匹配到的内容-->
style="margin-bottom: 10px;" <div v-for="(item, index) in filteredModelData" :value="item.key" :key="index">
></el-input> <el-text :type="modelCheckIndex === index ? 'primary' : ''" @click="onModelChecked(item, index)">
<div class="model-card"> {{ item.app + '--' + item.title + '(' + item.key + ')' }}
<!--注释编号:django-vue3-admin-index483211: 对请求回来的allModelData进行computed计算返加搜索框匹配到的内容--> </el-text>
<div v-for="(item,index) in filteredModelData" :value="item.key" :key="index"> </div>
<el-text :type="modelCheckIndex===index?'primary':''" @click="onModelChecked(item,index)"> </div>
{{ item.app + '--' + item.title + '(' + item.key + ')' }} <template #footer>
</el-text> <span class="dialog-footer">
</div> <el-button @click="modelDialog = false">取消</el-button>
</div> <el-button type="primary" @click="handleAutomatch"> 确定 </el-button>
<template #footer> </span>
<span class="dialog-footer"> </template>
<el-button @click="modelDialog = false">取消</el-button> </el-dialog>
<el-button type="primary" @click="handleAutomatch"> <div style="height: 72vh">
确定 <fs-crud ref="crudRef" v-bind="crudBinding">
</el-button> <template #pagination-left>
</span> <el-tooltip content="批量删除">
</template> <el-button text type="danger" :disabled="selectedRowsCount === 0" :icon="Delete" circle @click="handleBatchDelete" />
</el-dialog> </el-tooltip>
<div style="height: 80vh"> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> <template #pagination-right>
</fs-crud> <el-popover placement="top" :width="400" trigger="click">
<template #reference>
</div> <el-button text :type="selectedRowsCount > 0 ? 'primary' : ''">已选中{{ selectedRowsCount }}条数据</el-button>
</div> </template>
<el-table :data="selectedRows" size="small">
<el-table-column width="150" property="id" label="id" />
<el-table-column fixed="right" label="操作" min-width="60">
<template #default="scope">
<el-button text type="info" :icon="Close" @click="removeSelectedRows(scope.row)" circle />
</template>
</el-table-column>
</el-table>
</el-popover>
</template>
</fs-crud>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {ref, onMounted, reactive, computed } from 'vue'; import { ref, onMounted, reactive, computed } from 'vue';
import {useFs} from '@fast-crud/fast-crud'; import { useFs } from '@fast-crud/fast-crud';
import {createCrudOptions} from './crud'; import { createCrudOptions } from './crud';
import {getModelList} from './api' import { BatchDelete, getModelList } from './api';
import {MenuTreeItemType} from "/@/views/system/menu/types"; import { Close, Delete } from '@element-plus/icons-vue';
import {successMessage, successNotification, warningNotification} from '/@/utils/message'; import { MenuTreeItemType } from '/@/views/system/menu/types';
import {automatchColumnsData} from '/@/views/system/columns/components/ColumnsTableCom/api'; import { successMessage, successNotification, warningNotification } from '/@/utils/message';
import { automatchColumnsData } from '/@/views/system/columns/components/ColumnsTableCom/api';
import XEUtils from 'xe-utils';
import { ElMessage, ElMessageBox } from 'element-plus';
// 当前选择的菜单信息 // 当前选择的菜单信息
let selectOptions: any = ref({name: null}); let selectOptions: any = ref({ name: null });
const props = reactive({ const props = reactive({
model: '', model: '',
app: '', app: '',
menu: '' menu: '',
}) });
//model弹窗 //model弹窗
const modelDialog = ref(false) const modelDialog = ref(false);
// 获取所有model // 获取所有model
const allModelData = ref<any[]>([]); const allModelData = ref<any[]>([]);
const modelCheckIndex = ref(null) const modelCheckIndex = ref(null);
const onModelChecked = (row, index) => { const onModelChecked = (row, index) => {
modelCheckIndex.value = index modelCheckIndex.value = index;
props.model = row.key props.model = row.key;
props.app = row.app props.app = row.app;
} };
// 注释编号:django-vue3-admin-index083311:代码开始行 // 注释编号:django-vue3-admin-index083311:代码开始行
// 功能说明:搭配搜索的处理,返回搜索结果 // 功能说明:搭配搜索的处理,返回搜索结果
const searchQuery = ref(''); const searchQuery = ref('');
const filteredModelData = computed(() => { const filteredModelData = computed(() => {
if (!searchQuery.value) { if (!searchQuery.value) {
return allModelData.value; return allModelData.value;
} }
const query = searchQuery.value.toLowerCase(); const query = searchQuery.value.toLowerCase();
return allModelData.value.filter(item => return allModelData.value.filter(
item.app.toLowerCase().includes(query) || (item) => item.app.toLowerCase().includes(query) || item.title.toLowerCase().includes(query) || item.key.toLowerCase().includes(query)
item.title.toLowerCase().includes(query) || );
item.key.toLowerCase().includes(query) });
);
});
// 注释编号:django-vue3-admin-index083311:代码结束行 // 注释编号:django-vue3-admin-index083311:代码结束行
/** /**
* 菜单选中时,加载表格数据 * 菜单选中时,加载表格数据
* @param record * @param record
*/ */
const handleRefreshTable = (record: MenuTreeItemType) => { const handleRefreshTable = (record: MenuTreeItemType) => {
if (!record.is_catalog && record.id) { if (!record.is_catalog && record.id) {
selectOptions.value = record; selectOptions.value = record;
crudExpose.doRefresh(); crudExpose.doRefresh();
} else { } else {
//清空表格数据 //清空表格数据
crudExpose.setTableData([]); crudExpose.setTableData([]);
} }
}; };
/** /**
* 自动匹配列 * 自动匹配列
*/ */
const handleAutomatch = async () => { const handleAutomatch = async () => {
props.menu = selectOptions.value.id props.menu = selectOptions.value.id;
modelDialog.value = false modelDialog.value = false;
if (props.menu && props.model) { if (props.menu && props.model) {
const res = await automatchColumnsData(props); const res = await automatchColumnsData(props);
if (res?.code === 2000) { if (res?.code === 2000) {
successNotification('匹配成功'); successNotification('匹配成功');
} }
crudExpose.doSearch({form: {menu: props.menu, model: props.model}}); crudExpose.doSearch({ form: { menu: props.menu, model: props.model } });
}else { } else {
warningNotification('请选择角色和模型表!'); warningNotification('请选择角色和模型表!');
} }
}; };
// 选中行的条数
const {crudBinding, crudRef, crudExpose} = useFs({createCrudOptions, props, modelDialog, selectOptions,allModelData}); const selectedRowsCount = computed(() => {
onMounted(async () => { return selectedRows.value.length;
const res = await getModelList();
allModelData.value = res.data;
}); });
defineExpose({selectOptions, handleRefreshTable}); // 批量删除
const handleBatchDelete = async () => {
await ElMessageBox.confirm(`确定要批量删除这${selectedRows.value.length}条记录吗`, '确认', {
distinguishCancelAndClose: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
});
await BatchDelete(XEUtils.pluck(selectedRows.value, 'id'));
ElMessage.info('删除成功');
selectedRows.value = [];
await crudExpose.doRefresh();
};
// 移除已选中的行
const removeSelectedRows = (row: any) => {
const tableRef = crudExpose.getBaseTableRef();
const tableData = crudExpose.getTableData();
if (XEUtils.pluck(tableData, 'id').includes(row.id)) {
tableRef.toggleRowSelection(row, false);
} else {
selectedRows.value = XEUtils.remove(selectedRows.value, (item: any) => item.id !== row.id);
}
};
const { crudBinding, crudRef, crudExpose, selectedRows } = useFs({ createCrudOptions, props, modelDialog, selectOptions, allModelData });
onMounted(async () => {
const res = await getModelList();
allModelData.value = res.data;
});
defineExpose({ selectOptions, handleRefreshTable });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.model-card { .model-card {
margin-top: 10px; margin-top: 10px;
height: 30vh; height: 30vh;
overflow-y: scroll; overflow-y: scroll;
div { div {
margin: 15px 0; margin: 15px 0;
cursor: pointer; cursor: pointer;
} }
} }
</style> </style>

View File

@@ -16,12 +16,12 @@
<el-col :span="18"> <el-col :span="18">
<el-tabs type="border-card"> <el-tabs type="border-card">
<el-tab-pane label="按钮权限配置" > <el-tab-pane label="按钮权限配置" >
<div style="height: 80vh"> <div style="height: 72vh">
<MenuButtonCom ref="menuButtonRef" /> <MenuButtonCom ref="menuButtonRef" />
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="列权限配置"> <el-tab-pane label="列权限配置">
<div style="height: 80vh"> <div style="height: 72vh">
<MenuFieldCom ref="menuFieldRef"></MenuFieldCom> <MenuFieldCom ref="menuFieldRef"></MenuFieldCom>
</div> </div>
</el-tab-pane> </el-tab-pane>