【后台管理系统】-【组件封装】

目录

  • 组件封装
    • 搜索组件
    • table列表组件
    • content组件自定义插槽定制
    • modal组件
    • 动态获取options数据

组件封装

搜索组件

给页面写一个配置文件,将配置文件传入组件,可直接生成页面,以下面页面为例,
请添加图片描述
新建src/views/main/system/department/config/search.config.ts:

const searchConfig = {
  formItems: [
    {
      type: 'input',// el组件类型
      prop: 'name',
      label: '部门名称',
      placeholder: '请输入查询的部门名称',
      initialValue: 'bbb'// 默认值
    },
    {
      type: 'input',
      prop: 'leader',
      label: '部门领导',
      placeholder: '请输入查询的领导名称'
    },
    {
      type: 'date-picker',
      prop: 'createAt',
      label: '创建时间'
    }
  ]
}

export default searchConfig

在department.vue文件中引入配置文件并传给search子组件:

<page-search
      :search-config="searchConfig"
      @query-click="handleQueryClick"
      @reset-click="handleResetClick"
/>
<script>
import contentConfig from './config/content.config'
</script>

search子组件使用defineProps接收配置文件中的数据,并遍历展示

<template>
  <div class="search">
    <!-- 1.输入搜索关键字的表单 -->
    <el-form
      :model="searchForm"
      ref="formRef"
      :label-width="searchConfig.labelWidth ?? '80px'"
      size="large"
    >
      <el-row :gutter="20">
        <template v-for="item in searchConfig.formItems" :key="item.prop">
        <!-- span的值也可以配置 -->
          <el-col :span="8">
          <!-- :label="item.label" :prop="item.prop"可以写成v-bind="item"来绑定所有属性 -->
            <el-form-item :label="item.label" :prop="item.prop">
            <!-- 也可以用动态组件 -->
            <!-- <component :is="‘el-${item.type}’" xxx></component>-->
              <template v-if="item.type === 'input'">
                <el-input
                  v-model="searchForm[item.prop]"
                  :placeholder="item.placeholder"
                />
              </template>
              <template v-if="item.type === 'date-picker'">
                <el-date-picker
                  v-model="searchForm[item.prop]"
                  type="daterange"
                  range-separator="-"
                  start-placeholder="开始时间"
                  end-placeholder="结束时间"
                />
              </template>
              <template v-if="item.type === 'select'">
                <el-select
                  v-model="searchForm[item.prop]"
                  :placeholder="item.placeholder"
                  style="width: 100%"
                >
                  <template v-for="option in item.options" :key="option.value">
                    <el-option :label="option.label" :value="option.value" />
                  </template>
                </el-select>
              </template>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
    </el-form>

    <!-- 2.重置和搜索的按钮 -->
    <div class="btns">
      <el-button icon="Refresh" @click="handleResetClick">重置</el-button>
      <el-button icon="Search" type="primary" @click="handleQueryClick"
        >查询</el-button
      >
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref } from 'vue'
import type { ElForm } from 'element-plus'

// 定义自定义事件/接收的属性,从伏组件
interface IProps {
  searchConfig: {
    labelWidth?: string
    formItems: any[]
  }
}
const emit = defineEmits(['queryClick', 'resetClick'])
const props = defineProps<IProps>()

// 定义form的数据
const initialForm: any = {}
for (const item of props.searchConfig.formItems) {
  initialForm[item.prop] = item.initialValue ?? ''
}
const searchForm = reactive(initialForm)

// 重置操作
const formRef = ref<InstanceType<typeof ElForm>>()
function handleResetClick() {
  // 1.form中的数据全部重置
  formRef.value?.resetFields()

  // 2.将事件出去, content内部重新发送网络请求
  emit('resetClick')
}

function handleQueryClick() {
  emit('queryClick', searchForm)
}
</script>

<style lang="less" scoped>
.search {
  background-color: #fff;
  padding: 20px;

  .el-form-item {
    padding: 20px 30px;
    margin-bottom: 0;
  }

  .btns {
    text-align: right;
    padding: 0 50px 10px 0;

    .el-button {
      height: 36px;
    }
  }
}
</style>

table列表组件

请添加图片描述
创建src/views/main/system/department/config/content.config.ts文件,将列表标题和按钮标题写在header里,将table列表展示的列信息写在propsList中

