Bootstrap4 高级开发:企业级后台管理平台的架构设计与落地

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>

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容