@xubill/xx-cli 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,95 @@
1
+ /**
2
+ * 筛选器转换逻辑
3
+ * 将筛选器配置转换为 HTML 内容
4
+ */
5
+
6
+ /**
7
+ * 生成字段名
8
+ * @param {string} label - 标签名称
9
+ * @returns {string} 字段名
10
+ */
11
+ function generateFieldName(label) {
12
+ // 中文标签映射
13
+ const chineseMap = {
14
+ '用户名': 'username',
15
+ '状态': 'status',
16
+ '创建时间': 'createTime',
17
+ '邮箱': 'email',
18
+ '手机号': 'phone',
19
+ '地址': 'address',
20
+ 'ID': 'id',
21
+ '名称': 'name',
22
+ '类型': 'type',
23
+ '描述': 'description',
24
+ '备注': 'remark'
25
+ };
26
+
27
+ // 如果有映射,直接返回
28
+ if (chineseMap[label]) {
29
+ return chineseMap[label];
30
+ }
31
+
32
+ // 否则,移除空格并转换为小写
33
+ return label.toLowerCase().replace(/\s+/g, '');
34
+ }
35
+
36
+ /**
37
+ * 生成筛选器内容
38
+ * @param {Object} filter - 筛选器配置
39
+ * @param {Object} material - 组件材料
40
+ * @returns {string} 筛选器 HTML 内容
41
+ */
42
+ function transformFilter(filter, material) {
43
+ let content = '';
44
+ for (const [label, type] of Object.entries(filter || {})) {
45
+ // 生成英文字段名
46
+ const fieldName = generateFieldName(label);
47
+ switch (type) {
48
+ case 'input':
49
+ content += `
50
+ <${material.form.item} label="${label}">
51
+ <${material.form.input} v-model="searchForm.${fieldName}" placeholder="请输入${label}"></${material.form.input}>
52
+ </${material.form.item}>`;
53
+ break;
54
+ case 'select':
55
+ content += `
56
+ <${material.form.item} label="${label}">
57
+ <${material.form.select} v-model="searchForm.${fieldName}" placeholder="请选择${label}">
58
+ <${material.form.option} label="选项1" value="1"></${material.form.option}>
59
+ <${material.form.option} label="选项2" value="2"></${material.form.option}>
60
+ </${material.form.select}>
61
+ </${material.form.item}>`;
62
+ break;
63
+ case 'date':
64
+ content += `
65
+ <${material.form.item} label="${label}">
66
+ <${material.form.datePicker} v-model="searchForm.${fieldName}" type="date" placeholder="选择日期"></${material.form.datePicker}>
67
+ </${material.form.item}>`;
68
+ break;
69
+ case 'daterange':
70
+ content += `
71
+ <${material.form.item} label="${label}">
72
+ <${material.form.datePicker} v-model="searchForm.${fieldName}" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></${material.form.datePicker}>
73
+ </${material.form.item}>`;
74
+ break;
75
+ case 'datetime':
76
+ content += `
77
+ <${material.form.item} label="${label}">
78
+ <${material.form.datePicker} v-model="searchForm.${fieldName}" type="datetime" placeholder="选择日期时间"></${material.form.datePicker}>
79
+ </${material.form.item}>`;
80
+ break;
81
+ case 'datetimerange':
82
+ content += `
83
+ <${material.form.item} label="${label}">
84
+ <${material.form.datePicker} v-model="searchForm.${fieldName}" type="datetimerange" range-separator="至" start-placeholder="开始日期时间" end-placeholder="结束日期时间"></${material.form.datePicker}>
85
+ </${material.form.item}>`;
86
+ break;
87
+ }
88
+ }
89
+ return content;
90
+ }
91
+
92
+ module.exports = {
93
+ transformFilter,
94
+ generateFieldName
95
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * 列表页面转换逻辑
3
+ * 将页面配置转换为完整的 Vue 组件内容
4
+ */
5
+
6
+ // 导入筛选器和表格转换方法
7
+ const { transformFilter } = require('./transformFilter');
8
+ const { transformTable } = require('./transformTable');
9
+ const { generateTemplateHeader, generateTemplateFooter, generateScriptSection, generateStyleSection } = require('./transformTemplate');
10
+
11
+ /**
12
+ * 生成 Vue 文件内容
13
+ * @param {Object} template - 页面模板配置
14
+ * @param {Object} filter - 筛选器配置
15
+ * @param {Object} table - 表格配置
16
+ * @param {Object} material - 组件材料
17
+ * @returns {string} Vue 文件内容
18
+ */
19
+ function transformList(template, filter, table, material) {
20
+ // 使用默认材料(Element UI)
21
+ const defaultMaterial = {
22
+ form: {
23
+ wrapper: 'el-form',
24
+ item: 'el-form-item',
25
+ input: 'el-input',
26
+ select: 'el-select',
27
+ option: 'el-option',
28
+ datePicker: 'el-date-picker',
29
+ button: 'el-button'
30
+ },
31
+ table: {
32
+ wrapper: 'el-table',
33
+ column: 'el-table-column',
34
+ selection: 'selection',
35
+ index: 'index'
36
+ },
37
+ pagination: 'el-pagination',
38
+ tag: 'el-tag',
39
+ classes: {
40
+ container: 'list-container',
41
+ btnGroup: 'btn-group',
42
+ pagination: 'pagination'
43
+ }
44
+ };
45
+
46
+ // 合并材料
47
+ const finalMaterial = { ...defaultMaterial, ...material };
48
+
49
+ // 生成筛选器内容
50
+ const filterContent = transformFilter(filter, finalMaterial);
51
+
52
+ // 生成表格内容
53
+ const tableContent = transformTable(table, finalMaterial);
54
+
55
+ // 生成按钮组
56
+ const btnGroupContent = template.btn?.map(btn => `<${finalMaterial.form.button} type="primary">${btn}</${finalMaterial.form.button}>`).join('\n ') || '';
57
+
58
+ // 生成模板内容
59
+ const templateContent = `<!-- ${template.cn} -->
60
+ <template>
61
+ <div class="${template.en}">
62
+ <!-- 顶部插槽 -->
63
+ ${template.top ? '<slot name="filter-top"></slot>' : ''}
64
+
65
+ <!-- 筛选器 -->
66
+ <${finalMaterial.form.wrapper} :inline="true" :model="searchForm" class="demo-form-inline">
67
+ ${filterContent}
68
+ <${finalMaterial.form.item}>
69
+ <${finalMaterial.form.button} type="primary" @click="handleSearch">查询</${finalMaterial.form.button}>
70
+ <${finalMaterial.form.button} @click="resetForm">重置</${finalMaterial.form.button}>
71
+ </${finalMaterial.form.item}>
72
+ </${finalMaterial.form.wrapper}>
73
+
74
+ <!-- 按钮组 -->
75
+ <div class="btn-group">
76
+ ${btnGroupContent}
77
+ </div>
78
+
79
+ <!-- 表格 -->
80
+ <${finalMaterial.table.wrapper}
81
+ v-loading="loading"
82
+ :data="tableData"
83
+ style="width: 100%"
84
+ ${template.select ? '\n @selection-change="handleSelectionChange"' : ''}
85
+ >
86
+ ${tableContent}
87
+ </${finalMaterial.table.wrapper}>
88
+
89
+ <!-- 底部插槽 -->
90
+ ${template.bottom ? '<slot name="table-bottom"></slot>' : ''}
91
+
92
+ <!-- 分页 -->
93
+ <div class="pagination">
94
+ <${finalMaterial.pagination}
95
+ @size-change="handleSizeChange"
96
+ @current-change="handleCurrentChange"
97
+ :current-page="currentPage"
98
+ :page-sizes="[10, 20, 50, 100]"
99
+ :page-size="pageSize"
100
+ layout="total, sizes, prev, pager, next, jumper"
101
+ :total="total"
102
+ >
103
+ </${finalMaterial.pagination}>
104
+ </div>
105
+ </div>
106
+ </template>`;
107
+
108
+ // 生成脚本部分
109
+ const scriptContent = generateScriptSection(template, finalMaterial);
110
+
111
+ // 生成样式部分
112
+ const styleContent = generateStyleSection(template, finalMaterial);
113
+
114
+ // 组合所有部分
115
+ return `${templateContent}\n${scriptContent}\n${styleContent}`;
116
+ }
117
+
118
+ module.exports = {
119
+ transformList
120
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Mock 数据转换逻辑
3
+ * 将页面配置转换为 Mock 数据文件内容
4
+ */
5
+
6
+ /**
7
+ * 生成 mock 数据内容
8
+ * @param {Object} template - 页面模板配置
9
+ * @returns {string} mock 数据内容
10
+ */
11
+ function transformMock(template) {
12
+ return `// Mock 数据
13
+ const Mock = require('mockjs');
14
+
15
+ // 生成模拟数据
16
+ const mockData = Mock.mock({
17
+ 'list|10-20': [{
18
+ 'id|+1': 1,
19
+ 'name': '@cname',
20
+ 'email': '@email',
21
+ 'phone': /^1[3-9]\d{9}$/,
22
+ 'createTime': '@datetime',
23
+ 'status|1': ['正常', '禁用']
24
+ }]
25
+ });
26
+
27
+ // 导出 mock 数据
28
+ module.exports = {
29
+ 'GET /api/${template.en}': {
30
+ code: 200,
31
+ message: 'success',
32
+ data: {
33
+ list: mockData.list,
34
+ total: mockData.list.length
35
+ }
36
+ }
37
+ };
38
+ `;
39
+ }
40
+
41
+ module.exports = {
42
+ transformMock
43
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 路由转换逻辑
3
+ * 将页面配置转换为路由配置文件内容
4
+ */
5
+
6
+ /**
7
+ * 生成路由配置内容
8
+ * @param {Object} config - 页面配置
9
+ * @returns {string} 路由配置内容
10
+ */
11
+ function transformRoutes(config) {
12
+ const routes = [];
13
+ const pages = config.pages || {};
14
+
15
+ for (const [pageName, pageConfig] of Object.entries(pages)) {
16
+ const { template } = pageConfig;
17
+ routes.push({
18
+ path: `/${template.en}`,
19
+ name: template.en,
20
+ component: () => import(`./${template.en}.vue`),
21
+ meta: {
22
+ title: template.cn
23
+ }
24
+ });
25
+ }
26
+
27
+ return `// 路由配置
28
+ const routes = ${JSON.stringify(routes, null, 2)};
29
+
30
+ export default routes;
31
+ `;
32
+ }
33
+
34
+ module.exports = {
35
+ transformRoutes
36
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 表格转换逻辑
3
+ * 将表格配置转换为 HTML 内容
4
+ */
5
+
6
+ // 导入生成字段名的方法
7
+ const { generateFieldName } = require('./transformFilter');
8
+
9
+ /**
10
+ * 生成表格内容
11
+ * @param {Object} table - 表格配置
12
+ * @param {Object} material - 组件材料
13
+ * @returns {string} 表格 HTML 内容
14
+ */
15
+ function transformTable(table, material) {
16
+ let content = '';
17
+ for (const [label, type] of Object.entries(table || {})) {
18
+ if (typeof type === 'object') {
19
+ if (type.type === 'operate') {
20
+ content += `
21
+ <${material.table.column} label="${label}" ${type.fixed ? `fixed="${type.fixed}"` : ''} width="180">
22
+ <template slot-scope="scope">
23
+ ${type.render?.map(action => `<${material.button} size="small">${action}</${material.button}>`).join('\n ') || ''}
24
+ </template>
25
+ </${material.table.column}>`;
26
+ }
27
+ } else {
28
+ // 生成英文字段名
29
+ const fieldName = generateFieldName(label);
30
+ switch (type) {
31
+ case 'selection':
32
+ content += `
33
+ <${material.table.column} type="selection" width="55"></${material.table.column}>`;
34
+ break;
35
+ case 'index':
36
+ content += `
37
+ <${material.table.column} type="index" label="${label}" width="80"></${material.table.column}>`;
38
+ break;
39
+ case 'text':
40
+ content += `
41
+ <${material.table.column} prop="${fieldName}" label="${label}"></${material.table.column}>`;
42
+ break;
43
+ case 'time':
44
+ case 'date':
45
+ case 'datetime':
46
+ content += `
47
+ <${material.table.column} prop="${fieldName}" label="${label}"></${material.table.column}>`;
48
+ break;
49
+ case 'option':
50
+ content += `
51
+ <${material.table.column} prop="${fieldName}" label="${label}">
52
+ <template slot-scope="scope">
53
+ <el-tag>{{ scope.row.${fieldName} }}</el-tag>
54
+ </template>
55
+ </${material.table.column}>`;
56
+ break;
57
+ case 'link':
58
+ content += `
59
+ <${material.table.column} prop="${fieldName}" label="${label}">
60
+ <template slot-scope="scope">
61
+ <a href="#">{{ scope.row.${fieldName} }}</a>
62
+ </template>
63
+ </${material.table.column}>`;
64
+ break;
65
+ case 'tag':
66
+ content += `
67
+ <${material.table.column} prop="${fieldName}" label="${label}">
68
+ <template slot-scope="scope">
69
+ <el-tag>{{ scope.row.${fieldName} }}</el-tag>
70
+ </template>
71
+ </${material.table.column}>`;
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ return content;
77
+ }
78
+
79
+ module.exports = {
80
+ transformTable
81
+ };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * 模板转换的通用逻辑
3
+ * 提供通用的模板处理功能
4
+ */
5
+
6
+ /**
7
+ * 转换模板配置
8
+ * @param {Object} template - 模板配置
9
+ * @param {Object} material - 组件材料
10
+ * @returns {Object} 转换后的模板配置
11
+ */
12
+ function transformTemplateConfig(template, material) {
13
+ return {
14
+ ...template,
15
+ // 添加组件材料信息
16
+ material: material,
17
+ // 生成唯一标识
18
+ id: `${template.en}_${Date.now()}`
19
+ };
20
+ }
21
+
22
+ /**
23
+ * 生成模板头部
24
+ * @param {Object} template - 模板配置
25
+ * @returns {string} 模板头部注释
26
+ */
27
+ function generateTemplateHeader(template) {
28
+ return `<!-- ${template.cn} -->
29
+ <template>
30
+ <div class="${template.en}">`;
31
+ }
32
+
33
+ /**
34
+ * 生成模板底部
35
+ * @param {Object} template - 模板配置
36
+ * @returns {string} 模板底部
37
+ */
38
+ function generateTemplateFooter(template) {
39
+ return ` </div>
40
+ </template>`;
41
+ }
42
+
43
+ /**
44
+ * 生成脚本部分
45
+ * @param {Object} template - 模板配置
46
+ * @param {Object} material - 组件材料
47
+ * @returns {string} 脚本部分
48
+ */
49
+ function generateScriptSection(template, material) {
50
+ return `<script>
51
+ export default {
52
+ name: '${template.en}',
53
+ data() {
54
+ return {
55
+ searchForm: {},
56
+ tableData: [],
57
+ loading: false,
58
+ currentPage: 1,
59
+ pageSize: 10,
60
+ total: 0,
61
+ ${template.select ? 'multipleSelection: [],' : ''}
62
+ };
63
+ },
64
+ created() {
65
+ this.getList();
66
+ },
67
+ methods: {
68
+ getList() {
69
+ this.loading = true;
70
+ // TODO: 替换为实际接口调用
71
+ setTimeout(() => {
72
+ this.tableData = [];
73
+ this.total = 0;
74
+ this.loading = false;
75
+ }, 500);
76
+ },
77
+ handleSearch() {
78
+ this.currentPage = 1;
79
+ this.getList();
80
+ },
81
+ resetForm() {
82
+ this.searchForm = {};
83
+ this.getList();
84
+ },
85
+ ${template.select ? `
86
+ handleSelectionChange(val) {
87
+ this.multipleSelection = val;
88
+ },` : ''}
89
+ handleSizeChange(size) {
90
+ this.pageSize = size;
91
+ this.getList();
92
+ },
93
+ handleCurrentChange(current) {
94
+ this.currentPage = current;
95
+ this.getList();
96
+ }
97
+ }
98
+ };
99
+ </script>`;
100
+ }
101
+
102
+ /**
103
+ * 生成样式部分
104
+ * @param {Object} template - 模板配置
105
+ * @param {Object} material - 组件材料
106
+ * @returns {string} 样式部分
107
+ */
108
+ function generateStyleSection(template, material) {
109
+ return `<style scoped>
110
+ .${template.en} {
111
+ padding: 20px;
112
+ }
113
+ .btn-group {
114
+ margin: 20px 0;
115
+ }
116
+ .pagination {
117
+ margin-top: 20px;
118
+ text-align: right;
119
+ }
120
+ </style>`;
121
+ }
122
+
123
+ module.exports = {
124
+ transformTemplateConfig,
125
+ generateTemplateHeader,
126
+ generateTemplateFooter,
127
+ generateScriptSection,
128
+ generateStyleSection
129
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 输入验证工具
3
+ * 用于验证用户输入,防止注入攻击
4
+ */
5
+
6
+ /**
7
+ * 验证文件路径
8
+ * @param {string} path - 文件路径
9
+ * @returns {boolean} 是否为有效路径
10
+ */
11
+ exports.validatePath = (path) => {
12
+ // 检查是否为字符串
13
+ if (typeof path !== 'string') {
14
+ return false;
15
+ }
16
+
17
+ // 检查是否包含危险字符
18
+ const dangerousChars = [
19
+ '..', // 目录遍历
20
+ ';', // 命令注入
21
+ '&', // 命令注入
22
+ '|', // 命令注入
23
+ '>', // 重定向
24
+ '<', // 重定向
25
+ '`', // 命令替换
26
+ '\\', // 转义字符
27
+ ];
28
+
29
+ return !dangerousChars.some(char => path.includes(char));
30
+ };
31
+
32
+ /**
33
+ * 验证命令参数
34
+ * @param {string} param - 命令参数
35
+ * @returns {boolean} 是否为有效参数
36
+ */
37
+ exports.validateCommandParam = (param) => {
38
+ // 检查是否为字符串
39
+ if (typeof param !== 'string') {
40
+ return false;
41
+ }
42
+
43
+ // 检查是否包含危险字符
44
+ const dangerousChars = [
45
+ ';', // 命令注入
46
+ '&', // 命令注入
47
+ '|', // 命令注入
48
+ '>', // 重定向
49
+ '<', // 重定向
50
+ '`', // 命令替换
51
+ '\\', // 转义字符
52
+ '\'', // 单引号
53
+ '"', // 双引号
54
+ ];
55
+
56
+ return !dangerousChars.some(char => param.includes(char));
57
+ };
58
+
59
+ /**
60
+ * 验证URL
61
+ * @param {string} url - URL
62
+ * @returns {boolean} 是否为有效URL
63
+ */
64
+ exports.validateUrl = (url) => {
65
+ // 检查是否为字符串
66
+ if (typeof url !== 'string') {
67
+ return false;
68
+ }
69
+
70
+ // 简单的URL格式验证
71
+ const urlPattern = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
72
+ return urlPattern.test(url);
73
+ };
74
+
75
+ /**
76
+ * 验证数字
77
+ * @param {string|number} value - 要验证的值
78
+ * @returns {boolean} 是否为有效数字
79
+ */
80
+ exports.validateNumber = (value) => {
81
+ return !isNaN(Number(value));
82
+ };
83
+
84
+ /**
85
+ * 验证字符串长度
86
+ * @param {string} str - 字符串
87
+ * @param {number} min - 最小长度
88
+ * @param {number} max - 最大长度
89
+ * @returns {boolean} 是否为有效长度
90
+ */
91
+ exports.validateStringLength = (str, min, max) => {
92
+ if (typeof str !== 'string') {
93
+ return false;
94
+ }
95
+ return str.length >= min && str.length <= max;
96
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@xubill/xx-cli",
3
+ "version": "1.0.6",
4
+ "description": "个人工具集",
5
+ "main": "lib/core/index.js",
6
+ "bin": {
7
+ "xx": "bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "build": "echo \"Build completed\"",
12
+ "doc": "vitepress dev doc",
13
+ "doc:build": "vitepress build doc",
14
+ "doc:preview": "vitepress preview doc",
15
+ "release": "standard-version",
16
+ "release:patch": "standard-version --release-as patch",
17
+ "release:minor": "standard-version --release-as minor",
18
+ "release:major": "standard-version --release-as major",
19
+ "release:beta": "standard-version --prerelease beta",
20
+ "release:alpha": "standard-version --prerelease alpha"
21
+ },
22
+ "keywords": [
23
+ "cli",
24
+ "xx",
25
+ "utilities"
26
+ ],
27
+ "author": "",
28
+ "license": "ISC",
29
+ "dependencies": {
30
+ "axios": "^1.13.2",
31
+ "browser-sync": "^3.0.4",
32
+ "chalk": "^5.3.0",
33
+ "clipboardy": "^5.0.2",
34
+ "commander": "^12.0.0",
35
+ "diff": "^8.0.2",
36
+ "docx": "^9.5.1",
37
+ "dotenv": "^17.2.3",
38
+ "ejs": "^3.1.10",
39
+ "envinfo": "^7.21.0",
40
+ "exceljs": "^4.4.0",
41
+ "figlet": "^1.9.4",
42
+ "form-data": "^4.0.5",
43
+ "fs-extra": "^11.3.3",
44
+ "globby": "^16.1.0",
45
+ "inquirer": "8.2.4",
46
+ "ip": "^2.0.1",
47
+ "node-ssh": "^13.2.1",
48
+ "open": "^11.0.0",
49
+ "ora": "^9.0.0",
50
+ "prettier": "^3.7.4",
51
+ "qrcode": "^1.5.4",
52
+ "rimraf": "^6.1.2",
53
+ "update-notifier": "^7.3.1",
54
+ "winston": "^3.10.0",
55
+ "zx": "^8.8.5"
56
+ },
57
+ "devDependencies": {
58
+ "standard-version": "^9.5.0",
59
+ "vitepress": "^1.6.4",
60
+ "vue": "^3.5.26"
61
+ }
62
+ }