const contentConfig = {
  pageName: 'department',
  header: {
    title: '部门列表',
    btnTitle: '新建部门'
  },
  propsList: [
    // 1.selection 2.index
    { type: 'selection', label: '选择', width: '80px' },
    { type: 'index', label: '序号', width: '80px' },
    { type: 'normal', label: '部门名称', prop: 'name', width: '150px' },
    { type: 'normal', label: '部门领导', prop: 'leader', width: '150px' },
    { type: 'normal', label: '上级部门', prop: 'parentId', width: '150px' },
    { type: 'timer', label: '创建时间', prop: 'createAt' },
    { type: 'timer', label: '更新时间', prop: 'updateAt' },
    { type: 'handler', label: '操作', width: '150px' }
  ]
}

export default contentConfig

在department.vue文件中引入配置文件并传给page-content子组件(代码参考前面的,此处不在赘述),page-content子组件使用defineProps接收并遍历展示

<template>
  <div class="content">
    <div class="header">
      <h3 class="title">{{ contentConfig?.header?.title ?? '数据列表' }}</h3>
      <el-button type="primary" @click="handleNewUserClick">
        {{ contentConfig?.header?.btnTitle ?? '新建数据' }}
      </el-button>
    </div>
    <div class="table">
      <el-table
        :data="pageList"
        border
        style="width: 100%"
        v-bind="contentConfig.childrenTree"
      >
        <template v-for="item in contentConfig.propsList" :key="item.prop">
          <template v-if="item.type === 'timer'">
            <el-table-column align="center" v-bind="item">
              <template #default="scope">
                {{ formatUTC(scope.row[item.prop]) }}
              </template>
            </el-table-column>
          </template>
          <template v-else-if="item.type === 'handler'">
            <el-table-column align="center" v-bind="item">
              <template #default="scope">
                <el-button
                  size="small"
                  icon="Edit"
                  type="primary"
                  text
                  @click="handleEditBtnClick(scope.row)"
                >
                  编辑
                </el-button>
                <el-button
                  size="small"
                  icon="Delete"
                  type="danger"
                  text
                  @click="handleDeleteBtnClick(scope.row.id)"
                >
                  删除
                </el-button>
              </template>
            </el-table-column>
          </template>
          <template v-else-if="item.type === 'custom'">
            <el-table-column align="center" v-bind="item">
              <template #default="scope">
                <slot
                  :name="item.slotName"
                  v-bind="scope"
                  :prop="item.prop"
                  hName="why"
                ></slot>
              </template>
            </el-table-column>
          </template>
          <template v-else>
            <el-table-column align="center" v-bind="item" />
          </template>
        </template>
      </el-table>
    </div>
    <div class="pagination">
      <el-pagination
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :page-sizes="[10, 20, 30]"
        layout="total, sizes, prev, pager, next, jumper"
        :total="pageTotalCount"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia'
import useSystemStore from '@/store/main/system/system'
import { formatUTC } from '@/utils/format'
import { ref } from 'vue'

interface IProps {
  contentConfig: {
    pageName: string
    header?: {
      title?: string
      btnTitle?: string
    }
    propsList: any[]
    childrenTree?: any
  }
}

const props = defineProps<IProps>()

// 定义事件
const emit = defineEmits(['newClick', 'editClick'])

// 1.发起action,请求usersList的数据
const systemStore = useSystemStore()
const currentPage = ref(1)
const pageSize = ref(10)
fetchPageListData()

// 2.获取usersList数据,进行展示
const { pageList, pageTotalCount } = storeToRefs(systemStore)

// 3.页码相关的逻辑
function handleSizeChange() {
  fetchPageListData()
}
function handleCurrentChange() {
  fetchPageListData()
}

// 4.定义函数, 用于发送网络请求
function fetchPageListData(formData: any = {}) {
  // 1.获取offset/size
  const size = pageSize.value
  const offset = (currentPage.value - 1) * size
  const pageInfo = { size, offset }

  // 2.发起网络请求
  const queryInfo = { ...pageInfo, ...formData }
  systemStore.postPageListAction(props.contentConfig.pageName, queryInfo)
}

// 5.删除/新建/编辑的操作
function handleDeleteBtnClick(id: number) {
  systemStore.deletePageByIdAction(props.contentConfig.pageName, id)
}
function handleNewUserClick() {
  emit('newClick')
}
function handleEditBtnClick(itemData: any) {
  emit('editClick', itemData)
}

defineExpose({ fetchPageListData })
</script>

<style lang="less" scoped>
.content {
  margin-top: 20px;
  padding: 20px;
  background-color: #fff;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  margin-bottom: 10px;

  .title {
    font-size: 22px;
  }
}

.table {
  :deep(.el-table__cell) {
    padding: 12px 0;
  }

  .el-button {
    margin-left: 0;
    padding: 5px 8px;
  }
}

.pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}
</style>

content组件自定义插槽定制

目前的设计不支持定制化,就是在子组件中,对propsList的类型判断不够全,确实也无法判断全,因为就算把所有的el组件都判断一遍,也会有用户自定义组件,所以本节讨论如何对table列表的某一列进行定制化。在content.config.ts中添加自定义的内容:

const contentConfig = {
  pageName: 'department',
  header: {
    title: '部门列表',
    btnTitle: '新建部门'
  },
  propsList: [
    // 1.selection 2.index
    { type: 'selection', label: '选择', width: '80px' },
    { type: 'index', label: '序号', width: '80px' },

    { type: 'normal', label: '部门名称', prop: 'name', width: '150px' },
    { type: 'normal', label: '部门领导', prop: 'leader', width: '150px' },
    { type: 'normal', label: '上级部门', prop: 'parentId', width: '150px' },

    {
      type: 'custom',
      label: '部门领导',
      prop: 'leader',
      width: '150px',
      slotName: 'leader'
    },
    {
      type: 'custom',
      label: '上级部门',
      prop: 'parentId',
      width: '150px',
      slotName: 'parent'
    },

    { type: 'timer', label: '创建时间', prop: 'createAt' },
    { type: 'timer', label: '更新时间', prop: 'updateAt' },

    { type: 'handler', label: '操作', width: '150px' }
  ]
}

export default contentConfig

在page-content子组件遍历propsList的时候,增加对custom类型的判断,既然要自定义,那肯定得使用el-table-column的默认插槽插入自定义的内容,但是这个自定义的东西是不确定的(不固定的),所以在el-table-column的默认插槽中再插入一个插槽,可能表格中有很多可以自定义的列,就会有多个插槽,所以这个插槽得是具名插槽,不然在使用的时候根本不知道数据要插入哪个插槽,且具名插槽的名字由配置来决定:

<template v-else-if="item.type === 'custom'">
            <el-table-column align="center" v-bind="item">
              <template #default="scope">
                <slot
                  :name="item.slotName"
                ></slot>
              </template>
            </el-table-column>
          </template>

父组件使用:

  1. contentConfig是引入的content.config.ts配置文件
  2. 通过<template #leader>制定插入的内容
<page-content
      :content-config="contentConfig"
      ref="contentRef"
      @new-click="handleNewClick"
      @edit-click="handleEditClick"
    >
      <template #leader>哈哈哈</template>
      <template #parent>呵呵呵</span></template>
    </page-content>

但是现在父组件传入的东西只能是写死的,父组件如果想根据这行数据来插入内容应该怎么办?在page-content子组件中将scope传给插槽,父组件使用插槽时接收数据。page-content子组件代码:

<template v-else-if="item.type === 'custom'">
            <el-table-column align="center" v-bind="item">
              <template #default="scope">
                <slot
                  :name="item.slotName"
                  v-bind="scope"
                ></slot>
              </template>
            </el-table-column>
          </template>

父组件接收数据:#leader="scope",并且可以通过添加class的方式,给这一列添加样式。

<page-content
      :content-config="contentConfig"
      ref="contentRef"
      @new-click="handleNewClick"
      @edit-click="handleEditClick"
    >
      <template #leader="scope">
        <span class="leader">哈哈哈: {{ scope.row.leader }}</span>
      </template>
      <template #parent="scope">
        <span class="parent">呵呵呵: {{ scope.row.parent }}</span>
      </template>
    </page-content>

<style scoped>
.leader {
  color: red;
}

.parent {
  color: blue;
}
</style>

如果直接写scope.row.leader,这个leader也是写死的,如果希望从配置中读取,取prop的值,那么page-content子组件需要将prop数据传给插槽(类似于父组件给子组件传值),代码如下:

<template v-else-if="item.type === 'custom'">
            <el-table-column align="center" v-bind="item">
              <template #default="scope">
                <slot
                  :name="item.slotName"
                  v-bind="scope"
                  :prop="item.prop"
                ></slot>
              </template>
            </el-table-column>
          </template>

父组件在使用插槽时,直接从传过来的scope.prop中获取,因为传过来的所有东西都在scope上面,而item.prop是传到了prop上,所以通过scope.prop获取

<page-content
      :content-config="contentConfig"
      ref="contentRef"
      @new-click="handleNewClick"
      @edit-click="handleEditClick"
    >
      <template #leader="scope">
        <span class="leader">哈哈哈: {{ scope.row[scope.prop] }}</span>
      </template>
      <template #parent="scope">
        <span class="parent">呵呵呵: {{ scope.row[scope.prop] }}</span>
      </template>
    </page-content>

