|
|
@@ -3,181 +3,23 @@
|
|
|
<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 || !!nameWarning" @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>
|
|
|
+ <a-tabs v-model:activeKey="activeTab" type="line">
|
|
|
+ <a-tab-pane key="upload" tab="上传部署包">
|
|
|
+ <PackageUpload />
|
|
|
+ </a-tab-pane>
|
|
|
+ <a-tab-pane key="history" tab="部署历史">
|
|
|
+ <DeployHistory />
|
|
|
+ </a-tab-pane>
|
|
|
+ </a-tabs>
|
|
|
</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;
|
|
|
- }
|
|
|
+ import { ref } from 'vue';
|
|
|
+ import PackageUpload from './components/PackageUpload.vue';
|
|
|
+ import DeployHistory from './components/DeployHistory.vue';
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
+ const activeTab = ref<string>('upload');
|
|
|
</script>
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
@@ -188,15 +30,6 @@
|
|
|
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;
|
|
|
|
|
|
@@ -207,102 +40,26 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .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;
|
|
|
+ :deep(.zxm-tabs-nav) {
|
|
|
+ .zxm-tabs-tab {
|
|
|
+ color: rgba(255, 255, 255, 0.55);
|
|
|
|
|
|
- .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;
|
|
|
+ &:hover {
|
|
|
+ color: rgba(255, 255, 255, 0.85);
|
|
|
}
|
|
|
|
|
|
- &::before {
|
|
|
- transform: translate(-50%, -50%) rotate(45deg);
|
|
|
- }
|
|
|
+ &.zxm-tabs-tab-active {
|
|
|
+ color: #0a84ff;
|
|
|
|
|
|
- &::after {
|
|
|
- transform: translate(-50%, -50%) rotate(-45deg);
|
|
|
+ .zxm-tabs-tab-btn {
|
|
|
+ color: #0a84ff;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- .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;
|
|
|
+ .zxm-tabs-ink-bar {
|
|
|
+ background: #0a84ff;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|