项目要求实现一键换肤的功能,不仅仅是主题颜色上的替换,还有图片素材的替换,主题颜色替换的方案大同小异,下面仅对图片素材的一件替换进行方法描述。

主要思路

使用本地仓库对当前主题进行存储,系统根据主题去加载不同文件夹下的素材文件。

实现方式

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>

效果如下:
在这里插入图片描述
点击一键换肤以后
在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