vue3+typescript+vite封装自己的UI组件库并上传至npm

0 阅读10分钟

简介

在现代化前端工程体系中,封装一套属于自己的 UI 组件库,是提升研发效能与保障产品体验一致性的核心战略。它不仅能够将设计中沉淀的视觉语言和交互规范转化为可执行的代码资产,从根源上消灭每个页面“各自为政”的样式冗余与逻辑碎片;更能通过统一的 API 设计与版本管理,大幅降低跨团队协作的沟通成本与联调风险。当业务需求迭代时,修改一个基础组件的内部逻辑便可实现全局生效,既避免了“牵一发而动全身”的维护噩梦,确保了品牌调性在每一次交互中的稳定传递。长远来看,这更是技术团队积累领域知识、构建工程护城河的关键一步——让开发者从重复造轮子的琐碎中解放出来,专注于业务价值的深度创新。下面是使用vue3+typescript+vite封装自己的UI组件库并上传至npm教程,该框架功能包括边开发组件即时浏览组件效果,使用该组件库包括按需加载引用组件及样式,全量引入组件和样式两种使用模式。

项目结构

MY-UI/
├── _test_/
│   └── button.test.js
├── command/
│   ├── build.js
│   └── index.d.ts
├── src/
│   ├── assets/
│   │   ├── fonts/
│   │   │   ├── iconfont.css
│   │   │   ├── iconfont.ttf
│   │   │   ├── iconfont.woff
│   │   │   └── iconfont.woff2
│   │   └── styles/
│   │       ├── base.scss
│   │       ├── darkVar.scss
│   │       ├── lightVar.scss
│   │       ├── oldVar.scss
│   │       └── youndVar.scss
│   ├── components/
│   │   ├── button/
│   │   │   ├── index.scss
│   │   │   ├── index.ts
│   │   │   └── index.vue
│   │   ├── dialog/
│   │   │   ├── index.scss
│   │   │   ├── index.ts
│   │   │   └── index.vue
│   │   ├── icon/
│   │   │   ├── index.scss
│   │   │   ├── index.ts
│   │   │   └── index.vue
│   │   └── index.ts
│   ├── images/
│   ├── router/
│   │   └── index.ts
│   ├── types/
│   ├── utils/
│   │   ├── bus.ts
│   │   └── index.ts
│   ├── views/
│   │   ├── button.vue
│   │   ├── dialog.vue
│   │   └── icon.vue
│   ├── App.vue
│   └── main.ts
├── .commitlintrc.js
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc.js
├── index.html
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── README.md
├── shims-vue.d.ts
├── tsconfig.json
├── version.config.json
└── vite.config.ts

项目技术栈

node:v18.20.5

"@commitlint/cli": "^19.6.1",

"@types/node": "^22.10.5",

"@typescript-eslint/eslint-plugin": "^8.19.1",

"@typescript-eslint/parser": "^8.19.1",

"@vitejs/plugin-vue": "5.1.0",

"@vitejs/plugin-vue-jsx": "^4.1.1",

"@vue/compiler-sfc": "^3.5.13",

"@vue/runtime-dom": "^3.5.13",

"autoprefixer": "^10.4.20",

"ava": "^6.2.0",

"eslint": "^9.17.0",

"eslint-config-prettier": "^9.1.0",

"eslint-plugin-prettier": "^5.2.1",

"fs-extra": "^11.2.0",

"husky": "^9.1.7",

"lint-staged": "^15.3.0",

"postcss": "^8.5.1",

"postcss-preset-env": "^10.1.3",

"prettier": "^3.4.2",

"sass-embedded": "^1.83.1",

"ts-node": "^10.9.2",

"tsconfig-paths": "^4.2.0",

"typescript": "^5.7.2",

"vite": "5.2.0",

"vue-cli-plugin-typescript": "^0.0.1",

"vue-tsc": "^2.2.0"

my-ui库构建

npm run lib

my-ui上传npm

# 在my-ui/lib文件夹下, 执行命令

npm login

npm publish

my-ui安装

npm install my-ui

my-ui组件库按需引入

// 方式一
import { MyButton, MyHeader, MyTab } from 'my-ui'
// 方式二
import MyButton from 'my-ui/button'
import MyHeader from 'my-ui/header'
import MyTab from 'my-ui/tab'
import MyNumgrid from 'my-ui/numgrid'

my-ui组件库全量引入

import myUi from 'my-ui'
import 'my-ui/style.css'
const app = createApp(App)
app.use(myUi).mount('#app')

查看组件效果

示例:

http://localhost:3000/

http://localhost:3000/icon

业务使用组件

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div>
    <my-button :buttonText="'登录'" :bgcolor="'green'"></my-button>
    <my-loading :show="true" :loadColor="'green'"></my-loading>
    <a href="https://vite.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
</template>

<style scoped>

</style>

效果展示

创建项目

npm create vite@latest my-vue-app -- --template vue-ts
npm install my-ui

运行项目npm run dev

image.png

组件库开发规范

所有类名以 xc- 开头,组件像素采用rem为单位,template-vue3主工程的html, body设置为