modal组件

const initialData: any = {}
for (const item of props.modalConfig.formItems) {
  initialData[item.prop] = item.initialValue ?? ''
}
const formData = reactive<any>(initialData)

如果像以前一样,在定义formData的时候就给他设置默认值,默认值是设置不上去的,这是因为一开始modal组件是不展示的,所以在modal弹出来之后再通过遍历modalConfig.formItems来设置默认值

function setModalVisible(isNew: boolean = true, itemData?: any) {
  dialogVisible.value = true
  isNewRef.value = isNew
  if (!isNew && itemData) {
    // 编辑数据
    for (const key in formData) {
      formData[key] = itemData[key]
    }
    editData.value = itemData
  } else {
    // 新建数据
    for (const key in formData) {
      const item = props.modalConfig.formItems.find((item) => item.prop === key)
      formData[key] = item ? item.initialValue : ''
    }
    editData.value = null
  }
}

动态获取options数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/932102.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

「嵌入式系统设计与实现」书评:学习一个STM32的案例

本文最早发表于电子发烧友论坛&#xff1a;【新提醒】【「嵌入式系统设计与实现」阅读体验】 学习一个STM32的案例 - 发烧友官方/活动 - 电子技术论坛 - 广受欢迎的专业电子论坛!https://bbs.elecfans.com/jishu_2467617_1_1.html 感谢电子发烧友论坛和电子工业出版社的赠书。 …

设计模式:20、状态模式(状态对象)

目录 0、定义 1、状态模式的三种角色 2、状态模式的UML类图 3、示例代码 0、定义 允许一个对象在其内部状态改变时改变它的行为&#xff0c;对象看起来似乎修改了它的类。 1、状态模式的三种角色 环境&#xff08;Context&#xff09;&#xff1a;环境是一个类&#xff0…

idea中新建一个空项目

目的&#xff0c;为了在同一个目录下有多个小的项目&#xff1a;使用IDE为idea2022。 步骤&#xff1a; 点击新建项目&#xff0c;点击创建空项目&#xff0c;这里选择空项目是将其作为其他项目的一个容器&#xff0c;如图所示&#xff1a; 然后点击文件->项目结构&#xf…

Java基础复习

“任何时候我也不会满足&#xff0c;越是多读书&#xff0c;就越是深刻地感到不满足&#xff0c;越感到自己知识贫乏。科学是奥妙无穷的。” ——马克思 目录 一、方法&方法重载 二、运算符 三、数据类型 四、面向对象 1. 面向对象思想 2. 引用传递 3. 访问权限修饰…

嵌入式里的“移植”概念

这里因为最近一年看到公司某项目很多代码上有直接硬件的操作&#xff0c;这里有感而发&#xff0c;介绍移植的概念。 一、硬件 先上一个图&#xff1a; 举个例子&#xff0c;大学里应该都买过开发板&#xff0c;例如st的&#xff0c;这里三个层次&#xff0c; 内核&#xff…

量子计算与商业转型之旅

近年来&#xff0c;各组织对量子计算的应用有所增加&#xff0c;并使全球商业运营发生了显著变化。《财富商业洞察》的一份报告显示&#xff0c;2022 年量子计算市场价值为 7.17 亿美元&#xff0c;预计到 2030 年将达到 65.28 亿美元。 从本质上讲&#xff0c;量子计算机与经…

周末和男朋友户外运动美好时光

周末的时光总是那么令人期待&#xff0c;仿佛一周的忙碌和疲惫都在这一刻得到了释放。这次&#xff0c;我和男朋友决定到户外去锻炼身体&#xff0c;享受一下大自然的馈赠。 清晨的阳光透过窗帘的缝隙洒进房间&#xff0c;我懒洋洋地睁开眼睛&#xff0c;看到男朋友已经在一旁整…

【Git】:标签管理

目录 理解标签 创建标签 操作标签 理解标签 标签的作用 标记版本&#xff1a;标签 tag &#xff0c;可以简单的理解为是对某次 commit 的⼀个标识&#xff0c;相当于起了⼀个别名。例如&#xff0c;在项目发布某个版本的时候&#xff0c;针对最后⼀次 commit 起⼀个 v1.0 这样…

QT的ui界面显示不全问题(适应高分辨率屏幕)

//自动适应高分辨率 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);一、问题 电脑分辨率高&#xff0c;默认情况下&#xff0c;打开QT的ui界面&#xff0c;显示不全按钮内容 二、解决方案 如果自己的电脑分辨率较高&#xff0c;可以尝试以下方案&#xff1a;自…

