【前端小点】vue3项目内根据主题读取不同文件夹下的图片资源(图片文件)
项目要求实现一键换肤的功能,不仅仅是主题颜色上的替换,还有图片素材的替换,主题颜色替换的方案大同小异,下面仅对图片素材的一件替换进行方法描述。
主要思路
使用本地仓库对当前主题进行存储,系统根据主题去加载不同文件夹下的素材文件。
实现方式
1、动态读取文件夹下的图片文件。
注意:不同的素材我们用的是不同文件夹,但是文件名字是一样的,存储位置也是相对统一的,如下图所示。例如:暗色主题下有一个图片地址为bg.jpg,则亮色主题下也需要有一个图片地址为bg.jpg
 
 编写js/ts对文件内容进行读取并存储,我的文件位置: src/themes.js
 注意: import.meta.glob必须是静态字符串,不可设置为动态获取的.
themes.js
// 获取文件夹下所有文件
const images = import.meta.glob(`./assets/themes/light/**/**/*.*`)
const commonImages = import.meta.glob(`./assets/themes/common/**/**/*.*`)
let prefix = './assets/themes/light/'
let commonPrefix = './assets/themes/common/'
/**
 *
 * @param {*} imagePaths
 * @returns
 */
const handleFileName = (imagePaths, pathPrefix) => {
  console.log(imagePaths)
  let pathObj = {}
  Object.keys(imagePaths).forEach((key) => {
    key = key.replace(pathPrefix, '')
    let pathArr = key.split('/')
    let length = pathArr.length
    // 名字
    let name = pathArr[length - 1]
    // 文件夹
    let folder = ''
    if (length >= 2) {
      folder = pathArr[length - 2]
    }
    // 父文件夹
    let folderParent = ''
    if (length >= 3) {
      folderParent = pathArr[length - 3]
    }
    pathObj[key] = {
      name,
      folder,
      folderParent
    }
  })
  return pathObj
}
// 获取文件名和文件夹名
let files = handleFileName(images, prefix)
console.log(files)
let commonFiles = handleFileName(commonImages, commonPrefix)
const themes = {
  light: {},
  dark: {},
  common: {}
}
// 设置不同主题下的图片 文件夹/文件
Object.keys(files).forEach((key) => {
  let fileObj = files[key]
  let { name, folder, folderParent } = fileObj
  let lightHref = new URL(`./assets/themes/light/${key}`, import.meta.url).href
  let darkHref = new URL(`./assets/themes/dark/${key}`, import.meta.url).href
  if (folderParent) {
    themes.light[folderParent] ? {} : themes.light[folderParent] = {}
    themes.light[folderParent][folder] ? {} :themes.light[folderParent][folder]
    themes.light[folderParent][folder][name] = lightHref
    themes.dark[folderParent] ? {} : themes.dark[folderParent] = {}
    themes.dark[folderParent][folder] ? {} : themes.dark[folderParent] = {}
    themes.dark[folderParent][folder][name] = darkHref
  } else if (folder) {
    themes.light[folder] ? {} : themes.light[folder] = {}
    themes.light[folder][name] = lightHref
    themes.dark[folder] ? {} : themes.dark[folder] = {}
    themes.dark[folder][name] = darkHref
  } else {
    themes.light[name] = lightHref
    themes.dark[name] = darkHref
  }
})
// 设置公共图片文件
Object.keys(commonFiles).forEach((key) => {
  let fileObj = commonFiles[key]
  let { name, folder, folderParent } = fileObj
  let commonHref = new URL(`./assets/themes/common/${key}`, import.meta.url).href
  if (folderParent) {
    themes.common[folderParent] ? {} : themes.common[folderParent] = {}
    themes.common[folderParent][folder] = {}
    themes.common[folderParent][folder][name] = commonHref
  } else if (folder) {
    themes.common[folder] ? {} : themes.common[folder] = {}
    themes.common[folder][name] = commonHref
  } else {
    themes.common[name] = commonHref
  }
})
export default themes
 
2、对项目内的主题(暗色\亮色)进行仓库存储
这里我使用的是pinia进行本地数据存储,我的文件位置src/pinia/modules/theme.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import themesImage from '@/themes'
const useThemeStore = defineStore('themeStore', () => {
  let theme = ref('light')
  const toggleTheme = () => {
    theme.value = theme.value === 'light' ? 'dark' : 'light'
  }
  const getThemeImage = (imgPath: string) => {
    return getImage(theme.value, imgPath)
  }
  const getCommonImage = (imgPath: string) => {
    return getImage('common', imgPath)
  }
  const getImage = (themeName: string, imgPath: string) => {
    let imagePath = imgPath.split('/')
    imagePath.length
    if (imagePath.length == 1) {
      return themesImage[themeName][imagePath[0]]
    } else if (imagePath.length == 2) {
      return themesImage[themeName][imagePath[0]][imagePath[1]]
    } else {
      return themesImage[themeName][imagePath[0]][imagePath[1]][imagePath[2]]
    }
  }
  return {
    theme,
    toggleTheme,
    getThemeImage,
    getCommonImage
  }
})
export default useThemeStore
 
3、编写工具类获取图片地址
编写工具类,便于后续界面读取文件,我的文件地址为: src/utils/themes.ts
import useThemeStore from '@/pinia/modules/theme'
const themeStore = useThemeStore()
// 获取主题图片
export const getThemeImage = themeStore.getThemeImage
// 获取公共图片
export const getCommonImage = themeStore.getCommonImage
 
4、在vue组件中使用
做好准备工作以后,我们只需在组件中引入我们的工具类即可。
<template>
	<div class="container">
		<img :src="themeUtils.getThemeImage('index/logo.png')" alt="" class="img" />
	    <img :src="themeUtils.getCommonImage('logo.png')" alt="" class="img" />
	    <el-button @click="themeUtils.toggleTheme">一键换肤</el-button>
	</div>
</template>
<script lang="ts" setup>
import * as themeUtils from '@/utils/themes'
</script>
<style lang="scss" scoped>
.img{
  border: 1px solid #ccc;
  border-radius: 10px;
  margin-right: 10px;
}
.container{
  display: flex;
  align-items: center;
}
</style>
 
效果如下:
 
 点击一键换肤以后
 
