Pārlūkot izejas kodu

pnpm提供安装按钮

黄中银 3 nedēļas atpakaļ
vecāks
revīzija
f7629ce640

+ 5 - 1
ApqInstaller/src/i18n/en-US.ts

@@ -54,7 +54,11 @@ export default {
       pnpmRequiresNodejs: 'Requires Node.js to be installed first',
       mirror: 'Node.js Download Source',
       mirrorOfficial: 'Node.js Official',
-      mirrorNpmmirror: 'npmmirror (China)'
+      mirrorNpmmirror: 'npmmirror (China)',
+      pnpmTitle: 'pnpm Package Manager',
+      pnpmDesc: 'Fast, disk space efficient package manager [Recommended for China users]',
+      installPnpmBtn: 'Install pnpm',
+      installNodejsFirst: 'Please install Node.js first'
     },
     vscode: {
       name: 'Visual Studio Code',

+ 5 - 1
ApqInstaller/src/i18n/zh-CN.ts

@@ -54,7 +54,11 @@ export default {
       pnpmRequiresNodejs: '需要先安装 Node.js',
       mirror: '选择Node.js下载源',
       mirrorOfficial: 'Node.js 官方',
-      mirrorNpmmirror: 'npmmirror 镜像'
+      mirrorNpmmirror: 'npmmirror 镜像',
+      pnpmTitle: 'pnpm 包管理器',
+      pnpmDesc: '快速、节省磁盘空间的包管理器 [国内用户推荐]',
+      installPnpmBtn: '安装 pnpm',
+      installNodejsFirst: '请先安装 Node.js'
     },
     vscode: {
       name: 'Visual Studio Code',

+ 79 - 15
ApqInstaller/src/views/NodejsView.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { computed } from 'vue'
+import { computed, ref } from 'vue'
 import { useI18n } from 'vue-i18n'
 import { useVersionsStore } from '@/stores/versions'
 import { useInstallStore } from '@/stores/install'
@@ -54,6 +54,29 @@ function handleInstall() {
 function handleCancel() {
   emit('cancel')
 }
+
+// pnpm 安装相关
+const pnpmInstalling = ref(false)
+const pnpmError = ref('')
+
+const pnpmInstalled = computed(() => installStore.isInstalled('pnpm'))
+const pnpmInstalledVersion = computed(() => installStore.getInstalledVersion('pnpm'))
+
+async function handleInstallPnpm() {
+  if (pnpmInstalling.value || pnpmInstalled.value) return
+
+  pnpmInstalling.value = true
+  pnpmError.value = ''
+
+  try {
+    await window.electronAPI.install('pnpm', {})
+  } catch (error) {
+    pnpmError.value = (error as Error).message
+  } finally {
+    pnpmInstalling.value = false
+    await installStore.checkInstallStatus('pnpm')
+  }
+}
 </script>
 
 <template>
@@ -88,18 +111,6 @@ function handleCancel() {
         </el-select>
       </div>
 
-      <!-- pnpm 安装选项 -->
-      <div class="pnpm-option">
-        <div class="pnpm-row">
-          <el-checkbox v-model="installStore.installOptions.nodejs.installPnpm" :disabled="installStore.isInstalled('pnpm')">
-            {{ t('software.nodejs.installPnpm') }}
-          </el-checkbox>
-          <el-tag v-if="installStore.isInstalled('pnpm')" type="success" size="small">
-            {{ t('common.installed') }} v{{ installStore.getInstalledVersion('pnpm') }}
-          </el-tag>
-        </div>
-      </div>
-
       <div class="button-group">
         <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
           {{ buttonText }}
@@ -107,6 +118,30 @@ function handleCancel() {
         <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
       </div>
 
+      <!-- pnpm 安装选项 -->
+      <div class="pnpm-option">
+        <div class="ext-header">
+          <span class="ext-title">{{ t('software.nodejs.pnpmTitle') }}</span>
+          <el-tag v-if="pnpmInstalled" type="success" size="small">
+            {{ t('common.installed') }} v{{ pnpmInstalledVersion }}
+          </el-tag>
+        </div>
+        <p class="ext-desc">{{ t('software.nodejs.pnpmDesc') }}</p>
+        <div class="ext-actions">
+          <el-button
+            size="small"
+            :type="pnpmInstalled ? 'success' : 'primary'"
+            :disabled="pnpmInstalled || pnpmInstalling || !isInstalled"
+            :loading="pnpmInstalling"
+            @click="handleInstallPnpm"
+          >
+            {{ pnpmInstalled ? t('common.installed') : pnpmInstalling ? t('common.installing') : t('software.nodejs.installPnpmBtn') }}
+          </el-button>
+          <span v-if="!isInstalled" class="ext-hint">{{ t('software.nodejs.installNodejsFirst') }}</span>
+        </div>
+        <p v-if="pnpmError" class="ext-error">{{ pnpmError }}</p>
+      </div>
+
       <div class="status-container">
         <p :class="['status-text', { success: status.success, error: status.error }]">{{ statusText }}</p>
         <el-progress
@@ -135,10 +170,39 @@ function handleCancel() {
     border: 1px solid var(--border-color-lighter);
     border-radius: var(--border-radius);
 
-    .pnpm-row {
+    .ext-header {
+      display: flex;
+      align-items: center;
+      gap: var(--spacing-sm);
+      margin-bottom: var(--spacing-xs);
+    }
+
+    .ext-title {
+      font-weight: 500;
+      color: var(--text-primary);
+    }
+
+    .ext-desc {
+      font-size: 12px;
+      color: var(--text-secondary);
+      margin: 0 0 var(--spacing-sm) 0;
+    }
+
+    .ext-actions {
       display: flex;
       align-items: center;
-      gap: var(--spacing-md);
+      gap: var(--spacing-sm);
+    }
+
+    .ext-hint {
+      font-size: 12px;
+      color: var(--text-secondary);
+    }
+
+    .ext-error {
+      font-size: 12px;
+      color: var(--el-color-danger);
+      margin: var(--spacing-xs) 0 0 0;
     }
   }
 }

+ 7 - 7
ApqInstaller/src/views/VscodeView.vue

@@ -128,6 +128,13 @@ checkClaudeCodeExtInstalled()
         </el-select>
       </div>
 
+      <div class="button-group">
+        <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
+          {{ buttonText }}
+        </el-button>
+        <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
+      </div>
+
       <!-- Claude Code for VS Code 插件安装选项 -->
       <div class="claude-code-ext-option">
         <div class="ext-header">
@@ -157,13 +164,6 @@ checkClaudeCodeExtInstalled()
         <p v-if="claudeCodeExtError" class="ext-error">{{ claudeCodeExtError }}</p>
       </div>
 
-      <div class="button-group">
-        <el-button type="primary" :disabled="isDisabled" :loading="status.installing" @click="handleInstall">
-          {{ buttonText }}
-        </el-button>
-        <el-button v-if="status.installing" @click="handleCancel">{{ t('common.cancel') }}</el-button>
-      </div>
-
       <div class="status-container">
         <p :class="['status-text', { success: status.success, error: status.error }]">{{ statusText }}</p>
         <el-progress