html, body {
  height: 100%;
  font-family: $font-family;
  font-size: $font-size-base; // 默认:10px
  line-height: $line-height-base; // 行高默认为:1
}

组件命名

组件以my-xxxxx 开头小写进行命名,示例:

export default defineComponent({
  name: 'my-tab'
})

组件根节点

组件根节点必须绑定主题,供换肤使用

<template>
  <div class="xc-tab" :class="'xc-theme-' + theme">
    <div class="xc-each-grid">hello world</div>
  </div>
</template>

组件样式引入

样式统一使用@use的方式引入到组件中

index.vue文件

<style lang="scss" scoped>
// 引入公共样式文件,使用 @use 规则
// 默认标准版
@use './index.scss';
</style>

class命名

以xc-xxxx命名,示例

<div class="xc-each-grid">hello world</div>

标准版

// 标准版,默认1rem = 10px
$sizes-yound: (
  1: 0.1rem,
  2: 0.2rem,
  3: 0.3rem,
  4: 0.4rem,
  5: 0.5rem,
  6: 0.6rem,
  7: 0.7rem,
  8: 0.8rem,
  9: 0.9rem,
  10: 1.0rem,
  11: 1.1rem,
  12: 1.2rem,
  13: 1.3rem,
  14: 1.4rem,
  15: 1.5rem,
  16: 1.6rem,
  17: 1.7rem,
  18: 1.8rem,
  19: 1.9rem,
  20: 2.0rem,
  21: 2.1rem,
  22: 2.2rem,
  23: 2.3rem,
  24: 2.4rem,
  25: 2.5rem,
  26: 2.6rem,
  27: 2.7rem,
  28: 2.8rem,
  29: 2.9rem,
  30: 3.0rem,
  31: 3.1rem,
  32: 3.2rem,
  33: 3.3rem,
  34: 3.4rem,
  35: 3.5rem,
  36: 3.6rem,
  37: 3.7rem,
  38: 3.8rem,
  39: 3.9rem,
  40: 4.0rem,
  41: 4.1rem,
  42: 4.2rem,
  43: 4.3rem,
  44: 4.4rem,
  45: 4.5rem,
  46: 4.6rem,
  47: 4.7rem,
  48: 4.8rem,
  49: 4.9rem,
  50: 5.0rem,
  51: 5.1rem,
  52: 5.2rem,
  53: 5.3rem,
  54: 5.4rem,
  55: 5.5rem,
  56: 5.6rem,
  57: 5.7rem,
  58: 5.8rem,
  59: 5.9rem,
  60: 6.0rem,
  61: 6.1rem,
  62: 6.2rem,
  63: 6.3rem,
  64: 6.4rem,
  65: 6.5rem,
  66: 6.6rem,
  67: 6.7rem,
  68: 6.8rem,
  69: 6.9rem,
  70: 7.0rem,
  71: 7.1rem,
  72: 7.2rem,
  73: 7.3rem,
  74: 7.4rem,
  75: 7.5rem,
  76: 7.6rem,
  77: 7.7rem,
  78: 7.8rem,
  79: 7.9rem,
  80: 8.0rem,
  81: 8.1rem,
  82: 8.2rem,
  83: 8.3rem,
  84: 8.4rem,
  85: 8.5rem,
  86: 8.6rem,
  87: 8.7rem,
  88: 8.8rem,
  89: 8.9rem,
  90: 9.0rem,
  91: 9.1rem,
  92: 9.2rem,
  93: 9.3rem,
  94: 9.4rem,
  95: 9.5rem,
  96: 9.6rem,
  97: 9.7rem,
  98: 9.8rem,
  99: 9.9rem,
  100: 10rem
);

.xc-young {
  // 使用循环生成字体大小类
  @each $key, $value in $sizes-yound {
    .xc-font-size#{$key} {
      font-size: $value;
    }
    .xc-width#{$key} {
      width: $value;
    }
    .xc-height#{$key} {
      height: $value;
    }
    .xc-margin#{$key} {
      margin: $value;
    }
    .xc-padding#{$key} {
      padding: $value;
    }
    .xc-radius#{$key} {
      border-radius: $value;
    }
    .xc-lineh#{$key} {
      line-height: $value;
    }
  }
}

xc-yound(标准版),xc-old(适老版)继承至主工程template-vue3的body类名,具体组件设置类名示例:

<button class="xc-font-size10, xc-margin10">{{ buttonText }}</button>

示例说明

xc-font-size12:设置文字大小

xc-margin15:设置边间距

xc-padding20:设置内边距

内填充设置(标准版)

xc-padding10: 内填充设计稿10px,对应为1rem (1rem默认10px)

xc-paddingT10: 上内填充设计稿10px,对应为1rem (1rem默认10px)

xc-paddingR10: 右内填充设计稿10px,对应为1rem (1rem默认10px)

xc-paddingB10: 下内填充设计稿10px,对应为1rem (1rem默认10px)

xc-paddingL10: 左内填充设计稿10px,对应为1rem (1rem默认10px)

外间距设置(标准版)

xc-margin10: 外间距设计稿10px,对应为1rem (1rem默认10px)

xc-marginT10: 上外间距设计稿10px,对应为1rem (1rem默认10px)

