Răsfoiți Sursa

feat(components): 添加 SvgIcon 组件

ccnnde 2 luni în urmă
părinte
comite
88d4014bac

+ 11 - 1
package.json

@@ -6,7 +6,10 @@
     "start": "vite --host --open --port 88",
     "start": "vite --host --open --port 88",
     "prod": "vue-tsc --noEmit && vite build --base=https://assets.le5lecdn.com/v/",
     "prod": "vue-tsc --noEmit && vite build --base=https://assets.le5lecdn.com/v/",
     "build": "vite build --base=/v/",
     "build": "vite build --base=/v/",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "icon:svg": "svgo -rf src/icons/svg -o src/icons/svg",
+    "icon:gen": "cd src/icons && node generate-icon.cjs",
+    "icon:see": "open src/icons/fonts/index.html"
   },
   },
   "dependencies": {
   "dependencies": {
     "@meta2d/activity-diagram": "^1.0.0",
     "@meta2d/activity-diagram": "^1.0.0",
@@ -51,10 +54,17 @@
     "postcss": "^8.4.6",
     "postcss": "^8.4.6",
     "postcss-import": "^14.1.0",
     "postcss-import": "^14.1.0",
     "postcss-nested": "^6.0.1",
     "postcss-nested": "^6.0.1",
+    "svgo": "^3.3.2",
+    "svgtofont": "4.2.0",
     "typescript": "^4.7.4",
     "typescript": "^4.7.4",
     "vite": "^4.4.2",
     "vite": "^4.4.2",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-monaco-editor": "^1.1.0",
     "vite-plugin-monaco-editor": "^1.1.0",
     "vue-tsc": "^1.8.3"
     "vue-tsc": "^1.8.3"
+  },
+  "pnpm": {
+    "overrides": {
+      "cheerio": "1.0.0-rc.12"
+    }
   }
   }
 }
 }

+ 19 - 0
src/icons/fonts/iconfont.css

