前端 - 后台系统(部分)

335 阅读4分钟

前端 - 后台系统系统

项目模板 gitee.com/tuuuuuun/bl…

项目技术栈

配置文件

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" />

recording-1708774949608-8.gif

二、动态图标渲染

前言: 网站中存在多处, 需要使用动态渲染图标的能力。如左侧导航栏和顶部导航栏的目录、菜单的图标会根据 菜单管理中定义的图标进行动态渲染

image-20240222205532649.png

需求: 后端返回 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>
​

recording-1708773766338-6.gif

三 布局响应式

基于 tailwindcss 响应屏幕配置 sm: 640pxmd: 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>

recording-1708777174546-10.gif

四、暗黑模式

使用 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,
  };
};
​
  1. 封装通用主题切换按钮
<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>

recording.gif

五、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);
}

image-20240224202940136.png

只需要修改 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,
  };
});
​

recording-1708779782954-14.gif

按钮鉴权

......

顶部导航栏及左右切换动画

......