完善 脚手架UI

This commit is contained in:
yinsx
2025-12-20 15:26:49 +08:00
parent ad60f8c3ec
commit 7a6a55b535
7 changed files with 313 additions and 194 deletions

45
Readme.md Normal file
View File

@ -0,0 +1,45 @@
# Zguiy Tool Box
子归云工具箱 - 3D模型与纹理处理工具集
## 🚀 快速开始
### 安装
```bash
npm -g i @yinshuangxi/yinx-cli
```
### 使用
在控制台输入以下命令启动工具箱:
```bash
yinx
```
启动后,你将看到一个交互式菜单,使用键盘方向键或数字键选择需要的工具,然后按照提示进行操作。
## ✨ 功能特性
- 🎨 **格式转换**: 支持多种3D模型格式之间的相互转换
- 📦 **KTX2纹理压缩**: 将图片转换为KTX2格式优化WebGL/Three.js应用性能
- 🔧 **glTF扩展**: 为glTF模型添加KHR_texture_basisu扩展支持高效纹理加载
- 🗜️ **模型压缩**: 压缩glTF/GLB模型减小文件体积
- 🖼️ **图片批量处理**: 批量裁剪、缩放和转换图片文件
- 📷 **Sprite图集**: 合并多个图片为Sprite图集
- 📊 **LOD生成器**: 为3D模型生成多级细节
- 🔊 **音频压缩**: 压缩音频文件,优化资源大小
- 🛠️ **项目脚手架**: 快速创建项目模板,提高开发效率
## 📖 项目简介
Zguiy Tool Box 是一个功能强大的命令行工具集专注于3D模型和纹理的处理、转换与优化。它提供了一系列实用工具帮助开发者高效地处理游戏和3D应用开发中的资源文件。
## 📄 许可证
MIT License
---
**你我皆牛马,生在人世间,终日奔波苦,一刻不得闲。**

View File

