svg 模块独立

This commit is contained in:
icssoa 2025-04-23 17:14:01 +08:00
parent 9592a61e72
commit 34f8849f34
22 changed files with 1849 additions and 3664 deletions

1547
build/cool/eps.d.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -39,7 +39,7 @@
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@cool-vue/vite-plugin": "^8.0.3", "@cool-vue/vite-plugin": "^8.1.0",
"@intlify/unplugin-vue-i18n": "^6.0.3", "@intlify/unplugin-vue-i18n": "^6.0.3",
"@rushstack/eslint-patch": "^1.10.5", "@rushstack/eslint-patch": "^1.10.5",
"@tsconfig/node20": "^20.1.4", "@tsconfig/node20": "^20.1.4",

View File

@ -41,6 +41,9 @@
}, },
], ],
}, },
svg: {
skipNames: ["base"],
},
}; };
// 根目录 // 根目录
@ -851,13 +854,23 @@
const dirs = fs.readdirSync(dir, { const dirs = fs.readdirSync(dir, {
withFileTypes: true, withFileTypes: true,
}); });
// 获取当前目录的模块名
const moduleName = dir.match(/[\/\\](?:src\/(?:plugins|modules)\/)([^\/\\]+)/)?.[1] || "";
for (const d of dirs) { for (const d of dirs) {
if (d.isDirectory()) { if (d.isDirectory()) {
arr.push(...findSvg(dir + d.name + "/")); arr.push(...findSvg(dir + d.name + "/"));
} }
else { else {
if (path.extname(d.name) == ".svg") { if (path.extname(d.name) == ".svg") {
svgIcons.push(path.basename(d.name, ".svg")); const baseName = path.basename(d.name, ".svg");
// 判断是否需要跳过拼接模块名
let shouldSkip = config.svg.skipNames?.includes(moduleName);
// 跳过包含icon-
if (baseName.includes("icon-")) {
shouldSkip = true;
}
const iconName = shouldSkip ? baseName : `${moduleName}-${baseName}`;
svgIcons.push(iconName);
const svg = fs.readFileSync(dir + d.name) const svg = fs.readFileSync(dir + d.name)
.toString() .toString()
.replace(/(\r)|(\n)/g, "") .replace(/(\r)|(\n)/g, "")
@ -876,7 +889,7 @@
if (!/(viewBox="[^>+].*?")/g.test($2)) { if (!/(viewBox="[^>+].*?")/g.test($2)) {
content += `viewBox="0 0 ${width} ${height}"`; content += `viewBox="0 0 ${width} ${height}"`;
} }
return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`; return `<symbol id="icon-${iconName}" ${content}>`;
}) })
.replace("</svg>", "</symbol>"); .replace("</svg>", "</symbol>");
arr.push(svg); arr.push(svg);
@ -995,6 +1008,10 @@ if (typeof window !== 'undefined') {
config.reqUrl = getProxyTarget(options.proxy); config.reqUrl = getProxyTarget(options.proxy);
// 是否开启名称标签 // 是否开启名称标签
config.nameTag = options.nameTag ?? true; config.nameTag = options.nameTag ?? true;
// svg
if (options.svg) {
lodash.assign(config.svg, options.svg);
}
// Eps // Eps
if (options.eps) { if (options.eps) {
const { dist, mapping, api, enable = true } = options.eps; const { dist, mapping, api, enable = true } = options.eps;

View File

@ -1,6 +1,6 @@
{ {
"name": "@cool-vue/vite-plugin", "name": "@cool-vue/vite-plugin",
"version": "8.0.3", "version": "8.1.0",
"description": "cool-admin、cool-uni builder", "description": "cool-admin、cool-uni builder",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"main": "/dist/index.js", "main": "/dist/index.js",

File diff suppressed because it is too large Load Diff

View File

@ -38,4 +38,7 @@ export const config: Config.Data = {
}, },
], ],
}, },
svg: {
skipNames: ["base"],
},
}; };

View File

@ -4,7 +4,7 @@ import { demo } from "./demo";
import { getProxyTarget } from "./proxy"; import { getProxyTarget } from "./proxy";
import type { Config } from "../types"; import type { Config } from "../types";
import { virtual } from "./virtual"; import { virtual } from "./virtual";
import { merge } from "lodash"; import { assign, merge } from "lodash";
export function cool(options: Config.Options) { export function cool(options: Config.Options) {
// 应用类型admin | app // 应用类型admin | app
@ -16,6 +16,11 @@ export function cool(options: Config.Options) {
// 是否开启名称标签 // 是否开启名称标签
config.nameTag = options.nameTag ?? true; config.nameTag = options.nameTag ?? true;
// svg
if (options.svg) {
assign(config.svg, options.svg);
}
// Eps // Eps
if (options.eps) { if (options.eps) {
const { dist, mapping, api, enable = true } = options.eps; const { dist, mapping, api, enable = true } = options.eps;

View File

@ -2,6 +2,7 @@ import { readFileSync, readdirSync } from "fs";
import { basename, extname } from "path"; import { basename, extname } from "path";
import { rootDir } from "../utils"; import { rootDir } from "../utils";
import svgo from "svgo"; import svgo from "svgo";
import { config } from "../config";
let svgIcons: string[] = []; let svgIcons: string[] = [];
@ -10,12 +11,28 @@ function findSvg(dir: string) {
const dirs = readdirSync(dir, { const dirs = readdirSync(dir, {
withFileTypes: true, withFileTypes: true,
}); });
// 获取当前目录的模块名
const moduleName = dir.match(/[\/\\](?:src\/(?:plugins|modules)\/)([^\/\\]+)/)?.[1] || "";
for (const d of dirs) { for (const d of dirs) {
if (d.isDirectory()) { if (d.isDirectory()) {
arr.push(...findSvg(dir + d.name + "/")); arr.push(...findSvg(dir + d.name + "/"));
} else { } else {
if (extname(d.name) == ".svg") { if (extname(d.name) == ".svg") {
svgIcons.push(basename(d.name, ".svg")); const baseName = basename(d.name, ".svg");
// 判断是否需要跳过拼接模块名
let shouldSkip = config.svg.skipNames?.includes(moduleName);
// 跳过包含icon-
if (baseName.includes("icon-")) {
shouldSkip = true;
}
const iconName = shouldSkip ? baseName : `${moduleName}-${baseName}`;
svgIcons.push(iconName);
const svg = readFileSync(dir + d.name) const svg = readFileSync(dir + d.name)
.toString() .toString()
@ -37,7 +54,7 @@ function findSvg(dir: string) {
if (!/(viewBox="[^>+].*?")/g.test($2)) { if (!/(viewBox="[^>+].*?")/g.test($2)) {
content += `viewBox="0 0 ${width} ${height}"`; content += `viewBox="0 0 ${width} ${height}"`;
} }
return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`; return `<symbol id="icon-${iconName}" ${content}>`;
}) })
.replace("</svg>", "</symbol>"); .replace("</svg>", "</symbol>");
@ -45,6 +62,7 @@ function findSvg(dir: string) {
} }
} }
} }
return arr; return arr;
} }

