mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2025-12-10 20:02:54 +00:00
Compare commits
2 Commits
ca823845ab
...
c496af0566
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c496af0566 | ||
|
|
c5e2ac3805 |
@ -165,10 +165,10 @@
|
||||
<body>
|
||||
<div class="preload__wrap" id="Loading">
|
||||
<div class="preload__container">
|
||||
<p class="preload__name"></p>
|
||||
<div class="preload__name"></div>
|
||||
<div class="preload__loading"></div>
|
||||
<p class="preload__title"></p>
|
||||
<p class="preload__sub-title"></p>
|
||||
<div class="preload__title"></div>
|
||||
<div class="preload__sub-title"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
24
packages/vite-plugin/dist/index.d.ts
vendored
24
packages/vite-plugin/dist/index.d.ts
vendored
@ -1,2 +1,24 @@
|
||||
import type { Config } from "../types";
|
||||
export declare function cool(options: Config.Options): (import("vite").Plugin<any> | Promise<import("vite").Plugin<any>>)[];
|
||||
export declare function cool(options: Config.Options): (import("vite").Plugin<any> | Promise<import("vite").Plugin<any>> | {
|
||||
name: string;
|
||||
enforce: "pre";
|
||||
config(): {
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: {
|
||||
postcssPlugin: string;
|
||||
prepare(): {
|
||||
Rule(rule: any): void;
|
||||
Declaration(decl: any): void;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
};
|
||||
};
|
||||
transform(code: string, id: string): {
|
||||
code: string;
|
||||
map: {
|
||||
mappings: string;
|
||||
};
|
||||
} | null;
|
||||
}[])[];
|
||||
|
||||
403
packages/vite-plugin/dist/index.js
vendored
403
packages/vite-plugin/dist/index.js
vendored
@ -1,8 +1,8 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fs'), require('path'), require('prettier'), require('axios'), require('lodash'), require('@vue/compiler-sfc'), require('magic-string'), require('glob'), require('node:util'), require('svgo')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'prettier', 'axios', 'lodash', '@vue/compiler-sfc', 'magic-string', 'glob', 'node:util', 'svgo'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.index = {}, global.fs, global.path, global.prettier, global.axios, global.lodash, global.compilerSfc, global.magicString, global.glob, global.util, global.svgo));
|
||||
})(this, (function (exports, fs, path, prettier, axios, lodash, compilerSfc, magicString, glob, util, svgo) { 'use strict';
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fs'), require('path'), require('prettier'), require('axios'), require('lodash'), require('@vue/compiler-sfc'), require('magic-string'), require('glob'), require('node:util'), require('svgo'), require('postcss-value-parser')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'prettier', 'axios', 'lodash', '@vue/compiler-sfc', 'magic-string', 'glob', 'node:util', 'svgo', 'postcss-value-parser'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.index = {}, global.fs, global.path, global.prettier, global.axios, global.lodash, global.compilerSfc, global.magicString, global.glob, global.util, global.svgo, global.valueParser));
|
||||
})(this, (function (exports, fs, path, prettier, axios, lodash, compilerSfc, magicString, glob, util, svgo, valueParser) { 'use strict';
|
||||
|
||||
const config = {
|
||||
type: "admin",
|
||||
@ -50,6 +50,7 @@
|
||||
function rootDir(path$1) {
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
case "uniapp-x":
|
||||
return path.join(process.env.UNI_INPUT_DIR, path$1);
|
||||
default:
|
||||
return path.join(process.cwd(), path$1);
|
||||
@ -136,6 +137,7 @@
|
||||
}
|
||||
switch (url) {
|
||||
case "app":
|
||||
case "uniapp-x":
|
||||
url = "/app/base/comm/eps";
|
||||
break;
|
||||
case "admin":
|
||||
@ -319,8 +321,8 @@
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
// 创建 Service
|
||||
async function createDts() {
|
||||
// 创建 Controller
|
||||
async function createController() {
|
||||
let controller = "";
|
||||
let chain = "";
|
||||
// 处理数据
|
||||
@ -379,7 +381,7 @@
|
||||
case "/page":
|
||||
res = `
|
||||
{
|
||||
pagination: { size: number; page: number; total: number; [key: string]: any };
|
||||
pagination: { size: number; page: number; total: number; [key: string]: any; };
|
||||
list: ${en} [];
|
||||
[key: string]: any;
|
||||
}
|
||||
@ -444,7 +446,7 @@
|
||||
|
||||
${controller}
|
||||
|
||||
type Service = {
|
||||
interface Service {
|
||||
/**
|
||||
* 基础请求
|
||||
*/
|
||||
@ -453,10 +455,7 @@
|
||||
method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
||||
data?: any;
|
||||
params?: any;
|
||||
headers?: {
|
||||
authorization?: string;
|
||||
[key: string]: any;
|
||||
},
|
||||
headers?: any,
|
||||
timeout?: number;
|
||||
proxy?: boolean;
|
||||
[key: string]: any;
|
||||
@ -469,19 +468,33 @@
|
||||
`;
|
||||
}
|
||||
// 文件内容
|
||||
const text = `
|
||||
declare namespace Eps {
|
||||
${createEntity()}
|
||||
${await createDts()}
|
||||
}
|
||||
let text = `
|
||||
${createEntity()}
|
||||
${await createController()}
|
||||
`;
|
||||
// 文件名
|
||||
let name = "eps.d.ts";
|
||||
if (config.type == "uniapp-x") {
|
||||
name = "eps.uts";
|
||||
text = text
|
||||
.replaceAll("interface ", "export interface ")
|
||||
.replaceAll("type Dict", "export type Dict")
|
||||
.replaceAll("[key: string]: any;", "");
|
||||
}
|
||||
else {
|
||||
text = `
|
||||
declare namespace Eps {
|
||||
${text}
|
||||
}
|
||||
`;
|
||||
}
|
||||
// 文本内容
|
||||
const content = await formatCode(text);
|
||||
const local_content = readFile(getEpsPath("eps.d.ts"));
|
||||
const local_content = readFile(getEpsPath(name));
|
||||
// 是否需要更新
|
||||
if (content && content != local_content) {
|
||||
// 创建 eps 描述文件
|
||||
fs.createWriteStream(getEpsPath("eps.d.ts"), {
|
||||
fs.createWriteStream(getEpsPath(name), {
|
||||
flags: "w",
|
||||
}).write(content);
|
||||
}
|
||||
@ -541,8 +554,18 @@
|
||||
}
|
||||
// 创建 dict
|
||||
async function createDict() {
|
||||
const url = config.reqUrl + "/" + config.type + "/dict/info/types";
|
||||
return axios
|
||||
let p = "";
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
case "uniapp-x":
|
||||
p = "/app";
|
||||
break;
|
||||
case "admin":
|
||||
p = "/admin";
|
||||
break;
|
||||
}
|
||||
const url = config.reqUrl + p + "/dict/info/types";
|
||||
const text = await axios
|
||||
.get(url)
|
||||
.then((res) => {
|
||||
const { code, data } = res.data;
|
||||
@ -557,6 +580,7 @@
|
||||
.catch(() => {
|
||||
error(`[cool-eps] Error:${url}`);
|
||||
});
|
||||
return text || "";
|
||||
}
|
||||
// 创建 eps
|
||||
async function createEps() {
|
||||
@ -787,7 +811,7 @@
|
||||
let ctx = {
|
||||
serviceLang: "Node",
|
||||
};
|
||||
if (config.type == "app") {
|
||||
if (config.type == "app" || config.type == "uniapp-x") {
|
||||
const manifest = readFile(rootDir("manifest.json"), true);
|
||||
// 文件路径
|
||||
const ctxPath = rootDir("pages.json");
|
||||
@ -1018,6 +1042,339 @@ if (typeof window !== 'undefined') {
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
/**
|
||||
* Tailwind CSS 特殊字符映射表
|
||||
* 用于将类名中的特殊字符转换为安全字符,避免编译或运行时冲突
|
||||
*/
|
||||
const TAILWIND_SAFE_CHAR_MAP = {
|
||||
"[": "-",
|
||||
"]": "-",
|
||||
"(": "-",
|
||||
")": "-",
|
||||
"#": "-h-",
|
||||
"!": "-i-",
|
||||
"/": "-s-",
|
||||
":": "-c-",
|
||||
",": "-2c-",
|
||||
};
|
||||
/**
|
||||
* Tailwind CSS 常用类名前缀集合
|
||||
* 按功能分类,便于维护和扩展
|
||||
*/
|
||||
const TAILWIND_CLASS_PREFIXES = [
|
||||
// 间距
|
||||
"p-",
|
||||
"px-",
|
||||
"py-",
|
||||
"pt-",
|
||||
"pr-",
|
||||
"pb-",
|
||||
"pl-",
|
||||
"m-",
|
||||
"mx-",
|
||||
"my-",
|
||||
"mt-",
|
||||
"mr-",
|
||||
"mb-",
|
||||
"ml-",
|
||||
"gap-",
|
||||
"gap-x-",
|
||||
"gap-y-",
|
||||
"space-x-",
|
||||
"space-y-",
|
||||
"inset-",
|
||||
"top-",
|
||||
"right-",
|
||||
"bottom-",
|
||||
"left-",
|
||||
// 尺寸
|
||||
"w-",
|
||||
"h-",
|
||||
"min-w-",
|
||||
"min-h-",
|
||||
"max-w-",
|
||||
"max-h-",
|
||||
// 排版
|
||||
"text-",
|
||||
"font-",
|
||||
"leading-",
|
||||
"tracking-",
|
||||
"indent-",
|
||||
// 边框
|
||||
"border-",
|
||||
"border-t-",
|
||||
"border-r-",
|
||||
"border-b-",
|
||||
"border-l-",
|
||||
"rounded-",
|
||||
"rounded-t-",
|
||||
"rounded-r-",
|
||||
"rounded-b-",
|
||||
"rounded-l-",
|
||||
"rounded-tl-",
|
||||
"rounded-tr-",
|
||||
"rounded-br-",
|
||||
"rounded-bl-",
|
||||
// 效果
|
||||
"shadow-",
|
||||
"blur-",
|
||||
"brightness-",
|
||||
"contrast-",
|
||||
"drop-shadow-",
|
||||
"grayscale-",
|
||||
"hue-rotate-",
|
||||
"invert-",
|
||||
"saturate-",
|
||||
"sepia-",
|
||||
"backdrop-blur-",
|
||||
"backdrop-brightness-",
|
||||
"backdrop-contrast-",
|
||||
"backdrop-grayscale-",
|
||||
"backdrop-hue-rotate-",
|
||||
"backdrop-invert-",
|
||||
"backdrop-opacity-",
|
||||
"backdrop-saturate-",
|
||||
"backdrop-sepia-",
|
||||
// 动画
|
||||
"transition-",
|
||||
"duration-",
|
||||
"delay-",
|
||||
"animate-",
|
||||
// 变换
|
||||
"translate-x-",
|
||||
"translate-y-",
|
||||
"rotate-",
|
||||
"scale-",
|
||||
"scale-x-",
|
||||
"scale-y-",
|
||||
"skew-x-",
|
||||
"skew-y-",
|
||||
"origin-",
|
||||
// 布局
|
||||
"columns-",
|
||||
"break-after-",
|
||||
"break-before-",
|
||||
"break-inside-",
|
||||
// Flexbox 和 Grid
|
||||
"basis-",
|
||||
"grow-",
|
||||
"shrink-",
|
||||
"grid-cols-",
|
||||
"grid-rows-",
|
||||
"col-span-",
|
||||
"row-span-",
|
||||
"col-start-",
|
||||
"col-end-",
|
||||
"row-start-",
|
||||
"row-end-",
|
||||
// SVG
|
||||
"stroke-",
|
||||
"stroke-w-",
|
||||
"fill-",
|
||||
];
|
||||
/**
|
||||
* Tailwind CSS 颜色变量映射
|
||||
* 用于移除不需要的 CSS 变量声明
|
||||
*/
|
||||
const TAILWIND_COLOR_VARS = {
|
||||
"--tw-text-opacity": 1,
|
||||
"--tw-bg-opacity": 1,
|
||||
};
|
||||
/**
|
||||
* 转换类名中的特殊字符为安全字符
|
||||
* @param value 原始类名或值
|
||||
* @param isSelector 是否为选择器(true)或普通值(false)
|
||||
* @returns 转换后的安全字符串
|
||||
*/
|
||||
function toSafeTailwindClass(value, isSelector = false) {
|
||||
// 处理任意值语法(如 w-[100px])
|
||||
const arbitrary = value.match(/^(.+?)-\[(.*?)\]$/);
|
||||
if (arbitrary) {
|
||||
if (isSelector)
|
||||
return value;
|
||||
const [, prefix, content] = arbitrary;
|
||||
const safePrefix = toSafeTailwindClass(prefix, isSelector);
|
||||
const safeContent = content.replace(/[^\d.\w]/g, "-");
|
||||
return `${safePrefix}-${safeContent}`;
|
||||
}
|
||||
let safeValue = value;
|
||||
// 移除转义字符
|
||||
if (safeValue.includes("\\")) {
|
||||
safeValue = safeValue.replace(/\\/g, "");
|
||||
}
|
||||
// 替换特殊字符
|
||||
for (const [char, rep] of Object.entries(TAILWIND_SAFE_CHAR_MAP)) {
|
||||
const reg = new RegExp("\\" + char, "g");
|
||||
if (reg.test(safeValue)) {
|
||||
safeValue = safeValue.replace(reg, rep);
|
||||
}
|
||||
}
|
||||
return safeValue;
|
||||
}
|
||||
/**
|
||||
* 将现代 rgb 格式(如 rgb(234 179 8 / 0.1))转换为标准 rgba 格式
|
||||
* @param value rgb 字符串
|
||||
* @returns 标准 rgba 字符串
|
||||
*/
|
||||
function rgbToRgba(value) {
|
||||
const match = value.match(/rgb\(([\d\s]+)\/\s*([\d.]+)\)/);
|
||||
if (match) {
|
||||
const [, rgb, alpha] = match;
|
||||
const [r, g, b] = rgb.split(/\s+/);
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* PostCSS 插件:将 rem 单位转换为 rpx,并处理 Tailwind 特殊字符
|
||||
* @param options 配置项
|
||||
* @returns PostCSS 插件对象
|
||||
*/
|
||||
function postcssRemToRpx(options) {
|
||||
return {
|
||||
postcssPlugin: "vite-cool-uniappx-remToRpx",
|
||||
prepare() {
|
||||
const handledSelectors = new Set();
|
||||
const { remUnit = 16, remPrecision = 6, rpxRatio = 2 } = options;
|
||||
const factor = remUnit * rpxRatio;
|
||||
return {
|
||||
Rule(rule) {
|
||||
const sel = rule.selector;
|
||||
if (handledSelectors.has(sel))
|
||||
return;
|
||||
const safeSel = toSafeTailwindClass(sel, true);
|
||||
if (safeSel !== sel) {
|
||||
rule.selector = safeSel;
|
||||
handledSelectors.add(sel);
|
||||
}
|
||||
},
|
||||
Declaration(decl) {
|
||||
if (decl.value.includes("/* no-rem */"))
|
||||
return;
|
||||
if (TAILWIND_COLOR_VARS[decl.prop]) {
|
||||
decl.remove();
|
||||
return;
|
||||
}
|
||||
if (decl.value.includes("rgb(") && decl.value.includes("/")) {
|
||||
decl.value = rgbToRgba(decl.value);
|
||||
}
|
||||
if (decl.value.includes("rpx") && decl.parent.selector.includes("text-")) {
|
||||
decl.prop = "font-size";
|
||||
}
|
||||
const parsed = valueParser(decl.value);
|
||||
let changed = false;
|
||||
parsed.walk((node) => {
|
||||
if (node.type === "word") {
|
||||
// rem 转 rpx
|
||||
const unit = valueParser.unit(node.value);
|
||||
if (unit?.unit === "rem") {
|
||||
const num = unit.number;
|
||||
const precision = (num.split(".")[1] || "").length;
|
||||
const rpxVal = (parseFloat(num) * factor)
|
||||
.toFixed(precision || remPrecision)
|
||||
.replace(/\.?0+$/, "");
|
||||
node.value = `${rpxVal}rpx`;
|
||||
changed = true;
|
||||
}
|
||||
// 特殊字符处理
|
||||
if (node.value.includes(".") || /[[\]()#!/:,]/.test(node.value)) {
|
||||
const safe = toSafeTailwindClass(node.value, true);
|
||||
if (safe !== node.value) {
|
||||
node.value = safe;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理 var(--tw-xxx)
|
||||
if (node.type === "function" && node.value === "var") {
|
||||
if (node.nodes.length > 0 && node.nodes[0].value.startsWith("--tw-")) {
|
||||
node.type = "word";
|
||||
node.value = TAILWIND_COLOR_VARS[node.nodes[0].value];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
decl.value = parsed.toString();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
postcssRemToRpx.postcss = true;
|
||||
/**
|
||||
* Vite 插件:自动转换 .uvue 文件中的 Tailwind 类名为安全字符
|
||||
* 并自动注入 rem 转 rpx 的 PostCSS 插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件对象
|
||||
*/
|
||||
function tailwindTransformPlugin(options = {}) {
|
||||
const merged = {
|
||||
remUnit: 16,
|
||||
remPrecision: 6,
|
||||
rpxRatio: 2,
|
||||
...options,
|
||||
};
|
||||
return {
|
||||
name: "vite-cool-uniappx-tailwind",
|
||||
enforce: "pre",
|
||||
config() {
|
||||
return {
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [postcssRemToRpx(merged)],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
transform(code, id) {
|
||||
if (!id.includes(".uvue"))
|
||||
return null;
|
||||
let resultCode = code;
|
||||
const tplMatch = resultCode.match(/<template>([\s\S]*?)<\/template>/);
|
||||
if (!tplMatch?.[1])
|
||||
return null;
|
||||
let tpl = tplMatch[1];
|
||||
const tplOrigin = tpl;
|
||||
TAILWIND_CLASS_PREFIXES.forEach((prefix) => {
|
||||
for (const [char, rep] of Object.entries(TAILWIND_SAFE_CHAR_MAP)) {
|
||||
const reg = new RegExp(`(${prefix}[^\\s'"]*?\\${char}[^\\s'"]*?)`, "g");
|
||||
const matches = [...tpl.matchAll(reg)];
|
||||
matches.forEach((m) => {
|
||||
const raw = m[1];
|
||||
const safe = raw.replace(new RegExp("\\" + char, "g"), rep);
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log(`类名转换: ${raw} → ${safe}`);
|
||||
}
|
||||
tpl = tpl.replace(raw, safe);
|
||||
});
|
||||
}
|
||||
});
|
||||
if (tpl !== tplOrigin) {
|
||||
resultCode = resultCode.replace(tplMatch[0], `<template>${tpl}</template>`);
|
||||
return {
|
||||
code: resultCode,
|
||||
map: { mappings: "" },
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* uniappX 入口,自动注入 Tailwind 类名转换插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件数组
|
||||
*/
|
||||
function uniappX(options) {
|
||||
if (config.type == "uniapp-x") {
|
||||
return [tailwindTransformPlugin(options?.tailwind)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function cool(options) {
|
||||
// 应用类型,admin | app
|
||||
config.type = options.type;
|
||||
@ -1047,7 +1404,7 @@ if (typeof window !== 'undefined') {
|
||||
lodash.merge(config.eps.mapping, mapping);
|
||||
}
|
||||
}
|
||||
return [base(), virtual(), demo(options.demo)];
|
||||
return [base(), virtual(), uniappX(), demo(options.demo)];
|
||||
}
|
||||
|
||||
exports.cool = cool;
|
||||
|
||||
67
packages/vite-plugin/dist/uniapp-x.d.ts
vendored
Normal file
67
packages/vite-plugin/dist/uniapp-x.d.ts
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
interface PostcssRemToRpxOptions {
|
||||
remUnit?: number;
|
||||
remPrecision?: number;
|
||||
rpxRatio?: number;
|
||||
}
|
||||
interface TailwindTransformOptions extends PostcssRemToRpxOptions {
|
||||
}
|
||||
/**
|
||||
* Vite 插件:自动转换 .uvue 文件中的 Tailwind 类名为安全字符
|
||||
* 并自动注入 rem 转 rpx 的 PostCSS 插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件对象
|
||||
*/
|
||||
export declare function tailwindTransformPlugin(options?: TailwindTransformOptions): {
|
||||
name: string;
|
||||
enforce: "pre";
|
||||
config(): {
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: {
|
||||
postcssPlugin: string;
|
||||
prepare(): {
|
||||
Rule(rule: any): void;
|
||||
Declaration(decl: any): void;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
};
|
||||
};
|
||||
transform(code: string, id: string): {
|
||||
code: string;
|
||||
map: {
|
||||
mappings: string;
|
||||
};
|
||||
} | null;
|
||||
};
|
||||
/**
|
||||
* uniappX 入口,自动注入 Tailwind 类名转换插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件数组
|
||||
*/
|
||||
export declare function uniappX(options?: {
|
||||
tailwind?: TailwindTransformOptions;
|
||||
}): {
|
||||
name: string;
|
||||
enforce: "pre";
|
||||
config(): {
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: {
|
||||
postcssPlugin: string;
|
||||
prepare(): {
|
||||
Rule(rule: any): void;
|
||||
Declaration(decl: any): void;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
};
|
||||
};
|
||||
transform(code: string, id: string): {
|
||||
code: string;
|
||||
map: {
|
||||
mappings: string;
|
||||
};
|
||||
} | null;
|
||||
}[];
|
||||
export {};
|
||||
9
packages/vite-plugin/dist/uniapp-x/color.d.ts
vendored
Normal file
9
packages/vite-plugin/dist/uniapp-x/color.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
type primaryColor = "emerald" | "green" | "lime" | "orange" | "amber" | "yellow" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink";
|
||||
type surfaceColor = "slate" | "gray" | "zinc" | "neutral" | "stone" | "soho" | "viva" | "ocean";
|
||||
export declare function colorPalette(options: {
|
||||
primary: primaryColor;
|
||||
surface: surfaceColor;
|
||||
}): {
|
||||
[x: string]: string;
|
||||
};
|
||||
export {};
|
||||
38
packages/vite-plugin/dist/uniapp-x/index.d.ts
vendored
Normal file
38
packages/vite-plugin/dist/uniapp-x/index.d.ts
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
interface PostcssRemToRpxOptions {
|
||||
remUnit?: number;
|
||||
remPrecision?: number;
|
||||
rpxRatio?: number;
|
||||
}
|
||||
interface TailwindTransformOptions extends PostcssRemToRpxOptions {
|
||||
}
|
||||
/**
|
||||
* uniappX 入口,自动注入 Tailwind 类名转换插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件数组
|
||||
*/
|
||||
export declare function uniappX(options?: {
|
||||
tailwind?: TailwindTransformOptions;
|
||||
}): {
|
||||
name: string;
|
||||
enforce: "pre";
|
||||
config(): {
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: {
|
||||
postcssPlugin: string;
|
||||
prepare(): {
|
||||
Rule(rule: any): void;
|
||||
Declaration(decl: any): void;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
};
|
||||
};
|
||||
transform(code: string, id: string): {
|
||||
code: string;
|
||||
map: {
|
||||
mappings: string;
|
||||
};
|
||||
} | null;
|
||||
}[];
|
||||
export {};
|
||||
@ -13,7 +13,7 @@ export async function createCtx() {
|
||||
serviceLang: "Node",
|
||||
};
|
||||
|
||||
if (config.type == "app") {
|
||||
if (config.type == "app" || config.type == "uniapp-x") {
|
||||
const manifest = readFile(rootDir("manifest.json"), true);
|
||||
|
||||
// 文件路径
|
||||
|
||||
@ -20,6 +20,7 @@ function getEpsUrl() {
|
||||
|
||||
switch (url) {
|
||||
case "app":
|
||||
case "uniapp-x":
|
||||
url = "/app/base/comm/eps";
|
||||
break;
|
||||
|
||||
@ -237,8 +238,8 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
return t0;
|
||||
}
|
||||
|
||||
// 创建 Service
|
||||
async function createDts() {
|
||||
// 创建 Controller
|
||||
async function createController() {
|
||||
let controller = "";
|
||||
let chain = "";
|
||||
|
||||
@ -311,7 +312,7 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
case "/page":
|
||||
res = `
|
||||
{
|
||||
pagination: { size: number; page: number; total: number; [key: string]: any };
|
||||
pagination: { size: number; page: number; total: number; [key: string]: any; };
|
||||
list: ${en} [];
|
||||
[key: string]: any;
|
||||
}
|
||||
@ -387,7 +388,7 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
|
||||
${controller}
|
||||
|
||||
type Service = {
|
||||
interface Service {
|
||||
/**
|
||||
* 基础请求
|
||||
*/
|
||||
@ -396,10 +397,7 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
||||
data?: any;
|
||||
params?: any;
|
||||
headers?: {
|
||||
authorization?: string;
|
||||
[key: string]: any;
|
||||
},
|
||||
headers?: any,
|
||||
timeout?: number;
|
||||
proxy?: boolean;
|
||||
[key: string]: any;
|
||||
@ -413,22 +411,37 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
}
|
||||
|
||||
// 文件内容
|
||||
const text = `
|
||||
declare namespace Eps {
|
||||
${createEntity()}
|
||||
${await createDts()}
|
||||
}
|
||||
let text = `
|
||||
${createEntity()}
|
||||
${await createController()}
|
||||
`;
|
||||
|
||||
// 文件名
|
||||
let name = "eps.d.ts";
|
||||
|
||||
if (config.type == "uniapp-x") {
|
||||
name = "eps.uts";
|
||||
text = text
|
||||
.replaceAll("interface ", "export interface ")
|
||||
.replaceAll("type Dict", "export type Dict")
|
||||
.replaceAll("[key: string]: any;", "");
|
||||
} else {
|
||||
text = `
|
||||
declare namespace Eps {
|
||||
${text}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
// 文本内容
|
||||
const content = await formatCode(text);
|
||||
|
||||
const local_content = readFile(getEpsPath("eps.d.ts"));
|
||||
const local_content = readFile(getEpsPath(name));
|
||||
|
||||
// 是否需要更新
|
||||
if (content && content != local_content) {
|
||||
// 创建 eps 描述文件
|
||||
createWriteStream(getEpsPath("eps.d.ts"), {
|
||||
createWriteStream(getEpsPath(name), {
|
||||
flags: "w",
|
||||
}).write(content);
|
||||
}
|
||||
@ -500,9 +513,22 @@ function createService() {
|
||||
|
||||
// 创建 dict
|
||||
async function createDict() {
|
||||
const url = config.reqUrl + "/" + config.type + "/dict/info/types";
|
||||
let p = "";
|
||||
|
||||
return axios
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
case "uniapp-x":
|
||||
p = "/app";
|
||||
break;
|
||||
|
||||
case "admin":
|
||||
p = "/admin";
|
||||
break;
|
||||
}
|
||||
|
||||
const url = config.reqUrl + p + "/dict/info/types";
|
||||
|
||||
const text = await axios
|
||||
.get(url)
|
||||
.then((res) => {
|
||||
const { code, data } = res.data as { code: number; data: any[] };
|
||||
@ -520,6 +546,8 @@ async function createDict() {
|
||||
.catch(() => {
|
||||
error(`[cool-eps] Error:${url}`);
|
||||
});
|
||||
|
||||
return text || "";
|
||||
}
|
||||
|
||||
// 创建 eps
|
||||
|
||||
@ -5,6 +5,7 @@ import { getProxyTarget } from "./proxy";
|
||||
import type { Config } from "../types";
|
||||
import { virtual } from "./virtual";
|
||||
import { assign, merge } from "lodash";
|
||||
import { uniappX } from "./uniapp-x";
|
||||
|
||||
export function cool(options: Config.Options) {
|
||||
// 应用类型,admin | app
|
||||
@ -44,5 +45,5 @@ export function cool(options: Config.Options) {
|
||||
}
|
||||
}
|
||||
|
||||
return [base(), virtual(), demo(options.demo)];
|
||||
return [base(), virtual(), uniappX(), demo(options.demo)];
|
||||
}
|
||||
|
||||
370
packages/vite-plugin/src/uniapp-x/index.ts
Normal file
370
packages/vite-plugin/src/uniapp-x/index.ts
Normal file
@ -0,0 +1,370 @@
|
||||
// @ts-ignore
|
||||
import valueParser from "postcss-value-parser";
|
||||
import { config } from "../config";
|
||||
|
||||
/**
|
||||
* Tailwind CSS 特殊字符映射表
|
||||
* 用于将类名中的特殊字符转换为安全字符,避免编译或运行时冲突
|
||||
*/
|
||||
const TAILWIND_SAFE_CHAR_MAP: Record<string, string> = {
|
||||
"[": "-",
|
||||
"]": "-",
|
||||
"(": "-",
|
||||
")": "-",
|
||||
"#": "-h-",
|
||||
"!": "-i-",
|
||||
"/": "-s-",
|
||||
":": "-c-",
|
||||
",": "-2c-",
|
||||
};
|
||||
|
||||
/**
|
||||
* Tailwind CSS 常用类名前缀集合
|
||||
* 按功能分类,便于维护和扩展
|
||||
*/
|
||||
const TAILWIND_CLASS_PREFIXES: string[] = [
|
||||
// 间距
|
||||
"p-",
|
||||
"px-",
|
||||
"py-",
|
||||
"pt-",
|
||||
"pr-",
|
||||
"pb-",
|
||||
"pl-",
|
||||
"m-",
|
||||
"mx-",
|
||||
"my-",
|
||||
"mt-",
|
||||
"mr-",
|
||||
"mb-",
|
||||
"ml-",
|
||||
"gap-",
|
||||
"gap-x-",
|
||||
"gap-y-",
|
||||
"space-x-",
|
||||
"space-y-",
|
||||
"inset-",
|
||||
"top-",
|
||||
"right-",
|
||||
"bottom-",
|
||||
"left-",
|
||||
|
||||
// 尺寸
|
||||
"w-",
|
||||
"h-",
|
||||
"min-w-",
|
||||
"min-h-",
|
||||
"max-w-",
|
||||
"max-h-",
|
||||
|
||||
// 排版
|
||||
"text-",
|
||||
"font-",
|
||||
"leading-",
|
||||
"tracking-",
|
||||
"indent-",
|
||||
|
||||
// 边框
|
||||
"border-",
|
||||
"border-t-",
|
||||
"border-r-",
|
||||
"border-b-",
|
||||
"border-l-",
|
||||
"rounded-",
|
||||
"rounded-t-",
|
||||
"rounded-r-",
|
||||
"rounded-b-",
|
||||
"rounded-l-",
|
||||
"rounded-tl-",
|
||||
"rounded-tr-",
|
||||
"rounded-br-",
|
||||
"rounded-bl-",
|
||||
|
||||
// 效果
|
||||
"shadow-",
|
||||
"blur-",
|
||||
"brightness-",
|
||||
"contrast-",
|
||||
"drop-shadow-",
|
||||
"grayscale-",
|
||||
"hue-rotate-",
|
||||
"invert-",
|
||||
"saturate-",
|
||||
"sepia-",
|
||||
"backdrop-blur-",
|
||||
"backdrop-brightness-",
|
||||
"backdrop-contrast-",
|
||||
"backdrop-grayscale-",
|
||||
"backdrop-hue-rotate-",
|
||||
"backdrop-invert-",
|
||||
"backdrop-opacity-",
|
||||
"backdrop-saturate-",
|
||||
"backdrop-sepia-",
|
||||
|
||||
// 动画
|
||||
"transition-",
|
||||
"duration-",
|
||||
"delay-",
|
||||
"animate-",
|
||||
|
||||
// 变换
|
||||
"translate-x-",
|
||||
"translate-y-",
|
||||
"rotate-",
|
||||
"scale-",
|
||||
"scale-x-",
|
||||
"scale-y-",
|
||||
"skew-x-",
|
||||
"skew-y-",
|
||||
"origin-",
|
||||
|
||||
// 布局
|
||||
"columns-",
|
||||
"break-after-",
|
||||
"break-before-",
|
||||
"break-inside-",
|
||||
|
||||
// Flexbox 和 Grid
|
||||
"basis-",
|
||||
"grow-",
|
||||
"shrink-",
|
||||
"grid-cols-",
|
||||
"grid-rows-",
|
||||
"col-span-",
|
||||
"row-span-",
|
||||
"col-start-",
|
||||
"col-end-",
|
||||
"row-start-",
|
||||
"row-end-",
|
||||
|
||||
// SVG
|
||||
"stroke-",
|
||||
"stroke-w-",
|
||||
"fill-",
|
||||
];
|
||||
|
||||
/**
|
||||
* Tailwind CSS 颜色变量映射
|
||||
* 用于移除不需要的 CSS 变量声明
|
||||
*/
|
||||
const TAILWIND_COLOR_VARS: Record<string, number> = {
|
||||
"--tw-text-opacity": 1,
|
||||
"--tw-bg-opacity": 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* 转换类名中的特殊字符为安全字符
|
||||
* @param value 原始类名或值
|
||||
* @param isSelector 是否为选择器(true)或普通值(false)
|
||||
* @returns 转换后的安全字符串
|
||||
*/
|
||||
function toSafeTailwindClass(value: string, isSelector: boolean = false): string {
|
||||
// 处理任意值语法(如 w-[100px])
|
||||
const arbitrary = value.match(/^(.+?)-\[(.*?)\]$/);
|
||||
if (arbitrary) {
|
||||
if (isSelector) return value;
|
||||
const [, prefix, content] = arbitrary;
|
||||
const safePrefix = toSafeTailwindClass(prefix, isSelector);
|
||||
const safeContent = content.replace(/[^\d.\w]/g, "-");
|
||||
return `${safePrefix}-${safeContent}`;
|
||||
}
|
||||
|
||||
let safeValue = value;
|
||||
|
||||
// 移除转义字符
|
||||
if (safeValue.includes("\\")) {
|
||||
safeValue = safeValue.replace(/\\/g, "");
|
||||
}
|
||||
|
||||
// 替换特殊字符
|
||||
for (const [char, rep] of Object.entries(TAILWIND_SAFE_CHAR_MAP)) {
|
||||
const reg = new RegExp("\\" + char, "g");
|
||||
if (reg.test(safeValue)) {
|
||||
safeValue = safeValue.replace(reg, rep);
|
||||
}
|
||||
}
|
||||
|
||||
return safeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将现代 rgb 格式(如 rgb(234 179 8 / 0.1))转换为标准 rgba 格式
|
||||
* @param value rgb 字符串
|
||||
* @returns 标准 rgba 字符串
|
||||
*/
|
||||
function rgbToRgba(value: string): string {
|
||||
const match = value.match(/rgb\(([\d\s]+)\/\s*([\d.]+)\)/);
|
||||
if (match) {
|
||||
const [, rgb, alpha] = match;
|
||||
const [r, g, b] = rgb.split(/\s+/);
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
interface PostcssRemToRpxOptions {
|
||||
remUnit?: number;
|
||||
remPrecision?: number;
|
||||
rpxRatio?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostCSS 插件:将 rem 单位转换为 rpx,并处理 Tailwind 特殊字符
|
||||
* @param options 配置项
|
||||
* @returns PostCSS 插件对象
|
||||
*/
|
||||
function postcssRemToRpx(options: PostcssRemToRpxOptions) {
|
||||
return {
|
||||
postcssPlugin: "vite-cool-uniappx-remToRpx",
|
||||
prepare() {
|
||||
const handledSelectors = new Set<string>();
|
||||
const { remUnit = 16, remPrecision = 6, rpxRatio = 2 } = options;
|
||||
const factor = remUnit * rpxRatio;
|
||||
|
||||
return {
|
||||
Rule(rule: any) {
|
||||
const sel = rule.selector;
|
||||
if (handledSelectors.has(sel)) return;
|
||||
const safeSel = toSafeTailwindClass(sel, true);
|
||||
if (safeSel !== sel) {
|
||||
rule.selector = safeSel;
|
||||
handledSelectors.add(sel);
|
||||
}
|
||||
},
|
||||
Declaration(decl: any) {
|
||||
if (decl.value.includes("/* no-rem */")) return;
|
||||
if (TAILWIND_COLOR_VARS[decl.prop]) {
|
||||
decl.remove();
|
||||
return;
|
||||
}
|
||||
if (decl.value.includes("rgb(") && decl.value.includes("/")) {
|
||||
decl.value = rgbToRgba(decl.value);
|
||||
}
|
||||
if (decl.value.includes("rpx") && decl.parent.selector.includes("text-")) {
|
||||
decl.prop = "font-size";
|
||||
}
|
||||
|
||||
const parsed = valueParser(decl.value);
|
||||
let changed = false;
|
||||
|
||||
parsed.walk((node: any) => {
|
||||
if (node.type === "word") {
|
||||
// rem 转 rpx
|
||||
const unit = valueParser.unit(node.value);
|
||||
if (unit?.unit === "rem") {
|
||||
const num = unit.number;
|
||||
const precision = (num.split(".")[1] || "").length;
|
||||
const rpxVal = (parseFloat(num) * factor)
|
||||
.toFixed(precision || remPrecision)
|
||||
.replace(/\.?0+$/, "");
|
||||
node.value = `${rpxVal}rpx`;
|
||||
changed = true;
|
||||
}
|
||||
// 特殊字符处理
|
||||
if (node.value.includes(".") || /[[\]()#!/:,]/.test(node.value)) {
|
||||
const safe = toSafeTailwindClass(node.value, true);
|
||||
if (safe !== node.value) {
|
||||
node.value = safe;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理 var(--tw-xxx)
|
||||
if (node.type === "function" && node.value === "var") {
|
||||
if (node.nodes.length > 0 && node.nodes[0].value.startsWith("--tw-")) {
|
||||
node.type = "word";
|
||||
node.value = TAILWIND_COLOR_VARS[node.nodes[0].value];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
decl.value = parsed.toString();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
postcssRemToRpx.postcss = true;
|
||||
|
||||
interface TailwindTransformOptions extends PostcssRemToRpxOptions {}
|
||||
|
||||
/**
|
||||
* Vite 插件:自动转换 .uvue 文件中的 Tailwind 类名为安全字符
|
||||
* 并自动注入 rem 转 rpx 的 PostCSS 插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件对象
|
||||
*/
|
||||
function tailwindTransformPlugin(options: TailwindTransformOptions = {}) {
|
||||
const merged: Required<TailwindTransformOptions> = {
|
||||
remUnit: 16,
|
||||
remPrecision: 6,
|
||||
rpxRatio: 2,
|
||||
...options,
|
||||
};
|
||||
|
||||
return {
|
||||
name: "vite-cool-uniappx-tailwind",
|
||||
enforce: "pre" as const,
|
||||
|
||||
config() {
|
||||
return {
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [postcssRemToRpx(merged)],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
transform(code: string, id: string) {
|
||||
if (!id.includes(".uvue")) return null;
|
||||
|
||||
let resultCode = code;
|
||||
const tplMatch = resultCode.match(/<template>([\s\S]*?)<\/template>/);
|
||||
if (!tplMatch?.[1]) return null;
|
||||
|
||||
let tpl = tplMatch[1];
|
||||
const tplOrigin = tpl;
|
||||
|
||||
TAILWIND_CLASS_PREFIXES.forEach((prefix) => {
|
||||
for (const [char, rep] of Object.entries(TAILWIND_SAFE_CHAR_MAP)) {
|
||||
const reg = new RegExp(`(${prefix}[^\\s'"]*?\\${char}[^\\s'"]*?)`, "g");
|
||||
const matches = [...tpl.matchAll(reg)];
|
||||
matches.forEach((m) => {
|
||||
const raw = m[1];
|
||||
const safe = raw.replace(new RegExp("\\" + char, "g"), rep);
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log(`类名转换: ${raw} → ${safe}`);
|
||||
}
|
||||
tpl = tpl.replace(raw, safe);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (tpl !== tplOrigin) {
|
||||
resultCode = resultCode.replace(tplMatch[0], `<template>${tpl}</template>`);
|
||||
return {
|
||||
code: resultCode,
|
||||
map: { mappings: "" },
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* uniappX 入口,自动注入 Tailwind 类名转换插件
|
||||
* @param options 配置项
|
||||
* @returns Vite 插件数组
|
||||
*/
|
||||
export function uniappX(options?: { tailwind?: TailwindTransformOptions }) {
|
||||
if (config.type == "uniapp-x") {
|
||||
return [tailwindTransformPlugin(options?.tailwind)];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
@ -7,6 +7,7 @@ import prettier from "prettier";
|
||||
export function rootDir(path: string) {
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
case "uniapp-x":
|
||||
return join(process.env.UNI_INPUT_DIR!, path);
|
||||
|
||||
default:
|
||||
|
||||
7
packages/vite-plugin/types/index.d.ts
vendored
7
packages/vite-plugin/types/index.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
export declare type Type = "app" | "admin";
|
||||
export declare type Type = "admin" | "app" | "uniapp-x";
|
||||
|
||||
export declare namespace Eps {
|
||||
interface Column {
|
||||
@ -76,7 +76,6 @@ export declare namespace Ctx {
|
||||
}
|
||||
|
||||
export declare namespace Config {
|
||||
type Type = "app" | "admin";
|
||||
interface Eps {
|
||||
// 是否开启Eps
|
||||
enable: boolean;
|
||||
@ -93,7 +92,7 @@ export declare namespace Config {
|
||||
}
|
||||
interface Options {
|
||||
// 应用类型
|
||||
type: Config.Type;
|
||||
type: Type;
|
||||
// 代理配置
|
||||
proxy: any;
|
||||
// Eps
|
||||
@ -109,7 +108,7 @@ export declare namespace Config {
|
||||
};
|
||||
}
|
||||
interface Data {
|
||||
type: Config.Type;
|
||||
type: Type;
|
||||
reqUrl: string;
|
||||
eps: Config.Eps;
|
||||
demo: boolean;
|
||||
|
||||
1369
pnpm-lock.yaml
generated
1369
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -50,10 +50,19 @@ export default (): ModuleConfig => {
|
||||
const loading = document.querySelector('#Loading');
|
||||
|
||||
if (loading) {
|
||||
loading.querySelector('.preload__name')!.innerHTML = config.app.name;
|
||||
loading.querySelector('.preload__title')!.innerHTML = t('正在加载资源...');
|
||||
loading.querySelector('.preload__sub-title')!.innerHTML =
|
||||
t('初次加载资源可能需要较多时间,请耐心等待');
|
||||
const name = loading.querySelector('.preload__name');
|
||||
const title = loading.querySelector('.preload__title');
|
||||
const subTitle = loading.querySelector('.preload__sub-title');
|
||||
|
||||
if (name) {
|
||||
name.innerHTML = config.app.name;
|
||||
}
|
||||
if (title) {
|
||||
title.innerHTML = t('正在加载资源...');
|
||||
}
|
||||
if (subTitle) {
|
||||
subTitle.innerHTML = t('初次加载资源可能需要较多时间,请耐心等待');
|
||||
}
|
||||
}
|
||||
},
|
||||
async onLoad() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user