zhixuan-plugin-cli 1.0.3 → 1.0.5

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.
package/bin/index.js CHANGED
@@ -23,7 +23,8 @@ program
23
23
  program
24
24
  .command('pull')
25
25
  .description('从智轩平台拉取插件')
26
- .option('--versionId <versionId>', '版本ID')
26
+ .option('--pluginId <pluginId>', '插件ID')
27
+ .option('-vNo, --versionNum <version>', '版本号')
27
28
  .option('-t, --token <token>', '访问令牌')
28
29
  .option('--server <server>', '服务器地址')
29
30
  .option('-d, --dir <directory>', '下载到指定目录')
@@ -32,6 +33,16 @@ program
32
33
  require('../commands/pull')(options);
33
34
  });
34
35
 
36
+ // 注册 createPage 命令 - 支持name和page参数
37
+ program
38
+ .command('createPage')
39
+ .description('添加一个新页面')
40
+ .option('--name <pageName>', '页面名称')
41
+ .option('--path <pagePath>', '页面路径')
42
+ .action((options) => {
43
+ require('../commands/createPage')(options);
44
+ });
45
+
35
46
  // 注册 push 命令 - 支持pluginId、version和token参数
36
47
  program
37
48
  .command('push')
@@ -63,6 +74,9 @@ program.on('--help', () => {
63
74
  console.log(chalk.cyan(' # 拉取插件到指定目录'));
64
75
  console.log(chalk.white(' $ zx-cli pull --versionId <version-id> --token <token> --dir ./my-plugins'));
65
76
  console.log();
77
+ console.log(chalk.cyan(' # 添加页面(使用页面名称、页面路径)'));
78
+ console.log(chalk.white(' $ zx-cli createPage --name <pageName> --path <pagePath>'));
79
+ console.log();
66
80
  console.log(chalk.cyan(' # 推送插件(使用插件ID、插件版本、token)'));
67
81
  console.log(chalk.white(' $ zx-cli push --pluginId <plugin-id> --versionNum <version> -vDesc <versionDescription> --token <token>'));
68
82
  console.log();
@@ -0,0 +1,226 @@
1
+ // commands/createPage.js
2
+ const chalk = require('chalk');
3
+ const inquirer = require('inquirer');
4
+ const fs = require('fs-extra');
5
+ const path = require('path');
6
+
7
+ module.exports = async function createPage(options) {
8
+ let { name: pageName, path: pagePath } = options;
9
+
10
+ console.log();
11
+ console.log(chalk.cyan('✨ 开始创建页面'));
12
+
13
+ // 验证参数
14
+ if (!pageName || !pagePath) {
15
+ if (!pageName) {
16
+ const nameAnswer = await inquirer.prompt([
17
+ {
18
+ name: 'pageName',
19
+ type: 'input',
20
+ message: '请输入页面名称:',
21
+ validate: input => {
22
+ if (!input.trim()) {
23
+ return '页面名称不能为空';
24
+ }
25
+ return true;
26
+ }
27
+ }
28
+ ]);
29
+ pageName = nameAnswer.pageName;
30
+ }
31
+
32
+ if (!pagePath) {
33
+ const pageAnswer = await inquirer.prompt([
34
+ {
35
+ name: 'pagePath',
36
+ type: 'input',
37
+ message: '请输入页面路径:',
38
+ default: `/${pageName.toLowerCase()}`,
39
+ validate: input => {
40
+ if (!input.trim()) {
41
+ return '页面路径不能为空';
42
+ }
43
+ return true;
44
+ }
45
+ }
46
+ ]);
47
+ pagePath = pageAnswer.pagePath;
48
+ }
49
+ }
50
+
51
+ // 获取当前工作目录
52
+ const projectRoot = process.cwd();
53
+
54
+ // 查找页面目录位置
55
+ let pagesDir = null;
56
+ const possiblePagesDirs = ['src/pages', 'src/views', 'pages', 'views'];
57
+
58
+ for (const dir of possiblePagesDirs) {
59
+ const fullPath = path.join(projectRoot, dir);
60
+ if (await fs.pathExists(fullPath)) {
61
+ pagesDir = fullPath;
62
+ break;
63
+ }
64
+ }
65
+
66
+ if (!pagesDir) {
67
+ console.log(chalk.red('❌ 未找到页面目录 (src/pages, src/views, pages, views)'));
68
+ return;
69
+ }
70
+
71
+ // 创建页面目录
72
+ const pageDir = path.join(pagesDir, pagePath);
73
+ if (await fs.pathExists(pageDir)) {
74
+ const { confirm } = await inquirer.prompt([
75
+ {
76
+ name: 'confirm',
77
+ type: 'confirm',
78
+ message: `页面目录 ${chalk.cyan(pageName)} 已存在,是否覆盖?`
79
+ }
80
+ ]);
81
+
82
+ if (!confirm) {
83
+ console.log(chalk.yellow('操作已取消'));
84
+ return;
85
+ }
86
+ } else {
87
+ await fs.ensureDir(pageDir);
88
+ }
89
+
90
+ // 创建页面文件
91
+ const pageFileName = `index.vue`;
92
+ const pageFilePath = path.join(pageDir, pageFileName);
93
+
94
+ // 页面模板内容
95
+ const relativePath = path.relative(path.join(projectRoot, 'src'), pageDir).replace(/\\/g, '/');
96
+ const pageContent = `<!-- ${relativePath}/index.vue -->
97
+ <template>
98
+ <div class="${pagePath.toLowerCase()}">
99
+ <h1>${pageName}</h1>
100
+ <p>欢迎来到 ${pageName} 页面</p>
101
+ userInfo:
102
+ <json-viewer :value="userInfo"></json-viewer>
103
+ <div>getModelList 结果:</div>
104
+ <json-viewer :value="modelListData"></json-viewer>
105
+ <div>getFormConfig 结果:</div>
106
+ <json-viewer :value="formConfigData"></json-viewer>
107
+ </div>
108
+ </template>
109
+
110
+ <script>
111
+ import JsonViewer from 'vue-json-viewer';
112
+ export default {
113
+ name: '${pagePath.charAt(0).toUpperCase() + pagePath.slice(1)}Index',
114
+ components: { JsonViewer },
115
+ data() {
116
+ return {
117
+ modelListData: {},
118
+ formConfigData: {}
119
+ }
120
+ },
121
+ computed: {
122
+ userInfo() {
123
+ return this.$zxUtils?.userInfo ?? {}
124
+ }
125
+ },
126
+ mounted() {
127
+ console.log('@@@zxUtils', this.$zxUtils);
128
+ this.fetchModelList();
129
+ this.fetchFormConfig();
130
+ },
131
+ methods: {
132
+ async fetchModelList() {
133
+ const getModelList = this.$zxUtils?.viewApi?.getModelList;
134
+ if (!getModelList || typeof getModelList !== 'function') {
135
+ this.modelListData = { error: '未接入主应用 viewApi.getModelList' };
136
+ return;
137
+ }
138
+ try {
139
+ const res = await getModelList({ currentPage: 1, pageSize: 20 });
140
+ this.modelListData = res;
141
+ } catch (e) {
142
+ this.modelListData = { error: e?.message || '获取列表失败', detail: e };
143
+ }
144
+ },
145
+ async fetchFormConfig() {
146
+ const getFormConfig = this.$zxUtils?.viewApi?.getFormConfig;
147
+ if (!getFormConfig || typeof getFormConfig !== 'function') {
148
+ this.formConfigData = { error: '未接入主应用 viewApi.getFormConfig' };
149
+ return;
150
+ }
151
+ try {
152
+ const res = await getFormConfig();
153
+ this.formConfigData = res;
154
+ } catch (e) {
155
+ this.formConfigData = { error: e?.message || '获取表单配置失败', detail: e };
156
+ }
157
+ }
158
+ }
159
+ };
160
+ </script>
161
+
162
+ <style scoped>
163
+ .${pagePath.toLowerCase()} {
164
+ padding: 20px;
165
+ overflow: auto;
166
+ }
167
+ </style>
168
+ `;
169
+
170
+ await fs.writeFile(pageFilePath, pageContent);
171
+ console.log(chalk.green(`✅ 📁 页面文件创建成功: ${pageFilePath}`));
172
+ // 尝试更新页面配置
173
+ await updatePluginViews(projectRoot, pageName, pagePath, pagesDir);
174
+ console.log(chalk.green('✅ 页面创建完成!'));
175
+ console.log();
176
+ };
177
+
178
+ // 更新页面配置
179
+ async function updatePluginViews(projectRoot, pageName, pagePath, pagesDir) {
180
+ // 查找 pluginViews.js 文件
181
+ const pluginViewsPath = path.join(projectRoot, 'src/pluginViews.js');
182
+
183
+ let routerFile = null;
184
+ let routerPath = null;
185
+
186
+ if (await fs.pathExists(pluginViewsPath)) {
187
+ routerFile = await fs.readFile(pluginViewsPath, 'utf8');
188
+ routerPath = pluginViewsPath;
189
+ } else {
190
+ console.log(chalk.yellow('⚠️ 未找到 pluginViews.js 文件,无法自动添加路由'));
191
+ return;
192
+ }
193
+
194
+ // 检查是否已存在相同名称的页面
195
+ if (routerFile.includes(`name: '${pageName}'`) ||
196
+ routerFile.includes(`name: "${pageName}"`)) {
197
+ console.log(chalk.yellow(`⚠️ 页面 ${pageName} 已存在,跳过添加`));
198
+ return;
199
+ }
200
+
201
+ try {
202
+ // 计算相对于 src 的路径,用于导入
203
+ const relativePagesPath = path.relative(path.join(projectRoot, 'src'), pagesDir);
204
+ let importPath = '';
205
+ if (relativePagesPath) {
206
+ importPath = `@/${relativePagesPath}/${pagePath}/index.vue`.replace(/\\/g, '/');
207
+ } else {
208
+ importPath = `@/views/${pagePath}/index.vue`.replace(/\\/g, '/'); // 默认路径
209
+ }
210
+
211
+ // 构建新页面配置
212
+ const newRoute = ` {
213
+ name: '${pageName}',
214
+ component: () => import('${importPath}')
215
+ }`;
216
+
217
+ const updatedRouter = routerFile.replace(
218
+ /(\s*\]\s*;?\s*)$/,
219
+ `\n${newRoute},$1`
220
+ );
221
+ await fs.writeFile(routerPath, updatedRouter);
222
+ console.log(chalk.green(`✅ 页面配置已更新: ${routerPath}`));
223
+ } catch (error) {
224
+ console.log(chalk.yellow(`⚠️ 更新页面配置失败: ${error.message}`));
225
+ }
226
+ }
package/commands/pull.js CHANGED
@@ -2,50 +2,37 @@ const chalk = require('chalk');
2
2
  const inquirer = require('inquirer');
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
- const Conf = require('conf');
6
5
  const ora = require('ora');
7
6
 
8
7
  module.exports = async function pull(options) {
9
8
  console.log();
10
9
  console.log(chalk.cyan(`🔄 正在拉取插件`));
11
10
 
12
- // 使用 Conf 管理配置
13
- const config = new Conf({
14
- configName: 'package',
15
- fileExtension: 'json',
16
- cwd: process.cwd(),
17
- defaults: {
18
- name: 'plugin',
19
- version: '1.0.0',
20
- pluginId: '',
21
- versionDescription: '描述'
22
- }
23
- });
24
-
25
11
  try {
26
12
  // 获取服务器地址(优先使用命令行参数)
27
- const serverUrl = options.server || config.get('settings.serverUrl');
13
+ const serverUrl = options.server || 'https://dev.zhixuan.cloud/';
28
14
 
29
15
  // 获取token(优先使用命令行参数)
30
16
  let token = options.token;
31
17
  if (!token) {
32
- // 如果命令行没有提供token,尝试从配置中获取
33
- const auth = config.get('auth');
34
- if (auth.token) {
35
- token = auth.token;
36
- } else {
37
- console.log(chalk.red('❌ 未提供访问令牌'));
38
- console.log(chalk.cyan('请使用 --token 参数提供访问令牌'));
39
- return;
40
- }
18
+ console.log(chalk.red('❌ 未提供访问令牌'));
19
+ console.log(chalk.cyan('请使用 --token 参数提供访问令牌'));
20
+ return;
41
21
  }
42
22
 
43
23
  // 获取版本ID
44
- const versionId = options.versionId;
24
+ const pluginId = options.pluginId;
25
+ const versionNum = options.versionNum;
26
+
27
+ if (!pluginId) {
28
+ console.log(chalk.red('❌ 未提供插件ID'));
29
+ console.log(chalk.cyan('请使用 --pluginId 插件ID'));
30
+ return;
31
+ }
45
32
 
46
- if (!versionId) {
47
- console.log(chalk.red('❌ 未提供版本ID'));
48
- console.log(chalk.cyan('请使用 --versionId 参数提供版本ID'));
33
+ if (!versionNum) {
34
+ console.log(chalk.red('❌ 未提供版本号'));
35
+ console.log(chalk.cyan('请使用 --versionNum 插件版本号'));
49
36
  return;
50
37
  }
51
38
 
@@ -55,7 +42,7 @@ module.exports = async function pull(options) {
55
42
  targetDir = path.resolve(options.dir);
56
43
  } else {
57
44
  // 默认使用版本ID作为目录名的一部分
58
- targetDir = path.join(process.cwd(), `version-${versionId}`);
45
+ targetDir = path.join(process.cwd(), `plugin_${pluginId}_${versionNum}`);
59
46
  }
60
47
 
61
48
  // 检查目录是否已存在
@@ -100,17 +87,17 @@ module.exports = async function pull(options) {
100
87
 
101
88
  try {
102
89
  // 构建下载API端点 - 使用版本ID
103
- const downloadUrl = `${serverUrl.replace(/\/$/, '')}/api/plugins/versions/${versionId}/download`;
90
+ const downloadUrl = `${serverUrl.replace(/\/$/, '')}/api/ViewPlugin/version/download/${pluginId}/${versionNum}`;
104
91
 
105
92
  // 下载插件
106
- await downloadPlugin(downloadUrl, token, targetDir, versionId);
93
+ await downloadPlugin(downloadUrl, token, targetDir, `${pluginId}_${versionNum}`);
107
94
 
108
95
  spinner.succeed(chalk.green('插件下载成功!'));
109
96
 
110
97
  console.log();
111
98
  console.log(chalk.green('✅ 插件拉取成功!'));
112
99
  console.log();
113
- console.log(chalk.cyan(`版本ID: ${versionId}`));
100
+ console.log(chalk.cyan(`版本ID: ${versionNum}`));
114
101
  console.log(chalk.cyan('插件已下载到: ' + targetDir));
115
102
  console.log();
116
103
  } catch (error) {
@@ -124,42 +111,65 @@ module.exports = async function pull(options) {
124
111
  }
125
112
  };
126
113
 
114
+ // 下载插件的函数
127
115
  // 下载插件的函数
128
116
  async function downloadPlugin(downloadUrl, token, targetDir, versionId) {
129
- // 模拟下载过程,实际应用中应替换为真实的下载逻辑
130
- return new Promise(async (resolve, reject) => {
131
- try {
132
- // 模拟网络请求,实际应用中应使用真实的HTTP客户端
133
- console.log(chalk.yellow(`正在从 ${downloadUrl} 下载版本 ${versionId}...`));
134
-
135
- // 模拟等待时间
136
- await new Promise(resolve => setTimeout(resolve, 1500));
137
-
138
- // 创建目标目录
139
- await fs.ensureDir(targetDir);
140
-
141
- // 模拟创建插件文件结构
142
- const packageJson = {
143
- name: `version-${versionId}`,
144
- version: '1.0.0',
145
- description: 'A Zhixuan plugin version',
146
- main: 'index.js',
147
- scripts: {
148
- dev: 'zx-cli serve',
149
- build: 'zx-cli build'
150
- }
151
- };
117
+ const axios = require('axios');
118
+ const fs = require('fs-extra');
119
+ const path = require('path');
120
+ const AdmZip = require('adm-zip'); // 需要安装 adm-zip: npm install adm-zip
152
121
 
153
- // 写入文件
154
- await fs.writeJson(path.join(targetDir, 'package.json'), packageJson, { spaces: 2 });
155
- await fs.writeFile(path.join(targetDir, 'index.js'), '// Plugin entry point\nconsole.log("Plugin loaded");', 'utf8');
156
- await fs.writeFile(path.join(targetDir, 'README.md'), `# Version ${versionId}\n\nThis is a Zhixuan plugin version.`, 'utf8');
157
- await fs.ensureDir(path.join(targetDir, 'src'));
158
- await fs.ensureDir(path.join(targetDir, 'public'));
122
+ try {
123
+ console.log(chalk.yellow(`正在从 ${downloadUrl} 下载 ${versionId}...`));
124
+
125
+ // 发起下载请求
126
+ const response = await axios({
127
+ method: 'GET',
128
+ url: downloadUrl,
129
+ headers: {
130
+ 'Authorization': `Bearer ${token}`
131
+ },
132
+ responseType: 'arraybuffer', // 接收二进制数据
133
+ timeout: 60000 // 设置超时时间为60秒
134
+ });
159
135
 
160
- resolve();
161
- } catch (error) {
162
- reject(error);
136
+ if (response.status !== 200) {
137
+ throw new Error(`下载失败: ${response.status} ${response.statusText}`);
138
+ }
139
+
140
+ // 确保目标目录存在
141
+ await fs.ensureDir(targetDir);
142
+
143
+ // 获取响应中的文件数据
144
+ const zipData = response.data;
145
+
146
+ // 创建临时ZIP文件
147
+ const tempZipPath = path.join(targetDir, `${versionId}.zip`);
148
+ await fs.writeFile(tempZipPath, zipData);
149
+
150
+ // 解压ZIP文件
151
+ const zip = new AdmZip(zipData);
152
+
153
+ // 获取ZIP中的根目录(通常ZIP文件会有一个顶层目录)
154
+ const zipEntries = zip.getEntries();
155
+ if (zipEntries.length === 0) {
156
+ throw new Error('下载的文件为空或损坏');
163
157
  }
164
- });
158
+
159
+ // 解压到目标目录
160
+ zip.extractAllTo(targetDir, true); // true表示覆盖已存在的文件
161
+
162
+ // 删除临时ZIP文件
163
+ await fs.remove(tempZipPath);
164
+
165
+ console.log(chalk.green(`插件已成功解压到: ${targetDir}`));
166
+ } catch (error) {
167
+ if (error.code === 'ECONNABORTED') {
168
+ throw new Error('下载超时,请检查网络连接');
169
+ } else if (error.response) {
170
+ throw new Error(`下载失败: ${error.response.status} ${error.response.statusText}`);
171
+ } else {
172
+ throw new Error(`下载过程中发生错误: ${error.message}`);
173
+ }
174
+ }
165
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zhixuan-plugin-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "zhixuan插件项目脚手架CLI工具",
5
5
  "main": "bin/index.js",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "author": "shituanquan",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
+ "adm-zip": "^0.5.16",
24
25
  "archiver": "^7.0.1",
25
26
  "axios": "^1.13.6",
26
27
  "chalk": "^4.1.2",