@ -5,154 +5,212 @@ export const projectTypes = [
{ name: "全栈", desc: "前后端一体化" }, { name: "全栈", desc: "前后端一体化" },
]; ];
// 三级菜单 - 具体框架 // 框架选项
export const frameworks = { export const frameworkOptions = {
前端: [ 前端: [
{ name: "React", desc: "Vite + React", value: "react-vite" }, { value: "vue", label: "VueVite + Vue" },
{ name: "Vue", desc: "Vite + Vue", value: "vue-vite" }, { value: "react", label: "ReactVite + React" },
], ],
后端: [ 后端: [
{ name: "Bun", desc: "Bun运行时", value: "bun" }, { value: "bun", label: "BunBun运行时" },
{ name: "NestJS", desc: "企业级框架", value: "nestjs" }, { value: "node", label: "Node.jsNode运行时" },
{ name: "Express", desc: "Express Generator", value: "express" },
{ name: "Koa", desc: "Koa Generator", value: "koa" },
{ name: "Egg", desc: "阿里Egg.js", value: "egg" },
{ name: "Midway", desc: "阿里Midway", value: "midway" },
{ name: "Fastify", desc: "高性能框架", value: "fastify" },
{ name: "AdonisJS", desc: "全功能MVC", value: "adonisjs" },
], ],
全栈: [ 全栈: [
{ name: "Next.js", desc: "React全栈框架", value: "nextjs" }, { value: "nextjs", label: "Next.jsReact全栈框架" },
{ name: "Nuxt", desc: "Vue全栈框架", value: "nuxt" }, { value: "nuxt", label: "NuxtVue全栈框架" },
], ],
}; };
// 前端组件配置步骤 const bunComponents = {
export const frontendSteps = [ 框架: [
{ { value: "normal", label: "原生" },
name: "路由", { value: "elysia", label: "Elysia(官方亲儿子)" },
type: "select", { value: "hono", label: "Hono(轻量级)" },
message: "选择路由方案", ]
options: [ };
const nodeComponents = {
框架: [
{ value: "nestjs", label: "NestJS企业级框架" },
{ value: "express", label: "ExpressExpress Generator" },
{ value: "koa", label: "KoaKoa Generator" },
{ value: "egg", label: "Egg阿里Egg.js" },
{ value: "midway", label: "Midway阿里Midway" },
{ value: "fastify", label: "Fastify高性能框架" },
{ value: "adonisjs", label: "AdonisJS全功能MVC" },
]
};
//后端公共框架
const backendCommonComponents = {
数据库: [
{ value: "none", label: "不需要数据库" },
{ value: "mongoose", label: "MongoDB + Mongoose" },
{ value: "prisma", label: "Prisma支持 MySQL / PostgreSQL / SQLite" },
{ value: "typeorm", label: "TypeORM支持 MySQL / PostgreSQL 等)" },
]
};
// 组件选项 - 按框架分类
const reactComponents = {
路由: [
{ value: "none", label: "不需要路由" }, { value: "none", label: "不需要路由" },
{ value: "react-router", label: "React RouterReact项目" }, { value: "react-router", label: "React Router" },
{ value: "vue-router", label: "Vue RouterVue项目" }, { value: "tanstack-router", label: "TanStack Router" },
], ],
default: "none" 状态管理: [
},
{
name: "状态管理",
type: "select",
message: "选择状态管理方案",
options: [
{ value: "none", label: "不需要状态管理" }, { value: "none", label: "不需要状态管理" },
{ value: "zustand", label: "Zustand轻量级React推荐)" }, { value: "zustand", label: "Zustand轻量级推荐" },
{ value: "pinia", label: "PiniaVue官方推荐" }, { value: "redux", label: "Redux Toolkit" },
{ value: "redux", label: "Redux Toolkit大型项目" }, { value: "jotai", label: "Jotai原子化" },
{ value: "mobx", label: "MobX(响应式状态管理)" }, { value: "mobx", label: "MobX" },
], ],
default: "none" UI组件库: [
},
{
name: "HTTP请求",
type: "select",
message: "选择HTTP请求库",
options: [
{ value: "none", label: "不需要HTTP库" },
{ value: "axios", label: "Axios功能全面拦截器支持" },
{ value: "ky", label: "Ky轻量级基于Fetch" },
{ value: "ofetch", label: "ofetchNuxt团队出品" },
],
default: "none"
},
{
name: "UI组件库",
type: "select",
message: "选择UI组件库",
options: [
{ value: "none", label: "不需要UI库" }, { value: "none", label: "不需要UI库" },
{ value: "antd", label: "Ant Design企业级React" }, { value: "antd", label: "Ant Design" },
{ value: "element-plus", label: "Element Plus饿了么Vue" }, { value: "arco", label: "Arco Design" },
{ value: "arco", label: "Arco Design字节跳动" }, { value: "shadcn", label: "shadcn/ui" },
{ value: "naive-ui", label: "Naive UIVue3原生" }, { value: "mui", label: "Material UI" },
{ value: "shadcn", label: "shadcn/ui可定制React" },
], ],
default: "none" 表单验证: [
},
{
name: "CSS方案",
type: "select",
message: "选择CSS方案",
options: [
{ value: "none", label: "原生CSS" },
{ value: "tailwind", label: "Tailwind CSS原子化CSS" },
{ value: "unocss", label: "UnoCSS即时原子化" },
{ value: "sass", label: "Sass/SCSS预处理器" },
{ value: "less", label: "Less预处理器" },
{ value: "styled", label: "Styled ComponentsCSS-in-JS" },
],
default: "none"
},
{
name: "工具库",
type: "multiselect",
message: "选择常用工具库",
options: [
{ value: "lodash", label: "Lodash工具函数库" },
{ value: "dayjs", label: "Day.js日期处理" },
{ value: "iconify", label: "Iconify图标库" },
{ value: "vueuse", label: "VueUseVue组合式工具" },
{ value: "ahooks", label: "ahooksReact Hooks库" },
],
default: []
},
{
name: "表单验证",
type: "select",
message: "选择表单验证方案",
options: [
{ value: "none", label: "不需要表单验证" }, { value: "none", label: "不需要表单验证" },
{ value: "zod", label: "ZodTypeScript优先" },
{ value: "yup", label: "Yup声明式验证" },
{ value: "vee-validate", label: "VeeValidateVue专用" },
{ value: "react-hook-form", label: "React Hook Form" }, { value: "react-hook-form", label: "React Hook Form" },
{ value: "formik", label: "Formik" },
{ value: "zod", label: "Zod" },
], ],
default: "none" 工具库: [
}, { value: "ahooks", label: "ahooksReact Hooks库" },
{ { value: "lodash", label: "Lodash" },
name: "国际化", { value: "dayjs", label: "Day.js" },
type: "select", { value: "iconify", label: "Iconify图标" },
message: "选择国际化方案", ],
options: [ 国际化: [
{ value: "none", label: "不需要国际化" }, { value: "none", label: "不需要国际化" },
{ value: "i18next", label: "i18next(通用方案)" }, { value: "i18next", label: "i18next" },
{ value: "vue-i18n", label: "Vue I18nVue专用" },
], ],
default: "none" };
},
{ const vueComponents = {
name: "代码规范", 路由: [
type: "multiselect", { value: "none", label: "不需要路由" },
message: "选择代码规范工具", { value: "vue-router", label: "Vue Router" },
options: [
{ value: "eslint", label: "ESLint代码检查" },
{ value: "prettier", label: "Prettier代码格式化" },
{ value: "husky", label: "HuskyGit Hooks" },
{ value: "lint-staged", label: "lint-staged暂存区检查" },
{ value: "commitlint", label: "Commitlint提交信息规范" },
], ],
default: [] 状态管理: [
}, { value: "none", label: "不需要状态管理" },
{ { value: "pinia", label: "Pinia官方推荐" },
name: "其他", ],
type: "multiselect", UI组件库: [
message: "选择其他功能", { value: "none", label: "不需要UI库" },
options: [ { value: "element-plus", label: "Element Plus" },
{ value: "mock", label: "Mock.js模拟数据" }, { value: "naive-ui", label: "Naive UI" },
{ value: "arco-vue", label: "Arco Design Vue" },
{ value: "ant-design-vue", label: "Ant Design Vue" },
],
表单验证: [
{ value: "none", label: "不需要表单验证" },
{ value: "vee-validate", label: "VeeValidate" },
{ value: "vuelidate", label: "Vuelidate" },
{ value: "zod", label: "Zod" },
],
工具库: [
{ value: "vueuse", label: "VueUse组合式工具" },
{ value: "lodash", label: "Lodash" },
{ value: "dayjs", label: "Day.js" },
{ value: "iconify", label: "Iconify图标" },
], 国际化: [
{ value: "none", label: "不需要国际化" },
{ value: "vue-i18n", label: "Vue-i18n" },
],
};
// 通用组件React和Vue共用
const commonComponents = {
HTTP请求: [
{ value: "none", label: "不需要HTTP库" },
{ value: "axios", label: "Axios" },
{ value: "ky", label: "Ky轻量级" },
{ value: "ofetch", label: "ofetch" },
],
CSS方案: [
{ value: "none", label: "原生CSS" },
{ value: "tailwind", label: "Tailwind CSS" },
{ value: "unocss", label: "UnoCSS" },
{ value: "sass", label: "Sass/SCSS" },
{ value: "less", label: "Less" },
],
代码规范: [
{ value: "eslint", label: "ESLint" },
{ value: "prettier", label: "Prettier" },
{ value: "husky", label: "Husky" },
{ value: "lint-staged", label: "lint-staged" },
],
其他: [
{ value: "mock", label: "Mock.js" },
{ value: "pwa", label: "PWA支持" }, { value: "pwa", label: "PWA支持" },
{ value: "storage", label: "持久化存储封装" }, { value: "storage", label: "持久化存储" },
{ value: "env", label: "环境变量配置" }, { value: "threejs", label: "3D集成(Three.js)" },
{ value: "babylonjs", label: "3D集成(Babylon.js)" },
], ],
default: [] };
// 根据框架获取组件配置
export function getComponentsByFramework(framework) {
if (!framework || framework === "none") return {};
const isReact = ["react", "nextjs"].includes(framework);
const isVue = ["vue", "nuxt"].includes(framework);
const isBun = framework === "bun";
const isNode = framework === "node";
if (isReact) {
return { ...reactComponents, ...commonComponents };
} else if (isVue) {
return { ...vueComponents, ...commonComponents };
} else if (isBun) {
return { ...bunComponents, ...backendCommonComponents };
} else if (isNode) {
return { ...nodeComponents, ...backendCommonComponents };
} }
];
return {};
}
// 获取默认框架
function getDefaultFramework(projectType) {
const opts = frameworkOptions[projectType];
return opts?.[0]?.value || "none";
}
// 生成前端/全栈项目的步骤配置
export function generateSteps(projectType, selectedFramework) {
const defaultFramework = getDefaultFramework(projectType);
const framework = selectedFramework || defaultFramework;
const steps = [
{
name: "运行时",
type: "select",
message: "选择项目框架",
options: frameworkOptions[projectType] || [],
default: defaultFramework
}
];
const components = getComponentsByFramework(framework);
const componentNames = Object.keys(components);
componentNames.forEach(name => {
const isMulti = ["工具库", "代码规范", "其他"].includes(name);
const opts = components[name] || [];
steps.push({
name,
type: isMulti ? "multiselect" : "select",
message: `选择${name}`,
options: opts,
default: isMulti ? [] : opts[0]?.value
});
});
return steps;
}