@@ -0,0 +1,19 @@
+@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff') format('woff');
+  font-weight: normal;
+  font-style: normal;
+}
+
+[class^='svg-icon-'],
+[class*=' svg-icon-'] {
+  display: inline-block;
+  font: normal normal normal 14px/1 iconfont;
+  font-size: inherit;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.svg-icon-check:before { content: "\ea01"; }
+.svg-icon-close:before { content: "\ea02"; }

+ 8 - 0
src/icons/fonts/iconfont.ts

@@ -0,0 +1,8 @@
+export enum Iconfont {
+  Check = "svg-icon-check",
+  Close = "svg-icon-close"
+}
+
+export type IconfontClassname = "svg-icon-check" | "svg-icon-close"
+export type IconfontIcon = "check" | "close"
+export const IconfontPrefix = "svg-icon-"

BIN
src/icons/fonts/iconfont.woff


+ 100 - 0
src/icons/fonts/index.html

@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>iconfont</title>
+  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
+  
+  
+  
+  <link rel="stylesheet" href="iconfont.css" />
+  
+  <style>
+    *{margin: 0;padding: 0;list-style: none;}
+    body { color: #696969; font: 12px/1.5 tahoma, arial, \5b8b\4f53, sans-serif; }
+    a { color: #333; text-decoration: underline; }
+    a:hover { color: rgb(9, 73, 209); }
+    .header { color: #333; text-align: center; padding: 80px 0 60px 0; min-height: 153px; font-size: 14px; }
+    .header .logo svg { height: 120px; width: 120px; }
+    .header h1 { font-size: 42px; padding: 26px 0 10px 0; }
+    .header sup {font-size: 14px; margin: 8px 0 0 8px; position: absolute; color: #7b7b7b; }
+    .info {
+      color: #999;
+      font-weight: normal;
+      max-width: 346px;
+      margin: 0 auto;
+      padding: 20px 0;
+      font-size: 14px;
+    }
+
+    .icons { max-width: 1190px; margin: 0 auto; }
+    .icons ul { text-align: center; }
+    .icons ul li {
+      vertical-align: top;
+      width: 120px;
+      display: inline-block;
+      text-align: center;
+      background-color: rgba(0,0,0,.02);
+      border-radius: 3px;
+      padding: 29px 0 10px 0;
+      margin-right: 10px;
+      margin-top: 10px;
+      transition: all 0.6s ease;
+    }
+    .icons ul li:hover { background-color: rgba(0,0,0,.06); }
+    .icons ul li:hover span { color: #3c75e4; opacity: 1; }
+    .icons ul li .unicode { color: #8c8c8c; opacity: 0.3; }
+    .icons ul li h4 {
+      font-weight: normal;
+      padding: 10px 0 5px 0;
+      display: block;
+      color: #8c8c8c;
+      font-size: 14px;
+      line-height: 12px;
+      opacity: 0.8;
+    }
+    .icons ul li:hover h4 { opacity: 1; }
+    .icons ul li svg { width: 24px; height: 24px; }
+    .icons ul li:hover { color: #3c75e4; }
+    .footer { text-align: center; padding: 10px 0 90px 0; }
+    .footer a { text-align: center; padding: 10px 0 90px 0; color: #696969;}
+    .footer a:hover { color: #0949d1; }
+    .links { text-align: center; padding: 50px 0 0 0; font-size: 14px; }
+    
+    .icons ul li.class-icon { font-size: 21px; line-height: 21px; padding-bottom: 20px; }
+    .icons ul li.class-icon p{ font-size: 12px; }
+    .icons ul li.class-icon [class^="svg-icon-"]{ font-size: 26px; }
+    
+    
+    
+  </style>
+  
+</head>
+
+<body>
+  
+  <div class="header">
+    
+    <h1>iconfont<sup>1.0.0</sup></h1>
+    <div class="info">
+      
+    </div>
+    <p>
+      
+    </p>
+  </div>
+  <div class="icons">
+    <ul>
+      <li class="class-icon"><i class="svg-icon-check"></i><p class="name">check</p></li><li class="class-icon"><i class="svg-icon-close"></i><p class="name">close</p></li>
+    </ul>
+  </div>
+  <p class="links">
+    
+  </p>
+  <div class="footer">
+    
+    <div><a target="_blank" href="https://github.com/jaywcjlove/svgtofont">Created By svgtofont</a></div>
+  </div>
+</body>
+
+</html>

+ 60 - 0
src/icons/generate-icon.cjs

@@ -0,0 +1,60 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+const fs = require('fs');
+const path = require('path');
+const svgtofont = require('svgtofont');
+const { createTypescript } = require('svgtofont/lib/utils');
+
+const FONT_NAME = 'iconfont';
+const FONT_CLASS_PREFIX = 'svg-icon';
+const FONT_DIR_NAME = 'fonts';
+const SVG_DIR_NAME = 'svg';
+const STYLE_TEMPLATE_DIR_NAME = 'styles';
+
+const cwdPath = process.cwd();
+const svgPath = path.resolve(cwdPath, SVG_DIR_NAME);
+const fontPath = path.resolve(cwdPath, FONT_DIR_NAME);
+const styleTemplatePath = path.resolve(cwdPath, STYLE_TEMPLATE_DIR_NAME);
+
+const generateIconFont = async () => {
+  await svgtofont({
+    src: svgPath,
+    dist: fontPath,
+    fontName: FONT_NAME,
+    css: {
+      include: /\.css$/,
+    },
+    styleTemplates: styleTemplatePath,
+    classNamePrefix: FONT_CLASS_PREFIX,
+    svgicons2svgfont: {
+      fontHeight: 1000,
+      normalize: true,
+    },
+    emptyDist: false,
+    website: {
+      version: '1.0.0',
+    },
+  });
+
+  await createTypescript({
+    src: svgPath,
+    dist: fontPath,
+    fontName: FONT_NAME,
+    classNamePrefix: FONT_CLASS_PREFIX,
+    typescript: {
+      extension: 'ts',
+    },
+  });
+
+  deleteUselessFiles();
+  console.log('generate iconfont done!');
+};
+
+const deleteUselessFiles = () => {
+  const keepFilesRegex = /^(iconfont\.(ts|css|woff)|index\.html)$/;
+
+  fs.readdirSync(fontPath)
+    .filter((file) => !keepFilesRegex.test(file))
+    .forEach((file) => fs.unlinkSync(`${fontPath}/${file}`));
+};
+
+generateIconFont();

+ 18 - 0
src/icons/styles/_{{filename}}.css

@@ -0,0 +1,18 @@
+@font-face {
+  font-family: '{{fontname}}';
+  src: url('{{cssPath}}{{fontname}}.woff') format('woff');
+  font-weight: normal;
+  font-style: normal;
+}
+
+[class^='{{prefix}}-'],
+[class*=' {{prefix}}-'] {
+  display: inline-block;
+  font: normal normal normal 14px/1 {{fontname}};
+  font-size: inherit;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+{{cssString}}

+ 1 - 0
src/icons/svg/check.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path d="M328.021 816.683a40.11 40.11 0 0 1-34.688-20.182l-159.36-278.698a39.979 39.979 0 1 1 69.504-39.68l133.163 233.13 491.093-492.458a40.107 40.107 0 0 1 56.619-.086 40.107 40.107 0 0 1 .085 56.576l-526.25 527.787-1.878 1.92a39.98 39.98 0 0 1-28.288 11.69"/></svg>

+ 1 - 0
src/icons/svg/close.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path d="M567.68 511.915 822.485 257.28a39.253 39.253 0 0 0 0-55.595 39.253 39.253 0 0 0-55.594 0L512 456.192 257.11 201.6a39.253 39.253 0 0 0-55.595 0 39.253 39.253 0 0 0 0 55.595L456.32 511.787l-254.805 254.72a39.253 39.253 0 0 0 27.776 67.072c10.112 0 20.096-3.798 27.818-11.52L512 567.552l254.89 254.72c7.68 7.68 17.707 11.477 27.82 11.477a39.253 39.253 0 0 0 27.818-67.114z"/></svg>

+ 1 - 0
src/styles/index.css

@@ -1,5 +1,6 @@
 @import 'tdesign-vue-next/es/style/index.css';
 @import 'tdesign-vue-next/es/style/index.css';
 @import '../assets/fonts/fonts.css';
 @import '../assets/fonts/fonts.css';
+@import '../icons/fonts/iconfont.css';
 
 
 @import './var.css';
 @import './var.css';
 @import './tdesign.css';
 @import './tdesign.css';

+ 7 - 0
src/types/index.ts

@@ -0,0 +1,7 @@
+import { IconfontIcon } from "@/icons/fonts/iconfont";
+
+export interface IconObject {
+  name: IconfontIcon;
+  size?: number;
+  color?: string;
+}

+ 3 - 0
src/utils/index.ts

@@ -0,0 +1,3 @@
+export const addUnit = (val: number, unit: string = 'px'): string => {
+  return val + unit;
+};

+ 35 - 0
src/views/components/SvgIcon.vue

@@ -0,0 +1,35 @@
+<script setup lang="ts">
+import { computed } from 'vue';
+
+import { addUnit } from '@/utils';
+import { IconfontPrefix } from '@/icons/fonts/iconfont';
+
+import type { CSSProperties } from 'vue';
+import type { IconObject } from '@/types';
+
+interface Props extends IconObject {
+  marginTop?: number;
+}
+
+const props = defineProps<Props>();
+
+defineEmits<{
+  click: [];
+}>();
+
+const iconClass = computed(() => {
+  return IconfontPrefix + props.name;
+});
+
+const iconStyle = computed<CSSProperties>(() => {
+  return {
+    fontSize: props.size && addUnit(props.size),
+    color: props.color,
+    marginTop: props.marginTop && addUnit(props.marginTop),
+  };
+});
+</script>
+
+<template>
+  <i :class="iconClass" :style="iconStyle" @click="$emit('click')"></i>
+</template>