View File

@ -78,9 +78,13 @@ export declare namespace Ctx {
export declare namespace Config { export declare namespace Config {
type Type = "app" | "admin"; type Type = "app" | "admin";
interface Eps { interface Eps {
// 是否开启Eps
enable: boolean; enable: boolean;
// 请求地址
api: "app" | "admin" | (string & {}); api: "app" | "admin" | (string & {});
// 输出目录
dist: string; dist: string;
// 映射
mapping: { mapping: {
type?: string; type?: string;
test?: string[]; test?: string[];
@ -88,11 +92,21 @@ export declare namespace Config {
}[]; }[];
} }
interface Options { interface Options {
// 应用类型
type: Config.Type; type: Config.Type;
// 代理配置
proxy: any; proxy: any;
// Eps
eps?: Partial<Config.Eps>; eps?: Partial<Config.Eps>;
// 是否开启演示模式
demo?: boolean; demo?: boolean;
// 是否开启名称标签
nameTag?: boolean; nameTag?: boolean;
// svg
svg?: {
// 跳过拼接模块名
skipNames?: string[];
};
} }
interface Data { interface Data {
type: Config.Type; type: Config.Type;

1482
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

6
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,6 @@
onlyBuiltDependencies:
- '@parcel/watcher'
- core-js
- es5-ext
- esbuild
- vue-demi

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1719536095395" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2583" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M61.44 512C61.44 263.168 263.168 61.44 512 61.44s450.56 201.728 450.56 450.56-201.728 450.56-450.56 450.56S61.44 760.832 61.44 512z m397.312 180.85888c29.61408 31.92832 76.88192 31.92832 106.496 0l199.63904-215.28576 0.90112-1.00352c17.01888-19.456 16.52736-50.44224-1.10592-69.2224-19.29216-20.52096-48.82432-23.81824-71.4752-7.96672l-150.58944 105.49248c-18.67776 13.1072-42.55744 13.1072-61.2352 0L330.1376 398.9504c-22.26176-15.5648-51.3024-12.34944-70.26688 7.84384l-0.55296 0.57344a46.8992 46.8992 0 0 0-0.90112 1.024c-17.14176 19.31264-16.83456 50.29888 0.69632 69.2224l199.65952 215.26528z" p-id="2584"></path></svg>

Before

Width:  |  Height:  |  Size: 957 B

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1719377496622" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6926" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M213.333 128h85.334v85.333h-85.334v213.334A85.333 85.333 0 0 1 128 512a85.333 85.333 0 0 1 85.333 85.333v213.334h85.334V896h-85.334C167.68 884.48 128 857.6 128 810.667V640a85.333 85.333 0 0 0-85.333-85.333H0v-85.334h42.667A85.333 85.333 0 0 0 128 384V213.333A85.333 85.333 0 0 1 213.333 128m597.334 0A85.333 85.333 0 0 1 896 213.333V384a85.333 85.333 0 0 0 85.333 85.333H1024v85.334h-42.667A85.333 85.333 0 0 0 896 640v170.667A85.333 85.333 0 0 1 810.667 896h-85.334v-85.333h85.334V597.333A85.333 85.333 0 0 1 896 512a85.333 85.333 0 0 1-85.333-85.333V213.333h-85.334V128h85.334M512 640a42.667 42.667 0 0 1 42.667 42.667A42.667 42.667 0 0 1 512 725.333a42.667 42.667 0 0 1-42.667-42.666A42.667 42.667 0 0 1 512 640m-170.667 0A42.667 42.667 0 0 1 384 682.667a42.667 42.667 0 0 1-42.667 42.666 42.667 42.667 0 0 1-42.666-42.666A42.667 42.667 0 0 1 341.333 640m341.334 0a42.667 42.667 0 0 1 42.666 42.667 42.667 42.667 0 0 1-42.666 42.666A42.667 42.667 0 0 1 640 682.667 42.667 42.667 0 0 1 682.667 640z" p-id="6927"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,18 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg
t="1739266182011"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="16931"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="128"
height="128"
>
<path
class="p"
d="M305.572 958.122c-20.41-0.01-36.95-16.56-36.94-36.97 0-3.35 0.46-6.69 1.36-9.92l84.28-302.19-184.32 1.42a36.055 36.055 0 0 1-30.89-16.24 36.946 36.946 0 0 1-3.6-34.7l193.8-474.36a36.97 36.97 0 0 1 34.22-22.98h362.23c20.42 0.01 36.96 16.57 36.95 36.99 0 4.65-0.88 9.27-2.6 13.6l-117.61 296.37h210.65c20.41 0.01 36.95 16.56 36.94 36.97 0 10.71-4.65 20.88-12.74 27.9l-547.57 475.05a36.875 36.875 0 0 1-24.16 9.06z m97.47-423.35c20.41 0.01 36.95 16.56 36.94 36.97 0 3.35-0.46 6.68-1.36 9.9l-64.45 231.07 379.98-329.68h-166.05c-20.39 0.02-36.94-16.5-36.96-36.89 0-4.68 0.88-9.33 2.61-13.68l117.62-296.39h-283.04l-163.49 400.05 177.9-1.42 0.3 0.07z"
p-id="16932"
></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -2,7 +2,7 @@
<div class="i18n-switch"> <div class="i18n-switch">
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<div class="cl-comm__icon"> <div class="cl-comm__icon">
<cl-svg name="lang" /> <cl-svg name="i18n-lang" />
</div> </div>
<template #dropdown> <template #dropdown>

View File

@ -29,7 +29,7 @@
<template v-else> <template v-else>
<!-- 图标 --> <!-- 图标 -->
<div class="cl-upload-item__icon"> <div class="cl-upload-item__icon">
<cl-svg :name="item.type" /> <cl-svg :name="'upload-' + item.type" />
</div> </div>
<!-- 文件名 --> <!-- 文件名 -->
<div class="cl-upload-item__name"> <div class="cl-upload-item__name">

View File

@ -39,13 +39,13 @@ export default (): ModuleConfig => {
name: '文档', name: '文档',
type: 'word', type: 'word',
color: '#53B7F4', color: '#53B7F4',
exts: ['doc', 'dot', 'wps', 'wpt', 'docx', 'dotx', 'docm', 'dotm'] exts: ['doc', 'docx', 'docm', 'dot', 'dotx', 'dotm']
}, },
{ {
name: '表格', name: '表格',
type: 'excel', type: 'excel',
color: '#53D39C', color: '#53D39C',
exts: ['xls', 'xlt', 'et', 'xlsx', 'xltx', 'xlsm', 'xltm'] exts: ['xls', 'xlsx', 'xlsm', 'xlt', 'xltx', 'xltm']
}, },
{ {
name: '演示', name: '演示',

View File

@ -0,0 +1 @@
export * from './utils';

View File

@ -35,7 +35,7 @@ export function fileRule(path?: string) {
// 获取规则 // 获取规则
export function getRule(type?: string) { export function getRule(type?: string) {
return (rules.find(e => e.type == type) || last(rules))!; return (rules.find(e => e.type == type?.replace('application/', '')) || last(rules))!;
} }
// 获取类型 // 获取类型

View File

@ -23,6 +23,8 @@
<cl-svg name="plus-border" /> <cl-svg name="plus-border" />
</div> </div>
</el-tooltip> </el-tooltip>
<slot name="left-op"></slot>
</div> </div>
<div v-if="config.enableKeySearch" class="search"> <div v-if="config.enableKeySearch" class="search">
@ -114,7 +116,11 @@
:selected="selected" :selected="selected"
:index="index" :index="index"
> >
<span>{{ item.name }}</span> <span
class="text-ellipsis overflow-hidden mr-2"
>
{{ item.name }}
</span>
</slot> </slot>
<cl-svg <cl-svg
@ -143,7 +149,11 @@
<!-- 右侧 --> <!-- 右侧 -->
<div class="cl-view-group__right"> <div class="cl-view-group__right">
<div class="head"> <div class="head">
<div class="icon" :class="{ 'is-fold': !isExpand }" @click="expand()"> <div
class="icon is-bg absolute left-[10px]"
:class="{ 'is-fold': !isExpand }"
@click="expand()"
>
<cl-svg name="back" /> <cl-svg name="back" />
</div> </div>
@ -152,6 +162,10 @@
{{ config.title }}{{ selected?.name || $t('未选择') }} {{ config.title }}{{ selected?.name || $t('未选择') }}
</span> </span>
</slot> </slot>
<div class="absolute right-[10px]">
<slot name="right-op"></slot>
</div>
</div> </div>
<div v-if="selected || config.custom" class="content"> <div v-if="selected || config.custom" class="content">
@ -425,7 +439,7 @@ function onContextMenu(e: any, item: ClViewGroup.Item) {
list: [ list: [
{ {
label: t('编辑'), label: t('编辑'),
hidden: !config.service._permission.update, hidden: !config.service._permission?.update,
callback(done) { callback(done) {
done(); done();
edit(item); edit(item);
@ -433,7 +447,7 @@ function onContextMenu(e: any, item: ClViewGroup.Item) {
}, },
{ {
label: t('删除'), label: t('删除'),
hidden: !config.service._permission.delete, hidden: !config.service._permission?.delete,
callback(done) { callback(done) {
done(); done();
remove(item); remove(item);
@ -474,7 +488,7 @@ defineExpose({
$left-width: v-bind('config.leftWidth'); $left-width: v-bind('config.leftWidth');
$bg: var(--el-bg-color); $bg: var(--el-bg-color);
.icon { :deep(.icon) {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -490,6 +504,10 @@ defineExpose({
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
} }
&.is-bg {
background-color: var(--el-fill-color-lighter);
}
&.is-fold { &.is-fold {
transform: rotate(180deg); transform: rotate(180deg);
} }
@ -647,16 +665,6 @@ defineExpose({
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }
.icon {
position: absolute;
left: 10px;
background-color: var(--el-fill-color-lighter);
&:hover {
background-color: var(--el-fill-color-light);
}
}
} }
.content { .content {

View File

@ -1,13 +1,13 @@
import { fileURLToPath, URL } from "node:url"; import { fileURLToPath, URL } from 'node:url';
import { ConfigEnv, UserConfig } from "vite"; import { ConfigEnv, UserConfig } from 'vite';
import vue from "@vitejs/plugin-vue"; import vue from '@vitejs/plugin-vue';
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from '@vitejs/plugin-vue-jsx';
import compression from "vite-plugin-compression"; import compression from 'vite-plugin-compression';
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"; import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import vueDevTools from "vite-plugin-vue-devtools"; import vueDevTools from 'vite-plugin-vue-devtools';
import { visualizer } from "rollup-plugin-visualizer"; import { visualizer } from 'rollup-plugin-visualizer';
import { proxy } from "./src/config/proxy"; import { proxy } from './src/config/proxy';
import { cool } from "@cool-vue/vite-plugin"; import { cool } from '@cool-vue/vite-plugin';
function toPath(dir: string) { function toPath(dir: string) {
return fileURLToPath(new URL(dir, import.meta.url)); return fileURLToPath(new URL(dir, import.meta.url));
@ -15,7 +15,7 @@ function toPath(dir: string) {
// https://vitejs.dev/config // https://vitejs.dev/config
export default ({ mode }: ConfigEnv): UserConfig => { export default ({ mode }: ConfigEnv): UserConfig => {
const isDev = mode === "development"; const isDev = mode === 'development';
return { return {
plugins: [ plugins: [
@ -24,12 +24,15 @@ export default ({ mode }: ConfigEnv): UserConfig => {
vueJsx(), vueJsx(),
// vueDevTools(), // vueDevTools(),
cool({ cool({
type: "admin", type: 'admin',
proxy, proxy,
eps: { eps: {
enable: true enable: true
}, },
demo: mode == "demo" // 是否开启演示模式 svg: {
skipNames: ['base', 'theme']
},
demo: mode == 'demo' // 是否开启演示模式
}), }),
// visualizer({ // visualizer({
// open: false, // open: false,
@ -37,10 +40,10 @@ export default ({ mode }: ConfigEnv): UserConfig => {
// brotliSize: true // brotliSize: true
// }), // }),
VueI18nPlugin({ VueI18nPlugin({
include: [toPath("./src/{modules,plugins}/**/locales/**")] include: [toPath('./src/{modules,plugins}/**/locales/**')]
}) })
], ],
base: "/", base: '/',
server: { server: {
port: 9000, port: 9000,
proxy, proxy,
@ -52,23 +55,23 @@ export default ({ mode }: ConfigEnv): UserConfig => {
preprocessorOptions: { preprocessorOptions: {
scss: { scss: {
charset: false, charset: false,
api: "modern-compiler" api: 'modern-compiler'
} }
} }
}, },
resolve: { resolve: {
alias: { alias: {
"/@": toPath("./src"), '/@': toPath('./src'),
"/$": toPath("./src/modules"), '/$': toPath('./src/modules'),
"/#": toPath("./src/plugins"), '/#': toPath('./src/plugins'),
"/~": toPath("./packages") '/~': toPath('./packages')
} }
}, },
esbuild: { esbuild: {
drop: isDev ? [] : ["console", "debugger"] drop: isDev ? [] : ['console', 'debugger']
}, },
build: { build: {
minify: "esbuild", minify: 'esbuild',
// terserOptions: { // terserOptions: {
// compress: { // compress: {
// drop_console: true, // drop_console: true,
@ -78,23 +81,23 @@ export default ({ mode }: ConfigEnv): UserConfig => {
sourcemap: isDev, sourcemap: isDev,
rollupOptions: { rollupOptions: {
output: { output: {
chunkFileNames: "static/js/[name]-[hash].js", chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: "static/js/[name]-[hash].js", entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: "static/[ext]/[name]-[hash].[ext]", assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks(id) { manualChunks(id) {
if (id.includes("node_modules")) { if (id.includes('node_modules')) {
if (!["@cool-vue/crud"].find((e) => id.includes(e))) { if (!['@cool-vue/crud'].find(e => id.includes(e))) {
if (id.includes("prettier")) { if (id.includes('prettier')) {
return; return;
} }
return id return id
.toString() .toString()
.split("node_modules/")[1] .split('node_modules/')[1]
.replace(".pnpm/", "") .replace('.pnpm/', '')
.split("/")[0]; .split('/')[0];
} else { } else {
return "comm"; return 'comm';
} }
} }
} }