Bladeren bron

[Feat 0000]新增系统包部署页面

wangkeyi 1 dag geleden
bovenliggende
commit
ee3bfa2706
2 gewijzigde bestanden met toevoegingen van 323 en 0 verwijderingen
  1. 308 0
      src/views/vent/packageUpload/index.vue
  2. 15 0
      src/views/vent/packageUpload/package.api.ts

+ 308 - 0
src/views/vent/packageUpload/index.vue

@@ -0,0 +1,308 @@
+<template>
+  <div class="package-upload">
+    <div class="upload-header">
+      <span class="title">部署包管理</span>
+    </div>
+    <div class="upload-content">
+      <a-card title="上传部署包" :bordered="true" style="width: 100%">
+        <a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
+          <a-form-item label="部署包类型">
+            <a-radio-group v-model:value="packageType">
+              <a-radio value="frontend">前端部署包 (zip)</a-radio>
+              <a-radio value="backend">后端部署包 (jar)</a-radio>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item label="选择文件">
+            <a-alert type="warning" show-icon style="margin-bottom: 12px">
+              <template #message>
+                <template v-if="packageType === 'frontend'">
+                  <span style="font-weight: bold">前端包文件命名规则:</span>
+                  <div>
+                    dist + 端口号.zip,例如一通三防智能管控平台前端包为
+                    <b>dist8092.zip</b>
+                  </div>
+                  <div style="margin-top: 4px; font-weight: bold">上传包时请严格按照命名规则,否则会导致部署失败或引起其他系统问题!!!</div>
+                </template>
+                <template v-else>
+                  <span style="font-weight: bold">后端包文件命名规则:</span>
+                  <div style="margin-top: 4px">
+                    <div>jeecg-cloud-gateway-3.2.0.jar</div>
+                    <div>jeecg-cloud-nacos-3.2.0.jar</div>
+                    <div>jeecg-cloud-system-start-3.2.0.jar</div>
+                    <div>ventanaly-monitor-3.2.0.jar</div>
+                    <div>ventanaly-model-3.2.0.jar</div>
+                    <div>ventanaly-jingtaifengliang-3.2.0.jar</div>
+                    <div>ventanaly-sharefile-3.2.0.jar</div>
+                    <div>ventanaly-province-3.2.0.jar</div>
+                    <div>ventanaly-ai-service-3.2.0.jar</div>
+                    <div>ventanaly-company-3.2.0.jar</div>
+                  </div>
+                  <div style="margin-top: 4px; font-weight: bold">上传包时请严格按照命名规则,否则会导致部署失败或引起其他系统问题!!!</div>
+                </template>
+              </template>
+            </a-alert>
+            <a-upload :before-upload="beforeUpload" :file-list="fileList" :remove="handleRemove" :max-count="1">
+              <a-button type="primary">
+                <template #icon><upload-outlined /></template>
+                选择文件
+              </a-button>
+            </a-upload>
+            <div class="file-tip" v-if="fileList.length > 0">
+              <span>已选择: {{ fileList[0]?.name }}</span>
+              <span style="margin-left: 12px; color: #999">({{ formatFileSize(fileList[0]?.size) }})</span>
+              <span v-if="nameWarning" class="name-warning">{{ nameWarning }}</span>
+            </div>
+          </a-form-item>
+          <a-form-item :wrapper-col="{ offset: 4, span: 16 }">
+            <a-button type="primary" danger :loading="deploying" :disabled="fileList.length === 0" @click="handleUploadAndDeploy">
+              <template #icon><cloud-upload-outlined /></template>
+              上传并部署
+            </a-button>
+          </a-form-item>
+        </a-form>
+      </a-card>
+    </div>
+
+    <!-- 全屏loading -->
+    <div class="fullscreen-loading" v-if="deploying">
+      <a-spin size="large" />
+      <span class="loading-text">上传并部署中,请勿关闭或操作页面</span>
+    </div>
+
+    <!-- 部署结果弹框 -->
+    <a-modal v-model:visible="resultVisible" :title="deployIsSuccess ? '部署成功' : '部署失败'" :footer="null" width="600px">
+      <div class="deploy-status-icon" :class="deployIsSuccess ? 'is-success' : 'is-fail'">
+        <template v-if="deployIsSuccess">
+          <span class="icon-check"></span>
+        </template>
+        <template v-else>
+          <span class="icon-cross"></span>
+        </template>
+      </div>
+      <div class="deploy-msg">{{ deployMsg }}</div>
+    </a-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, watch } from 'vue';
+  import { UploadOutlined, CloudUploadOutlined } from '@ant-design/icons-vue';
+  import { message } from 'ant-design-vue';
+  import { uploadProgramApi } from './package.api';
+
+  const packageType = ref<string>('frontend');
+  const fileList = ref<any[]>([]);
+  const deploying = ref<boolean>(false);
+  const resultVisible = ref<boolean>(false);
+  const deployIsSuccess = ref<boolean>(false);
+  const deployMsg = ref<string>('');
+  const nameWarning = ref<string>('');
+
+  const acceptMap: Record<string, string> = {
+    frontend: '.zip',
+    backend: '.jar',
+  };
+
+  const backendJarNames = [
+    'jeecg-cloud-gateway-3.2.0.jar',
+    'jeecg-cloud-nacos-3.2.0.jar',
+    'jeecg-cloud-system-start-3.2.0.jar',
+    'ventanaly-monitor-3.2.0.jar',
+    'ventanaly-model-3.2.0.jar',
+    'ventanaly-jingtaifengliang-3.2.0.jar',
+    'ventanaly-sharefile-3.2.0.jar',
+    'ventanaly-province-3.2.0.jar',
+    'ventanaly-ai-service-3.2.0.jar',
+    'ventanaly-company-3.2.0.jar',
+  ];
+
+  watch(packageType, () => {
+    fileList.value = [];
+    nameWarning.value = '';
+  });
+
+  function beforeUpload(file) {
+    const accept = acceptMap[packageType.value];
+    const acceptExts = accept.split(',');
+    const ext = '.' + file.name.split('.').pop().toLowerCase();
+    if (!acceptExts.includes(ext)) {
+      message.error(`当前类型仅支持 ${accept} 格式的文件`);
+      return false;
+    }
+    if (packageType.value === 'frontend') {
+      nameWarning.value = /^dist\d+\.zip$/i.test(file.name) ? '' : '包名不符合命名规则,请检查!!!';
+    } else {
+      nameWarning.value = backendJarNames.includes(file.name) ? '' : '包名不符合命名规则,请检查!!!';
+    }
+    fileList.value = [file];
+    return false;
+  }
+
+  function handleRemove() {
+    fileList.value = [];
+    nameWarning.value = '';
+  }
+
+  function formatFileSize(size?: number): string {
+    if (!size) return '0 B';
+    const units = ['B', 'KB', 'MB', 'GB'];
+    let i = 0;
+    let s = size;
+    while (s >= 1024 && i < units.length - 1) {
+      s /= 1024;
+      i++;
+    }
+    return s.toFixed(2) + ' ' + units[i];
+  }
+
+  async function handleUploadAndDeploy() {
+    if (fileList.value.length === 0) {
+      message.warning('请先选择文件');
+      return;
+    }
+    deploying.value = true;
+    try {
+      const res = await uploadProgramApi(fileList.value[0]);
+      const result = res?.result || {};
+      deployIsSuccess.value = result.isSuccess === true;
+      deployMsg.value = result.msg || res?.message || '未知结果';
+      resultVisible.value = true;
+      if (result.isSuccess) {
+        fileList.value = [];
+      }
+    } catch (e: any) {
+      deployIsSuccess.value = false;
+      deployMsg.value = e?.message || '上传请求失败,请检查网络连接';
+      resultVisible.value = true;
+    } finally {
+      deploying.value = false;
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  @import '/@/design/theme.less';
+
+  .package-upload {
+    padding: 20px;
+    height: 100%;
+    overflow-y: auto;
+
+    :deep(.zxm-card-head-title),
+    :deep(.zxm-form-item-label > label) {
+      color: #fff !important;
+    }
+
+    :deep(.anticon-exclamation-circle) {
+      display: none;
+    }
+
+    .upload-header {
+      margin-bottom: 20px;
+
+      .title {
+        font-size: 20px;
+        font-weight: bold;
+        color: #fff;
+      }
+    }
+
+    .file-tip {
+      margin-top: 8px;
+      font-size: 13px;
+      color: #fff;
+    }
+
+    .name-warning {
+      margin-left: 12px;
+      color: #ff4d4f;
+      font-weight: bold;
+    }
+  }
+
+  .fullscreen-loading {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(0, 0, 0, 0.45);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    z-index: 9999;
+
+    .loading-text {
+      margin-top: 16px;
+      color: #fff;
+      font-size: 16px;
+    }
+  }
+
+  .deploy-status-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 48px;
+    height: 48px;
+    border-radius: 50%;
+    margin: 8px auto 20px;
+
+    &.is-success {
+      background: #52c41a;
+
+      .icon-check {
+        display: block;
+        width: 20px;
+        height: 36px;
+        border: solid #fff;
+        border-width: 0 5px 5px 0;
+        transform: rotate(45deg) translate(-3px, -3px);
+      }
+    }
+
+    &.is-fail {
+      background: #ff4d4f;
+
+      .icon-cross {
+        display: block;
+        width: 32px;
+        height: 32px;
+        position: relative;
+
+        &::before,
+        &::after {
+          content: '';
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          width: 5px;
+          height: 32px;
+          background: #fff;
+          border-radius: 2px;
+        }
+
+        &::before {
+          transform: translate(-50%, -50%) rotate(45deg);
+        }
+
+        &::after {
+          transform: translate(-50%, -50%) rotate(-45deg);
+        }
+      }
+    }
+  }
+
+  .deploy-msg {
+    white-space: pre-wrap;
+    word-break: break-all;
+    max-height: 400px;
+    overflow-y: auto;
+    padding: 12px;
+    border-radius: 4px;
+    font-size: 20px;
+    line-height: 1.6;
+    text-align: center;
+  }
+</style>

+ 15 - 0
src/views/vent/packageUpload/package.api.ts

@@ -0,0 +1,15 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  sysUploadProgram = '/ventanaly-device/safety/common/uploadProgram',
+  uploadProgram = '/jeecg-system/sys/common/uploadProgram',
+}
+
+/**
+ * 上传部署包 - 文件名包含system时使用sysUploadProgram,否则使用uploadProgram
+ */
+export const uploadProgramApi = async (file: File) => {
+  const fileName = file.name.toLowerCase();
+  const url = fileName.includes('system') ? Api.sysUploadProgram : Api.uploadProgram;
+  return defHttp.uploadFile({ url, timeout: 15 * 60 * 1000 }, { file, name: 'file' }, { isReturnResponse: true });
+};