View File

@ -1,6 +1,6 @@
import color from "picocolors"; import color from "picocolors";
import { projectTypes, frameworks } from "./config.js"; import { projectTypes } from "./config.js";
import { gridSelect, createComponentUI, formatResults, waitKey, showPlaceholder } from "./ui.js"; import { gridSelect, createScaffoldUI, formatResults, waitKey } from "./ui.js";
export async function run() { export async function run() {
while (true) { while (true) {
@ -9,31 +9,16 @@ export async function run() {
if (typeResult.action === "back") return "back"; if (typeResult.action === "back") return "back";
const projectType = typeResult.item.name; const projectType = typeResult.item.name;
const frameworkList = frameworks[projectType];
while (true) { // 三级页面 - 框架+组件配置(合并)
// 三级菜单 - 框架选择 const ui = createScaffoldUI(projectType);
const frameworkResult = await gridSelect(frameworkList, `${projectType}项目 - 选择框架`);
if (frameworkResult.action === "back") break;
const framework = frameworkResult.item;
if (projectType === "前端" || projectType === "全栈") {
// 组件配置
const ui = createComponentUI(framework.name);
const result = await ui.runInteractive(); const result = await ui.runInteractive();
if (result) { if (result) {
const summary = formatResults(result.results); const summary = formatResults(result.steps, result.results);
ui.showSummary(summary); ui.showSummary(summary);
console.log(color.yellow("功能开发中,敬请期待...")); console.log(color.yellow("功能开发中,敬请期待..."));
await waitKey(); await waitKey();
} }
} else {
// 后端暂无组件配置
showPlaceholder(framework);
await waitKey();
}
}
} }
} }

View File

@ -1,7 +1,7 @@
import color from "picocolors"; import color from "picocolors";
import { initKeypress, onKey, stopKeypress } from "../keyboard.js"; import { initKeypress, onKey, stopKeypress } from "../keyboard.js";
import { createStepUI } from "../utils/stepui.js"; import { createStepUI } from "../utils/stepui.js";
import { frontendSteps } from "./config.js"; import { frameworkOptions, generateSteps } from "./config.js";
function clearScreen() { function clearScreen() {
process.stdout.write('\x1Bc'); process.stdout.write('\x1Bc');
@ -103,24 +103,24 @@ export async function gridSelect(items, title) {
}); });
} }
// 创建组件配置UI // 创建框架+组件配置UI
export function createComponentUI(frameworkName) { export function createScaffoldUI(projectType) {
return createStepUI({ return createStepUI({
title: `${frameworkName} - 组件配置`, title: `${projectType} - 项目配置`,
getSteps: () => frontendSteps getSteps: () => generateSteps(projectType, null),
onStepChange: (framework) => generateSteps(projectType, framework)
}); });
} }
// 解析配置结果 // 解析配置结果
export function formatResults(results) { export function formatResults(steps, results) {
const stepNames = frontendSteps.map(s => s.name);
const summary = []; const summary = [];
results.forEach((val, i) => { results.forEach((val, i) => {
if (Array.isArray(val) && val.length > 0) { if (Array.isArray(val) && val.length > 0) {
summary.push(`${stepNames[i]}: ${val.join(", ")}`); summary.push(`${steps[i].name}: ${val.join(", ")}`);
} else if (val && val !== "none") { } else if (val && val !== "none") {
summary.push(`${stepNames[i]}: ${val}`); summary.push(`${steps[i].name}: ${val}`);
} }
}); });
@ -139,14 +139,3 @@ export async function waitKey(message = "按任意键返回") {
}); });
}); });
} }
// 显示占位信息
export function showPlaceholder(framework) {
clearScreen();
console.log("");
console.log(color.bgGreen(color.black(" 配置完成 ")));
console.log("");
console.log(color.cyan("框架: ") + framework.name);
console.log("");
console.log(color.yellow("功能开发中,敬请期待..."));
}

