Browse Source

将应用部署配置相关操作迁移到 AppDeployConfig.vue 页面

reghao 3 weeks ago
parent
commit
b9ef14b23b
3 changed files with 358 additions and 272 deletions
  1. 4 4
      src/api/devops.js
  2. 278 0
      src/components/card/AppDeployConfig.vue
  3. 76 268
      src/views/devops/app/AppConfig.vue

+ 4 - 4
src/api/devops.js

@@ -309,12 +309,12 @@ export function getDeployMachineList(env) {
   return get(devopsApi.getAppDeployConfigList + '/machine?env=' + env)
   return get(devopsApi.getAppDeployConfigList + '/machine?env=' + env)
 }
 }
 
 
-export function addAppDeployConfig(formData) {
-  return postForm(devopsApi.getAppDeployConfigList, formData)
+export function addAppDeployConfig(payload) {
+  return post(devopsApi.getAppDeployConfigList, payload)
 }
 }
 
 
-export function updateAppDeployConfig(formData) {
-  return postForm(devopsApi.getAppDeployConfigList + '/update', formData)
+export function updateAppDeployConfig(payload) {
+  return post(devopsApi.getAppDeployConfigList + '/update', payload)
 }
 }
 
 
 export function deleteAppDeployConfig(formData) {
 export function deleteAppDeployConfig(formData) {

+ 278 - 0
src/components/card/AppDeployConfig.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="deploy-config-container">
+    <el-dialog :title="appId + ' 的部署配置列表'" append-to-body :visible.sync="visible" width="80%" center @close="$emit('close')">
+      <el-button type="success" icon="el-icon-plus" style="margin-bottom: 10px" @click="handleAddDeployConfig">添加</el-button>
+      <el-table :data="appDeployConfigList" border height="400" style="width: 100%">
+        <el-table-column prop="machineIpv4" label="机器地址" />
+        <el-table-column prop="packType" label="打包类型" />
+        <el-table-column prop="startScript" label="运行参数" :show-overflow-tooltip="true" />
+        <el-table-column fixed="right" label="操作" width="180">
+          <template slot-scope="scope">
+            <el-button size="mini" @click="handleEditDeployConfig(scope.row)">编辑</el-button>
+            <el-button size="mini" type="danger" @click="handleDeleteDeployConfig(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+
+    <el-dialog :title="isEdit ? '编辑部署配置' : '添加部署配置'" append-to-body :visible.sync="showFormDialog" width="600px">
+      <el-form :model="activeForm" label-width="100px">
+        <el-form-item label="应用 ID">
+          <el-input v-model="activeForm.appId" readonly />
+        </el-form-item>
+
+        <el-form-item v-if="!isEdit" label="选择机器">
+          <el-select v-model="activeForm.machineId" placeholder="请选择机器" style="width: 100%">
+            <el-option v-for="item in machineList" :key="item.label" :label="item.value" :value="item.label" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-else label="机器地址">
+          <el-input v-model="activeForm.machineId" readonly />
+        </el-form-item>
+
+        <el-divider content-position="left">资源限制 <small style="color: #94a3b8; font-weight: normal; margin-left: 10px;">(设置为 0 表示不限制)</small></el-divider>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="CPU 限制">
+              <el-input-number v-model="activeForm.dockerParams.cpu" :min="0" :precision="1" :step="0.5" controls-position="right" />
+              <span class="unit-text">核</span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="内存限制">
+              <el-input-number v-model="activeForm.dockerParams.memory" :min="0" :step="128" controls-position="right" />
+              <span class="unit-text">MB</span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-divider content-position="left">环境变量</el-divider>
+        <div v-for="(env, index) in activeForm.dockerParams.envs" :key="index" class="env-row">
+          <el-input v-model="env.key" placeholder="KEY" style="width: 160px" size="small" />
+          <span class="env-split">=</span>
+          <el-input v-model="env.value" placeholder="VALUE" style="width: 200px" size="small" />
+          <el-button type="text" class="danger-text" icon="el-icon-delete" @click="removeEnv(index)" />
+        </div>
+        <el-button type="text" icon="el-icon-plus" style="margin-left: 100px; margin-bottom: 20px;" @click="addEnv">添加环境变量</el-button>
+
+        <div class="preview-box">
+          <div class="preview-label">生成的参数预览:</div>
+          <code>{{ previewScript }}</code>
+        </div>
+
+        <el-form-item style="margin-top: 20px; text-align: right;">
+          <el-button @click="showFormDialog = false">取消</el-button>
+          <el-button type="primary" @click="submitForm">确定保存</el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  getAppDeployConfigList,
+  getDeployMachineList,
+  addAppDeployConfig,
+  updateAppDeployConfig,
+  deleteAppDeployConfig
+} from '@/api/devops'
+
+export default {
+  name: 'AppDeployConfig',
+  props: {
+    appId: { type: String, default: '' },
+    packType: { type: String, default: '' },
+    env: { type: String, default: '' },
+    show: { type: Boolean, default: false }
+  },
+  data() {
+    return {
+      visible: false,
+      isEdit: false,
+      showFormDialog: false,
+      appDeployConfigList: [],
+      machineList: [],
+      activeForm: {
+        appId: '',
+        machineId: '',
+        packType: '',
+        dockerParams: {
+          cpu: 0.0,
+          memory: 0,
+          envs: []
+        }
+      }
+    }
+  },
+  computed: {
+    // 实时生成预览字符串
+    previewScript() {
+      return this.generateStartScript()
+    }
+  },
+  watch: {
+    show(val) {
+      this.visible = val
+      if (val && this.appId) this.fetchList()
+    }
+  },
+  methods: {
+    // 1. 生成脚本逻辑:处理 0 的特殊情况
+    generateStartScript() {
+      const { cpu, memory } = this.activeForm.dockerParams
+      const scriptParts = []
+
+      // 如果 cpu > 0 才添加限制,否则不添加(即不限制)
+      if (cpu > 0) {
+        scriptParts.push(`--cpus="${cpu.toFixed(1)}"`)
+      }
+
+      // 如果 memory > 0 才添加限制
+      if (memory > 0) {
+        scriptParts.push(`--memory="${memory}m"`)
+      }
+
+      // 环境变量
+      this.activeForm.dockerParams.envs.forEach(env => {
+        if (env.key && env.key.trim()) {
+          scriptParts.push(`-e ${env.key.trim()}=${env.value || ''}`)
+        }
+      })
+
+      return scriptParts.join(' ')
+    },
+
+    // 2. 解析回填逻辑:如果脚本中没找到对应的 key,则默认为 0
+    parseStartScript(script) {
+      const params = { cpu: 0, memory: 0 }
+      const envs = []
+      if (!script) return { params, envs }
+
+      const parts = script.split(/\s+/) // 按空格拆分
+      parts.forEach(part => {
+        if (part.startsWith('--cpus=')) {
+          const val = part.split('=')[1].replace(/"/g, '')
+          params.cpu = parseFloat(val) || 0
+        } else if (part.startsWith('--memory=')) {
+          const val = part.split('=')[1].replace('m', '').replace(/"/g, '')
+          params.memory = parseInt(val) || 0
+        } else if (part.startsWith('-e')) {
+          // 处理形如 -e KEY=VALUE 的情况
+          const envPart = script.substring(script.indexOf(part)).split(/\s+/)[0]
+          const pair = envPart.replace('-e', '').trim().split('=')
+          if (pair.length >= 2) {
+            envs.push({ key: pair[0], value: pair.slice(1).join('=') })
+          }
+        }
+      })
+      return { params, envs }
+    },
+
+    addEnv() {
+      this.activeForm.dockerParams.envs.push({ key: '', value: '' })
+    },
+    removeEnv(index) {
+      this.activeForm.dockerParams.envs.splice(index, 1)
+    },
+    async fetchList() {
+      try {
+        const resp = await getAppDeployConfigList(this.appId)
+        if (resp.code === 0) {
+          this.appDeployConfigList = resp.data
+        } else {
+          this.$message.warning(resp.msg)
+        }
+      } catch (e) { this.$message.error(e.message) }
+    },
+    async handleAddDeployConfig() {
+      this.isEdit = false
+      const resp = await getDeployMachineList(this.env)
+      if (resp.code === 0) {
+        this.machineList = resp.data
+        this.activeForm = {
+          appId: this.appId,
+          packType: this.packType,
+          machineId: null,
+          dockerParams: { cpu: 0, memory: 0, envs: [] }
+        }
+        this.showFormDialog = true
+      } else {
+        this.$message.warning(resp.msg)
+      }
+    },
+    handleEditDeployConfig(row) {
+      this.isEdit = true
+      this.activeForm.appId = row.appId
+      this.activeForm.machineId = row.machineId
+      this.activeForm.packType = row.packType
+      this.activeForm.dockerParams = row.dockerParams
+      this.showFormDialog = true
+    },
+    async submitForm() {
+      const startScript = this.generateStartScript()
+      const apiFunc = this.isEdit ? updateAppDeployConfig : addAppDeployConfig
+      const resp = await apiFunc(this.activeForm)
+
+      if (resp.code === 0) {
+        this.$message.success('保存成功')
+        this.showFormDialog = false
+        this.fetchList()
+        if (!this.isEdit) this.$emit('refresh')
+      } else {
+        this.$message.warning(resp.msg)
+      }
+    },
+    handleDeleteDeployConfig(row) {
+      this.$confirm('确定删除该机器部署配置?', '提示', { type: 'warning' }).then(async() => {
+        const fd = new FormData()
+        fd.append('appDeployConfigId', row.appDeployConfigId)
+        const resp = await deleteAppDeployConfig(fd)
+        if (resp.code === 0) {
+          this.fetchList()
+          this.$emit('refresh')
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.env-row {
+  margin-left: 100px;
+  margin-bottom: 8px;
+  display: flex;
+  align-items: center;
+}
+.env-split {
+  margin: 0 10px;
+  font-weight: bold;
+}
+.unit-text {
+  margin-left: 5px;
+  font-size: 12px;
+  color: #909399;
+}
+.danger-text {
+  color: #F56C6C;
+  margin-left: 10px;
+}
+.preview-box {
+  margin-left: 100px;
+  padding: 10px;
+  background-color: #f8f9fa;
+  border-radius: 4px;
+  border: 1px dashed #dcdfe6;
+}
+.preview-label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 5px;
+}
+code {
+  color: #409EFF;
+  font-family: monospace;
+  word-break: break-all;
+}
+</style>

+ 76 - 268
src/views/devops/app/AppConfig.vue

@@ -69,10 +69,18 @@
               style="margin-top: 5px; margin-left: 5px"
               style="margin-top: 5px; margin-left: 5px"
               size="mini"
               size="mini"
               type="success"
               type="success"
-              @click="handleDeployConfig(scope.$index, scope.row)"
+              @click="handleOpenDeploy(scope.row)"
             >查看</el-button>
             >查看</el-button>
           </template>
           </template>
         </el-table-column>
         </el-table-column>
+        <AppDeployConfig
+          :show="showDeployDialog"
+          :app-id="currentAppId"
+          :pack-type="currentPackType"
+          :env="queryInfo.env"
+          @close="showDeployDialog = false"
+          @refresh="getData"
+        />
         <el-table-column
         <el-table-column
           prop="totalDomains"
           prop="totalDomains"
           label="关联域名"
           label="关联域名"
@@ -270,36 +278,36 @@
       <template>
       <template>
         <div class="app-config-container">
         <div class="app-config-container">
           <el-steps :active="active" finish-status="success" simple style="margin-bottom: 20px">
           <el-steps :active="active" finish-status="success" simple style="margin-bottom: 20px">
-            <el-step title="基础信息"></el-step>
-            <el-step title="构建环境"></el-step>
-            <el-step title="详细配置"></el-step>
+            <el-step title="基础信息" />
+            <el-step title="构建环境" />
+            <el-step title="详细配置" />
           </el-steps>
           </el-steps>
 
 
-          <el-form :model="appForm" :rules="appFormRules" ref="appFormRef" label-width="120px">
+          <el-form ref="appFormRef" :model="appForm" :rules="appFormRules" label-width="120px">
             <div v-show="active === 0">
             <div v-show="active === 0">
               <el-form-item label="应用 ID" prop="appId">
               <el-form-item label="应用 ID" prop="appId">
-                <el-input v-model="appForm.appId" placeholder="e.g. user-service"></el-input>
+                <el-input v-model="appForm.appId" placeholder="e.g. user-service" />
               </el-form-item>
               </el-form-item>
               <el-form-item label="应用仓库" prop="appRepo">
               <el-form-item label="应用仓库" prop="appRepo">
-                <el-input v-model="appForm.appRepo" placeholder="e.g. https://git.reghao.cn/reghao/devops"></el-input>
+                <el-input v-model="appForm.appRepo" placeholder="e.g. https://git.reghao.cn/reghao/devops" />
               </el-form-item>
               </el-form-item>
               <el-form-item label="仓库分支" prop="repoBranch">
               <el-form-item label="仓库分支" prop="repoBranch">
-                <el-input v-model="appForm.repoBranch"></el-input>
+                <el-input v-model="appForm.repoBranch" />
               </el-form-item>
               </el-form-item>
               <el-form-item label="仓库认证" prop="repoAuthConfigId">
               <el-form-item label="仓库认证" prop="repoAuthConfigId">
                 <div style="display: flex; align-items: center;">
                 <div style="display: flex; align-items: center;">
                   <el-select
                   <el-select
-                      v-model="appForm.repoAuthConfigId"
-                      placeholder="选择仓库认证"
-                      style="flex: 1; margin-right: 10px;"
-                      clearable
+                    v-model="appForm.repoAuthConfigId"
+                    placeholder="选择仓库认证"
+                    style="flex: 1; margin-right: 10px;"
+                    clearable
                   >
                   >
                     <el-option
                     <el-option
-                        v-for="item in repoAuthList"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                    ></el-option>
+                      v-for="item in repoAuthList"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value"
+                    />
 
 
                     <div slot="empty" style="text-align: center; padding: 10px 0;">
                     <div slot="empty" style="text-align: center; padding: 10px 0;">
                       <p style="margin-bottom: 10px; color: #909399; font-size: 12px;">暂无认证配置</p>
                       <p style="margin-bottom: 10px; color: #909399; font-size: 12px;">暂无认证配置</p>
@@ -309,13 +317,13 @@
                     </div>
                     </div>
                   </el-select>
                   </el-select>
                   <el-button
                   <el-button
-                      type="primary"
-                      icon="el-icon-plus"
-                      circle
-                      size="mini"
-                      @click="showAddRepoAuthDialog = true"
-                      title="添加新认证"
-                  ></el-button>
+                    type="primary"
+                    icon="el-icon-plus"
+                    circle
+                    size="mini"
+                    title="添加新认证"
+                    @click="showAddRepoAuthDialog = true"
+                  />
                 </div>
                 </div>
               </el-form-item>
               </el-form-item>
             </div>
             </div>
@@ -323,12 +331,12 @@
             <div v-show="active === 1">
             <div v-show="active === 1">
               <el-form-item label="应用类型" prop="appType">
               <el-form-item label="应用类型" prop="appType">
                 <el-select v-model="appForm.appType" placeholder="选择应用类型">
                 <el-select v-model="appForm.appType" placeholder="选择应用类型">
-                  <el-option v-for="item in appTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                  <el-option v-for="item in appTypeList" :key="item.value" :label="item.label" :value="item.value" />
                 </el-select>
                 </el-select>
               </el-form-item>
               </el-form-item>
               <el-form-item label="环境" prop="env">
               <el-form-item label="环境" prop="env">
                 <el-radio-group v-model="appForm.env">
                 <el-radio-group v-model="appForm.env">
-                  <el-radio v-for="item in envList" :key="item.value" :label="item.label" :value="item.value"></el-radio>
+                  <el-radio v-for="item in envList" :key="item.value" :label="item.label" :value="item.value" />
                 </el-radio-group>
                 </el-radio-group>
               </el-form-item>
               </el-form-item>
               <el-form-item label="编译器配置" prop="compilerConfigId">
               <el-form-item label="编译器配置" prop="compilerConfigId">
@@ -344,7 +352,7 @@
                       :key="item.value"
                       :key="item.value"
                       :label="item.label"
                       :label="item.label"
                       :value="item.value"
                       :value="item.value"
-                    ></el-option>
+                    />
 
 
                     <div slot="empty" style="text-align: center; padding: 10px 0;">
                     <div slot="empty" style="text-align: center; padding: 10px 0;">
                       <p style="margin-bottom: 10px; color: #909399; font-size: 12px;">暂无编译配置</p>
                       <p style="margin-bottom: 10px; color: #909399; font-size: 12px;">暂无编译配置</p>
@@ -358,9 +366,9 @@
                     icon="el-icon-plus"
                     icon="el-icon-plus"
                     circle
                     circle
                     size="mini"
                     size="mini"
-                    @click="showAddCompilerDialog = true"
                     title="添加新编译配置"
                     title="添加新编译配置"
-                  ></el-button>
+                    @click="showAddCompilerDialog = true"
+                  />
                 </div>
                 </div>
               </el-form-item>
               </el-form-item>
               <el-form-item label="打包配置" prop="packerConfigId">
               <el-form-item label="打包配置" prop="packerConfigId">
@@ -376,7 +384,7 @@
                       :key="item.value"
                       :key="item.value"
                       :label="item.label"
                       :label="item.label"
                       :value="item.value"
                       :value="item.value"
-                    ></el-option>
+                    />
 
 
                     <div slot="empty" style="text-align: center; padding: 10px 0;">
                     <div slot="empty" style="text-align: center; padding: 10px 0;">
                       <p style="margin-bottom: 10px; color: #909399; font-size: 12px;">暂无打包配置</p>
                       <p style="margin-bottom: 10px; color: #909399; font-size: 12px;">暂无打包配置</p>
@@ -390,19 +398,19 @@
                     icon="el-icon-plus"
                     icon="el-icon-plus"
                     circle
                     circle
                     size="mini"
                     size="mini"
-                    @click="showAddPackerDialog = true"
                     title="添加新打包配置"
                     title="添加新打包配置"
-                  ></el-button>
+                    @click="showAddPackerDialog = true"
+                  />
                 </div>
                 </div>
               </el-form-item>
               </el-form-item>
             </div>
             </div>
 
 
             <div v-show="active === 2">
             <div v-show="active === 2">
               <el-form-item label="HTTP 端口" prop="httpPort">
               <el-form-item label="HTTP 端口" prop="httpPort">
-                <el-input v-model="appForm.httpPort" placeholder="8080"></el-input>
+                <el-input v-model="appForm.httpPort" placeholder="8080" />
               </el-form-item>
               </el-form-item>
               <el-form-item label="Dockerfile" prop="dockerfile">
               <el-form-item label="Dockerfile" prop="dockerfile">
-                <el-input type="textarea" :rows="8" v-model="appForm.dockerfile" placeholder="FROM ..."></el-input>
+                <el-input v-model="appForm.dockerfile" type="textarea" :rows="8" placeholder="FROM ..." />
               </el-form-item>
               </el-form-item>
             </div>
             </div>
 
 
@@ -415,114 +423,6 @@
         </div>
         </div>
       </template>
       </template>
     </el-dialog>
     </el-dialog>
-    <el-dialog
-      :title="deployConfigDialogTitle"
-      append-to-body
-      :visible.sync="showDeployConfigDialog"
-      width="70%"
-      center
-    >
-      <template>
-        <el-button type="success" icon="el-icon-plus" style="margin-bottom: 5px" @click="handleAddDeployConfig">添加</el-button>
-        <el-table
-          :data="appDeployConfigList"
-          border
-          height="480"
-          style="width: 100%"
-        >
-          <el-table-column
-            prop="machineIpv4"
-            label="机器地址"
-          />
-          <el-table-column
-            prop="packType"
-            label="打包类型"
-          />
-          <el-table-column
-            prop="startScript"
-            label="启动脚本"
-          />
-          <el-table-column
-            fixed="right"
-            label="操作"
-            width="280"
-          >
-            <template slot-scope="scope">
-              <el-button
-                style="margin-top: 5px; margin-left: 5px"
-                size="mini"
-                @click="handleEditDeployConfig(scope.$index, scope.row)"
-              >编辑</el-button>
-              <el-button
-                style="margin-top: 5px; margin-left: 5px"
-                size="mini"
-                type="danger"
-                @click="handleDeleteDeployConfig(scope.$index, scope.row)"
-              >删除</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </template>
-    </el-dialog>
-    <el-dialog
-      title="添加部署配置"
-      append-to-body
-      :visible.sync="showAddDeployConfigDialog"
-      center
-    >
-      <template>
-        <el-form :model="addDeployForm" label-width="80px">
-          <el-form-item label="应用 ID" style="width: 70%; padding-right: 2px">
-            <el-input v-model="addDeployForm.appId" style="width: 70%; padding-right: 2px" readonly />
-          </el-form-item>
-          <el-form-item label="打包类型" style="width: 70%; padding-right: 2px">
-            <el-input v-model="addDeployForm.packType" style="width: 70%; padding-right: 2px" readonly />
-          </el-form-item>
-          <el-form-item label="选择机器">
-            <el-select v-model="addDeployForm.machineId" placeholder="选择机器">
-              <el-option
-                v-for="(item, index) in machineList"
-                :key="index"
-                :label="item.value"
-                :value="item.label"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="启动脚本" style="width: 70%; padding-right: 2px">
-            <el-input v-model="addDeployForm.startScript" type="textarea" :rows="10" style="width: 70%; padding-right: 2px" />
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="onAddDeploy">确定</el-button>
-          </el-form-item>
-        </el-form>
-      </template>
-    </el-dialog>
-    <el-dialog
-      title="编辑部署配置"
-      append-to-body
-      :visible.sync="showEditDeployConfigDialog"
-      center
-    >
-      <template>
-        <el-form :model="editDeployForm" label-width="80px">
-          <el-form-item label="应用 ID" style="width: 70%; padding-right: 2px">
-            <el-input v-model="editDeployForm.appId" style="width: 70%; padding-right: 2px" readonly />
-          </el-form-item>
-          <el-form-item label="打包类型" style="width: 70%; padding-right: 2px">
-            <el-input v-model="editDeployForm.packType" style="width: 70%; padding-right: 2px" readonly />
-          </el-form-item>
-          <el-form-item label="机器地址" style="width: 70%; padding-right: 2px">
-            <el-input v-model="editDeployForm.machineIpv4" style="width: 70%; padding-right: 2px" readonly />
-          </el-form-item>
-          <el-form-item label="启动脚本" style="width: 70%; padding-right: 2px">
-            <el-input v-model="editDeployForm.startScript" type="textarea" :rows="10" style="width: 70%; padding-right: 2px" />
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="onEditDeploy">确定</el-button>
-          </el-form-item>
-        </el-form>
-      </template>
-    </el-dialog>
     <el-dialog
     <el-dialog
       title="添加仓库认证"
       title="添加仓库认证"
       :visible.sync="showAddRepoAuthDialog"
       :visible.sync="showAddRepoAuthDialog"
@@ -569,21 +469,20 @@
 import RepoAuthAddCard from '@/components/card/RepoAuthAddCard.vue'
 import RepoAuthAddCard from '@/components/card/RepoAuthAddCard.vue'
 import CompilerAddCard from '@/components/card/CompilerAddCard.vue'
 import CompilerAddCard from '@/components/card/CompilerAddCard.vue'
 import PackerAddCard from '@/components/card/PackerAddCard.vue'
 import PackerAddCard from '@/components/card/PackerAddCard.vue'
-import AppConfigCard from '@/components/card/AppConfigCard.vue';
+import AppConfigCard from '@/components/card/AppConfigCard.vue'
+import AppDeployConfig from '@/components/card/AppDeployConfig.vue'
 
 
 import {
 import {
   addAppBindDomain,
   addAppBindDomain,
-  addAppConfig, addAppDeployConfig,
+  addAppConfig,
   copyAppConfig, deleteAppBindDomain,
   copyAppConfig, deleteAppBindDomain,
-  deleteAppConfig, deleteAppDeployConfig,
+  deleteAppConfig,
   eraseAppRepo, getAppBindDomain,
   eraseAppRepo, getAppBindDomain,
   getAppConfig,
   getAppConfig,
   getAppConfigList,
   getAppConfigList,
-  getAppDeployConfigList,
   getBuildConfig,
   getBuildConfig,
-  getDeployMachineList,
   getEnvList,
   getEnvList,
-  updateAppConfig, updateAppDeployConfig
+  updateAppConfig
 } from '@/api/devops'
 } from '@/api/devops'
 
 
 export default {
 export default {
@@ -592,7 +491,8 @@ export default {
     RepoAuthAddCard,
     RepoAuthAddCard,
     CompilerAddCard,
     CompilerAddCard,
     PackerAddCard,
     PackerAddCard,
-    AppConfigCard
+    AppConfigCard,
+    AppDeployConfig
   },
   },
   data() {
   data() {
     return {
     return {
@@ -620,7 +520,7 @@ export default {
         compilerConfigId: null,
         compilerConfigId: null,
         packerConfigId: null,
         packerConfigId: null,
         dockerfile: '',
         dockerfile: '',
-        appId: '',
+        appId: ''
       },
       },
       appFormRules: {
       appFormRules: {
         appId: [{ required: true, message: '应用 ID 不能为空', trigger: 'blur' }],
         appId: [{ required: true, message: '应用 ID 不能为空', trigger: 'blur' }],
@@ -631,7 +531,7 @@ export default {
         env: [{ required: true, message: '应用环境不能为空', trigger: 'blur' }],
         env: [{ required: true, message: '应用环境不能为空', trigger: 'blur' }],
         'compilerConfigId': [{ required: true, message: '请选择编译器', trigger: 'blur' }],
         'compilerConfigId': [{ required: true, message: '请选择编译器', trigger: 'blur' }],
         'packerConfigId': [{ required: true, message: '请选择打包配置', trigger: 'blur' }],
         'packerConfigId': [{ required: true, message: '请选择打包配置', trigger: 'blur' }],
-        dockerfile: [{ required: true, message: '请填写 Dockerfile', trigger: 'blur' }],
+        dockerfile: [{ required: true, message: '请填写 Dockerfile', trigger: 'blur' }]
       },
       },
       // **********************************************************************
       // **********************************************************************
       showAddRepoAuthDialog: false,
       showAddRepoAuthDialog: false,
@@ -684,25 +584,9 @@ export default {
       showDetailDialog: false,
       showDetailDialog: false,
       appConfigDetail: null,
       appConfigDetail: null,
       // **********************************************************************
       // **********************************************************************
-      deployConfigDialogTitle: '',
-      showDeployConfigDialog: false,
-      appDeployConfigList: [],
-      showAddDeployConfigDialog: false,
-      machineList: [],
-      packTypes: [],
-      addDeployForm: {
-        appId: null,
-        packType: null,
-        machineId: null,
-        machineIpv4: null,
-        startScript: null
-      },
-      showEditDeployConfigDialog: false,
-      editDeployForm: {
-        appId: null,
-        machineId: null,
-        startScript: null
-      }
+      currentAppId: null,
+      currentPackType: null,
+      showDeployDialog: false
     }
     }
   },
   },
   created() {
   created() {
@@ -895,9 +779,9 @@ export default {
         ['appId', 'appRepo', 'repoBranch', 'repoAuthConfigId'],
         ['appId', 'appRepo', 'repoBranch', 'repoAuthConfigId'],
         ['appType', 'env', 'compilerConfigId', 'packerConfigId'],
         ['appType', 'env', 'compilerConfigId', 'packerConfigId'],
         ['dockerfile']
         ['dockerfile']
-      ];
+      ]
 
 
-      const currentFields = fieldsForSteps[this.active];
+      const currentFields = fieldsForSteps[this.active]
 
 
       // 1. 手动封装校验逻辑,确保所有字段校验完才继续
       // 1. 手动封装校验逻辑,确保所有字段校验完才继续
       try {
       try {
@@ -905,35 +789,35 @@ export default {
           return new Promise((resolve, reject) => {
           return new Promise((resolve, reject) => {
             this.$refs.appFormRef.validateField(field, (errorMessage) => {
             this.$refs.appFormRef.validateField(field, (errorMessage) => {
               if (errorMessage) {
               if (errorMessage) {
-                reject(errorMessage);
+                reject(errorMessage)
               } else {
               } else {
-                resolve();
+                resolve()
               }
               }
-            });
-          });
-        });
+            })
+          })
+        })
 
 
-        await Promise.all(validations);
+        await Promise.all(validations)
 
 
         // 2. 只有全部成功才会走到这里
         // 2. 只有全部成功才会走到这里
         if (this.active < 2) {
         if (this.active < 2) {
-          this.active++;
+          this.active++
         }
         }
       } catch (error) {
       } catch (error) {
-        this.$message.error('校验未通过:', error);
+        this.$message.error('校验未通过:', error)
         // 校验失败,保持在当前页
         // 校验失败,保持在当前页
       }
       }
     },
     },
     prev() {
     prev() {
-      if (this.active > 0) this.active--;
+      if (this.active > 0) this.active--
     },
     },
     onAdd() {
     onAdd() {
       this.$refs.appFormRef.validate((valid) => {
       this.$refs.appFormRef.validate((valid) => {
         if (valid) {
         if (valid) {
           addAppConfig(this.appForm).then(resp => {
           addAppConfig(this.appForm).then(resp => {
             if (resp.code === 0) {
             if (resp.code === 0) {
-              this.active = 0;
-              this.$refs.appFormRef.resetFields();
+              this.active = 0
+              this.$refs.appFormRef.resetFields()
               this.showAddDialog = false
               this.showAddDialog = false
               this.getData()
               this.getData()
             } else {
             } else {
@@ -945,7 +829,7 @@ export default {
             this.showAddDialog = false
             this.showAddDialog = false
           })
           })
         }
         }
-      });
+      })
     },
     },
     onSelectChange() {
     onSelectChange() {
       this.currentPage = 1
       this.currentPage = 1
@@ -987,92 +871,16 @@ export default {
       })
       })
     },
     },
     // ****************************************************************************************************************
     // ****************************************************************************************************************
-    // 应用部署配置
+    // AppDeployConfig
     // ****************************************************************************************************************
     // ****************************************************************************************************************
-    handleDeployConfig(index, row) {
-      this.addDeployForm.appId = row.appId
-      this.addDeployForm.packType = row.packType
-      this.getAppDeployConfigListWrapper(row.appId)
-    },
-    getAppDeployConfigListWrapper(appId) {
-      getAppDeployConfigList(appId).then(resp => {
-        if (resp.code === 0) {
-          this.appDeployConfigList = resp.data
-          this.deployConfigDialogTitle = appId + ' 的部署配置列表'
-          this.showDeployConfigDialog = true
-        } else {
-          this.$message.error(resp.msg)
-        }
-      }).catch(error => {
-        this.$message.error(error.message)
-      })
-    },
-    handleAddDeployConfig(index, row) {
-      getDeployMachineList(this.queryInfo.env).then(resp => {
-        if (resp.code === 0) {
-          this.machineList = resp.data
-          this.showAddDeployConfigDialog = true
-        } else {
-          this.$message.error(resp.msg)
-        }
-      }).catch(error => {
-        this.$message.error(error.message)
-      })
-    },
-    onAddDeploy() {
-      const formData = new FormData()
-      formData.append('appId', this.addDeployForm.appId)
-      formData.append('machineId', this.addDeployForm.machineId)
-      formData.append('packType', this.addDeployForm.packType)
-      formData.append('startScript', this.addDeployForm.startScript)
-      addAppDeployConfig(formData).then(resp => {
-        this.$message.info(resp.msg)
-        this.getAppDeployConfigListWrapper(this.addDeployForm.appId)
-      }).catch(error => {
-        this.$message.error(error.message)
-      }).finally(() => {
-        this.showAddDeployConfigDialog = false
-      })
-    },
-    handleEditDeployConfig(index, row) {
-      this.editDeployForm = row
-      this.showEditDeployConfigDialog = true
-    },
-    onEditDeploy() {
-      const formData = new FormData()
-      formData.append('appId', this.editDeployForm.appId)
-      formData.append('machineId', this.editDeployForm.machineId)
-      formData.append('startScript', this.editDeployForm.startScript)
-      updateAppDeployConfig(formData).then(resp => {
-        this.$message.info(resp.msg)
-        this.getAppDeployConfigListWrapper(this.editDeployForm.appId)
-      }).catch(error => {
-        this.$message.error(error.message)
-      }).finally(() => {
-        this.showEditDeployConfigDialog = false
-      })
-    },
-    handleDeleteDeployConfig(index, row) {
-      this.$confirm('确定要删除 ' + row.appId + '?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        const formData = new FormData()
-        formData.append('appDeployConfigId', row.appDeployConfigId)
-        deleteAppDeployConfig(formData).then(resp => {
-          this.$message.info(resp.msg)
-          this.getAppDeployConfigListWrapper(row.appId)
-        }).catch(error => {
-          this.$message.error(error.message)
-        })
-      }).catch(() => {
-        this.$message({
-          type: 'info',
-          message: '已取消'
-        })
-      })
+    handleOpenDeploy(row) {
+      this.currentAppId = row.appId
+      this.currentPackType = row.packType
+      this.showDeployDialog = true
     },
     },
+    // ****************************************************************************************************************
+    // 添加 AppConfig
+    // ****************************************************************************************************************
     handleAddRepoAuthSuccess() {
     handleAddRepoAuthSuccess() {
       this.showAddRepoAuthDialog = false
       this.showAddRepoAuthDialog = false
       this.getBuildConfigWrapper()
       this.getBuildConfigWrapper()