zhixuan-plugin-cli 1.0.3 → 1.0.4

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
@@ -32,6 +32,16 @@ program
32
32
  require('../commands/pull')(options);
33
33
  });
34
34
 
35
+ // 注册 createPage 命令 - 支持name和page参数
36
+ program
37
+ .command('createPage')
38
+ .description('添加应该新页面')
39
+ .option('--name <pageName>', '页面名称')
40
+ .option('--path <pagePath>', '页面路径')
41
+ .action((options) => {
42
+ require('../commands/createPage')(options);
43
+ });
44
+
35
45
  // 注册 push 命令 - 支持pluginId、version和token参数
36
46
  program
37
47
  .command('push')
@@ -63,6 +73,9 @@ program.on('--help', () => {
63
73
  console.log(chalk.cyan(' # 拉取插件到指定目录'));
64
74
  console.log(chalk.white(' $ zx-cli pull --versionId <version-id> --token <token> --dir ./my-plugins'));
65
75
  console.log();
76
+ console.log(chalk.cyan(' # 添加页面(使用页面名称、页面路径)'));
77
+ console.log(chalk.white(' $ zx-cli createPage --name <pageName> --path <pagePath>'));
78
+ console.log();
66
79
  console.log(chalk.cyan(' # 推送插件(使用插件ID、插件版本、token)'));
67
80
  console.log(chalk.white(' $ zx-cli push --pluginId <plugin-id> --versionNum <version> -vDesc <versionDescription> --token <token>'));
68
81
  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,42 +2,22 @@ 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
@@ -100,7 +80,7 @@ module.exports = async function pull(options) {
100
80
 
101
81
  try {
102
82
  // 构建下载API端点 - 使用版本ID
103
- const downloadUrl = `${serverUrl.replace(/\/$/, '')}/api/plugins/versions/${versionId}/download`;
83
+ const downloadUrl = `${serverUrl.replace(/\/$/, '')}/api/ViewPlugin/version/download/${versionId}`;
104
84
 
105
85
  // 下载插件
106
86
  await downloadPlugin(downloadUrl, token, targetDir, versionId);
@@ -124,42 +104,65 @@ module.exports = async function pull(options) {
124
104
  }
125
105
  };
126
106
 
107
+ // 下载插件的函数
127
108
  // 下载插件的函数
128
109
  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
- };
110
+ const axios = require('axios');
111
+ const fs = require('fs-extra');
112
+ const path = require('path');
113
+ const AdmZip = require('adm-zip'); // 需要安装 adm-zip: npm install adm-zip
114
+
115
+ try {
116
+ console.log(chalk.yellow(`正在从 ${downloadUrl} 下载版本 ${versionId}...`));
117
+
118
+ // 发起下载请求
119
+ const response = await axios({
120
+ method: 'GET',
121
+ url: downloadUrl,
122
+ headers: {
123
+ 'Authorization': `Bearer ${token}`
124
+ },
125
+ responseType: 'arraybuffer', // 接收二进制数据
126
+ timeout: 60000 // 设置超时时间为60秒
127
+ });
128
+
129
+ if (response.status !== 200) {
130
+ throw new Error(`下载失败: ${response.status} ${response.statusText}`);
131
+ }
152
132
 
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'));
133
+ // 确保目标目录存在
134
+ await fs.ensureDir(targetDir);
159
135
 
160
- resolve();
161
- } catch (error) {
162
- reject(error);
136
+ // 获取响应中的文件数据
137
+ const zipData = response.data;
138
+
139
+ // 创建临时ZIP文件
140
+ const tempZipPath = path.join(targetDir, `${versionId}.zip`);
141
+ await fs.writeFile(tempZipPath, zipData);
142
+
143
+ // 解压ZIP文件
144
+ const zip = new AdmZip(zipData);
145
+
146
+ // 获取ZIP中的根目录(通常ZIP文件会有一个顶层目录)
147
+ const zipEntries = zip.getEntries();
148
+ if (zipEntries.length === 0) {
149
+ throw new Error('下载的文件为空或损坏');
163
150
  }
164
- });
151
+
152
+ // 解压到目标目录
153
+ zip.extractAllTo(targetDir, true); // true表示覆盖已存在的文件
154
+
155
+ // 删除临时ZIP文件
156
+ await fs.remove(tempZipPath);
157
+
158
+ console.log(chalk.green(`插件已成功解压到: ${targetDir}`));
159
+ } catch (error) {
160
+ if (error.code === 'ECONNABORTED') {
161
+ throw new Error('下载超时,请检查网络连接');
162
+ } else if (error.response) {
163
+ throw new Error(`下载失败: ${error.response.status} ${error.response.statusText}`);
164
+ } else {
165
+ throw new Error(`下载过程中发生错误: ${error.message}`);
166
+ }
167
+ }
165
168
  }
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.4",
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",