View File

@ -6,7 +6,7 @@ function clearScreen() {
} }
export function createStepUI(options) { export function createStepUI(options) {
const { title, getSteps } = options; const { title, getSteps, onStepChange } = options;
let steps = []; let steps = [];
let results = []; let results = [];
@ -19,16 +19,33 @@ export function createStepUI(options) {
steps = typeof getSteps === "function" ? getSteps() : getSteps; steps = typeof getSteps === "function" ? getSteps() : getSteps;
results = steps.map(s => s.type === "multiselect" ? [...(s.default || [])] : s.default); results = steps.map(s => s.type === "multiselect" ? [...(s.default || [])] : s.default);
completed = new Set(); completed = new Set();
// 有默认值的步骤自动标记为已完成
steps.forEach((s, i) => {
if (s.default && s.default !== "none" && (!Array.isArray(s.default) || s.default.length > 0)) {
completed.add(i);
}
});
currentStep = 0; currentStep = 0;
currentOption = 0; currentOption = 0;
resolved = false; resolved = false;
} }
function updateSteps(newSteps) {
steps = newSteps;
// 保留第一个步骤的结果,重置其他步骤为默认值
const firstResult = results[0];
results = steps.map((s, i) => {
if (i === 0) return firstResult;
return s.type === "multiselect" ? [...(s.default || [])] : s.default;
});
if (currentStep >= steps.length) currentStep = steps.length - 1;
}
function renderNav() { function renderNav() {
const nav = steps.map((step, i) => { const nav = steps.map((step, i) => {
if (completed.has(i)) return color.green(" " + step.name); if (step.disabled) return color.dim(" " + step.name);
if (i === currentStep) return color.bgCyan(color.black(" " + step.name + " ")); if (i === currentStep) return color.bgCyan(color.black(" " + step.name + " "));
return color.dim("□ " + step.name); return color.dim(step.name);
}); });
return "← " + nav.join(" ") + " " + color.green("✓Submit") + " →"; return "← " + nav.join(" ") + " " + color.green("✓Submit") + " →";
} }
@ -81,6 +98,20 @@ export function createStepUI(options) {
console.log(renderOptions()); console.log(renderOptions());
} }
function findPrevStep(from) {
for (let i = from - 1; i >= 0; i--) {
if (!steps[i].disabled) return i;
}
return from;
}
function findNextStep(from) {
for (let i = from + 1; i < steps.length; i++) {
if (!steps[i].disabled) return i;
}
return from;
}
function handleKey(key, resolve) { function handleKey(key, resolve) {
if (!key || resolved) return; if (!key || resolved) return;
@ -89,11 +120,13 @@ export function createStepUI(options) {
switch (key.name) { switch (key.name) {
case "left": case "left":
if (currentStep > 0) { currentStep--; currentOption = 0; } const prevStep = findPrevStep(currentStep);
if (prevStep !== currentStep) { currentStep = prevStep; currentOption = 0; }
render(); render();
break; break;
case "right": case "right":
if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; } const nextStep = findNextStep(currentStep);
if (nextStep !== currentStep) { currentStep = nextStep; currentOption = 0; }
render(); render();
break; break;
case "up": case "up":
@ -120,6 +153,10 @@ export function createStepUI(options) {
results[currentStep] = opt.value; results[currentStep] = opt.value;
} }
completed.add(currentStep); completed.add(currentStep);
if (onStepChange && currentStep === 0) {
const newSteps = onStepChange(results[0]);
if (newSteps) updateSteps(newSteps);
}
render(); render();
} }
break; break;
@ -127,7 +164,12 @@ export function createStepUI(options) {
if (optCount && step.type === "select") { if (optCount && step.type === "select") {
results[currentStep] = step.options[currentOption].value; results[currentStep] = step.options[currentOption].value;
completed.add(currentStep); completed.add(currentStep);
if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; } if (onStepChange && currentStep === 0) {
const newSteps = onStepChange(results[0]);
if (newSteps) updateSteps(newSteps);
}
const next = findNextStep(currentStep);
if (next !== currentStep) { currentStep = next; currentOption = 0; }
render(); render();
} }
break; break;

View File

@ -1,6 +1,6 @@
{ {
"name": "@yinshuangxi/yinx-cli", "name": "@yinshuangxi/yinx-cli",
"version": "1.0.3", "version": "1.0.4",
"description": "子归云工具箱-你想要的都在这", "description": "子归云工具箱-你想要的都在这",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",

BIN
xw_20251220142204.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB