后台管理项目总结

146 阅读4分钟

Vue3

过滤器

vue3取消了过滤器这个功能,我们可以使用新增的全局变量方法来替换过滤器,这里以时间过滤举例,使用dayjs

  • 首先定义过滤方法
import dayjs from 'dayjs'

const dateFilter = (val, format = 'YYYY-MM-DD') => {
  if (!isNaN(val)) {
    val = parseInt(val)
  }

  return dayjs(val).format(format)
}

export default app => {
  app.config.globalProperties.$filters = {
    dateFilter
  }
}
  • 然后在main.js引入,挂载到app上
  • 使用方法如下:
 <template #default="{ row }">
            {{ $filters.dateFilter(row.openTime) }}
  </template>

换肤功能

Elementplus组件换肤

首先要用到3个包:rgb-hex, css-color-function,formula.json,前两者是对颜色进行转化的包,后者是根据一个主色,将白色和黑色与主色进行一定比例的混合,生成颜色,用于换肤后基于主色的其他颜色替换。

  1. 获取ElementPlus最新样式表,不赘述。
  2. ElementPlus中要替换的部分打上标记,比如在ElementPlus#3a8ee6primary颜色,我们要替换所有基于#3a8ee6ElementPlus中的颜色,下面的函数就是将和primary(#3a8ee6)有关的颜色进行替换
/**

 * 返回 style 的 template

 * 打上标记,即将#3a8ee6 全部替换为 'primary'

 */

const getStyleTemplate = (data) => {

  // element-plus 默认色值

  const colorMap = {

    '#3a8ee6': 'shade-1',

    '#409eff': 'primary',

    '#53a8ff': 'light-1',

    '#66b1ff': 'light-2',

    '#79bbff': 'light-3',

    '#8cc5ff': 'light-4',

    '#a0cfff': 'light-5',

    '#b3d8ff': 'light-6',

    '#c6e2ff': 'light-7',

    '#d9ecff': 'light-8',

    '#ecf5ff': 'light-9'

  }

  // 根据默认色值为要替换的色值打上标记

  Object.keys(colorMap).forEach((key) => {

    const value = colorMap[key]

    data = data.replace(new RegExp(key, 'ig'), value)

  })

  return data

}
  1. 根据选择换肤的主色调通过formula.json生成颜色
  2. 根据上一步生成的颜色,将打上标记后的ElementPlus样式表进行替换。
export const generateNewStyle = async (primaryColor) => {

  const colors = generateColors(primaryColor)

  let cssText = await getOriginalStyle()

  // 遍历生成的样式表,在 CSS 的原样式中进行全局替换

  Object.keys(colors).forEach((key) => {

    cssText = cssText.replace(

      new RegExp('(:|\\s+)' + key, 'g'),

      '$1' + colors[key]

    )

  })
  return cssText

}
  1. 将生成的样式表插入style标签中

国际化

切换语言后国际化未更改

监听保存的language变化后重新调用回调函数即可,可以编写一个这样的工具函数

/**
 *
 * @param  {...any} cbs 所有的回调
 */
export function watchSwitchLang(...cbs) {
  watch(
    () => store.getters.language,
    () => {
      cbs.forEach(cb => cb(store.getters.language))
    }
  )
}

TagsView

麵包屑的右鍵菜單觸發事件

vue中有@contextmenu事件,右鍵觸發

麵包屑的關閉

麵包屑的關閉本質上就是對數據的處理,創建一個數組儲存麵包屑上的數據,每個對象包括路徑和路由元信息,同时也可以存储到本地存储中。面包屑的删除其他,删除右侧,删除当前无非都是对这个数组的数据进行处理。

处理基于路由的动态过渡

写法如下

<router-view v-slot="{ Component, route }">
  <transition name="fade-transform" mode="out-in">
   <keep-alive>
     <component :is="Component" :key="route.path"></component>
   </keep-alive>
 </transition>
</router-view>

详情见router.vuejs.org/zh/guide/ad…

EXCEL导入

步骤

  • 获取传入的EXCEL表文件数据
  • 解析EXCEL文件
  • 得到数据返回

获取传入的EXCEL文件数据

这里使用原生的<input type="file" />实现。定义两个props,一个是上传前回调beforeUpload,一个是成功回调onSuccess,像这类回调函数类型的props,在当前组件中,逻辑就是通过判断回调的返回成功与失败,决定是否继续往下进行操作。
读取文件使用的是FileReader对象,这里的逻辑先使用apiFileReader.readAsArrayBuffer()解析文件,然后在onload(文件读取完后),使用xlsx对其进行解析,获取解析后的数据。

const readerData = (rawFile) => {
  loading.value = true
  return new Promise((resolve, reject) => {
    // https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
    const reader = new FileReader()
    // 启动读取指定的 Blob 或 File 内容
    reader.readAsArrayBuffer(rawFile)
    // 该事件在读取操作完成时触发
    // https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/onload
    reader.onload = (e) => {
      // 1. 获取解析到的数据
      const data = e.target.result
      // 2. 利用 XLSX 对数据进行解析
      const workbook = XLSX.read(data, { type: 'array' })
      // 3. 获取第一张表格(工作簿)名称
      const firstSheetName = workbook.SheetNames[0]
      // 4. 只读取 Sheet1(第一张表格)的数据
      const worksheet = workbook.Sheets[firstSheetName]
      // 5. 解析数据表头
      const header = getHeaderRow(worksheet)
      // 6. 解析数据体
      const results = XLSX.utils.sheet_to_json(worksheet)
      // 7. 传入解析之后的数据
      generateData({ header, results })
      // 8. loading 处理
      loading.value = false
      // 9. 异步完成
      resolve()
    }
  })
}

解析EXCEL文件

使用XLSX,注意两个通用要点:解析表头和上传后的时间解析错误

  • 解析表头
/**
 * 获取表头(通用方式)
 */
export const getHeaderRow = sheet => {
  const headers = []
  const range = XLSX.utils.decode_range(sheet['!ref'])
  let C
  const R = range.s.r
  /* start in the first row */
  for (C = range.s.c; C <= range.e.c; ++C) {
    /* walk every column in the range */
    const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
    /* find the cell in the first row */
    let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
    if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
    headers.push(hdr)
  }
  return headers
}
/**
 * 解析 excel 导入的时间格式
 */
export const formatDate = (numb) => {
  const time = new Date((numb - 1) * 24 * 3600000 + 1)
  time.setYear(time.getFullYear() - 70)
  const year = time.getFullYear() + ''
  const month = time.getMonth() + 1 + ''
  const date = time.getDate() - 1 + ''
  return (
    year +
    '-' +
    (month < 10 ? '0' + month : month) +
    '-' +
    (date < 10 ? '0' + date : date)
  )
}

EXCEL导出

  • 获取table数据
  • 转化成相应格式
  • 使用Export2Excel导出

局部打印

由于浏览器自带打印会打印整个页面,所以使用vue3-print-nb,传入id即可实现对应位置打印

TODO:待补充