【Elasticsearch】初始化默认字段及分词

1、添加分词插件 1&#xff09;在线安装 执行命令 需要指定相同的版本 bin/elasticsearch-plugin.bat install https://get.infini.cloud/elasticsearch/analysis-ik/7.17.24 2&#xff09;离线安装 将安装包解压到 /plugins 目录下 安装包可以从对应的资源处下载 启动成…

MATLAB直流电机模型,直流电机控制

直流电机控制简介 直流电机&#xff08;DC motor&#xff09;广泛应用于各种机械驱动和电力控制系统中&#xff0c;其运行性能的控制至关重要。为了精准地控制直流电机的输出特性&#xff0c;可以通过不同的控制方式进行调节。常见的控制方式包括电枢电流控制、速度控制、电机位…

Linux之封装线程库和线程的互斥

Linux之封装线程库和线程的互斥与同步 一.封装线程库二.线程的互斥2.1互斥量的概念2.2初始化和销毁互斥量2.3加锁和解锁2.4互斥量的原理2.5可重入和线程安全2.6死锁 一.封装线程库 其实在我们C内部也有一个线程库而C中的线程库也是封装的原生线程库的函数&#xff0c;所以我们…

PHP语法学习(第九天)—PHP连接mysql详解(下)

首先&#xff0c;温馨提示&#xff0c;该部分内容跟昨天“PHP语法学习(第八天)—PHP连接mysql详解(上)”一起食用更佳噢&#xff01;&#xff01; 学习本篇内容必须掌握数据库基础命令点击“MYSQL 数据库”~~ 本文是接着PHP连接mysql的知识点接着讲&#xff0c;今天主要讲述PHP…

qt基本部分控件用法(一)

前言: 以前 windows下做工具主要是MFC&#xff0c;趁有点空时间&#xff0c;研究了QT&#xff0c;感觉跟MFC 差不多&#xff0c;VS 比 QT CREATOR 还是强大&#xff0c;不过QT可以跨平台&#xff0c;功能更强大&#xff0c;MFC 只能在win平台下.&#xff1b; 1&#xff1a;环境…

Mysql索引,聚簇索引,非聚簇索引,回表查询

什么是索引 数据库索引是为了实现高效数据查询的一种有序的数据数据结构&#xff0c;类似于书的目录&#xff0c;通过目录可以快速的定位到想要的数据&#xff0c;因为一张表中的数据会有很多&#xff0c;如果直接去表中检索数据效率会很低&#xff0c;所以需要为表中的数据建立…

以MP6924A为核心的LLC拓扑学习【一】

PFCLLC: 在PFC&#xff08;功率因数校正&#xff09;和LLC&#xff08;谐振变换器&#xff09;组成的电源系统中&#xff0c;各个电路有特定的作用&#xff0c;它们协同工作以实现高效率和高功率因数的电能转换。 1. PFC&#xff08;功率因数校正&#xff09;电路的作用 PFC电…

️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南20241206

&#x1f6e0;️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南 &#x1f4dd; 引言 随着大语言模型&#xff08;LLM&#xff09;和人工智能的飞速发展&#xff0c;越来越多的开发者尝试在本地环境中部署大模型进行实验。然而&#xff0c;由于资源需求高、网络限制多…

1-1 ESP32开发环境配置

前言&#xff1a; 基于Arduio配置ESP32开发环境... 目录 前言&#xff1a; 1.0 安装Python 2.0 安装VSCode 3.0 VSCode实用插件 4.0 替换VSCode配置&#xff08;可选&#xff09; 后记 1.0 安装Python 在windows操作系统的搜索框中搜索Microsoft Store 点击获取 安装完成…

【k8s 深入学习之 event 聚合】event count累记聚合(采用 Patch),Message 聚合形成聚合 event(采用Create)

参考 15.深入k8s:Event事件处理及其源码分析 - luozhiyun - 博客园event 模块总览 EventRecorder:是事件生成者,k8s组件通过调用它的方法来生成事件;EventBroadcaster:事件广播器,负责消费EventRecorder产生的事件,然后分发给broadcasterWatcher;broadcasterWatcher:用…

AURIX TC3xx学习笔记2 GTM模块

文章目录 引言功能改进一些缩写 功能细节GTM Clock and Time Base Management (CTBM)Clock Management Unit (CMU)External Generation Unit (EGU)Configurable Clock Generation sub-unit (CFGU)Fixed Clock Generation (FXU) Time Base Unit (TBU) Cluster Configuration Mod…