xc-marginR0: 右外间距设计稿10px,对应为1rem (1rem默认10px)

xc-marginB10: 下外间距设计稿10px,对应为1rem (1rem默认10px)

xc-marginL10: 左外间距设计稿10px,对应为1rem (1rem默认10px)

宽度、高度设置(标准版)

xc-width10: 宽设计稿10px,对应为1rem (1rem默认10px)

xc-height10: 高设计稿10px,对应为1rem (1rem默认10px)

.xc-width#{$key} {
  width: $value;
}
.xc-height#{$key} {
  height: $value;
}

最小宽度、高度设置(标准版)

.xc-min-width#{$key} {
  min-width: $value;
}
.xc-min-height#{$key} {
  min-height: $value;
}

换肤

通过公共方法获取主题,组件根节点中设置获取的主题。

<script lang="ts">
  import { getTheme } from '@/utils/index'
  export default defineComponent({
    setup(props, { emit }) {
      // 获取主题色
      const theme = ref<string>('')
      theme.value = getTheme()

      return {
        theme
      }
    }
  })
</script>
// 品牌色
$brand-light-colors: (
  0: #E54C3A,
  1: #FF5541,
  2: #FF6654,
  3: #FF7767,
  4: #FF887A,
  5: #FF998D,
  6: #FFAAA0,
  7: #FFBBB3,
  8: #FFCCC6,
  9: #FFDDD9,
  10: #FFF6F4
);
// 功能色--涨
$advance-light-colors: (
  0: #D9424E,
  1: #F14957,
  2: #F25B67,
  3: #F46D79,
  4: #F57F89,
  5: #F7929A,
  6: #F8A4AB,
  7: #F9B6BC,
  8: #FAC8CC,
  9: #FAC8CC,
  10: #FFF4F6
);
// 功能色--跌
$dip-light-colors: (
  0: #09956D,
  1: #0BAF80,
  2: #23B78C,
  3: #3CBF99,
  4: #54C7A6,
  5: #6DCFB3,
  6: #85D7BF,
  7: #9DDFCC,
  8: #B5E7D8,
  9: #CEEFE6,
  10: #EEFFFA
);
// 功能色--亏、卖
$sell-light-colors: (
  0: #356BB8,
  1: #3E7ED9,
  2: #518ADC,
  3: #6598E1,
  4: #77A4E4,
  5: #8BB2E8,
  6: #9EBEEC,
  7: #B2CBF0,
  8: #C5D8F3,
  9: #D8E5F7,
  10: #F3FAFF
);
// 辅助色--金
$king-light-colors: (
  0: #A47752,
  1: #C18C60,
  2: #C7976F,
  3: #CDA380,
  4: #D3AE8F,
  5: #DABAA0,
  6: #E0C5AF,
  7: #E6D1BF,
  8: #ECDCCF,
  9: #F3E8DF,
  10: #F8F3EF
);
// 辅助色--警示
$warn-light-colors: (
  0: #E59B32,
  1: #FFAC37,
  2: #FFB44B,
  3: #FFBD5F,
  4: #FFC473,
  5: #FFCD87,
  6: #FFD59B,
  7: #FFDEAF,
  8: #FFE6C3,
  9: #FFEED7,
  10: #FFF6EB
);

// 文本颜色
$text-light-colors: map-merge(
  (
    1: #2F313C,
    2: #797B85,
    3: #AAACB8,
    4: #F1F3F6,
    5: #FFFFFF,
  ),
  (
    6: map-get($brand-light-colors, 0),
    7: map-get($brand-light-colors, 1),
    8: map-get($brand-light-colors, 5),
    9: map-get($brand-light-colors, 10),
    10: map-get($advance-light-colors, 0),
    11: map-get($advance-light-colors, 1),
    12: map-get($advance-light-colors, 5),
    13: map-get($advance-light-colors, 10),
    14: map-get($dip-light-colors, 0),
    15: map-get($dip-light-colors, 1),
    16: map-get($dip-light-colors, 5),
    17: map-get($dip-light-colors, 10),
    18: map-get($sell-light-colors, 0),
    19: map-get($sell-light-colors, 1),
    20: map-get($sell-light-colors, 5),
    21: map-get($sell-light-colors, 10),
    22: map-get($king-light-colors, 0),
    23: map-get($king-light-colors, 1),
    24: map-get($king-light-colors, 5),
    25: map-get($king-light-colors, 10),
    26: map-get($warn-light-colors, 0),
    27: map-get($warn-light-colors, 1),
    28: map-get($warn-light-colors, 5),
    29: map-get($warn-light-colors, 10)
  )
);

