前端 - 后台系统系统
项目技术栈
配置文件
uno.config.ts
// uno.config.ts
import { defineConfig, presetIcons, presetWind } from 'unocss';
import transformerDirective from '@unocss/transformer-directives';
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders';
export default defineConfig({
presets: [
presetIcons({
mode: 'auto',
scale: 1.2,
prefix: 'i-',
autoInstall: true,
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
collections: {
// 自定义路径下的 svg -> 类 format: i-banner-[filename] or i-icon-[filename]
banner: FileSystemIconLoader('./assets/banner', (svg) => svg),
icon: FileSystemIconLoader('./assets/icon', (svg) => svg),
},
}),
presetWind(),
], // 能够使用 @apply......
transformers: [transformerDirective()],
});
...
一、接口管理
....
二、图标渲染
基于 iconify 提供近 10万个图标
前往 icones.js.org/collection/… 复制图标名称 如 i-tabler:123
具体图标预设文档 unocss.net/presets/ico…
使用
<!-- Phosphor 图标中的基本锚点图标 -->
<div class="i-ph-anchor-simple-thin" />
<!-- 来自 Material Design 图标的橙色闹钟 -->
<div class="i-mdi-alarm text-orange-400" />
二、动态图标渲染
前言: 网站中存在多处, 需要使用动态渲染图标的能力。如左侧导航栏和顶部导航栏的目录、菜单的图标会根据 菜单管理中定义的图标进行动态渲染
需求: 后端返回 tabler:align-box-left-top 字符串, 前端渲染该图标
tabler:align-box-left-top 图标来自于 icon-sets.iconify.design/ 网站(可直接访问 icones.js.org/collection/…), 该提供了将近10万多个图标, 并且提供获取单个图标的接口 api.iconify.design/tabler:123.… (需要处理跨域问题)
1. 封装
基于上述可以通过 mask-image 渲染图标, 传递 --svg css 变量 (如 api.iconify.design/tabler:123.…),
注意: 需要处理跨域问题
<!-- Iconify.vue -->
<script lang="ts" setup>
const props = defineProps<{
/**
* https://icon-sets.iconify.design/ 图标名称
*/
name?: string;
}>();
</script>
<template>
<i
v-if="!!name"
:style="{
'--svg': `url(https://api.iconify.design/${props.name}.svg)`,
}"
class="icon"
/>
</template>
<!-- icon class 可定义在全局中 -->
<style>
.icon {
display: inline-block;
width: 1.1em;
height: 1.1em;
--svg: url("data:image/svg+xml'");
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
vertical-align: -3px;
}
</style>
2. 封装彩色图标
注意: 需要处理跨域问题
<!-- IconifyColor.vue -->
<script lang="ts" setup>
const props = defineProps<{
name?: string;
}>();
</script>
<template>
<i
v-if="!!name"
:style="{
content: `url('https://api.iconify.design/${props.name}.svg')`,
}"
class="color-icon"
/>
</template>
<!-- icon class 可定义在全局中 -->
<style>
.color-icon::after {
display: inline-block;
width: 1.1em;
height: 1.1em;
vertical-align: -0.125em;
content: '';
}
</style>
2. 使用
<script lang="ts" setup>
const iconName = ref('');
</script>
<template>
<a-card>
<a-input v-model="iconName" placeholder="请输入图标名称" />
<a-divider />
<a-space>
<Iconify :name="iconName" class="text-lg" />
<span>图标名称: {{ iconName }}</span>
</a-space>
<a-divider />
<a-space>
<IconifyColor :name="iconName" class="text-lg" />
<span>彩色图标名称: {{ iconName }}</span>
</a-space>
</a-card>
</template>
<style scoped></style>
三 布局响应式
基于 tailwindcss 响应屏幕配置 sm: 640px,md: 768px,lg: 1024px
如果需要自定义css 响应配置需在 uno.config.ts 中配置 theme.screens 属性, 具体配置参考 tailwindcss.com/docs/screen…。
js 自定义响应配置查看 www.vueusejs.com/core/useBre…
css 层面
:deep(.arco-table-cell) {
@apply px-0 lg:px-4;
}
js 层面
// useLayout.ts
import { breakpointsTailwind } from '@vueuse/core';
export const useLayout = () => {
// breakpointsTailwind https://tailwindcss.com/docs/screens
const breakpoints = useBreakpoints(breakpointsTailwind);
// 手机
const mobile = breakpoints.smaller('md');
// 桌面
const desktop = breakpoints.greater('md');
return {
isMobile: mobile,
isDesktop: desktop,
};
};
使用
<script lang="ts" setup>
const { isMobile } = useLayout();
</script>
<template>
<a-card :title="isMobile ? 'mobile' : 'desktop'">
<a-button :class="{ 'arco-btn-only-icon': isMobile }">
<span v-if="!isMobile">显示</span>
<template #icon>
<i class="i-tabler:alert-square-rounded" />
</template>
</a-button>
<p class="py-1 md:py-20 bg-blue">你好</p>
</a-card>
</template>
四、暗黑模式
使用 windcss windicss.org/features/da… 暗黑配色, 在 html 标签 需要添加 dark类 识别为暗黑模式, 添加 light 识别为明亮模式
Arco UI 组件也维护了暗黑/明亮 模式 ,需要在 body 标签中 添加 arco-theme 属性, 值为 dark
和 light
1. 配置 windcss 和 Arco UI 组件 暗黑模式
const dark = 'dark';
const light = 'light';
// arco ui 主题切换
const isThemeDark = useDark({
storageKey: 'arcoTheme',
selector: 'body',
attribute: 'arco-theme',
valueDark: dark,
valueLight: light,
});
// wind 主题切换
const isThemeDark2 = useDark({
storageKey: 'tailwindcssTheme',
selector: 'html',
attribute: 'class',
valueDark: dark,
valueLight: light,
});
const toggle = useToggle(isThemeDark);
const toggle2 = useToggle(isThemeDark2);
export const useTheme = () => {
const toggleTheme = () => {
toggle();
toggle2();
};
const currentMode = computed(() => {
return isThemeDark.value ? dark : light;
});
return {
toggleTheme,
isDark: isThemeDark,
currentMode,
};
};
- 封装通用主题切换按钮
<script lang="ts" setup>
withDefaults(
defineProps<{
size?: 'mini' | 'medium' | 'large' | 'small' | undefined;
}>(),
{
size: 'medium',
}
);
const { toggleTheme, isDark } = useTheme();
</script>
<template>
<a-button
:shape="'circle'"
:size="size"
:type="'secondary'"
@click="toggleTheme()"
>
<template #icon>
<div
v-if="isDark"
class="i-line-md-moon-alt-to-sunny-outline-loop-transition text-yellow-500"
/>
<div v-else class="i-line-md-moon-loop text-purple-900" />
</template>
</a-button>
</template>
3.使用
<script lang="ts" setup></script>
<template>
<a-card>
<h1 class="text-red dark:text-amber font-bold">
unocss text-red dark:text-amber
</h1>
<ThemeButton />
</a-card>
</template>
<style scoped></style>
五、UI 主题关键色
Arco UI 组件中定义了主题配色 --primary-6
# button
.arco-btn-primary, .arco-btn-primary[type='button'], .arco-btn-primary[type='submit'] {
color: #fff;
background-color: rgb(var(--primary-6));
border: 1px solid transparent;
}
# tag
body[arco-theme='dark'] .arco-tag-checked.arco-tag-arcoblue {
background-color: rgba(var(--arcoblue-6), 0.2);
}
只需要修改 body --primary 中定义的 1 ~ 10 色阶颜色即可
1. 实现
@arco-design/color 包中存在色阶生成函数
useLocalStorage 主题色字符串 存储至 local
useCssVar 控制在 body中的 --primary-6 的 css 变量
import { toRefs } from '@vueuse/shared';
// @ts-ignore
import colorUtil from '@arco-design/color';
// @ts-ignore
import Color from 'color';
/**
* 主题关键色
*
* @author tuuuuuun
* @version v1.0
* @since 2024/2/16 21:24
*/
export const useGlobalVars = createGlobalState(() => {
const { isDark } = useTheme();
const primary = useLocalStorage(
'my-primary',
useCssVar('--primary-6', document.body, { initialValue: '#4826FF' })
);
/**
* 修改主题关键色
*
* @param rgbColor rgb color
*/
const changePrimaryColor = (rgbColor: string) => {
if (rgbColor) {
primary.value = getRgbStr(rgbColor);
}
};
/**
* red -> 243, 60, 60
*
* @param color 颜色
* @return 243, 60, 60
*/
const getRgbStr = function (color: string) {
return Color(color).rgb().round().array().join(',');
};
/**
* 监听主题关键色变化 和 当前主题是否为暗色
*/
watch(
[primary, isDark],
([primary, isDark]) => {
const presetColors: string[] = colorUtil.generate(`rgb(${primary})`, {
format: 'rgb',
index: 6,
list: true,
dark: isDark,
});
presetColors.forEach((color, index) => {
document.body.style.setProperty(
`--arcoblue-${index + 1}`,
getRgbStr(color)
);
});
},
{ immediate: true }
);
return {
color: primary,
changePrimaryColor,
};
});
按钮鉴权
......
顶部导航栏及左右切换动画
......