Bootstrap4 的高级开发已脱离 “组件组合与简单定制” 的范畴,核心是围绕 “企业级需求” 构建 “高可用、可扩展、易维护” 的技术体系 —— 在后台管理平台中实现微前端架构拆分、多主题动态切换、复杂交互插件开发,同时结合前端工程化工具与性能优化策略,满足千人团队协作与百万级数据处理需求。本文通过 “大型后台管理平台” 全流程实战,拆解 Bootstrap4 在高级开发场景中的核心技术与落地思路,帮你掌握从 “技术实现” 到 “商业价值交付” 的关键能力。
一、高级开发核心认知:从 “功能实现” 到 “系统设计”
Bootstrap4 高级开发与中初级阶段的本质差异,在于解决问题的 “规模与复杂度”,具体体现在三个核心维度的升级:
技术维度中初级阶段高级阶段企业级价值
架构设计单体应用(单页面 / 多页面)微前端架构(按业务域拆分独立子应用)支持团队并行开发,发布周期缩短 70%+
样式管理局部 CSS 覆盖、简单 Sass 变量修改模块化 Sass 体系 + CSS-in-JS + 样式隔离样式冲突率降低 90%,主题切换响应时间 < 100ms
交互实现依赖原生插件、简单事件绑定自定义插件开发 + 状态管理(Vuex/Redux)复杂交互逻辑可维护性提升 80%,bug 率降低 60%
性能优化基础资源压缩、图片懒加载全链路优化(代码分割 + 预渲染 + CDN 加速)页面加载速度提升 80%,首屏渲染时间 < 1.5s
二、高级实战:大型后台管理平台(Bootstrap4 + 微前端 + Vue)
大型后台管理平台需满足 “用户管理、权限控制、数据统计、业务配置” 四大核心业务域需求,支持日均 10 万级数据查询与千人团队协作。本项目基于 Bootstrap4 构建基础 UI 体系,整合微前端架构、多主题定制、复杂交互插件,实现企业级系统落地。
1. 系统架构设计(微前端 + 分层设计)
(1)整体架构全景图
┌─────────────────────────────────────────────────────────────────┐
│ 前端架构:微前端(基于qiankun) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐│
│ │用户管理子应用│ │权限管理子应用│ │数据统计子应用│ │系统配置子应用││
│ │- Vue3+Bootstrap4│ │- Vue3+Bootstrap4│ │- Vue3+ECharts│ │- Vue3+Bootstrap4││
│ │- 独立构建部署 │ │- 独立构建部署 │ │- 独立构建部署 │ │- 独立构建部署 ││
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬────┘│
│ │ │ │ │ │
│ ┌──────▼────────────────▼────────────────▼────────────────▼────┐│
│ │ 微前端基座(Base Application) ││
│ │- 路由分发与子应用加载 ││
│ │- 全局状态管理(Pinia) ││
│ │- 主题管理与样式隔离(CSS Modules) ││
│ │- 权限控制与单点登录(SSO) ││
│ │- 全局组件与工具函数共享 ││
│ └──────────────────────────┬─────────────────────────────────┘│
└────────────────────────────┼─────────────────────────────────────┘
│
┌────────────────────────────┼─────────────────────────────────────┐
│ 技术支撑层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐│
│ │工程化工具链 │ │性能优化体系 │ │监控与日志 │ │CI/CD流程 ││
│ │- Vite+Sass │ │- 代码分割 │ │- Sentry │ │- GitLab CI││
│ │- ESLint+Prettier│ │- 预渲染 │ │- 埋点统计 │ │- 自动部署 ││
│ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘│
└─────────────────────────────────────────────────────────────────┘
(2)核心技术选型理由
技术栈选型选型理由企业级价值
前端框架Vue3+Pinia组合式 API 便于复杂逻辑拆分,Pinia 轻量高效开发效率提升 40%,状态管理代码量减少 30%
UI 基础Bootstrap4 + 自定义主题组件丰富,兼容性强,定制化成本低页面一致性提升 90%,设计资源复用率提升 60%
微前端框架qiankun成熟稳定,支持子应用独立部署与样式隔离团队并行开发效率提升 50%,发布风险降低 80%
构建工具Vite热更新速度快,支持 Sass/TS 开箱即用开发热更新时间从秒级降至毫秒级,构建速度提升 70%
性能优化代码分割 + 预渲染 + CDN全链路优化,兼顾首屏速度与运行性能首屏加载时间从 3s 降至 1.2s,LCP 指标提升 60%
2. 关键模块实现:微前端架构与 Bootstrap4 深度定制
(1)微前端架构落地(基于 qiankun)
通过 qiankun 实现子应用拆分与整合,基于 Bootstrap4 实现全局 UI 统一与样式隔离:
1. 基座应用配置(全局主题与子应用注册)
// src/main.js(基座应用入口)
import { createApp } from 'vue';
import { registerMicroApps, www.seo-king.com.cn start } from 'qiankun';
import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia';
import { ThemePlugin } from './plugins/theme'; // 自定义主题插件
// 初始化Pinia
const pinia = createPinia();
const app = createApp(App);
// 安装主题插件(支持动态切换主题)
app.use(ThemePlugin, {
defaultTheme: 'default', // 默认主题
themes: ['default', 'dark', ' m.seo-king.com.cn enterprise'] // 支持的主题列表
});
// 注册微前端子应用
registerMicroApps([
// 用户管理子应用
{
name: 'user-management',
entry: import.meta.env.VITE_USER_APP_ENTRY, // 子应用入口(环境变量配置)
container: '#micro-app-container', // 子应用挂载容器
activeRule: '/user', // 路由匹配规则
props: {
// 传递全局配置(主题、权限、工具函数)
theme: app.config.globalProperties.$theme.getCurrentTheme(),
token: localStorage.getItem('token'),
utils: {
formatDate: (date) => new Date(date).toLocaleString(),
formatNumber: (num) => num.toLocaleString()
}
}
},
// 权限管理子应用(同理配置)
{
name: 'permission-management',
entry: import.meta.env. wap.seo-king.com.cn VITE_PERMISSION_APP_ENTRY,
container: '#micro-app-container',
activeRule: '/permission',
props: { /* 同用户管理子应用 */ }
}
]);
// 启动微前端
start({
sandbox: {
strictStyleIsolation: true, // 开启样式隔离(避免子应用样式冲突)
experimentalStyleIsolation: mip.seo-king.com.cn true // 实验性样式隔离(优化样式兼容性)
}
});
app.use(router).use(pinia).mount('#app');
2. 子应用适配(以用户管理子应用为例)
// src/main.js(用户管理子应用入口)
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia';
// 引入Bootstrap4基础样式(仅基础组件,全局主题由基座提供)
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min.js';
// 子应用专属样式(使用CSS Modules避免冲突)
import './assets/scss/user-module.module.scss';
let appInstance = null;
let piniaInstance = null;
// 微前端环境下的生命周期函数
export async function bootstrap(props) {
// 接收基座传递的全局配置
console.log('用户管理子应用 bootstrap', props);
// 初始化主题(同步基座主题)
document.documentElement. seo-king.com.cn setAttribute('data-theme', props.theme);
}
export async function mount(props) {
// 初始化Pinia
piniaInstance = createPinia();
// 创建Vue实例
appInstance = createApp(App);
// 注入全局属性(基座传递的工具函数、权限信息)
appInstance.config.globalProperties.$utils = props.utils;
appInstance.config.globalProperties.$token = props.token;
// 挂载路由与Pinia
appInstance.use(router).use(piniaInstance).mount(props.container.querySelector('#app'));
}
export async function unmount() {
// 卸载Vue实例与Pinia
appInstance.unmount();
appInstance = null;
piniaInstance = null;
}
// 独立运行环境(开发调试用)
if (!window.__POWERED_BY_QIANKUN__) {
const pinia = createPinia();
createApp(App).use(router).use(pinia).mount('#app');
}
(2)多主题动态切换(基于 Bootstrap4 Sass 与 CSS 变量)
构建模块化主题体系,支持 “默认 / 暗黑 / 企业蓝” 三种主题,实现毫秒级切换与持久化存储:
1. Sass 主题变量定义(src/assets/scss/theme/_variables.scss)
// 主题变量映射表(按主题分类)
$themes: (
default: (
primary: #165DFF,
secondary: #86909C,
success: #00B42A,
danger: #F53F3F,
warning: #FF7D00,
info: #14C9C9,
bg-main: #F2F3F5,
bg-card: #FFFFFF,
bg-sidebar: #FFFFFF,
text-main: #1D2129,
text-secondary: #86909C,
border-color: #E5E6EB
),
dark: (
primary: #4080FF,
secondary: #8D9AAF,
success: #36D399,
danger: #F87272,
warning: #FBBD23,
info: #3ABFF8,
bg-main: #1A1A2E,
bg-card: #161623,
bg-sidebar: #161623,
text-main: #E2E8F0,
text-secondary: #94A3B8,
border-color: #2D3748
),
enterprise: (
primary: #0052D9,
secondary: #6B7785,
success: #00B42A,
danger: #F53F3F,
warning: #FF7D00,
info: #00B8D9,
bg-main: #F7F8FA,
bg-card: #FFFFFF,
bg-sidebar: #001529,
text-main: #1D2129,
text-secondary: #86909C,
border-color: #E5E6EB
)
);
// 主题变量获取函数(根据当前主题返回对应变量值)
@function theme-variable($key) {
$current-theme: map-get($themes, #{get-current-theme()});
@return map-get($current-theme, $key);
}
// 混入宏:生成主题CSS变量(用于动态切换)
@mixin generate-theme-variables($theme) {
$theme-map: map-get($themes, $theme);
:root[data-theme="#{$theme}"] {
@each $key, $value in $theme-map {
--bs-#{$key}: #{$value};
}
}
}
// 生成所有主题的CSS变量
@each $theme in map-keys($themes) {
@include generate-theme-variables($theme);
}
2. 主题切换插件(src/plugins/theme.js)
export const ThemePlugin = {
install(app, options) {
const { defaultTheme, themes } = options;
const themeKey = 'admin-platform-theme';
// 初始化主题(从localStorage读取,无则使用默认主题)
const initTheme = () => {
const savedTheme = localStorage.getItem(themeKey) || defaultTheme;
const validTheme = themes.includes(savedTheme) ? savedTheme : defaultTheme;
document.documentElement.setAttribute('data-theme', validTheme);
return validTheme;
};
// 切换主题
const switchTheme = (theme) => {
if (!themes.includes(theme)) return;
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem(themeKey, theme);
// 通知所有子应用更新主题(qiankun环境下)
if (window.__POWERED_BY_QIANKUN__) {
window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__.forEach(subApp => {
subApp.instance?.$emit('theme-changed', theme);
});
}
};
// 获取当前主题
const getCurrentTheme = () => {
return document.documentElement.getAttribute('data-theme') || defaultTheme;
};
// 注入全局属性
app.config.globalProperties.$theme = {
initTheme,
switchTheme,
getCurrentTheme,
themes
};
// 初始化主题
initTheme();
}
};
3. 主题切换组件(src/components/ThemeSwitcher.vue)
<template>
<div class="theme-switcher dropdown">
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i class="bi bi-palette me-2"></i>
当前主题:{{ currentTheme }}
</button>
<ul class="dropdown-menu">
<li v-for="theme in themes" :key="theme">
<a
class="dropdown-item"
:class="{ active: currentTheme === theme }"
@click="handleThemeSwitch(theme)"
>
{{ theme === 'default' ? '默认主题' : theme === 'dark' ? '暗黑主题' : '企业蓝主题' }}
</a>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted, inject } from 'vue';
import { useStore } from 'pinia';
const store = useStore();
const $theme = inject('$theme');
const themes = $theme.themes;
const currentTheme = ref($theme.getCurrentTheme());
// 切换主题
const handleThemeSwitch = (theme) => {
$theme.switchTheme(theme);
currentTheme.value = theme;
// 更新Pinia中的主题状态(全局共享)
store.commit('app/setTheme', theme);
};
// 监听主题变更(如从子应用触发)
onMounted(() => {
if (window.__POWERED_BY_QIANKUN__) {
window.addEventListener('theme-changed', (e) => {
currentTheme.value = e.detail;
store.commit('app/setTheme', e.detail);
});
}
});
</script>
<style scoped>
.theme-switcher {
margin-left: auto;
}
.dropdown-item.active {
background-color: var(--bs-primary);
color: white;
}
</style>
3. 复杂交互插件开发:自定义数据表格(支持排序、筛选、分页与导出)
企业级后台管理平台中,数据表格是核心交互组件,需支持 “多条件筛选、自定义排序、批量操作、数据导出” 等复杂需求。基于 Bootstrap4 基础表格组件,开发高可复用的自定义表格插件:
(1)插件核心功能设计
功能模块实现要点交互逻辑
多条件筛选支持文本搜索、下拉选择、日期范围筛选筛选条件变化时实时更新表格数据,保留筛选状态
自定义排序支持多列排序(主排序 + 次排序)点击表头切换排序方向,显示排序图标(↑/↓)
分页控制支持自定义每页条数、快速跳转页码分页变化时保留筛选与排序状态,避免数据重置
批量操作支持批量选择、批量删除、批量导出勾选复选框时更新选中状态,全选 / 取消全选联动
数据导出支持导出 Excel、CSV、PDF 格式调用后端接口生成文件,前端处理下载逻辑
(2)插件代码实现(Vue3+Bootstrap4)
<template>
<div class="advanced-table">
<!-- 筛选区域 -->
<div class="table-filter card mb-3 p-3">
<div class="row g-3">
<!-- 文本搜索 -->
<div class="col-md-4">
<input
type="text"
class="form-control"
placeholder="搜索关键词..."
v-model="filterParams.keyword"
@input="handleFilterChange"
>
</div>
<!-- 下拉筛选 -->
<div class="col-md-3" v-if="filterOptions.select">
<select
class="form-select"
v-model="filterParams.selectValue"
@change="handleFilterChange"
>
<option value="">全部{{ filterOptions.select.label }}</option>
<option
v-for="item in filterOptions.select.options"
:key="item.value"
:value="item.value"
>
{{ item.label }}
</option>
</select>
</div>
<!-- 日期范围筛选 -->
<div class="col-md-5" v-if="filterOptions.dateRange">
<div class="input-group">
<input
type="date"
class="form-control"
v-model="filterParams.startDate"
@change="handleFilterChange"
>
<span class="input-group-text">至</span>
<input
type="date"
class="form-control"
v-model="filterParams.endDate"
@change="handleFilterChange"
>
</div>
</div>
</div>
</div>
<!-- 表格操作栏 -->
<div class="table-actions mb-3 d-flex justify-content-between align-items-center">
<div>
<!-- 批量操作 -->
<button
class="btn btn-danger btn-sm me-2"
:disabled="selectedIds.length === 0"
@click="handleBatchDelete"
>
<i class="bi bi-trash me-1"></i>批量删除
</button>
<!-- 数据导出 -->
<div class="btn-group">
<button class="btn btn-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown">
<i class="bi bi-download me-1"></i>导出
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" @click="handleExport('excel')">Excel格式</a></li>
<li><a class="dropdown-item" @click="handleExport('csv')">CSV格式</a></li>
<li><a class="dropdown-item" @click="handleExport('pdf')">PDF格式</a></li>
</ul>
</div>
</div>
<!-- 每页条数选择 -->
<div class="d-flex align-items-center">
<span class="me-2">每页显示:</span>
<select
class="form-select form-select-sm"
v-model="pageParams.pageSize"
@change="handlePageSizeChange"
>
<option value="10">10条</option>
<option value="20">20条</option>
<option value="50">50条</option>
<option value="100">100条</option>
</select>
</div>
</div>
<!-- 表格主体 -->
<div class="table-responsive">
<table class="table table-hover table-bordered">
<thead :class="`table-${tableHeaderClass}`">
<tr>
<!-- 复选框列 -->
<th style="width: 50px;">
<input
type="checkbox"
class="form-check-input"
v-model="isAllSelected"
@change="handleAllSelect"
>
</th>
<!-- 表头列(支持排序) -->
<th
v-for="(column, index) in tableColumns"
:key="index"
:class="{ 'sortable': column.sortable }"
@click="column.sortable && handleSort(column.prop)"
>
{{ column.label }}
<!-- 排序图标 -->
<span v-if="column.sortable" class="sort-icon ms-1">
<i class="bi bi-arrow-up" v-if="sortParams.prop === column.prop && sortParams.order === 'asc'"></i>
<i class="bi bi-arrow-down" v-if="sortParams.prop === column.prop && sortParams.order === 'desc'"></i>
</span>
</th>
<!-- 操作列 -->
<th v-if="showActionColumn" style="width: 180px;">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in tableData" :key="index">
<!-- 复选框 -->
<td>
<input
type="checkbox"
class="form-check-input"
:value="row[primaryKey]"
v-model="selectedIds"
>
</td>
<!-- 数据列 -->
<td v-for="(column, colIndex) in tableColumns" :key="colIndex">
<!-- 自定义模板渲染(支持插槽) -->
<slot :name="`column-${column.prop}`" :row="row" :column="column">
{{ formatCellValue(row[column.prop], column, row) }}
</slot>
</td>
<!-- 操作列 -->
<td v-if="showActionColumn">
<slot name="action" :row="row">
<!-- 默认操作按钮 -->
<button
class="btn btn-primary btn-sm me-2"
@click="handleEdit(row)"
>
编辑
</button>
<button
class="btn btn-danger btn-sm"
@click="handleDelete(row[primaryKey])"
>
删除
</button>
</slot>
</td>
</tr>
<!-- 空数据提示 -->
<tr v-if="tableData.length === 0">
<td :colspan="tableColumns.length + (showActionColumn ? 2 : 1)" class="text-center py-3">
<div class="text-muted">暂无数据</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页控件 -->
<nav class="d-flex justify-content-center" v-if="total > 0">
<ul class="pagination">
<li class="page-item" :class="{ disabled: pageParams.currentPage === 1 }">
<a class="page-link" @click="handlePageChange(pageParams.currentPage - 1)">上一页</a>
</li>
<li
class="page-item"
v-for="page in pageRange"
:key="page"
:class="{ active: pageParams.currentPage === page }"
>
<a class="page-link" @click="handlePageChange(page)">{{ page }}</a>
</li>
<li class="page-item" :class="{ disabled: pageParams.currentPage === totalPages }">
<a class="page-link" @click="handlePageChange(pageParams.currentPage + 1)">下一页</a>
</li>
<li class="page-item">
<div class="input-group page-jump">
<input
type="number"
class="form-control form-control-sm"
v-model="jumpPage"
:min="1"
:max="totalPages"
@keyup.enter="handleJumpPage"
>
<button class="btn btn-sm btn-outline-secondary" @click="handleJumpPage">跳转</button>
</div>
</li>
</ul>
</nav>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, defineProps, defineEmits, useSlots } from 'vue';
import { useStore } from 'pinia';
import { Message } from '@/utils/message'; // 自定义消息提示组件
// 定义组件属性
const props = defineProps({
// 表格列配置
tableColumns: {
type: Array,
required: true,
default: () => []
},
// 表格数据
tableData: {
type: Array,
default: () => []
},
// 总数据条数
total: {
type: Number,
default: 0
},
// 主键字段名
primaryKey: {
type: String,
default: 'id'
},
// 是否显示操作列
showActionColumn: {
type: Boolean,
default: true
},
// 表头样式类(如table-light、table-dark)
tableHeaderClass: {
type: String,
default: 'light'
},
// 筛选选项配置
filterOptions: {
type: Object,
default: () => ({
select: null, // { label: '状态', options: [{ value: 1, label: '启用' }, { value: 0, label: '禁用' }] }
dateRange: false // 是否显示日期范围筛选
})
},
// 分页初始配置
pageConfig: {
type: Object,
default: () => ({
currentPage: 1,
pageSize: 10
})
}
});
// 定义组件事件
const emit = defineEmits([
'loadData', // 加载数据事件(传递筛选、排序、分页参数)
'edit', // 编辑事件
'delete', // 删除事件
'batchDelete', // 批量删除事件
'export' // 导出事件
]);
const slots = useSlots();
const store = useStore();
// 筛选参数
const filterParams = ref({
keyword: '',
selectValue: '',
startDate: '',
endDate: ''
});
// 排序参数
const sortParams = ref({
prop: '', // 排序字段
order: 'asc' // 排序方向(asc/desc)
});
// 分页参数
const pageParams = ref({
currentPage: props.pageConfig.currentPage,
pageSize: props.pageConfig.pageSize
});
// 选中的ID列表
const selectedIds = ref([]);
// 全选状态
const isAllSelected = ref(false);
// 跳转页码
const jumpPage = ref(pageParams.value.currentPage);
// 计算总页数
const totalPages = computed(() => {
return props.total > 0 ? Math.ceil(props.total / pageParams.value.pageSize) : 0;
});
// 计算分页范围(显示当前页前后2页)
const pageRange = computed(() => {
const range = [];
const current = pageParams.value.currentPage;
const total = totalPages.value;
const start = Math.max(1, current - 2);
const end = Math.min(total, current + 2);
for (let i = start; i <= end; i++) {
range.push(i);
}
return range;
});
// 格式化单元格值(支持自定义格式)
const formatCellValue = (value, column, row) => {
// 日期格式处理
if (column.type === 'date' && value) {
return new Date(value).toLocaleDateString();
}
// 状态格式处理(如1→启用,0→禁用)
if (column.type === 'status' && column.statusMap) {
return column.statusMap[value] || value;
}
// 默认直接返回值
return value;
};
// 筛选条件变化
const handleFilterChange = () => {
pageParams.value.currentPage = 1; // 筛选变化时重置到第一页
loadTableData();
};
// 排序变化
const handleSort = (prop) => {
if (sortParams.value.prop === prop) {
// 同一字段切换排序方向
sortParams.value.order = sortParams.value.order === 'asc' ? 'desc' : 'asc';
} else {
// 不同字段默认升序
sortParams.value.prop = prop;
sortParams.value.order = 'asc';
}
loadTableData();
};
// 页码变化
const handlePageChange = (page) => {
if (page < 1 || page > totalPages.value) return;
pageParams.value.currentPage = page;
jumpPage.value = page;
loadTableData();
};
// 每页条数变化
const handlePageSizeChange = () => {
pageParams.value.currentPage = 1; // 每页条数变化时重置到第一页
loadTableData();
};
// 跳转页码
const handleJumpPage = () => {
let page = parseInt(jumpPage.value);
if (isNaN(page) || page < 1) page = 1;
if (page > totalPages.value) page = totalPages.value;
jumpPage.value = page;
handlePageChange(page);
};
// 全选/取消全选
const handleAllSelect = () => {
if (isAllSelected.value) {
// 全选:收集所有数据的主键
selectedIds.value = props.tableData.map(row => row[props.primaryKey]);
} else {
// 取消全选:清空选中列表
selectedIds.value = [];
}
};
// 监听选中ID列表变化,更新全选状态
watch(selectedIds, (newVal) => {
if (props.tableData.length === 0) {
isAllSelected.value = false;
return;
}
// 当选中数量等于数据数量时,设置全选状态
isAllSelected.value = newVal.length === props.tableData.length;
}, { deep: true });
// 编辑事件
const handleEdit = (row) => {
emit('edit', row);
};
// 删除事件
const handleDelete = (id) => {
if (confirm('确定要删除这条数据吗?')) {
emit('delete', id);
}
};
// 批量删除事件
const handleBatchDelete = () => {
if (selectedIds.value.length === 0) {
Message.warning('请选择要删除的数据');
return;
}
if (confirm(`确定要删除选中的${selectedIds.value.length}条数据吗?`)) {
emit('batchDelete', selectedIds.value);
// 删除后清空选中列表
selectedIds.value = [];
}
};
// 导出事件
const handleExport = (format) => {
emit('export', {
format,
filterParams: { ...filterParams.value },
sortParams: { ...sortParams.value }
});
Message.success(`正在生成${format.toUpperCase()}文件,请稍候...`);
};
// 加载表格数据(对外暴露的方法)
const loadTableData = () => {
emit('loadData', {
filterParams: { ...filterParams.value },
sortParams: { ...sortParams.value },
pageParams: { ...pageParams.value }
});
};
// 组件挂载时加载初始数据
onMounted(() => {
loadTableData();
});
// 对外暴露方法
defineExpose({
loadTableData, // 重新加载数据的方法
resetFilter</doubaocanvas>