// 背景颜色
$bg-light-colors: map-merge(
  /* 这里重复的原因是和黑色主题一一对应 */
  (
    1: #1E2B3F,
    2: #797B85,
    3: #AAACB8,
    4: #F1F3F6,
    5: #F7F8FA,
    6: #FFFFFF,
    7: #F1F3F6,
    8: #FFFFFF,
    9: #FFFFFF00,
    10: rgba(0, 0, 0, 0.5),
  ),
  (
    11: map-get($brand-light-colors, 0),
    12: map-get($brand-light-colors, 1),
    13: map-get($brand-light-colors, 5),
    14: map-get($brand-light-colors, 10),
    15: map-get($advance-light-colors, 0),
    16: map-get($advance-light-colors, 1),
    17: map-get($advance-light-colors, 5),
    18: map-get($advance-light-colors, 10),
    19: map-get($dip-light-colors, 0),
    20: map-get($dip-light-colors, 1),
    21: map-get($dip-light-colors, 5),
    22: map-get($dip-light-colors, 10),
    23: map-get($sell-light-colors, 0),
    24: map-get($sell-light-colors, 1),
    25: map-get($sell-light-colors, 5),
    26: map-get($sell-light-colors, 10),
    27: map-get($king-light-colors, 0),
    28: map-get($king-light-colors, 1),
    29: map-get($king-light-colors, 5),
    30: map-get($king-light-colors, 10),
    31: map-get($warn-light-colors, 0),
    32: map-get($warn-light-colors, 1),
    33: map-get($warn-light-colors, 5),
    34: map-get($warn-light-colors, 10)
  )
);

// 分隔线和线框颜色
$line-light-colors: map-merge(
  (
    1: #82848F,
    2: #E5E6EB,
    3: #F1F3F6
  ),
  (
    4: map-get($brand-light-colors, 0),
    5: map-get($brand-light-colors, 1),
    6: map-get($brand-light-colors, 5),
    7: map-get($brand-light-colors, 10),
    8: map-get($advance-light-colors, 0),
    9: map-get($advance-light-colors, 1),
    10: map-get($advance-light-colors, 5),
    11: map-get($advance-light-colors, 10),
    12: map-get($dip-light-colors, 0),
    13: map-get($dip-light-colors, 1),
    14: map-get($dip-light-colors, 5),
    15: map-get($dip-light-colors, 10),
    16: map-get($sell-light-colors, 0),
    17: map-get($sell-light-colors, 1),
    18: map-get($sell-light-colors, 5),
    19: map-get($sell-light-colors, 10),
    20: map-get($king-light-colors, 0),
    21: map-get($king-light-colors, 1),
    22: map-get($king-light-colors, 5),
    23: map-get($king-light-colors, 10),
    24: map-get($warn-light-colors, 0),
    25: map-get($warn-light-colors, 1),
    26: map-get($warn-light-colors, 5),
    27: map-get($warn-light-colors, 10)
  )
);

.xc-theme-light {
  // 文本颜色
  @each $keyText, $valueText in $text-light-colors {
    .xc-font-color#{$keyText} {
      color: $valueText;
    }
  }
  // 字体图标颜色
  @each $keyIcon, $valueIcon in $bg-light-colors {
    .xc-icon-color#{$keyIcon} {
      color: $valueIcon;
    }
  }
  // 背景色
  @each $keyBg, $valueBg in $bg-light-colors {
    .xc-bg-color#{$keyBg} {
      background-color: $valueBg;
    }
  }
  // 分割线颜色
  @each $keyLine, $valueLine in $line-light-colors {
    .xc-line-color#{$keyLine} {
      background-color: $valueLine;
    }
  }
  // 边框线颜色
  @each $keyBorder, $valueBorder in $line-light-colors {
    .xc-border-color#{$keyBorder} {
      border-color: $valueBorder;
    }
  }
}

组件封装换肤示例

<template>
  <div class="xc-tab" :class="'xc-theme-' + theme">
    <p class="xc-font-color1">1234</p>
  </div>
</template>

示例说明

xc-font-color1:控制文字颜色

xc-bg-color1:控制背景色

xc-border-color1:控制边框颜色

字体图标

使用参考

<i class="iconfont icon-dianzan-xuanzhong2"></i>

示例说明:

class="iconfont"为必须的图标基础类,icon-dianzan-xuanzhong2为具体的某个图标

props属性

要求对属性进行注释说明,参考示例

interface TabItem {
  title: string, // tab标题
  active: boolean, // tab是否激活
  key: string, // 唯一标识
  needIcon: boolean, // 是否需要图标
  iconName: string // 图标class名称
}
export default defineComponent({
  name: 'my-tab',
  props: {
    tabList: {
      type: Array as PropType<TabItem[]>,
      required: true,
      default: () => []
    }
  }
})

老年版

老年版下的字号大小、内间距、外间距,宽度、高度先暂定与标准版保持一致,根据实际需要修改对应关系,示例如下:

// 老年版下的字号大小、内间距、外间距,宽度、高度 先暂定与标准版保持一致,根据实际需要修改对应关系
// 标准版,默认1rem = 10px
$sizes-old: (
  1: 0.1rem,
  2: 0.2rem,
  3: 0.3rem,
  4: 0.4rem,
  5: 0.5rem,
  6: 0.6rem,
  7: 0.7rem,
  8: 0.8rem,
  9: 0.9rem,
  10: 1.0rem,
  11: 1.1rem,
  12: 1.2rem,
  13: 1.3rem,
  14: 1.4rem,
  15: 1.5rem,
  16: 1.6rem,
  17: 1.7rem,
  18: 1.8rem,
  19: 1.9rem,
  20: 2.0rem,
  21: 2.1rem,
  22: 2.2rem,
  23: 2.3rem,
  24: 2.4rem,
  25: 2.5rem,
  26: 2.6rem,
  27: 2.7rem,
  28: 2.8rem,
  29: 2.9rem,
  30: 3.0rem,
  31: 3.1rem,
  32: 3.2rem,
  33: 3.3rem,
  34: 3.4rem,
  35: 3.5rem,
  36: 3.6rem,
  37: 3.7rem,
  38: 3.8rem,
  39: 3.9rem,
  40: 4.0rem,
  41: 4.1rem,
  42: 4.2rem,
  43: 4.3rem,
  44: 4.4rem,
  45: 4.5rem,
  46: 4.6rem,
  47: 4.7rem,
  48: 4.8rem,
  49: 4.9rem,
  50: 5.0rem,
  51: 5.1rem,
  52: 5.2rem,
  53: 5.3rem,
  54: 5.4rem,
  55: 5.5rem,
  56: 5.6rem,
  57: 5.7rem,
  58: 5.8rem,
  59: 5.9rem,
  60: 6.0rem,
  61: 6.1rem,
  62: 6.2rem,
  63: 6.3rem,
  64: 6.4rem,
  65: 6.5rem,
  66: 6.6rem,
  67: 6.7rem,
  68: 6.8rem,
  69: 6.9rem,
  70: 7.0rem,
  71: 7.1rem,
  72: 7.2rem,
  73: 7.3rem,
  74: 7.4rem,
  75: 7.5rem,
  76: 7.6rem,
  77: 7.7rem,
  78: 7.8rem,
  79: 7.9rem,
  80: 8.0rem,
  81: 8.1rem,
  82: 8.2rem,
  83: 8.3rem,
  84: 8.4rem,
  85: 8.5rem,
  86: 8.6rem,
  87: 8.7rem,
  88: 8.8rem,
  89: 8.9rem,
  90: 9.0rem,
  91: 9.1rem,
  92: 9.2rem,
  93: 9.3rem,
  94: 9.4rem,
  95: 9.5rem,
  96: 9.6rem,
  97: 9.7rem,
  98: 9.8rem,
  99: 9.9rem,
  100: 10rem
);

.xc-old {
  // 使用循环生成字体大小类
  @each $key, $value in $sizes-old {
    .xc-font-size#{$key} {
      font-size: $value;
    }
    .xc-width#{$key} {
      width: $value;
    }
    .xc-height#{$key} {
      height: $value;
    }
    .xc-margin#{$key} {
      margin: $value;
    }
    .xc-marginT#{$key} {
      margin-top: $value;
    }
    .xc-marginR#{$key} {
      margin-right: $value;
    }
    .xc-marginB#{$key} {
      margin-bottom: $value;
    }
    .xc-marginL#{$key} {
      margin-left: $value;
    }
    .xc-padding#{$key} {
      padding: $value;
    }
    .xc-paddingT#{$key} {
      padding-top: $value;
    }
    .xc-paddingR#{$key} {
      padding-right: $value;
    }
    .xc-paddingB#{$key} {
      padding-bottom: $value;
    }
    .xc-paddingL#{$key} {
      padding-left: $value;
    }
    .xc-radius#{$key} {
      border-radius: $value;
    }
    .xc-lineh#{$key} {
      line-height: $value;
    }
  }
}

定位设置

.xc-positionT#{$key} {
  top: $value;
}
.xc-positionR#{$key} {
  right: $value;
}
.xc-positionB#{$key} {
  bottom: $value;
}
.xc-positionL#{$key} {
  left: $value;
}

边框设置

<div class="xc-border xc-borderW4 xc-border-color4">边框设置</div>
<style>
  .xc-border {
    border-style: solid;
  }
</style>

上边框设置

<div class="xc-border xc-borderW4 xc-border-color4">边框设置</div>
<style>
  .xc-border {
    border-top-style: solid;
  }
</style>

圆角设置

// 四个角
.xc-radius#{$key} {
  border-radius: $value;
}
// 下左
.xc-radiusBL#{$key} {
  border-bottom-left-radius: $value
}
// 下右
.xc-radiusBR#{$key} {
  border-bottom-right-radius: $value
}
// 左上
.xc-radiusTL#{$key} {
  border-top-left-radius: $value
}
// 右上
.xc-radiusTR#{$key} {
  border-top-right-radius: $value
}

浅色主题

@use 'sass:map';
// 品牌色
$brand-light-colors: (
  0: #e54c3a,
  1: #ff5541,
  2: #ff6654,
  3: #ff7767,
  4: #ff887a,
  5: #ff998d,
  6: #ffaaa0,
  7: #ffbbb3,
  8: #ffccc6,
  9: #ffddd9,
  10: #fff6f4
);
// 功能色--涨
$advance-light-colors: (
  0: #d9424e,
  1: #f14957,
  2: #f25b67,
  3: #f46d79,
  4: #f57f89,
  5: #f7929a,
  6: #f8a4ab,
  7: #f9b6bc,
  8: #fac8cc,
  9: #fac8cc,
  10: #fff4f6
);
// 功能色--跌
$dip-light-colors: (
  0: #09956d,
  1: #0baf80,
  2: #23b78c,
  3: #3cbf99,
  4: #54c7a6,
  5: #6dcfb3,
  6: #85d7bf,
  7: #9ddfcc,
  8: #b5e7d8,
  9: #ceefe6,
  10: #eefffa
);
// 功能色--亏、卖
$sell-light-colors: (
  0: #356bb8,
  1: #3e7ed9,
  2: #518adc,
  3: #6598e1,
  4: #77a4e4,
  5: #8bb2e8,
  6: #9ebeec,
  7: #b2cbf0,
  8: #c5d8f3,
  9: #d8e5f7,
  10: #f3faff
);
// 辅助色--金
$king-light-colors: (
  0: #a47752,
  1: #c18c60,
  2: #c7976f,
  3: #cda380,
  4: #d3ae8f,
  5: #dabaa0,
  6: #e0c5af,
  7: #e6d1bf,
  8: #ecdccf,
  9: #f3e8df,
  10: #f8f3ef
);
// 辅助色--警示
$warn-light-colors: (
  0: #e59b32,
  1: #ffac37,
  2: #ffb44b,
  3: #ffbd5f,
  4: #ffc473,
  5: #ffcd87,
  6: #ffd59b,
  7: #ffdeaf,
  8: #ffe6c3,
  9: #ffeed7,
  10: #fff6eb
);

// 文本颜色
$text-light-colors: map.merge(
  (
    1: #2f313c,
    2: #797b85,
    3: #aaacb8,
    4: #f1f3f6,
    5: #ffffff
  ),
  (
    6: map.get($brand-light-colors, 0),
    // #E54C3A
    7: map.get($brand-light-colors, 1),
    // #FF5541
    8: map.get($brand-light-colors, 5),
    // #FF998D
    9: map.get($brand-light-colors, 10),
    // #FFF6F4
    10: map.get($advance-light-colors, 0),
    // #D9424E
    11: map.get($advance-light-colors, 1),
    12: map.get($advance-light-colors, 5),
    13: map.get($advance-light-colors, 10),
    14: map.get($dip-light-colors, 0),
    // #09956d
    15: map.get($dip-light-colors, 1),
    //  #0Baf80
    16: map.get($dip-light-colors, 5),
    17: map.get($dip-light-colors, 10),
    18: map.get($sell-light-colors, 0),
    19: map.get($sell-light-colors, 1),
    20: map.get($sell-light-colors, 5),
    21: map.get($sell-light-colors, 10),
    22: map.get($king-light-colors, 0),
    23: map.get($king-light-colors, 1),
    24: map.get($king-light-colors, 5),
    25: map.get($king-light-colors, 10),
    26: map.get($warn-light-colors, 0),
    27: map.get($warn-light-colors, 1),
    28: map.get($warn-light-colors, 5),
    29: map.get($warn-light-colors, 10)
  )
);

// 字体图标及背景颜色
$bg-light-colors: map.merge(
  /* 这里重复的原因是和黑色主题一一对应 */
    (
      1: #1e2b3f,
      2: #797b85,
      3: #aaacb8,
      4: #f1f3f6,
      5: #f7f8fa,
      6: #ffffff,
      7: #f1f3f6,
      8: #ffffff,
      9: #ffffff00,
      10: rgba(0, 0, 0, 0.5)
    ),
  (
    11: map.get($brand-light-colors, 0),
    // #E54C3A
    12: map.get($brand-light-colors, 1),
    // #FF5541
    13: map.get($brand-light-colors, 5),
    // #FF998D
    14: map.get($brand-light-colors, 10),
    // #FFF6F4
    15: map.get($advance-light-colors, 0),
    // #D93434
    16: map.get($advance-light-colors, 1),
    // #FF3D3D
    17: map.get($advance-light-colors, 5),
    // #FF8B8B
    18: map.get($advance-light-colors, 10),
    // #FFECEC
    19: map.get($dip-light-colors, 0),
    // #09956D
    20: map.get($dip-light-colors, 1),
    // #0BAF80
    21: map.get($dip-light-colors, 5),
    // #6DCFB3
    22: map.get($dip-light-colors, 10),
    // #EEFFFA
    23: map.get($sell-light-colors, 0),
    // #356BB8
    24: map.get($sell-light-colors, 1),
    // #3E7ED9
    25: map.get($sell-light-colors, 5),
    // #8BB2E8
    26: map.get($sell-light-colors, 10),
    // #F3FAFF
    27: map.get($king-light-colors, 0),
    // #A47752
    28: map.get($king-light-colors, 1),
    // #C18C60
    29: map.get($king-light-colors, 5),
    // #DABAA0
    30: map.get($king-light-colors, 10),
    // #F8F3EF
    31: map.get($warn-light-colors, 0),
    // #E59B32
    32: map.get($warn-light-colors, 1),
    // #FFAC37
    33: map.get($warn-light-colors, 5),
    // #FFCD87
    34: map.get($warn-light-colors, 10) // #FFF6EB
  )
);

// 分隔线和线框颜色
$line-light-colors: map.merge(
  (
    1: #82848f,
    2: #e5e6eb,
    3: #f1f3f6
  ),
  (
    4: map.get($brand-light-colors, 0),
    5: map.get($brand-light-colors, 1),
    6: map.get($brand-light-colors, 5),
    7: map.get($brand-light-colors, 10),
    8: map.get($advance-light-colors, 0),
    9: map.get($advance-light-colors, 1),
    10: map.get($advance-light-colors, 5),
    11: map.get($advance-light-colors, 10),
    12: map.get($dip-light-colors, 0),
    13: map.get($dip-light-colors, 1),
    14: map.get($dip-light-colors, 5),
    15: map.get($dip-light-colors, 10),
    16: map.get($sell-light-colors, 0),
    17: map.get($sell-light-colors, 1),
    18: map.get($sell-light-colors, 5),
    19: map.get($sell-light-colors, 10),
    20: map.get($king-light-colors, 0),
    21: map.get($king-light-colors, 1),
    22: map.get($king-light-colors, 5),
    23: map.get($king-light-colors, 10),
    24: map.get($warn-light-colors, 0),
    25: map.get($warn-light-colors, 1),
    26: map.get($warn-light-colors, 5),
    27: map.get($warn-light-colors, 10)
  )
);

.xc-theme-light {
  // 文本颜色
  @each $keyText, $valueText in $text-light-colors {
    .xs-font-color#{$keyText} {
      color: $valueText;
    }
  }
  // 字体图标颜色
  @each $keyIcon, $valueIcon in $bg-light-colors {
    .xs-icon-color#{$keyIcon} {
      color: $valueIcon;
    }
  }
  // 背景色
  @each $keyBg, $valueBg in $bg-light-colors {
    .xs-bg-color#{$keyBg} {
      background-color: $valueBg;
    }
  }
  // 分割线颜色
  @each $keyLine, $valueLine in $line-light-colors {
    .xs-line-color#{$keyLine} {
      background-color: $valueLine;
    }
  }
  // 边框线颜色
  @each $keyBorder, $valueBorder in $line-light-colors {
    .xs-border-color#{$keyBorder} {
      border-color: $valueBorder;
    }
  }

  // 左边边框线颜色
  @each $keyBorder, $valueBorder in $line-light-colors {
    .xs-border-left-color#{$keyBorder} {
      border-left-color: $valueBorder;
    }
  }
}

黑色主题

@use 'sass:map';
// 品牌色
$brand-dark-colors: (
  0: #e54c3a,
  1: #ff5541,
  2: #ff6654,
  3: #ff7767,
  4: #ff887a,
  5: #9f3931,
  6: #ffaaa0,
  7: #ffbbb3,
  8: #ffccc6,
  9: #ffddd9,
  10: #321b1f
);
// 功能色--涨
$advance-dark-colors: (
  0: #d9424e,
  1: #f14957,
  2: #f25b67,
  3: #f46d79,
  4: #f57f89,
  5: #97323e,
  6: #f8a4ab,
  7: #f9b6bc,
  8: #fac8cc,
  9: #fac8cc,
  10: #301922
);
// 功能色--跌
$dip-dark-colors: (
  0: #09956d,
  1: #0baf80,
  2: #23b78c,
  3: #3cbf99,
  4: #54c7a6,
  5: #0d6f57,
  6: #85d7bf,
  7: #9ddfcc,
  8: #b5e7d8,
  9: #ceefe6,
  10: #0e2828
);
// 功能色--亏、卖
$sell-dark-colors: (
  0: #356bb8,
  1: #3e7ed9,
  2: #518adc,
  3: #6598e1,
  4: #77a4e4,
  5: #2b528c,
  6: #9ebeec,
  7: #b2cbf0,
  8: #c5d8f3,
  9: #d8e5f7,
  10: #152136
);
// 辅助色--金
$king-dark-colors: (
  0: #a47752,
  1: #c18c60,
  2: #c7976f,
  3: #cda380,
  4: #d3ae8f,
  5: #7a5a44,
  6: #e0c5af,
  7: #e6d1bf,
  8: #ecdccf,
  9: #f3e8df,
  10: #201d20
);
// 辅助色--警示
$warn-dark-colors: (
  0: #e59b32,
  1: #ffac37,
  2: #ffb44b,
  3: #ffbd5f,
  4: #ffc473,
  5: #9f6d2b,
  6: #ffd59b,
  7: #ffdeaf,
  8: #ffe6c3,
  9: #ffeed7,
  10: #27201c
);

// 文本颜色
$text-dark-colors: map.merge(
  (
    1: #e4e9fd,
    2: #939baf,
    3: #6f7888,
    4: #1a1d24,
    5: #ffffff
  ),
  (
    6: map.get($brand-dark-colors, 0),
    7: map.get($brand-dark-colors, 1),
    8: map.get($brand-dark-colors, 5),
    9: map.get($brand-dark-colors, 10),
    10: map.get($advance-dark-colors, 0),
    11: map.get($advance-dark-colors, 1),
    12: map.get($advance-dark-colors, 5),
    13: map.get($advance-dark-colors, 10),
    14: map.get($dip-dark-colors, 0),
    15: map.get($dip-dark-colors, 1),
    16: map.get($dip-dark-colors, 5),
    17: map.get($dip-dark-colors, 10),
    18: map.get($sell-dark-colors, 0),
    19: map.get($sell-dark-colors, 1),
    20: map.get($sell-dark-colors, 5),
    21: map.get($sell-dark-colors, 10),
    22: map.get($king-dark-colors, 0),
    23: map.get($king-dark-colors, 1),
    24: map.get($king-dark-colors, 5),
    25: map.get($king-dark-colors, 10),
    26: map.get($warn-dark-colors, 0),
    27: map.get($warn-dark-colors, 1),
    28: map.get($warn-dark-colors, 5),
    29: map.get($warn-dark-colors, 10)
  )
);

// 背景颜色
$bg-dark-colors: map.merge(
  /* 这里重复的原因是和黑色主题一一对应 */
    (
      1: #cfd3e6,
      2: #939baf,
      3: #6f7887,
      4: #1a1d24,
      5: #14171d,
      6: #0f1119,
      7: #040406,
      8: #ffffff,
      9: #ffffff00,
      10: rgba(0, 0, 0, 0.5)
    ),
  (
    11: map.get($brand-dark-colors, 0),
    // #e54c3a
    12: map.get($brand-dark-colors, 1),
    // #ff5541
    13: map.get($brand-dark-colors, 5),
    // #9f3931
    14: map.get($brand-dark-colors, 10),
    // 321b1f
    15: map.get($advance-dark-colors, 0),
    16: map.get($advance-dark-colors, 1),
    17: map.get($advance-dark-colors, 5),
    18: map.get($advance-dark-colors, 10),
    19: map.get($dip-dark-colors, 0),
    20: map.get($dip-dark-colors, 1),
    21: map.get($dip-dark-colors, 5),
    22: map.get($dip-dark-colors, 10),
    23: map.get($sell-dark-colors, 0),
    24: map.get($sell-dark-colors, 1),
    25: map.get($sell-dark-colors, 5),
    26: map.get($sell-dark-colors, 10),
    27: map.get($king-dark-colors, 0),
    28: map.get($king-dark-colors, 1),
    29: map.get($king-dark-colors, 5),
    30: map.get($king-dark-colors, 10),
    31: map.get($warn-dark-colors, 0),
    32: map.get($warn-dark-colors, 1),
    33: map.get($warn-dark-colors, 5),
    34: map.get($warn-dark-colors, 10)
  )
);

// 分隔线和线框颜色
$line-dark-colors: map.merge(
  (
    1: #939baf,
    2: #6f7888,
    3: #222730
  ),
  (
    4: map.get($brand-dark-colors, 0),
    5: map.get($brand-dark-colors, 1),
    6: map.get($brand-dark-colors, 5),
    7: map.get($brand-dark-colors, 10),
    8: map.get($advance-dark-colors, 0),
    9: map.get($advance-dark-colors, 1),
    10: map.get($advance-dark-colors, 5),
    11: map.get($advance-dark-colors, 10),
    12: map.get($dip-dark-colors, 0),
    13: map.get($dip-dark-colors, 1),
    14: map.get($dip-dark-colors, 5),
    15: map.get($dip-dark-colors, 10),
    16: map.get($sell-dark-colors, 0),
    17: map.get($sell-dark-colors, 1),
    18: map.get($sell-dark-colors, 5),
    19: map.get($sell-dark-colors, 10),
    20: map.get($king-dark-colors, 0),
    21: map.get($king-dark-colors, 1),
    22: map.get($king-dark-colors, 5),
    23: map.get($king-dark-colors, 10),
    24: map.get($warn-dark-colors, 0),
    25: map.get($warn-dark-colors, 1),
    26: map.get($warn-dark-colors, 5),
    27: map.get($warn-dark-colors, 10)
  )
);

.xc-theme-dark {
  // 文本颜色
  @each $keyText, $valueText in $text-dark-colors {
    .xs-font-color#{$keyText} {
      color: $valueText;
    }
  }
  // 字体图标颜色
  @each $keyIcon, $valueIcon in $bg-dark-colors {
    .xs-icon-color#{$keyIcon} {
      color: $valueIcon;
    }
  }
  // 背景色
  @each $keyBg, $valueBg in $bg-dark-colors {
    .xs-bg-color#{$keyBg} {
      background-color: $valueBg;
    }
  }
  // 分割线颜色
  @each $keyLine, $valueLine in $line-dark-colors {
    .xs-line-color#{$keyLine} {
      background-color: $valueLine;
    }
  }
  // 边框线颜色
  @each $keyBorder, $valueBorder in $line-dark-colors {
    .xs-border-color#{$keyBorder} {
      border-color: $valueBorder;
    }
  }

  // 左边边框线颜色
  @each $keyBorder, $valueBorder in $line-dark-colors {
    .xs-border-left-color#{$keyBorder} {
      border-left-color: $valueBorder;
    }
  }
}

具体代码

git clone https://gitee.com/yuqifang/my-ui.git