mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-01 19:38:09 +08:00
升级前端剩余功能
This commit is contained in:
parent
36461e207e
commit
d50f857946
@ -1,5 +1,20 @@
|
||||
# 🚀 版本日志
|
||||
|
||||
### 2.11.0.7-beta
|
||||
|
||||
### 🐞 解决BUG、优化功能
|
||||
|
||||
### 注意
|
||||
|
||||
1. 取消全局 loading(局部loading)
|
||||
2. 编辑器延迟 1 秒加载(避免样式错乱)
|
||||
3. 所有快捷复制区域变小为一个点击复制图标
|
||||
4. 弹窗、抽屉样式变动
|
||||
5. 取消操作引导(临时)
|
||||
6. 表格将跟随列内容长度自动拉伸出现横向滚动(不会折叠)
|
||||
|
||||
------
|
||||
|
||||
### 2.11.0.6-beta (2024-01-05)
|
||||
|
||||
### 🐞 解决BUG、优化功能
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:width="style.width"
|
||||
v-model:open="visibleModel"
|
||||
:width="style.width"
|
||||
:bodyStyle="style.bodyStyle"
|
||||
:style="style.style"
|
||||
:footer="null"
|
||||
@ -69,6 +69,9 @@ export default {
|
||||
...mapState(useGuideStore, ['getFullscreenViewLogStyle']),
|
||||
regModifier() {
|
||||
return this.regModifiers.join('')
|
||||
},
|
||||
style() {
|
||||
return this.getFullscreenViewLogStyle()
|
||||
}
|
||||
},
|
||||
props: {
|
||||
@ -96,12 +99,10 @@ export default {
|
||||
// 自动换行
|
||||
wordBreak: false
|
||||
},
|
||||
visibleModel: false,
|
||||
style: {}
|
||||
visibleModel: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.style = this.getFullscreenViewLogStyle()
|
||||
this.visibleModel = this.visible
|
||||
},
|
||||
mounted() {
|
||||
|
@ -42,8 +42,9 @@ export default {
|
||||
},
|
||||
computed: {},
|
||||
created() {
|
||||
console.log(this.$options._scopeId)
|
||||
this.domId =
|
||||
(this.$options._parentVnode?.tag || '' + '-' + this.$options._componentTag || '') + '-' + new Date().getTime()
|
||||
(this.$options._parentVnode?.tag || '' + '-' + this.$options._scopeId || '') + '-' + new Date().getTime()
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@ -97,14 +98,15 @@ export default {
|
||||
// this.wp = 100;
|
||||
//;
|
||||
this.rows = document.querySelector('#' + this.domId).offsetHeight / 16
|
||||
this.cols = document.querySelector('#' + this.domId).offsetWidth / 8
|
||||
this.cols = document.querySelector('#' + this.domId).offsetWidth / 8.4
|
||||
this.hp = this.rows * 8
|
||||
this.wp = this.cols * 8
|
||||
//
|
||||
this.terminal = new Terminal({
|
||||
fontSize: 14,
|
||||
rows: parseInt(this.rows), //行数
|
||||
cols: parseInt(this.cols), // 不指定行数,自动回车后光标从下一行开始
|
||||
// 不指定行数,自动回车后光标从下一行开始
|
||||
cols: parseInt(this.cols),
|
||||
convertEol: true, //启用时,光标将设置为下一行的开头
|
||||
cursorBlink: true,
|
||||
// Whether input should be disabled.
|
||||
@ -164,8 +166,8 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.flex-100 {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
/* display: flex; */
|
||||
/* flex-flow: column; */
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
@ -173,3 +175,4 @@ export default {
|
||||
/* box-shadow: inset 0 0 10px 0 #e8e8e8; */
|
||||
}
|
||||
</style>
|
||||
<style></style>
|
||||
|
1
web-vue3/src/d.ts/components.d.ts
vendored
1
web-vue3/src/d.ts/components.d.ts
vendored
@ -168,5 +168,6 @@ declare module 'vue' {
|
||||
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
||||
ViewPre: typeof import('./../components/logView/view-pre.vue')['default']
|
||||
WarningOutlined: typeof import('@ant-design/icons-vue')['WarningOutlined']
|
||||
WarningTwoTone: typeof import('@ant-design/icons-vue')['WarningTwoTone']
|
||||
}
|
||||
}
|
||||
|
@ -822,18 +822,6 @@
|
||||
placeholder="如果需要定时自动构建则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])"
|
||||
:options="CRON_DATA_SOURCE"
|
||||
>
|
||||
<!-- <template v-slot:dataSource>
|
||||
<a-select-opt-group v-for="group in CRON_DATA_SOURCE" :key="group.title">
|
||||
<template v-slot:label>
|
||||
<span>
|
||||
{{ group.title }}
|
||||
</span>
|
||||
</template>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value">
|
||||
{{ opt.title }} {{ opt.value }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</template> -->
|
||||
<template #option="item"> {{ item.title }} {{ item.value }} </template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
@ -1096,7 +1084,6 @@
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
</a-drawer>
|
||||
<!-- 选择脚本 -->
|
||||
@ -1104,17 +1091,19 @@
|
||||
destroyOnClose
|
||||
:title="`选择脚本`"
|
||||
placement="right"
|
||||
:visible="chooseScriptVisible != 0"
|
||||
width="50vw"
|
||||
:open="chooseScriptVisible != 0"
|
||||
width="70vw"
|
||||
:zIndex="1009"
|
||||
@close="
|
||||
() => {
|
||||
this.chooseScriptVisible = 0
|
||||
}
|
||||
"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
>
|
||||
<scriptPage
|
||||
v-if="chooseScriptVisible"
|
||||
ref="scriptPage"
|
||||
:choose="this.chooseScriptVisible === 1 ? 'checkbox' : 'radio'"
|
||||
:choose-val="
|
||||
this.chooseScriptVisible === 1
|
||||
@ -1140,13 +1129,36 @@
|
||||
}
|
||||
"
|
||||
></scriptPage>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.chooseScriptVisible = false
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
this.$refs['scriptPage'].handerConfirm()
|
||||
}
|
||||
"
|
||||
>
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
|
||||
<!-- 查看命令示例 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
width="50vw"
|
||||
v-model:value="viewScriptTemplVisible"
|
||||
v-model:open="viewScriptTemplVisible"
|
||||
title="构建命令示例"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
@ -1160,15 +1172,15 @@
|
||||
>
|
||||
<a-collapse-panel v-for="(group, index) in buildScipts" :key="`${index}`" :header="group.title">
|
||||
<a-list size="small" bordered :data-source="group.children">
|
||||
<template v-slot:renderItem="opt">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-space>
|
||||
{{ opt.title }}
|
||||
{{ item.title }}
|
||||
|
||||
<SwapOutlined
|
||||
@click="
|
||||
() => {
|
||||
temp = { ...temp, script: opt.value }
|
||||
temp = { ...temp, script: item.value }
|
||||
viewScriptTemplVisible = false
|
||||
}
|
||||
"
|
||||
|
@ -181,7 +181,7 @@
|
||||
}
|
||||
"
|
||||
/>
|
||||
<!-- 选择确认区域 -->
|
||||
<!-- 选择确认区域
|
||||
<div style="padding-top: 50px" v-if="this.choose">
|
||||
<div
|
||||
:style="{
|
||||
@ -209,7 +209,7 @@
|
||||
<a-button type="primary" @click="handerConfirm"> 确定 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -681,7 +681,7 @@
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 选择确认区域 -->
|
||||
<!-- 选择确认区域
|
||||
<div style="padding-top: 50px" v-if="this.choose">
|
||||
<div
|
||||
:style="{
|
||||
@ -709,7 +709,7 @@
|
||||
<a-button type="primary" @click="handerConfirm"> 确定 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1319,7 +1319,7 @@ export default {
|
||||
})
|
||||
return
|
||||
}
|
||||
$emit(this, 'confirm', selectData)
|
||||
this.$emit('confirm', selectData)
|
||||
}
|
||||
},
|
||||
emits: ['cancel', 'confirm']
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
@ -15,22 +15,26 @@
|
||||
"
|
||||
bordered
|
||||
rowKey="id"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-space>
|
||||
<a-input
|
||||
allowClear
|
||||
class="search-input-item"
|
||||
@pressEnter="loadData"
|
||||
v-model="listQuery['%issuerDnName%']"
|
||||
v-model:value="listQuery['%issuerDnName%']"
|
||||
placeholder="颁发者"
|
||||
/>
|
||||
<a-input
|
||||
allowClear
|
||||
class="search-input-item"
|
||||
@pressEnter="loadData"
|
||||
v-model="listQuery['%subjectDnName%']"
|
||||
v-model:value="listQuery['%subjectDnName%']"
|
||||
placeholder="主题"
|
||||
/>
|
||||
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
|
||||
@ -40,36 +44,43 @@
|
||||
</a-space>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #tooltip slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-popover #name slot-scope="text, item" title="证书描述">
|
||||
<template #content>
|
||||
<p>描述:{{ item.description }}</p>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'name'">
|
||||
<a-popover title="证书描述">
|
||||
<template v-slot:content>
|
||||
<p>描述:{{ record.description }}</p>
|
||||
</template>
|
||||
<!-- {{ text }} -->
|
||||
<a-button type="link" style="padding: 0" @click="handleEdit(record)" size="small">{{ text }}</a-button>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'fileExists'">
|
||||
<a-tag v-if="text" color="green">存在</a-tag>
|
||||
<a-tag v-else color="red">丢失</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'workspaceId'">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleDeployFile(record)">部署</a-button>
|
||||
<a-button size="small" type="primary" @click="handleDownload(record)">导出</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<!-- {{ text }} -->
|
||||
<a-button type="link" style="padding: 0" @click="handleEdit(item)" size="small">{{ text }}</a-button>
|
||||
</a-popover>
|
||||
<template #fileExists slot-scope="text">
|
||||
<a-tag v-if="text" color="green">存在</a-tag>
|
||||
<a-tag v-else color="red">丢失</a-tag>
|
||||
</template>
|
||||
<template #global slot-scope="text">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleDeployFile(record)">部署</a-button>
|
||||
<a-button size="small" type="primary" @click="handleDownload(record)">导出</a-button>
|
||||
<a-button size="small" type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 导入 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="editCertVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editCertVisible"
|
||||
width="700px"
|
||||
title="导入证书"
|
||||
@ok="handleEditCertOk"
|
||||
@ -77,7 +88,7 @@
|
||||
>
|
||||
<a-form ref="importCertForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="证书类型" name="type">
|
||||
<a-radio-group v-model="temp.type">
|
||||
<a-radio-group v-model:value="temp.type">
|
||||
<a-radio value="pkcs12"> pkcs12(pfx) </a-radio>
|
||||
<a-radio value="JKS"> JKS </a-radio>
|
||||
<a-radio value="X.509"> X.509(pem、key、crt、cer) </a-radio>
|
||||
@ -88,9 +99,10 @@
|
||||
<a-upload
|
||||
v-if="temp.type"
|
||||
:file-list="uploadFileList"
|
||||
:remove="
|
||||
@remove="
|
||||
() => {
|
||||
uploadFileList = []
|
||||
return true
|
||||
}
|
||||
"
|
||||
:before-upload="
|
||||
@ -101,7 +113,7 @@
|
||||
"
|
||||
:accept="typeAccept[temp.type]"
|
||||
>
|
||||
<a-button><a-icon type="upload" />选择文件</a-button>
|
||||
<a-button><UploadOutlined />选择文件</a-button>
|
||||
</a-upload>
|
||||
<template v-else>请选选择类型</template>
|
||||
</a-form-item>
|
||||
@ -111,42 +123,46 @@
|
||||
name="password"
|
||||
help="如果未填写将解析压缩包里面的 txt"
|
||||
>
|
||||
<a-input v-model="temp.password" placeholder="证书密码" />
|
||||
<a-input v-model:value="temp.password" placeholder="证书密码" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 编辑证书 -->
|
||||
<a-modal destroyOnClose v-model:visible="editVisible" :title="`编辑证书`" @ok="handleEditOk" :maskClosable="false">
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editVisible"
|
||||
:title="`编辑证书`"
|
||||
@ok="handleEditOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="证书共享" name="global">
|
||||
<a-radio-group v-model="temp.global">
|
||||
<a-radio-group v-model:value="temp.global">
|
||||
<a-radio :value="true"> 全局 </a-radio>
|
||||
<a-radio :value="false"> 当前工作空间 </a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="证书描述" name="description">
|
||||
<a-textarea v-model="temp.description" placeholder="请输入证书描述" />
|
||||
<a-textarea v-model:value="temp.description" placeholder="请输入证书描述" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 发布文件 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="releaseFileVisible"
|
||||
v-model:open="releaseFileVisible"
|
||||
title="部署证书"
|
||||
width="50%"
|
||||
:maskClosable="false"
|
||||
@ok="
|
||||
() => {
|
||||
this.$refs.releaseFile?.tryCommit()
|
||||
}
|
||||
"
|
||||
@ok="releaseFileOk()"
|
||||
>
|
||||
<a-alert message="证书将打包成 zip 文件上传到对应的文件夹" type="info" show-icon />
|
||||
<a-alert message="证书将打包成 zip 文件上传到对应的文件夹" type="info" show-icon style="margin-bottom: 10px" />
|
||||
<releaseFile ref="releaseFile" v-if="releaseFileVisible" @commit="handleCommitTask"></releaseFile>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
certificateImportFile,
|
||||
@ -183,28 +199,27 @@ export default {
|
||||
title: '序列号 (SN)',
|
||||
dataIndex: 'serialNumberStr',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'name' }
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '证书类型',
|
||||
dataIndex: 'keyType',
|
||||
ellipsis: true,
|
||||
width: '80px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '文件状态',
|
||||
dataIndex: 'fileExists',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'fileExists' },
|
||||
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '共享',
|
||||
dataIndex: 'workspaceId',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'global' },
|
||||
|
||||
width: '90px'
|
||||
},
|
||||
{
|
||||
@ -212,63 +227,63 @@ export default {
|
||||
dataIndex: 'issuerDnName',
|
||||
ellipsis: true,
|
||||
width: 200,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '主题',
|
||||
dataIndex: 'subjectDnName',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '密钥算法',
|
||||
dataIndex: 'sigAlgName',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '算法 OID',
|
||||
dataIndex: 'sigAlgOid',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
|
||||
{
|
||||
title: '生效时间',
|
||||
dataIndex: 'effectiveTime',
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
sorter: true,
|
||||
width: '160px'
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '到期时间',
|
||||
dataIndex: 'expirationTime',
|
||||
sorter: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
width: '160px'
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '版本号',
|
||||
dataIndex: 'certVersion',
|
||||
ellipsis: true,
|
||||
width: '80px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
@ -276,8 +291,8 @@ export default {
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
width: '160px'
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@ -295,12 +310,23 @@ export default {
|
||||
type: [{ required: true, message: '请选择证书类型', trigger: 'blur' }]
|
||||
},
|
||||
releaseFileVisible: false,
|
||||
editVisible: false
|
||||
editVisible: false,
|
||||
confirmLoading: false,
|
||||
tableSelections: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
rowSelection() {
|
||||
return {
|
||||
onChange: (selectedRowKeys) => {
|
||||
this.tableSelections = selectedRowKeys
|
||||
},
|
||||
selectedRowKeys: this.tableSelections,
|
||||
type: 'radio'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -339,12 +365,9 @@ export default {
|
||||
// 提交 Cert 数据
|
||||
handleEditCertOk() {
|
||||
// 检验表单
|
||||
this.$refs['importCertForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['importCertForm'].validate().then(() => {
|
||||
if (this.uploadFileList.length === 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择证书文件'
|
||||
})
|
||||
return false
|
||||
@ -355,23 +378,29 @@ export default {
|
||||
formData.append('password', this.temp.password || '')
|
||||
|
||||
// 提交数据
|
||||
certificateImportFile(formData).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.confirmLoading = true
|
||||
certificateImportFile(formData)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.editCertVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.editCertVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除该证书么,删除会将证书文件一并删除奥?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -382,7 +411,7 @@ export default {
|
||||
}
|
||||
deleteCert(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -402,27 +431,33 @@ export default {
|
||||
},
|
||||
// 编辑
|
||||
handleEdit(item) {
|
||||
this.temp = { ...item, global: item.workspaceId === 'GLOBAL', workspaceId: '' }
|
||||
this.temp = {
|
||||
...item,
|
||||
global: item.workspaceId === 'GLOBAL',
|
||||
workspaceId: ''
|
||||
}
|
||||
this.editVisible = true
|
||||
this.$refs['editForm']?.resetFields()
|
||||
},
|
||||
// 编辑确认
|
||||
handleEditOk() {
|
||||
this.$refs['editForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
certificateEdit(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editForm'].validate().then(() => {
|
||||
this.confirmLoading = true
|
||||
certificateEdit(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
handleDeployFile(record) {
|
||||
@ -431,18 +466,40 @@ export default {
|
||||
},
|
||||
|
||||
handleCommitTask(data) {
|
||||
certificateDeploy({ ...data, id: this.temp.id }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.confirmLoading = true
|
||||
certificateDeploy({ ...data, id: this.temp.id })
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.releaseFileVisible = false
|
||||
}
|
||||
})
|
||||
this.releaseFileVisible = false
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
releaseFileOk() {
|
||||
this.$refs.releaseFile?.tryCommit()
|
||||
},
|
||||
// 确认
|
||||
handerConfirm() {
|
||||
if (!this.tableSelections.length) {
|
||||
this.$notification.warning({
|
||||
message: '请选择要使用的证书'
|
||||
})
|
||||
return
|
||||
}
|
||||
const selectData = this.list.filter((item) => {
|
||||
return item.id === this.tableSelections[0]
|
||||
})[0]
|
||||
|
||||
this.$emit('confirm', `${selectData.serialNumberStr}:${selectData.keyType}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<!-- <div ref="filter" class="filter"> -->
|
||||
<!-- <a-button type="primary" @click="handleFilter">刷新</a-button> -->
|
||||
<!-- </div> -->
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
size="middle"
|
||||
@ -11,17 +8,19 @@
|
||||
:pagination="pagination"
|
||||
@change="changePage"
|
||||
bordered
|
||||
:rowKey="(record, index) => index"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-select v-model="listQuery.nodeId" allowClear placeholder="请选择节点" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.nodeId" allowClear placeholder="请选择节点" class="search-input-item">
|
||||
<a-select-option v-for="node in nodeList" :key="node.id">{{ node.name }}</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="listQuery.outGivingId" allowClear placeholder="分发项目" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.outGivingId" allowClear placeholder="分发项目" class="search-input-item">
|
||||
<a-select-option v-for="dispatch in dispatchList" :key="dispatch.id">{{ dispatch.name }}</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="listQuery.status" allowClear placeholder="请选择状态" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.status" allowClear placeholder="请选择状态" class="search-input-item">
|
||||
<a-select-option v-for="(item, key) in dispatchStatusMap" :key="key" :value="key">{{
|
||||
item
|
||||
}}</a-select-option>
|
||||
@ -37,91 +36,110 @@
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #outGivingId slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.dataIndex === 'outGivingId'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-tooltip #nodeName slot-scope="text, record" placement="topLeft" :title="text">
|
||||
<span>{{
|
||||
nodeList.filter((item) => item.id === record.nodeId) &&
|
||||
nodeList.filter((item) => item.id === record.nodeId)[0] &&
|
||||
nodeList.filter((item) => item.id === record.nodeId)[0].name
|
||||
}}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip #projectId slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultMsg
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="readJsonStrField(item.result, 'msg')"
|
||||
>
|
||||
<span
|
||||
>{{ readJsonStrField(item.result, 'code') }}-{{ readJsonStrField(item.result, 'msg') || item.result }}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultTime
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="readJsonStrField(item.result, 'upload_duration')"
|
||||
>
|
||||
<span>{{ readJsonStrField(item.result, 'upload_duration') }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultSize
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="readJsonStrField(item.result, 'upload_file_size')"
|
||||
>
|
||||
{{ readJsonStrField(item.result, 'upload_file_size') }}
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultMsgData
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="`${readJsonStrField(item.result, 'data')}`"
|
||||
>
|
||||
<template v-if="item.fileSize"> {{ Math.floor((item.progressSize / item.fileSize) * 100) }}% </template>
|
||||
{{ readJsonStrField(item.result, 'data') }}
|
||||
</a-tooltip>
|
||||
<a-tooltip #status slot-scope="text">
|
||||
<!-- {{ dispatchStatusMap[text] || "未知" }} -->
|
||||
<a-tag v-if="text === 2" color="green">{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 1 || text === 0 || text === 5" color="orange">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="text === 3 || text === 4 || text === 6" color="red">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else>{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
</a-tooltip>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-button type="primary" size="small" @click="handleDetail(record)">详情</a-button>
|
||||
<template v-else-if="column.dataIndex === 'nodeName'">
|
||||
<a-tooltip
|
||||
placement="topLeft"
|
||||
:title="
|
||||
nodeList.filter((item) => item.id === record.nodeId) &&
|
||||
nodeList.filter((item) => item.id === record.nodeId)[0] &&
|
||||
nodeList.filter((item) => item.id === record.nodeId)[0].name
|
||||
"
|
||||
>
|
||||
<span>{{
|
||||
nodeList.filter((item) => item.id === record.nodeId) &&
|
||||
nodeList.filter((item) => item.id === record.nodeId)[0] &&
|
||||
nodeList.filter((item) => item.id === record.nodeId)[0].name
|
||||
}}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'projectId'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'mode'">
|
||||
<a-tooltip placement="topLeft" :title="`${dispatchMode[text] || ''} 关联数据:${record.modeData || ''}`">
|
||||
<span>{{ dispatchMode[text] || '' }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultMsg'">
|
||||
<a-tooltip placement="topLeft" :title="readJsonStrField(record.result, 'msg')">
|
||||
<span
|
||||
>{{ readJsonStrField(record.result, 'code') }}-{{
|
||||
readJsonStrField(record.result, 'msg') || record.result
|
||||
}}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultTime'">
|
||||
<a-tooltip placement="topLeft" :title="readJsonStrField(record.result, 'upload_duration')">
|
||||
<span>{{ readJsonStrField(record.result, 'upload_duration') }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultSize'">
|
||||
<a-tooltip placement="topLeft" :title="readJsonStrField(record.result, 'upload_file_size')">
|
||||
{{ readJsonStrField(record.result, 'upload_file_size') }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultMsgData'">
|
||||
<a-tooltip placement="topLeft" :title="`${readJsonStrField(record.result, 'data')}`">
|
||||
<template v-if="record.fileSize">
|
||||
{{ Math.floor((record.progressSize / record.fileSize) * 100) }}%
|
||||
</template>
|
||||
{{ readJsonStrField(record.result, 'data') }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<!-- {{ dispatchStatusMap[text] || "未知" }} -->
|
||||
<a-tag v-if="text === 2" color="green">{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 1 || text === 0 || text === 5" color="orange">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="text === 3 || text === 4 || text === 6" color="red">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else>{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-button type="primary" size="small" @click="handleDetail(record)">详情</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 详情区 -->
|
||||
<a-modal destroyOnClose v-model:visible="detailVisible" width="600px" title="详情信息" :footer="null">
|
||||
<a-modal destroyOnClose v-model:open="detailVisible" width="600px" title="详情信息" :footer="null">
|
||||
<a-list item-layout="horizontal" :data-source="detailData">
|
||||
<a-list-item #renderItem slot-scope="item">
|
||||
<a-list-item-meta :description="item.description">
|
||||
<h4 #title>{{ item.title }}</h4>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="item.description">
|
||||
<template v-slot:title>
|
||||
<h4>{{ item.title }}</h4>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getNodeListAll } from '@/api/node'
|
||||
import { dispatchStatusMap, getDishPatchListAll, getDishPatchLogList } from '@/api/dispatch'
|
||||
import { dispatchStatusMap, getDishPatchListAll, getDishPatchLogList, dispatchMode } from '@/api/dispatch'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, readJsonStrField, parseTime } from '@/utils/const'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
dispatchMode,
|
||||
loading: true,
|
||||
list: [],
|
||||
nodeList: [],
|
||||
dispatchList: [],
|
||||
@ -135,40 +153,54 @@ export default {
|
||||
{
|
||||
title: '分发项目 ID',
|
||||
dataIndex: 'outGivingId',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'outGivingId' }
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{ title: '节点名称', dataIndex: 'nodeName', ellipsis: true, scopedSlots: { customRender: 'nodeName' } },
|
||||
{ title: '项目 ID', dataIndex: 'projectId', ellipsis: true, scopedSlots: { customRender: 'projectId' } },
|
||||
{
|
||||
title: '节点名称',
|
||||
dataIndex: 'nodeName',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '项目 ID',
|
||||
dataIndex: 'projectId',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '分发方式',
|
||||
dataIndex: 'mode',
|
||||
ellipsis: true,
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: '分发结果',
|
||||
dataIndex: 'outGivingResultMsg',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'outGivingResultMsg' }
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: '分发状态消息',
|
||||
dataIndex: 'outGivingResultMsgData',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'outGivingResultMsgData' }
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '分发耗时',
|
||||
dataIndex: 'outGivingResultTime',
|
||||
width: '120px',
|
||||
scopedSlots: { customRender: 'outGivingResultTime' }
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '文件大小',
|
||||
dataIndex: 'outGivingResultSize',
|
||||
width: '100px',
|
||||
scopedSlots: { customRender: 'outGivingResultSize' }
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: '开始时间',
|
||||
dataIndex: 'startTime',
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
sorter: true,
|
||||
@ -178,7 +210,7 @@ export default {
|
||||
title: '结束时间',
|
||||
dataIndex: 'endTime',
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
width: '170px'
|
||||
@ -187,11 +219,17 @@ export default {
|
||||
title: '操作人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
|
||||
width: 120
|
||||
},
|
||||
{ title: '状态', dataIndex: 'status', width: 100, ellipsis: true, scopedSlots: { customRender: 'status' } }
|
||||
// { title: "操作", dataIndex: "operation", align: "center", scopedSlots: { customRender: "operation" }, width: "100px" },
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
fixed: 'right'
|
||||
},
|
||||
{ title: '操作', dataIndex: 'operation', align: 'center', width: '100px', fixed: 'right' }
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -258,4 +296,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
@ -8,12 +8,14 @@
|
||||
:pagination="pagination"
|
||||
@change="changePage"
|
||||
bordered
|
||||
:rowKey="(record, index) => index"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="日志名称"
|
||||
class="search-input-item"
|
||||
@ -25,96 +27,107 @@
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #name slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="primary" size="small" @click="handleLogRead(record)">查看</a-button>
|
||||
<a-button type="danger" size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="primary" size="small" @click="handleLogRead(record)">查看</a-button>
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑区 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="editVisible"
|
||||
v-model:open="editVisible"
|
||||
width="60%"
|
||||
title="编辑日志搜索"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleEditOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="日志名称" name="name">
|
||||
<a-input v-model="temp.name" :maxLength="50" placeholder="日志项目名称" />
|
||||
<a-input v-model:value="temp.name" :maxLength="50" placeholder="日志项目名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="绑定节点" required>
|
||||
<a-row v-for="(item, index) in temp.projectList" :key="index">
|
||||
<a-col :span="11">
|
||||
<span>节点: </span>
|
||||
<a-select
|
||||
style="width: 80%"
|
||||
v-model="item.nodeId"
|
||||
placeholder="请选择节点"
|
||||
@change="
|
||||
() => {
|
||||
temp = {
|
||||
...temp,
|
||||
projectList: temp.projectList.map((item, index1) => {
|
||||
if (index1 === index && item.projectId) {
|
||||
return Object.assign(item, { projectId: undefined })
|
||||
}
|
||||
return item
|
||||
})
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-row v-for="(item, index) in temp.projectList" :key="index">
|
||||
<a-col :span="11">
|
||||
<span>节点: </span>
|
||||
<a-select
|
||||
style="width: 80%"
|
||||
v-model:value="item.nodeId"
|
||||
placeholder="请选择节点"
|
||||
@change="
|
||||
() => {
|
||||
temp = {
|
||||
...temp,
|
||||
projectList: temp.projectList.map((item, index1) => {
|
||||
if (index1 === index && item.projectId) {
|
||||
return Object.assign(item, { projectId: undefined })
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="nodeItem in nodeList"
|
||||
:key="nodeItem.id"
|
||||
:disabled="
|
||||
!nodeProjectList[nodeItem.id] || !nodeProjectList[nodeItem.id].projects || nodeItem.openStatus !== 1
|
||||
"
|
||||
>
|
||||
{{ nodeItem.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<span>项目: </span>
|
||||
<a-select
|
||||
:disabled="!item.nodeId"
|
||||
style="width: 80%"
|
||||
v-model="item.projectId"
|
||||
:placeholder="`请选择项目`"
|
||||
>
|
||||
<!-- <a-select-option value=""> 请先选择节点</a-select-option> -->
|
||||
<template v-if="nodeProjectList[item.nodeId]">
|
||||
<a-select-option
|
||||
v-for="project in nodeProjectList[item.nodeId].projects"
|
||||
v-for="nodeItem in nodeList"
|
||||
:key="nodeItem.id"
|
||||
:disabled="
|
||||
temp.projectList.filter((item, nowIndex) => {
|
||||
return (
|
||||
item.nodeId === project.nodeId && item.projectId === project.projectId && nowIndex !== index
|
||||
)
|
||||
}).length > 0
|
||||
!nodeProjectList[nodeItem.id] ||
|
||||
!nodeProjectList[nodeItem.id].projects ||
|
||||
nodeItem.openStatus !== 1
|
||||
"
|
||||
:key="project.projectId"
|
||||
>
|
||||
{{ project.name }}
|
||||
{{ nodeItem.name }}
|
||||
</a-select-option>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="2">
|
||||
<a-button type="danger" @click="() => temp.projectList.splice(index, 1)" icon="delete"></a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<span>项目: </span>
|
||||
<a-select
|
||||
:disabled="!item.nodeId"
|
||||
style="width: 80%"
|
||||
v-model:value="item.projectId"
|
||||
:placeholder="`请选择项目`"
|
||||
>
|
||||
<!-- <a-select-option value=""> 请先选择节点</a-select-option> -->
|
||||
<template v-if="nodeProjectList[item.nodeId]">
|
||||
<a-select-option
|
||||
v-for="project in nodeProjectList[item.nodeId].projects"
|
||||
:disabled="
|
||||
temp.projectList.filter((item, nowIndex) => {
|
||||
return (
|
||||
item.nodeId === project.nodeId && item.projectId === project.projectId && nowIndex !== index
|
||||
)
|
||||
}).length > 0
|
||||
"
|
||||
:key="project.projectId"
|
||||
>
|
||||
{{ project.name }}
|
||||
</a-select-option>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="2">
|
||||
<a-button type="primary" danger @click="() => temp.projectList.splice(index, 1)"
|
||||
><DeleteOutlined
|
||||
/></a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-button type="primary" @click="() => temp.projectList.push({})">添加</a-button>
|
||||
<a-button type="primary" @click="() => temp.projectList.push({})">添加</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
@ -123,7 +136,7 @@
|
||||
destroyOnClose
|
||||
placement="right"
|
||||
:width="`${this.getCollapsed ? 'calc(100vw - 80px)' : 'calc(100vw - 200px)'}`"
|
||||
:visible="logReadVisible"
|
||||
:open="logReadVisible"
|
||||
@close="
|
||||
() => {
|
||||
this.logReadVisible = false
|
||||
@ -137,7 +150,7 @@
|
||||
</template>
|
||||
<logReadView
|
||||
v-if="logReadVisible"
|
||||
:data="temp"
|
||||
:data="this.temp"
|
||||
@changeTitle="
|
||||
(logFile) => {
|
||||
const cacheData = { ...this.temp.cacheData, logFile: logFile }
|
||||
@ -148,11 +161,12 @@
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deleteLogRead, editLogRead, getLogReadList } from '@/api/log-read'
|
||||
import { getNodeListAll, getProjectListAll } from '@/api/node'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, itemGroupBy, parseTime } from '@/utils/const'
|
||||
|
||||
import { useGuideStore } from '@/stores/guide'
|
||||
import { mapState } from 'pinia'
|
||||
import logReadView from './logReadView'
|
||||
|
||||
@ -172,21 +186,26 @@ export default {
|
||||
temp: {},
|
||||
editVisible: false,
|
||||
columns: [
|
||||
{ title: '名称', dataIndex: 'name', ellipsis: true, scopedSlots: { customRender: 'name' } },
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
tooltip: true,
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '0') {
|
||||
return ''
|
||||
}
|
||||
@ -198,18 +217,19 @@ export default {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
width: 180,
|
||||
align: 'center'
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
name: [{ required: true, message: '请填写日志项目名称', trigger: 'blur' }]
|
||||
}
|
||||
},
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getCollapsed']),
|
||||
...mapState(useGuideStore, ['getCollapsed']),
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
}
|
||||
@ -277,7 +297,9 @@ export default {
|
||||
},
|
||||
// 修改
|
||||
handleEdit(record) {
|
||||
this.temp = Object.assign({}, record, { projectList: JSON.parse(record.nodeProject) })
|
||||
this.temp = Object.assign({}, record, {
|
||||
projectList: JSON.parse(record.nodeProject)
|
||||
})
|
||||
|
||||
this.loadNodeList().then(() => {
|
||||
this.editVisible = true
|
||||
@ -285,39 +307,41 @@ export default {
|
||||
},
|
||||
handleEditOk() {
|
||||
// 检验表单
|
||||
this.$refs['editForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editForm'].validate().then(() => {
|
||||
const temp = Object.assign({}, this.temp)
|
||||
temp.projectList = temp.projectList?.filter((item) => {
|
||||
return item.nodeId && item.projectId
|
||||
})
|
||||
if (!temp.projectList || !temp.projectList.length) {
|
||||
$notification.warn({
|
||||
this.$notification.warn({
|
||||
message: '至少选择一个节点和项目'
|
||||
})
|
||||
return false
|
||||
}
|
||||
// console.log(temp);
|
||||
|
||||
editLogRead(temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editForm'].resetFields()
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.confirmLoading = true
|
||||
editLogRead(temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editForm'].resetFields()
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除日志搜索么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -325,7 +349,7 @@ export default {
|
||||
// 删除
|
||||
deleteLogRead(record.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -352,4 +376,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 布局 -->
|
||||
<a-layout class="file-layout node-full-content">
|
||||
<a-layout class="file-layout">
|
||||
<!-- 目录树 -->
|
||||
<a-layout-sider theme="light" class="sider" width="25%">
|
||||
<div class="dir-container">
|
||||
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<a-directory-tree
|
||||
:replace-fields="treeReplaceFields"
|
||||
:fieldNames="treeReplaceFields"
|
||||
@select="nodeClick"
|
||||
:loadData="onTreeData"
|
||||
:treeData="treeList"
|
||||
@ -45,7 +45,7 @@
|
||||
<a-input
|
||||
placeholder="关键词,支持正则"
|
||||
:style="`width: 250px`"
|
||||
v-model="temp.cacheData.keyword"
|
||||
v-model:value="temp.cacheData.keyword"
|
||||
@pressEnter="sendSearchLog"
|
||||
>
|
||||
</a-input>
|
||||
@ -55,7 +55,7 @@
|
||||
显示前N行
|
||||
<a-input-number
|
||||
id="inputNumber"
|
||||
v-model="temp.cacheData.beforeCount"
|
||||
v-model:value="temp.cacheData.beforeCount"
|
||||
:min="0"
|
||||
:max="1000"
|
||||
@pressEnter="sendSearchLog"
|
||||
@ -65,22 +65,22 @@
|
||||
显示后N行
|
||||
<a-input-number
|
||||
id="inputNumber"
|
||||
v-model="temp.cacheData.afterCount"
|
||||
v-model:value="temp.cacheData.afterCount"
|
||||
:min="0"
|
||||
:max="1000"
|
||||
@pressEnter="sendSearchLog"
|
||||
/>
|
||||
</div>
|
||||
<a-popover title="正则语法参考">
|
||||
<template #content>
|
||||
<template v-slot:content>
|
||||
<ul>
|
||||
<li><b>^.*\d+.*$</b> - 匹配包含数字的行</li>
|
||||
<li><b>.*(a|b).*</b> - 匹配包含 a 或者 b 的行</li>
|
||||
<li><b>.*(异常).*</b> - 匹配包含 异常 的行</li>
|
||||
</ul>
|
||||
</template>
|
||||
<a-button type="link" style="padding: 0" icon="unordered-list"
|
||||
><span style="margin-left: 2px">语法参考</span></a-button
|
||||
<a-button type="link" style="padding: 0"
|
||||
><UnorderedListOutlined /><span style="margin-left: 2px">语法参考</span></a-button
|
||||
>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
@ -111,7 +111,7 @@
|
||||
文件前N行
|
||||
<a-input-number
|
||||
id="inputNumber"
|
||||
v-model="temp.cacheData.head"
|
||||
v-model:value="temp.cacheData.head"
|
||||
:min="0"
|
||||
:max="1000"
|
||||
@pressEnter="sendSearchLog"
|
||||
@ -121,14 +121,14 @@
|
||||
文件后N行
|
||||
<a-input-number
|
||||
id="inputNumber"
|
||||
v-model="temp.cacheData.tail"
|
||||
v-model:value="temp.cacheData.tail"
|
||||
:min="0"
|
||||
:max="1000"
|
||||
@pressEnter="sendSearchLog"
|
||||
/>
|
||||
</div>
|
||||
<a-popover title="搜索配置参考">
|
||||
<template #content>
|
||||
<template v-slot:content>
|
||||
<ul>
|
||||
<li><b>从尾搜索、文件前0行、文件后3行</b> - 在文件最后 3 行中搜索</li>
|
||||
<li><b>从头搜索、文件前0行、文件后3行</b> - 在文件第 3 - 2147483647 行中搜索</li>
|
||||
@ -139,18 +139,18 @@
|
||||
<li><b>从头搜索、文件前20行、文件后3行</b> - 在文件第 3 - 20 行中搜索</li>
|
||||
</ul>
|
||||
</template>
|
||||
<a-button type="link" style="padding: 0" icon="unordered-list"
|
||||
><span style="margin-left: 2px">搜索参考</span></a-button
|
||||
<a-button type="link" style="padding: 0"
|
||||
><UnorderedListOutlined /><span style="margin-left: 2px">搜索参考</span></a-button
|
||||
>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-tabs v-if="temp.cacheData" v-model="activeTagKey" :tabBarStyle="{ marginBottom: 0 }">
|
||||
<a-tabs v-if="temp.cacheData" v-model:value="activeTagKey" :tabBarStyle="{ marginBottom: 0 }">
|
||||
<template v-for="item in temp.projectList">
|
||||
<a-tab-pane forceRender v-if="nodeName[item.nodeId]" :key="`${item.nodeId},${item.projectId}`">
|
||||
<template #tab>
|
||||
<a-tab-pane forceRender v-if="nodeName[item.nodeId]">
|
||||
<template v-slot:tab>
|
||||
【{{ nodeName[item.nodeId] && nodeName[item.nodeId].name }}】
|
||||
{{
|
||||
nodeProjectList[item.nodeId] &&
|
||||
@ -175,12 +175,15 @@
|
||||
</a-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getNodeListAll, getProjectListAll } from '@/api/node'
|
||||
import { getFileList } from '@/api/node-project'
|
||||
import { itemGroupBy } from '@/utils/const'
|
||||
import { getWebSocketUrl } from '@/api/config'
|
||||
import { mapState } from 'pinia'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import viewPre from '@/components/logView/view-pre'
|
||||
import { updateCache } from '@/api/log-read'
|
||||
|
||||
@ -210,7 +213,8 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getLongTermToken', 'getWorkspaceId']),
|
||||
...mapState(useUserStore, ['getLongTermToken']),
|
||||
...mapState(useAppStore, ['getWorkspaceId']),
|
||||
selectPath() {
|
||||
if (!Object.keys(this.tempNode).length) {
|
||||
return ''
|
||||
@ -255,7 +259,7 @@ export default {
|
||||
'/socket/console',
|
||||
`userId=${this.getLongTermToken}&id=${itemProjectData.id}&nodeId=${
|
||||
item.nodeId
|
||||
}&type=console©Id=&workspaceId=${this.getWorkspaceId()}`
|
||||
}&type=console&workspaceId=${this.getWorkspaceId()}`
|
||||
)
|
||||
const domId = `pre-dom-${item.nodeId},${item.projectId}`
|
||||
this.socketCache = { ...this.socketCache, [domId]: {} }
|
||||
@ -263,7 +267,11 @@ export default {
|
||||
|
||||
this.socketCache = {
|
||||
...this.socketCache,
|
||||
[domId]: { socket: socket, projectId: item.projectId, nodeId: item.nodeId }
|
||||
[domId]: {
|
||||
socket: socket,
|
||||
projectId: item.projectId,
|
||||
nodeId: item.nodeId
|
||||
}
|
||||
}
|
||||
|
||||
// 连接成功后
|
||||
@ -278,27 +286,27 @@ export default {
|
||||
})
|
||||
this.activeTagKey = this.temp.cacheData.useNodeId + ',' + this.temp.cacheData.useProjectId
|
||||
// console.log(cacheData);
|
||||
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
|
||||
window.onbeforeunload = () => {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
Object.keys(this.socketCache).forEach((item) => {
|
||||
clearInterval(this.socketCache[item].heart)
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
Object.keys(this.socketCache).forEach((item) => {
|
||||
clearInterval(this.socketCache[item].heart)
|
||||
})
|
||||
beforeUnmount() {
|
||||
this.close()
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
Object.keys(this.socketCache).forEach((item) => {
|
||||
clearInterval(this.socketCache[item].heart)
|
||||
this.socketCache[item].socket?.close()
|
||||
})
|
||||
},
|
||||
initWebSocket(id, url) {
|
||||
let socket
|
||||
if (!socket || socket.readyState !== socket.OPEN || socket.readyState !== socket.CONNECTING) {
|
||||
socket = new WebSocket(url)
|
||||
}
|
||||
const socket = new WebSocket(url)
|
||||
|
||||
socket.onerror = (err) => {
|
||||
console.error(err)
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
key: 'log-read-error',
|
||||
message: 'web socket 错误,请检查是否开启 ws 代理'
|
||||
})
|
||||
@ -307,9 +315,9 @@ export default {
|
||||
socket.onclose = (err) => {
|
||||
//当客户端收到服务端发送的关闭连接请求时,触发onclose事件
|
||||
console.error(err)
|
||||
$notification.info({
|
||||
this.$notification.info({
|
||||
key: 'log-read-close',
|
||||
message: '会话已经关闭'
|
||||
message: '会话已经关闭[tail-log]'
|
||||
})
|
||||
clearInterval(this.socketCache[id].heart)
|
||||
}
|
||||
@ -377,7 +385,11 @@ export default {
|
||||
nodeChange(value) {
|
||||
const keyArray = value.split(',')
|
||||
|
||||
const cacheData = { ...this.temp.cacheData, useNodeId: keyArray[0], useProjectId: keyArray[1] }
|
||||
const cacheData = {
|
||||
...this.temp.cacheData,
|
||||
useNodeId: keyArray[0],
|
||||
useProjectId: keyArray[1]
|
||||
}
|
||||
this.temp = { ...this.temp, cacheData: cacheData }
|
||||
this.loadFileData()
|
||||
//
|
||||
@ -392,14 +404,17 @@ export default {
|
||||
if (node.dataRef.textFileEdit) {
|
||||
this.tempFileNode = node.dataRef
|
||||
// let cacheData = ;
|
||||
const cacheData = { ...this.temp.cacheData, logFile: this.selectFilePath }
|
||||
const cacheData = {
|
||||
...this.temp.cacheData,
|
||||
logFile: this.selectFilePath
|
||||
}
|
||||
this.temp = { ...this.temp, cacheData: cacheData }
|
||||
this.$emit('changeTitle', this.selectFilePath)
|
||||
//
|
||||
this.sendSearchLog()
|
||||
} else {
|
||||
//
|
||||
$message.error('当前文件不可读,需要配置可读文件白名单')
|
||||
this.$message.error('当前文件不可读,需要配置可读文件授权')
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -485,7 +500,8 @@ export default {
|
||||
}
|
||||
//
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['changeTitle']
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -502,13 +518,10 @@ export default {
|
||||
.file-content {
|
||||
height: calc(100vh - 80px);
|
||||
overflow-y: auto;
|
||||
/* margin: 10px 10px 0; */
|
||||
padding: 0 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.log-filter {
|
||||
/* margin-top: -22px; */
|
||||
/* margin-bottom: 10px; */
|
||||
padding: 0 10px 10px;
|
||||
line-height: 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
743
web-vue3/src/pages/dispatch/start.vue
Normal file
743
web-vue3/src/pages/dispatch/start.vue
Normal file
@ -0,0 +1,743 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:open="true"
|
||||
:closable="!uploading"
|
||||
:footer="uploading ? null : undefined"
|
||||
width="50%"
|
||||
:keyboard="false"
|
||||
:title="'分发项目-' + data.name"
|
||||
@ok="handleDispatchOk"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
@cancel="
|
||||
() => {
|
||||
$emit('cancel')
|
||||
}
|
||||
"
|
||||
>
|
||||
<a-form ref="dispatchForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="方式" name="type">
|
||||
<a-radio-group v-model:value="temp.type" name="type" :disabled="!!percentage" @change="restForm">
|
||||
<a-radio :value="'use-build'">构建产物</a-radio>
|
||||
<a-radio :value="'file-storage'">文件中心</a-radio>
|
||||
<a-radio :value="'static-file-storage'">静态文件</a-radio>
|
||||
<a-radio :value="'upload'">上传文件</a-radio>
|
||||
<a-radio :value="'download'">远程下载</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<!-- 手动上传 -->
|
||||
<a-form-item label="选择分发文件" name="clearOld" v-if="temp.type === 'upload'">
|
||||
<a-progress v-if="percentage" :percent="percentage">
|
||||
<template #format="percent">
|
||||
{{ percent }}%
|
||||
<template v-if="percentageInfo.total"> ({{ renderSize(percentageInfo.total) }}) </template>
|
||||
<template v-if="percentageInfo.duration"> 用时:{{ formatDuration(percentageInfo.duration) }} </template>
|
||||
</template>
|
||||
</a-progress>
|
||||
|
||||
<a-upload :file-list="fileList" :disabled="!!percentage" @remove="handleRemove" :before-upload="beforeUpload">
|
||||
<LoadingOutlined v-if="percentage" />
|
||||
<a-button v-else type="primary"><UploadOutlined />选择文件</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
<!-- 远程下载 -->
|
||||
<a-form-item label="远程下载URL" name="url" v-else-if="temp.type === 'download'">
|
||||
<a-input v-model:value="temp.url" placeholder="远程下载地址" />
|
||||
</a-form-item>
|
||||
<!-- 在线构建 -->
|
||||
<template v-else-if="temp.type == 'use-build'">
|
||||
<a-form-item label="选择构建">
|
||||
<a-space>
|
||||
{{ chooseBuildInfo.name }}
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
chooseVisible = 1
|
||||
}
|
||||
"
|
||||
>
|
||||
选择构建
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item label="选择产物">
|
||||
<a-space>
|
||||
<a-tag v-if="chooseBuildInfo.buildNumberId">#{{ chooseBuildInfo.buildNumberId }}</a-tag>
|
||||
<a-button
|
||||
type="primary"
|
||||
:disabled="!chooseBuildInfo.id"
|
||||
@click="
|
||||
() => {
|
||||
chooseVisible = 2
|
||||
}
|
||||
"
|
||||
>
|
||||
选择产物
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- 文件中心 -->
|
||||
<template v-else-if="temp.type === 'file-storage'">
|
||||
<a-form-item label="选择文件">
|
||||
<a-space>
|
||||
{{ chooseFileInfo.name }}
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
chooseVisible = 3
|
||||
}
|
||||
"
|
||||
>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item></template
|
||||
>
|
||||
<!-- 静态文件 -->
|
||||
<template v-else-if="temp.type === 'static-file-storage'">
|
||||
<a-form-item label="选择文件">
|
||||
<a-space>
|
||||
{{ chooseFileInfo.name }}
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
chooseVisible = 4
|
||||
}
|
||||
"
|
||||
>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item></template
|
||||
>
|
||||
<a-form-item name="clearOld">
|
||||
<template v-slot:label>
|
||||
清空发布
|
||||
<a-tooltip>
|
||||
<template v-slot:title>
|
||||
清空发布是指在上传新文件前,会将项目文件夹目录里面的所有文件先删除后再保存新文件
|
||||
</template>
|
||||
<QuestionCircleOutlined />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-switch v-model:checked="temp.clearOld" checked-children="是" un-checked-children="否" />
|
||||
</a-form-item>
|
||||
<a-form-item name="unzip" v-if="temp.type !== 'use-build'">
|
||||
<template v-slot:label>
|
||||
是否解压
|
||||
<a-tooltip>
|
||||
<template v-slot:title>
|
||||
如果上传的压缩文件是否自动解压 支持的压缩包类型有 tar.bz2, tar.gz, tar, bz2, zip, gz</template
|
||||
>
|
||||
<QuestionCircleOutlined />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-switch v-model:checked="temp.autoUnzip" checked-children="是" un-checked-children="否" />
|
||||
</a-form-item>
|
||||
<a-form-item label="剔除文件夹" v-if="temp.autoUnzip">
|
||||
<a-input-number
|
||||
style="width: 100%"
|
||||
v-model:value="temp.stripComponents"
|
||||
:min="0"
|
||||
placeholder="解压时候自动剔除压缩包里面多余的文件夹名"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="分发后操作" name="afterOpt">
|
||||
<a-select v-model:value="temp.afterOpt" placeholder="请选择发布后操作">
|
||||
<a-select-option v-for="item in afterOptList" :key="item.value">{{ item.title }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="secondaryDirectory" label="二级目录">
|
||||
<a-input v-model:value="temp.secondaryDirectory" placeholder="不填写则发布至项目的根目录" />
|
||||
</a-form-item>
|
||||
<a-form-item name="selectProject" label="筛选项目" help="筛选之后本次发布操作只发布筛选项,并且只对本次操作生效">
|
||||
<a-select mode="multiple" v-model:value="temp.selectProjectArray" placeholder="请选择指定发布的项目">
|
||||
<a-select-option v-for="item in itemProjectList" :key="item.id" :value="`${item.projectId}@${item.nodeId}`">
|
||||
{{ item.nodeName }}-{{ item.cacheProjectName || item.projectId }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 选择构建 -->
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
:title="`选择构建`"
|
||||
placement="right"
|
||||
:open="chooseVisible === 1"
|
||||
width="80vw"
|
||||
:zIndex="1009"
|
||||
@close="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
>
|
||||
<build-list
|
||||
v-if="chooseVisible === 1"
|
||||
:choose="'radio'"
|
||||
layout="table"
|
||||
mode="choose"
|
||||
ref="buildList"
|
||||
@confirm="
|
||||
(data) => {
|
||||
this.chooseBuildInfo = {
|
||||
id: data[0].id,
|
||||
name: data[0].name
|
||||
}
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
@cancel="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
></build-list>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
this.$refs['buildList'].handerConfirm()
|
||||
}
|
||||
"
|
||||
>
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
<!-- 选择构建产物 -->
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
:title="`选择构建产物`"
|
||||
placement="right"
|
||||
:open="chooseVisible === 2"
|
||||
width="80vw"
|
||||
:zIndex="1009"
|
||||
@close="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
>
|
||||
<!-- 选择构建产物 -->
|
||||
<build-history
|
||||
v-if="chooseVisible === 2"
|
||||
:choose="'radio'"
|
||||
mode="choose"
|
||||
ref="buildHistory"
|
||||
@confirm="
|
||||
(data) => {
|
||||
this.chooseBuildInfo = {
|
||||
...this.chooseBuildInfo,
|
||||
buildNumberId: data[0]
|
||||
}
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
@cancel="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
></build-history>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
this.$refs['buildHistory'].handerConfirm()
|
||||
}
|
||||
"
|
||||
>
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
<!-- 选择文件 -->
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
:title="`选择文件`"
|
||||
placement="right"
|
||||
:open="chooseVisible === 3"
|
||||
width="80vw"
|
||||
:zIndex="1009"
|
||||
@close="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
>
|
||||
<!-- 选择文件 -->
|
||||
<file-storage
|
||||
v-if="chooseVisible === 3"
|
||||
:choose="'radio'"
|
||||
mode="choose"
|
||||
ref="fileStorage"
|
||||
@confirm="
|
||||
(data) => {
|
||||
this.chooseFileInfo = { id: data[0].id, name: data[0].name }
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
@cancel="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
></file-storage>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
this.$refs['fileStorage'].handerConfirm()
|
||||
}
|
||||
"
|
||||
>
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
<!-- 选择静态文件 -->
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
:title="`选择静态文件`"
|
||||
placement="right"
|
||||
:open="chooseVisible === 4"
|
||||
width="80vw"
|
||||
:zIndex="1009"
|
||||
@close="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
>
|
||||
<!-- 选择静态文件 -->
|
||||
<static-file-storage
|
||||
v-if="chooseVisible === 4"
|
||||
:choose="'radio'"
|
||||
mode="choose"
|
||||
ref="staticFileStorage"
|
||||
@confirm="
|
||||
(data) => {
|
||||
this.chooseFileInfo = { id: data[0].id, name: data[0].name }
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
@cancel="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
></static-file-storage>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
this.$refs['staticFileStorage'].handerConfirm()
|
||||
}
|
||||
"
|
||||
>
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { uploadPieces } from '@/utils/upload-pieces'
|
||||
import {
|
||||
remoteDownload,
|
||||
uploadDispatchFile,
|
||||
uploadDispatchFileMerge,
|
||||
afterOptList,
|
||||
getDispatchProject,
|
||||
useBuild,
|
||||
useuseFileStorage,
|
||||
useuseStaticFileStorage
|
||||
} from '@/api/dispatch'
|
||||
import { renderSize, formatDuration } from '@/utils/const'
|
||||
import BuildList from '@/pages/build/list-info'
|
||||
import BuildHistory from '@/pages/build/history'
|
||||
import FileStorage from '@/pages/file-manager/fileStorage/list'
|
||||
import StaticFileStorage from '@/pages/file-manager/staticFileStorage/list'
|
||||
import { getBuildGet } from '@/api/build-info'
|
||||
import { hasFile } from '@/api/file-manager/file-storage'
|
||||
import { hasStaticFile } from '@/api/file-manager/static-storage'
|
||||
export default {
|
||||
components: {
|
||||
BuildList,
|
||||
BuildHistory,
|
||||
FileStorage,
|
||||
StaticFileStorage
|
||||
},
|
||||
props: {
|
||||
data: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
afterOptList,
|
||||
percentage: 0,
|
||||
percentageInfo: {},
|
||||
uploading: false,
|
||||
itemProjectList: [],
|
||||
fileList: [],
|
||||
rules: {
|
||||
afterOpt: [{ required: true, message: '请选择发布后操作', trigger: 'blur' }],
|
||||
url: [{ required: true, message: '请输入远程地址', trigger: 'blur' }]
|
||||
},
|
||||
temp: { type: 'upload' },
|
||||
chooseVisible: 0,
|
||||
chooseBuildInfo: {},
|
||||
chooseFileInfo: {},
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.temp = {
|
||||
...this.temp,
|
||||
afterOpt: this.data.afterOpt,
|
||||
id: this.data.id,
|
||||
clearOld: this.data.clearOld,
|
||||
secondaryDirectory: this.data.secondaryDirectory,
|
||||
type: this.data.mode || 'upload'
|
||||
}
|
||||
getDispatchProject(this.data.id, true).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.itemProjectList = res.data?.projectList
|
||||
|
||||
this.percentage = 0
|
||||
this.percentageInfo = {}
|
||||
this.fileList = []
|
||||
this.restForm()
|
||||
}
|
||||
})
|
||||
if (this.data.mode === 'use-build') {
|
||||
// 构建
|
||||
const buildData = (this.data.modeData || '').split(':')
|
||||
if (buildData.length === 2) {
|
||||
getBuildGet({
|
||||
id: buildData[0]
|
||||
}).then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.chooseBuildInfo = {
|
||||
id: res.data.id,
|
||||
name: res.data.name,
|
||||
buildNumberId: buildData[1]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (this.data.mode === 'download') {
|
||||
// 下载
|
||||
this.temp = { ...this.temp, url: this.data.modeData }
|
||||
} else if (this.data.mode === 'file-storage') {
|
||||
// 文件中心
|
||||
if (this.data.modeData) {
|
||||
hasFile({ fileSumMd5: this.data.modeData }).then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.chooseFileInfo = { id: res.data.id, name: res.data.name }
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (this.data.mode === 'static-file-storage') {
|
||||
// 静态文件
|
||||
if (this.data.modeData) {
|
||||
hasStaticFile({ fileId: this.data.modeData }).then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.chooseFileInfo = { id: res.data.id, name: res.data.name }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// console.log(this.temp);
|
||||
},
|
||||
methods: {
|
||||
renderSize,
|
||||
formatDuration,
|
||||
// 处理文件移除
|
||||
handleRemove(file) {
|
||||
const index = this.fileList.indexOf(file)
|
||||
const newFileList = this.fileList.slice()
|
||||
newFileList.splice(index, 1)
|
||||
this.fileList = newFileList
|
||||
return true
|
||||
},
|
||||
// 准备上传文件
|
||||
beforeUpload(file) {
|
||||
// 只允许上传单个文件
|
||||
this.fileList = [file]
|
||||
return false
|
||||
},
|
||||
// 提交分发文件
|
||||
handleDispatchOk() {
|
||||
// console.log(this.temp);
|
||||
this.temp = {
|
||||
...this.temp,
|
||||
selectProject: (this.temp.selectProjectArray && this.temp.selectProjectArray.join(',')) || ''
|
||||
}
|
||||
// 检验表单
|
||||
this.$refs['dispatchForm'].validate().then((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
// const key = this.temp.type;
|
||||
if (this.temp.type == 'upload') {
|
||||
// 判断文件
|
||||
if (this.fileList.length === 0) {
|
||||
this.$notification.error({
|
||||
message: '请选择文件'
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.percentage = 0
|
||||
this.percentageInfo = {}
|
||||
let file = this.fileList[0]
|
||||
this.uploading = true
|
||||
this.confirmLoading = true
|
||||
uploadPieces({
|
||||
file,
|
||||
process: (process, end, total, duration) => {
|
||||
this.percentage = Math.max(this.percentage, process)
|
||||
this.percentageInfo = { end, total, duration }
|
||||
},
|
||||
success: (uploadData) => {
|
||||
// 准备合并
|
||||
uploadDispatchFileMerge({
|
||||
...uploadData[0],
|
||||
id: this.temp.id,
|
||||
afterOpt: this.temp.afterOpt,
|
||||
clearOld: this.temp.clearOld,
|
||||
autoUnzip: this.temp.autoUnzip,
|
||||
secondaryDirectory: this.temp.secondaryDirectory || '',
|
||||
stripComponents: this.temp.stripComponents || 0,
|
||||
selectProject: this.temp.selectProject
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.fileList = []
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.percentage = 0
|
||||
this.percentageInfo = {}
|
||||
this.$emit('cancel')
|
||||
}, 2000)
|
||||
this.uploading = false
|
||||
})
|
||||
.catch(() => {
|
||||
this.uploading = false
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
error: (msg) => {
|
||||
this.$notification.error({
|
||||
message: msg
|
||||
})
|
||||
this.uploading = false
|
||||
this.confirmLoading = false
|
||||
},
|
||||
uploadCallback: (formData) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
formData.append('id', this.temp.id)
|
||||
// 上传文件
|
||||
uploadDispatchFile(formData)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
resolve()
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
} else if (this.temp.type == 'download') {
|
||||
if (!this.temp.url) {
|
||||
this.$notification.error({
|
||||
message: '请填写远程URL'
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.confirmLoading = true
|
||||
remoteDownload(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.$emit('cancel')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
return true
|
||||
} else if (this.temp.type == 'use-build') {
|
||||
// 构建
|
||||
if (!this.chooseBuildInfo || !this.chooseBuildInfo.id || !this.chooseBuildInfo.buildNumberId) {
|
||||
this.$notification.error({
|
||||
message: '请填写构建和产物'
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.confirmLoading = true
|
||||
useBuild({
|
||||
...this.temp,
|
||||
buildId: this.chooseBuildInfo.id,
|
||||
buildNumberId: this.chooseBuildInfo.buildNumberId
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.$emit('cancel')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
return true
|
||||
} else if (this.temp.type == 'file-storage') {
|
||||
// 文件中心
|
||||
if (!this.chooseFileInfo || !this.chooseFileInfo.id) {
|
||||
this.$notification.error({
|
||||
message: '请选择文件中心的文件'
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.confirmLoading = true
|
||||
useuseFileStorage({
|
||||
...this.temp,
|
||||
fileId: this.chooseFileInfo.id
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.$emit('cancel')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
return true
|
||||
} else if (this.temp.type == 'static-file-storage') {
|
||||
// 文件中心
|
||||
if (!this.chooseFileInfo || !this.chooseFileInfo.id) {
|
||||
this.$notification.error({
|
||||
message: '请选择静态文件中的文件'
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.confirmLoading = true
|
||||
useuseStaticFileStorage({
|
||||
...this.temp,
|
||||
fileId: this.chooseFileInfo.id
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$emit('cancel')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
return true
|
||||
}
|
||||
})
|
||||
},
|
||||
//
|
||||
restForm(e) {
|
||||
// console.log(e);
|
||||
if (e) {
|
||||
this.temp = { ...this.temp, type: e.target.value }
|
||||
}
|
||||
this.$refs['dispatchForm'] && this.$refs['dispatchForm'].clearValidate()
|
||||
}
|
||||
},
|
||||
emits: ['cancel']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .ant-progress-text {
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
@ -1,132 +1,203 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 嵌套表格 -->
|
||||
<a-table
|
||||
:loading="childLoading"
|
||||
:columns="childColumns"
|
||||
size="middle"
|
||||
:bordered="true"
|
||||
:data-source="list"
|
||||
:pagination="false"
|
||||
rowKey="id_no"
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
:title="`查看 ${name} 状态`"
|
||||
placement="right"
|
||||
width="85vw"
|
||||
:open="true"
|
||||
@close="
|
||||
() => {
|
||||
$emit('close')
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #title>
|
||||
<a-space>
|
||||
<div>
|
||||
当前状态:
|
||||
<a-tag v-if="data.status === 2" color="green">{{ statusMap[data.status] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="data.status === 1 || data.status === 0" color="orange">{{
|
||||
statusMap[data.status] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="data.status === 3 || data.status === 4" color="red">{{
|
||||
statusMap[data.status] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else>{{ statusMap[data.status] || '未知' }}</a-tag>
|
||||
<a-tabs v-model:value="tabKey" tab-position="left">
|
||||
<a-tab-pane key="1" tab="状态">
|
||||
<!-- 嵌套表格 -->
|
||||
<a-table
|
||||
:loading="childLoading"
|
||||
:columns="childColumns"
|
||||
size="middle"
|
||||
:bordered="true"
|
||||
:data-source="list"
|
||||
:pagination="false"
|
||||
rowKey="id_no"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<a-space>
|
||||
<div>
|
||||
当前状态:
|
||||
<a-tag v-if="data.status === 2" color="green">{{ statusMap[data.status] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="data.status === 1 || data.status === 0" color="orange">{{
|
||||
statusMap[data.status] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="data.status === 3 || data.status === 4" color="red">{{
|
||||
statusMap[data.status] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else>{{ statusMap[data.status] || '未知' }}</a-tag>
|
||||
</div>
|
||||
<div>状态描述:{{ data.statusMsg || '-' }}</div>
|
||||
<a-button type="primary" size="small" :loading="childLoading" @click="loadData">刷新</a-button>
|
||||
|
||||
<a-statistic-countdown
|
||||
format=" s 秒"
|
||||
title="刷新倒计时"
|
||||
:value="countdownTime"
|
||||
@finish="silenceLoadData"
|
||||
/>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.dataIndex === 'nodeId'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<a-button type="link" style="padding: 0" size="small" @click="toNode(text)">
|
||||
<span>{{ nodeNameMap[text] || text }}</span>
|
||||
<FullscreenOutlined />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'projectName'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<template v-if="record.disabled">
|
||||
<a-tooltip title="当前项目被禁用">
|
||||
<EyeInvisibleOutlined />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<span>{{ text || record.cacheProjectName }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingStatus'">
|
||||
<a-tag v-if="text === 2" color="green">{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 1 || text === 0 || text === 5" color="orange">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="text === 3 || text === 4 || text === 6" color="red">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else>{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultMsg'">
|
||||
<a-tooltip placement="topLeft" :title="readJsonStrField(record.outGivingResult, 'msg')">
|
||||
<span
|
||||
>{{ readJsonStrField(record.outGivingResult, 'code') }}-{{
|
||||
readJsonStrField(record.outGivingResult, 'msg') || record.outGivingResult
|
||||
}}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultTime'">
|
||||
<a-tooltip placement="topLeft" :title="readJsonStrField(record.outGivingResult, 'upload_duration')">
|
||||
<span>{{ readJsonStrField(record.outGivingResult, 'upload_duration') }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultSize'">
|
||||
<a-tooltip placement="topLeft" :title="readJsonStrField(record.outGivingResult, 'upload_file_size')">
|
||||
{{ readJsonStrField(record.outGivingResult, 'upload_file_size') }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'outGivingResultMsgData'">
|
||||
<a-tooltip placement="topLeft" :title="`${readJsonStrField(record.outGivingResult, 'data')}`">
|
||||
<template v-if="record.fileSize">
|
||||
{{ Math.floor((record.progressSize / record.fileSize) * 100) }}%
|
||||
</template>
|
||||
{{ readJsonStrField(record.outGivingResult, 'data') }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'projectStatus'">
|
||||
<a-tooltip v-if="record.errorMsg" :title="record.errorMsg">
|
||||
<WarningOutlined />
|
||||
</a-tooltip>
|
||||
<a-switch
|
||||
v-else
|
||||
:checked="text"
|
||||
:disabled="true"
|
||||
size="small"
|
||||
checked-children="运行中"
|
||||
un-checked-children="未运行"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'projectPid'">
|
||||
<a-tooltip
|
||||
placement="topLeft"
|
||||
:title="`进程号:${record.projectPid || '-'} / 端口号:${record.projectPort || '-'}`"
|
||||
>
|
||||
<span>{{ record.projectPid || '-' }}/{{ record.projectPort || '-' }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'child-operation'">
|
||||
<a-space>
|
||||
<a-button size="small" :disabled="!record.projectName" type="primary" @click="handleFile(record)"
|
||||
>文件</a-button
|
||||
>
|
||||
<a-button size="small" :disabled="!record.projectName" type="primary" @click="handleConsole(record)"
|
||||
>控制台</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="配置">
|
||||
<!-- 配置分发 -->
|
||||
<div style="width: 50vw">
|
||||
<draggable v-model="list" :group="`sortValue`" item-key="id" handle=".move" chosenClass="box-shadow">
|
||||
<template #item="{ element }">
|
||||
<a-row class="item-row">
|
||||
<a-col :span="18">
|
||||
<span> 节点名: {{ element.nodeName }} </span>
|
||||
<span> 项目名: {{ element.cacheProjectName }} </span>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-space>
|
||||
<a-switch
|
||||
checked-children="启用"
|
||||
un-checked-children="禁用"
|
||||
:checked="element.disabled ? false : true"
|
||||
@change="
|
||||
(checked) => {
|
||||
list = list.map((item2) => {
|
||||
if (element.id === item2.id) {
|
||||
item2.disabled = !checked
|
||||
}
|
||||
return { ...item2 }
|
||||
})
|
||||
}
|
||||
"
|
||||
/>
|
||||
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
size="small"
|
||||
@click="handleRemoveProject(element)"
|
||||
:disabled="!list || list.length <= 1"
|
||||
>
|
||||
解绑
|
||||
</a-button>
|
||||
<a-tooltip placement="left" :title="`长按可以拖动排序`" class="move">
|
||||
<MenuOutlined />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</draggable>
|
||||
<a-col style="margin-top: 10px">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="viewDispatchManagerOk">保存</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</div>
|
||||
<div>状态描述:{{ data.statusMsg || '-' }}</div>
|
||||
<a-button type="primary" :loading="childLoading" @click="loadData">刷新</a-button>
|
||||
|
||||
<a-statistic-countdown format=" s 秒" title="刷新倒计时" :value="countdownTime" @finish="silenceLoadData" />
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #nodeId slot-scope="text" placement="topLeft" :title="text">
|
||||
<a-button type="link" style="padding: 0" size="small" @click="toNode(text)">
|
||||
<span>{{ nodeNameMap[text] || text }}</span>
|
||||
<a-icon type="fullscreen" />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<template #projectName slot-scope="text, item">
|
||||
<template v-if="item.disabled">
|
||||
<a-tooltip title="当前项目被禁用">
|
||||
<a-icon type="eye-invisible" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tooltip #projectName placement="topLeft" :title="text">
|
||||
<span>{{ text || item.cacheProjectName }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #outGivingStatus slot-scope="text">
|
||||
<a-tag v-if="text === 2" color="green">{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 1 || text === 0 || text === 5" color="orange">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="text === 3 || text === 4 || text === 6" color="red">{{
|
||||
dispatchStatusMap[text] || '未知'
|
||||
}}</a-tag>
|
||||
<a-tag v-else>{{ dispatchStatusMap[text] || '未知' }}</a-tag>
|
||||
</template>
|
||||
<a-tooltip
|
||||
#outGivingResultMsg
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="readJsonStrField(item.outGivingResult, 'msg')"
|
||||
>
|
||||
<span
|
||||
>{{ readJsonStrField(item.outGivingResult, 'code') }}-{{
|
||||
readJsonStrField(item.outGivingResult, 'msg') || item.outGivingResult
|
||||
}}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultTime
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="readJsonStrField(item.outGivingResult, 'upload_duration')"
|
||||
>
|
||||
<span>{{ readJsonStrField(item.outGivingResult, 'upload_duration') }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultSize
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="readJsonStrField(item.outGivingResult, 'upload_file_size')"
|
||||
>
|
||||
{{ readJsonStrField(item.outGivingResult, 'upload_file_size') }}
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
#outGivingResultMsgData
|
||||
slot-scope="text, item"
|
||||
placement="topLeft"
|
||||
:title="`${readJsonStrField(item.outGivingResult, 'data')}`"
|
||||
>
|
||||
<template v-if="item.fileSize"> {{ Math.floor((item.progressSize / item.fileSize) * 100) }}% </template>
|
||||
{{ readJsonStrField(item.outGivingResult, 'data') }}
|
||||
</a-tooltip>
|
||||
|
||||
<template #projectStatus slot-scope="text, item">
|
||||
<a-tooltip v-if="item.errorMsg" :title="item.errorMsg">
|
||||
<a-icon type="warning" />
|
||||
</a-tooltip>
|
||||
<a-switch
|
||||
v-else
|
||||
:checked="text"
|
||||
:disabled="true"
|
||||
size="small"
|
||||
checked-children="运行中"
|
||||
un-checked-children="未运行"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<a-tooltip
|
||||
#projectPid
|
||||
slot-scope="text, record"
|
||||
placement="topLeft"
|
||||
:title="`进程号:${record.projectPid || '-'} / 端口号:${record.projectPort || '-'}`"
|
||||
>
|
||||
<span>{{ record.projectPid || '-' }}/{{ record.projectPort || '-' }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<template #child-operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button size="small" :disabled="!record.projectName" type="primary" @click="handleFile(record)"
|
||||
>文件</a-button
|
||||
>
|
||||
<a-button size="small" :disabled="!record.projectName" type="primary" @click="handleConsole(record)"
|
||||
>控制台</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-drawer>
|
||||
|
||||
<!-- 项目文件组件 -->
|
||||
<a-drawer
|
||||
@ -134,7 +205,7 @@
|
||||
:title="drawerTitle"
|
||||
placement="right"
|
||||
width="85vw"
|
||||
:visible="drawerFileVisible"
|
||||
:open="drawerFileVisible"
|
||||
@close="onFileClose"
|
||||
>
|
||||
<file
|
||||
@ -152,7 +223,7 @@
|
||||
:title="drawerTitle"
|
||||
placement="right"
|
||||
width="85vw"
|
||||
:visible="drawerConsoleVisible"
|
||||
:open="drawerConsoleVisible"
|
||||
@close="onConsoleClose"
|
||||
>
|
||||
<console
|
||||
@ -169,7 +240,7 @@
|
||||
:title="drawerTitle"
|
||||
placement="right"
|
||||
width="85vw"
|
||||
:visible="drawerReadFileVisible"
|
||||
:open="drawerReadFileVisible"
|
||||
@close="onReadFileClose"
|
||||
>
|
||||
<file-read
|
||||
@ -183,8 +254,15 @@
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getDispatchProject, dispatchStatusMap, statusMap } from '@/api/dispatch'
|
||||
import {
|
||||
getDispatchProject,
|
||||
dispatchStatusMap,
|
||||
statusMap,
|
||||
removeProject,
|
||||
saveDispatchProjectConfig
|
||||
} from '@/api/dispatch'
|
||||
import { getNodeListAll } from '@/api/node'
|
||||
import { getRuningProjectInfo } from '@/api/node-project'
|
||||
import {
|
||||
@ -199,25 +277,30 @@ import {
|
||||
import File from '@/pages/node/node-layout/project/project-file'
|
||||
import Console from '@/pages/node/node-layout/project/project-console'
|
||||
import FileRead from '@/pages/node/node-layout/project/project-file-read'
|
||||
import draggable from 'vuedraggable-es'
|
||||
export default {
|
||||
components: {
|
||||
File,
|
||||
Console,
|
||||
FileRead
|
||||
FileRead,
|
||||
draggable
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String
|
||||
},
|
||||
name: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
childLoading: false,
|
||||
childLoading: true,
|
||||
statusMap,
|
||||
dispatchStatusMap,
|
||||
|
||||
list: [],
|
||||
tabKey: '1',
|
||||
data: {},
|
||||
drawerTitle: '',
|
||||
drawerFileVisible: false,
|
||||
@ -226,82 +309,79 @@ export default {
|
||||
nodeNameMap: {},
|
||||
|
||||
childColumns: [
|
||||
{ title: '节点名称', dataIndex: 'nodeId', width: 120, ellipsis: true, scopedSlots: { customRender: 'nodeId' } },
|
||||
{
|
||||
title: '节点名称',
|
||||
dataIndex: 'nodeId',
|
||||
width: 120,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '项目名称',
|
||||
dataIndex: 'projectName',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'projectName' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '项目状态',
|
||||
dataIndex: 'projectStatus',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'projectStatus' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '进程/端口',
|
||||
dataIndex: 'projectPid',
|
||||
width: '120px',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'projectPid' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '分发状态',
|
||||
dataIndex: 'outGivingStatus',
|
||||
width: '120px',
|
||||
scopedSlots: { customRender: 'outGivingStatus' }
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '分发结果',
|
||||
dataIndex: 'outGivingResultMsg',
|
||||
ellipsis: true,
|
||||
width: 120,
|
||||
scopedSlots: { customRender: 'outGivingResultMsg' }
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '分发状态消息',
|
||||
dataIndex: 'outGivingResultMsgData',
|
||||
ellipsis: true,
|
||||
width: 120,
|
||||
scopedSlots: { customRender: 'outGivingResultMsgData' }
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '分发耗时',
|
||||
dataIndex: 'outGivingResultTime',
|
||||
width: '120px',
|
||||
scopedSlots: { customRender: 'outGivingResultTime' }
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '文件大小',
|
||||
dataIndex: 'outGivingResultSize',
|
||||
width: '100px',
|
||||
scopedSlots: { customRender: 'outGivingResultSize' }
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: '最后分发时间',
|
||||
dataIndex: 'lastTime',
|
||||
width: '170px',
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text)
|
||||
customRender: ({ text }) => parseTime(text)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'child-operation',
|
||||
fixed: 'right',
|
||||
scopedSlots: { customRender: 'child-operation' },
|
||||
|
||||
width: '140px',
|
||||
align: 'center'
|
||||
}
|
||||
],
|
||||
|
||||
countdownTime: Date.now(),
|
||||
refreshInterval: 5
|
||||
refreshInterval: 5,
|
||||
temp: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {},
|
||||
watch: {},
|
||||
created() {
|
||||
@ -337,6 +417,13 @@ export default {
|
||||
},
|
||||
// 静默
|
||||
silenceLoadData() {
|
||||
if (this.tabKey !== '1') {
|
||||
// 避免配置页面数据被刷新
|
||||
// 重新计算倒计时
|
||||
this.countdownTime = Date.now() + this.refreshInterval * 1000
|
||||
return
|
||||
}
|
||||
this.childLoading = true
|
||||
this.handleReloadById().then(() => {
|
||||
// 重新计算倒计时
|
||||
this.countdownTime = Date.now() + this.refreshInterval * 1000
|
||||
@ -350,7 +437,10 @@ export default {
|
||||
if (res.code === 200 && res.data) {
|
||||
let projectList =
|
||||
res.data?.projectList?.map((item) => {
|
||||
return { ...item, id_no: `${item.id}-${item.nodeId}-${item.projectId}-${new Date().getTime()}` }
|
||||
return {
|
||||
...item,
|
||||
id_no: `${item.id}-${item.nodeId}-${item.projectId}-${new Date().getTime()}`
|
||||
}
|
||||
}) || []
|
||||
this.data = res.data?.data || {}
|
||||
let oldProjectList = this.list
|
||||
@ -363,13 +453,14 @@ export default {
|
||||
const nodeProjects = itemGroupBy(projectList, 'nodeId')
|
||||
this.getRuningProjectInfo(nodeProjects)
|
||||
}
|
||||
this.childLoading = false
|
||||
resolve()
|
||||
})
|
||||
.catch(() => {
|
||||
resolve()
|
||||
})
|
||||
.finally(() => {
|
||||
// 取消加载中
|
||||
this.childLoading = false
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
@ -419,7 +510,12 @@ export default {
|
||||
} else {
|
||||
this.list = this.list.map((element) => {
|
||||
if (element.nodeId === data.type) {
|
||||
return { ...element, projectStatus: false, projectPid: '-', errorMsg: res2.msg }
|
||||
return {
|
||||
...element,
|
||||
projectStatus: false,
|
||||
projectPid: '-',
|
||||
errorMsg: res2.msg
|
||||
}
|
||||
}
|
||||
return element
|
||||
})
|
||||
@ -430,7 +526,12 @@ export default {
|
||||
.catch(() => {
|
||||
this.list = this.list.map((element) => {
|
||||
if (element.nodeId === data.type) {
|
||||
return { ...element, projectStatus: false, projectPid: '-', errorMsg: '网络异常' }
|
||||
return {
|
||||
...element,
|
||||
projectStatus: false,
|
||||
projectPid: '-',
|
||||
errorMsg: '网络异常'
|
||||
}
|
||||
}
|
||||
return element
|
||||
})
|
||||
@ -496,25 +597,86 @@ export default {
|
||||
}
|
||||
})
|
||||
window.open(newpage.href, '_blank')
|
||||
},
|
||||
// 删除项目
|
||||
handleRemoveProject(item) {
|
||||
const html =
|
||||
"<b style='font-size: 20px;'>真的要释放(删除)当前项目么?</b>" +
|
||||
"<ul style='font-size: 20px;color:red;font-weight: bold;'>" +
|
||||
'<li>不会真实请求节点删除项目信息</b></li>' +
|
||||
'<li>一般用于服务器无法连接且已经确定不再使用</li>' +
|
||||
'<li>如果误操作会产生冗余数据!!!</li>' +
|
||||
' </ul>'
|
||||
|
||||
this.$confirm({
|
||||
title: '危险操作!!!',
|
||||
zIndex: 1009,
|
||||
content: h('div', null, [h('p', { innerHTML: html }, null)]),
|
||||
okButtonProps: { type: 'primary', size: 'small', danger: true },
|
||||
cancelButtonProps: { type: 'primary' },
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
removeProject({
|
||||
nodeId: item.nodeId,
|
||||
projectId: item.projectId,
|
||||
id: this.id
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
//分发管理
|
||||
viewDispatchManagerOk() {
|
||||
const temp = {
|
||||
data: this.list.map((item, index) => {
|
||||
return {
|
||||
nodeId: item.nodeId,
|
||||
projectId: item.projectId,
|
||||
sortValue: index,
|
||||
disabled: item.disabled
|
||||
}
|
||||
}),
|
||||
id: this.id
|
||||
}
|
||||
saveDispatchProjectConfig(temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['close']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .ant-progress-text {
|
||||
width: auto;
|
||||
}
|
||||
/* .replica-btn-del {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 74px;
|
||||
} */
|
||||
/deep/ .ant-statistic div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/deep/ .ant-statistic-content-value,
|
||||
/deep/ .ant-statistic-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
.box-shadow {
|
||||
box-shadow: 0 0 10px 5px rgba(223, 222, 222, 0.5);
|
||||
border-radius: 5px;
|
||||
}
|
||||
.item-row {
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
|
@ -52,7 +52,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
temp: {},
|
||||
submitAble: false
|
||||
submitAble: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -61,9 +61,11 @@ export default {
|
||||
methods: {
|
||||
// load data
|
||||
loadData() {
|
||||
this.loading = true
|
||||
getDispatchWhiteList({ workspaceId: this.workspaceId }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.temp = res.data
|
||||
this.submitAble = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<template v-if="this.useSuggestions">
|
||||
<a-result
|
||||
title="当前工作空间还没有 Docker"
|
||||
@ -23,6 +23,9 @@
|
||||
bordered
|
||||
rowKey="id"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
@ -41,59 +44,68 @@
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
<template v-slot:tooltip="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-slot:status="text, record">
|
||||
<template v-if="record.machineDocker">
|
||||
<a-tag color="green" v-if="record.machineDocker.status === 1">正常</a-tag>
|
||||
<a-tooltip v-else :title="record.machineDocker.failureMsg">
|
||||
<a-tag color="red">无法连接</a-tag>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-tooltip v-else title="集群关联的 docker 信息丢失,不能继续使用管理功能">
|
||||
<a-tag color="red">信息丢失</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:tags="tags">
|
||||
<a-tooltip
|
||||
:title="
|
||||
(tags || '')
|
||||
.split(':')
|
||||
.filter((item) => item)
|
||||
.join(',')
|
||||
"
|
||||
>
|
||||
<a-tag v-for="item in (tags || '').split(':').filter((item) => item)" :key="item"> {{ item }}</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:operation="text, record">
|
||||
<a-space>
|
||||
<a-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:disabled="!record.machineDocker || record.machineDocker.status !== 1"
|
||||
@click="handleConsole(record)"
|
||||
>控制台</a-button
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('status')">
|
||||
<template v-if="record.machineDocker">
|
||||
<a-tag color="green" v-if="record.machineDocker.status === 1">正常</a-tag>
|
||||
<a-tooltip v-else :title="record.machineDocker.failureMsg">
|
||||
<a-tag color="red">无法连接</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-tooltip v-else title="集群关联的 docker 信息丢失,不能继续使用管理功能">
|
||||
<a-tag color="red">信息丢失</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'tags'">
|
||||
<a-tooltip
|
||||
:title="
|
||||
(text || '')
|
||||
.split(':')
|
||||
.filter((item) => item)
|
||||
.join(',')
|
||||
"
|
||||
>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<a-tag v-for="item in (text || '').split(':').filter((item) => item)" :key="item"> {{ item }}</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:disabled="!record.machineDocker || record.machineDocker.status !== 1"
|
||||
@click="handleConsole(record)"
|
||||
>控制台</a-button
|
||||
>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑区 -->
|
||||
<a-modal destroyOnClose v-model:value="editVisible" title="编辑 Docker" @ok="handleEditOk" :maskClosable="false">
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editVisible"
|
||||
title="编辑 Docker"
|
||||
@ok="handleEditOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="容器名称" name="name">
|
||||
<a-input v-model:value="temp.name" placeholder="容器名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="标签" name="tagInput" help="标签用于容器构建选择容器功能(fromTag)">
|
||||
<template>
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<div>
|
||||
<a-tooltip :key="index" :title="tag" v-for="(tag, index) in temp.tagsArray">
|
||||
<a-tag
|
||||
@ -109,32 +121,31 @@
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-input
|
||||
v-if="temp.inputVisible"
|
||||
ref="tagInput"
|
||||
type="text"
|
||||
size="small"
|
||||
placeholder="请输入标签名 字母数字 长度 1-10"
|
||||
v-model:value="temp.tagInput"
|
||||
@blur="handleInputConfirm"
|
||||
@keyup.enter="handleInputConfirm"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-tag
|
||||
v-if="!temp.tagsArray || temp.tagsArray.length < 10"
|
||||
style="background: #fff; borderstyle: dashed"
|
||||
@click="showInput"
|
||||
>
|
||||
<a-icon type="plus" /> 添加
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<a-input
|
||||
v-if="temp.inputVisible"
|
||||
ref="tagInput"
|
||||
type="text"
|
||||
size="small"
|
||||
placeholder="请输入标签名 字母数字 长度 1-10"
|
||||
v-model:value="temp.tagInput"
|
||||
@blur="handleInputConfirm"
|
||||
@keyup.enter="handleInputConfirm"
|
||||
/>
|
||||
<template v-else>
|
||||
<a-tag
|
||||
v-if="!temp.tagsArray || temp.tagsArray.length < 10"
|
||||
style="background: #fff; borderstyle: dashed"
|
||||
@click="showInput"
|
||||
>
|
||||
<PlusOutlined /> 添加
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 控制台 -->
|
||||
<!-- <a-drawer destroyOnClose :title="`${temp.name} 控制台`" placement="right" :width="`${this.getCollapsed ? 'calc(100vw - 80px)' : 'calc(100vw - 200px)'}`" :visible="consoleVisible" @close="onClose"> -->
|
||||
<console
|
||||
v-if="consoleVisible"
|
||||
:visible="consoleVisible"
|
||||
@ -146,7 +157,8 @@
|
||||
<!-- 同步到其他工作空间 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:value="syncToWorkspaceVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="syncToWorkspaceVisible"
|
||||
title="同步到其他工作空间"
|
||||
@ok="handleSyncToWorkspace"
|
||||
:maskClosable="false"
|
||||
@ -182,7 +194,7 @@
|
||||
<script>
|
||||
import { deleteDcoker, dockerList, editDocker, syncToWorkspace } from '@/api/docker-api'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from '@/utils/const'
|
||||
import { useGuideStore } from '@/stores/guide'
|
||||
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import Console from './console'
|
||||
@ -216,46 +228,43 @@ export default {
|
||||
},
|
||||
{
|
||||
title: 'host',
|
||||
dataIndex: 'machineDocker.host',
|
||||
dataIndex: ['machineDocker', 'host'],
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'machineDocker.status',
|
||||
dataIndex: ['machineDocker', 'status'],
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: '100px',
|
||||
scopedSlots: { customRender: 'status' }
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: 'docker版本',
|
||||
dataIndex: 'machineDocker.dockerVersion',
|
||||
dataIndex: ['machineDocker', 'dockerVersion'],
|
||||
ellipsis: true,
|
||||
width: '120px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '标签',
|
||||
dataIndex: 'tags',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tags' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '最后修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
width: '120px',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
@ -263,13 +272,13 @@ export default {
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
width: '190px'
|
||||
@ -291,11 +300,11 @@ export default {
|
||||
},
|
||||
workspaceList: [],
|
||||
tableSelections: [],
|
||||
syncToWorkspaceVisible: false
|
||||
syncToWorkspaceVisible: false,
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(useGuideStore, ['getCollapsed']),
|
||||
...mapState(useUserStore, ['getUserInfo']),
|
||||
...mapState(useAppStore, ['getWorkspaceId']),
|
||||
pagination() {
|
||||
@ -398,27 +407,28 @@ export default {
|
||||
// 提交 数据
|
||||
handleEditOk() {
|
||||
// 检验表单
|
||||
this.$refs['editForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editForm'].validate().then(() => {
|
||||
const temp = Object.assign({}, this.temp)
|
||||
|
||||
temp.tags = (temp.tagsArray || []).join(',')
|
||||
delete temp.tagsArray
|
||||
delete temp.inputVisible
|
||||
delete temp.tagInput
|
||||
|
||||
editDocker(temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.confirmLoading = true
|
||||
editDocker(temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
@ -459,14 +469,7 @@ export default {
|
||||
})
|
||||
},
|
||||
handleInputConfirm() {
|
||||
this.$refs['editForm'].validateField('tagInput', (errmsg) => {
|
||||
if (errmsg) {
|
||||
// console.log(err);
|
||||
this.$notification.warn({
|
||||
message: errmsg
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.$refs['editForm'].validateFields('tagInput').then(() => {
|
||||
const inputValue = this.temp.tagInput
|
||||
let tags = this.temp.tagsArray || []
|
||||
if (inputValue && tags.indexOf(inputValue) === -1) {
|
||||
@ -480,6 +483,15 @@ export default {
|
||||
inputVisible: false
|
||||
}
|
||||
})
|
||||
// .catch((error) => {
|
||||
// console.log(error)
|
||||
// if (errmsgs) {
|
||||
// this.$notification.warn({
|
||||
// message: errmsgs
|
||||
// })
|
||||
// return false
|
||||
// }
|
||||
// })
|
||||
},
|
||||
|
||||
// 加载工作空间数据
|
||||
@ -507,19 +519,24 @@ export default {
|
||||
return false
|
||||
}
|
||||
// 同步
|
||||
this.confirmLoading = true
|
||||
syncToWorkspace({
|
||||
ids: this.tableSelections.join(','),
|
||||
toWorkspaceId: this.temp.workspaceId
|
||||
}).then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,9 @@
|
||||
@change="changePage"
|
||||
:pagination="pagination"
|
||||
bordered
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
@ -40,56 +43,60 @@
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template v-slot:tooltip="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:status="text, record">
|
||||
<template v-if="record.machineDocker">
|
||||
<a-tag color="green" v-if="record.machineDocker.status === 1">正常</a-tag>
|
||||
<a-tooltip v-else :title="record.machineDocker.failureMsg">
|
||||
<a-tag color="red">无法连接</a-tag>
|
||||
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('status')">
|
||||
<template v-if="record.machineDocker">
|
||||
<a-tag color="green" v-if="record.machineDocker.status === 1">正常</a-tag>
|
||||
<a-tooltip v-else :title="record.machineDocker.failureMsg">
|
||||
<a-tag color="red">无法连接</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-tooltip v-else title="集群关联的 docker 信息丢失,不能继续使用管理功能">
|
||||
<a-tag color="red">信息丢失</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-tooltip v-else title="集群关联的 docker 信息丢失,不能继续使用管理功能">
|
||||
<a-tag color="red">信息丢失</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<template v-if="record.machineDocker">
|
||||
<a-button
|
||||
size="small"
|
||||
:disabled="record.machineDocker.status !== 1"
|
||||
type="primary"
|
||||
@click="handleConsole(record, 'server')"
|
||||
>服务</a-button
|
||||
>
|
||||
<a-button
|
||||
size="small"
|
||||
:disabled="record.machineDocker.status !== 1"
|
||||
type="primary"
|
||||
@click="handleConsole(record, 'node')"
|
||||
>节点</a-button
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button size="small" :disabled="true" type="primary">服务</a-button>
|
||||
<a-button size="small" :disabled="true" type="primary">节点</a-button>
|
||||
</template>
|
||||
|
||||
<template v-slot:operation="text, record">
|
||||
<a-space>
|
||||
<template v-if="record.machineDocker">
|
||||
<a-button
|
||||
size="small"
|
||||
:disabled="record.machineDocker.status !== 1"
|
||||
type="primary"
|
||||
@click="handleConsole(record, 'server')"
|
||||
>服务</a-button
|
||||
>
|
||||
<a-button
|
||||
size="small"
|
||||
:disabled="record.machineDocker.status !== 1"
|
||||
type="primary"
|
||||
@click="handleConsole(record, 'node')"
|
||||
>节点</a-button
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button size="small" :disabled="true" type="primary">服务</a-button>
|
||||
<a-button size="small" :disabled="true" type="primary">节点</a-button>
|
||||
</template>
|
||||
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 创建集群区 -->
|
||||
<!-- 编辑集群区 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:value="editVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editVisible"
|
||||
title="编辑 Docker 集群"
|
||||
@ok="handleEditOk"
|
||||
:maskClosable="false"
|
||||
@ -106,26 +113,31 @@
|
||||
</a-modal>
|
||||
|
||||
<!-- 控制台 -->
|
||||
<a-drawer
|
||||
<!-- <a-drawer
|
||||
destroyOnClose
|
||||
:title="`${temp.name} 控制台`"
|
||||
placement="right"
|
||||
:width="`${this.getCollapsed ? 'calc(100vw - 80px)' : 'calc(100vw - 200px)'}`"
|
||||
:visible="consoleVisible"
|
||||
:open="consoleVisible"
|
||||
@close="
|
||||
() => {
|
||||
this.consoleVisible = false
|
||||
}
|
||||
"
|
||||
>
|
||||
<console
|
||||
v-if="consoleVisible"
|
||||
:id="temp.id"
|
||||
:visible="consoleVisible"
|
||||
:initMenu="temp.menuKey"
|
||||
urlPrefix=""
|
||||
></console>
|
||||
</a-drawer>
|
||||
> -->
|
||||
<console
|
||||
v-if="consoleVisible"
|
||||
:id="temp.id"
|
||||
:visible="consoleVisible"
|
||||
:initMenu="temp.menuKey"
|
||||
urlPrefix=""
|
||||
@close="
|
||||
() => {
|
||||
this.consoleVisible = false
|
||||
}
|
||||
"
|
||||
></console>
|
||||
<!-- </a-drawer> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -154,7 +166,7 @@ export default {
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
|
||||
{
|
||||
@ -162,57 +174,55 @@ export default {
|
||||
dataIndex: 'swarmId',
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '容器标签',
|
||||
dataIndex: 'tag',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
dataIndex: ['machineDocker', 'status'],
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: '100px',
|
||||
scopedSlots: { customRender: 'status' }
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: '最后修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '集群创建时间',
|
||||
dataIndex: 'machineDocker.swarmCreatedAt',
|
||||
dataIndex: ['machineDocker', 'swarmCreatedAt'],
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '集群修改时间',
|
||||
dataIndex: 'machineDocker.swarmUpdatedAt',
|
||||
dataIndex: ['machineDocker', 'swarmUpdatedAt'],
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
width: '220px'
|
||||
}
|
||||
@ -225,7 +235,8 @@ export default {
|
||||
{ required: true, message: '请填写关联容器标签', trigger: 'blur' },
|
||||
{ pattern: /^\w{1,10}$/, message: '标签限制为字母数字且长度 1-10' }
|
||||
]
|
||||
}
|
||||
},
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -287,55 +298,26 @@ export default {
|
||||
// 提交 数据
|
||||
handleEditOk() {
|
||||
// 检验表单
|
||||
this.$refs['editForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editForm'].validate().then(() => {
|
||||
this.confirmLoading = true
|
||||
|
||||
editDockerSwarm(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
editDockerSwarm(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// // 解绑
|
||||
// handleUnbind(record) {
|
||||
// const html =
|
||||
// "<b style='font-size: 20px;'>真的要解绑该集群么?</b>" +
|
||||
// "<ul style='font-size: 20px;color:red;font-weight: bold;'>" +
|
||||
// "<li>解绑只删除在本系统的关联数据,不会删除容器里面数据</b></li>" +
|
||||
// " </ul>";
|
||||
|
||||
// //
|
||||
// this.$confirm({
|
||||
// title: "危险操作!!!",
|
||||
// content: h("div", null, [h("p", { domProps: { innerHTML: html } }, null)]),
|
||||
// okButtonProps: { props: { type: "danger", size: "small" } },
|
||||
// cancelButtonProps: { props: { type: "primary" } },
|
||||
// okText: "确认",
|
||||
// cancelText: "取消",
|
||||
// onOk: () => {
|
||||
// // 组装参数
|
||||
// const params = {
|
||||
// id: record.id,
|
||||
// };
|
||||
// unbindSwarm(params).then((res) => {
|
||||
// if (res.code === 200) {
|
||||
// this.$notification.success({
|
||||
// message: res.msg,
|
||||
// });
|
||||
// this.loadData();
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
this.$confirm({
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
@ -16,29 +16,32 @@
|
||||
bordered
|
||||
rowKey="id"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="文件名称"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model="listQuery['%aliasCode%']"
|
||||
v-model:value="listQuery['%aliasCode%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="别名码"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model="listQuery['extName']"
|
||||
v-model:value="listQuery['extName']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="后缀,精准搜索"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model="listQuery['id']"
|
||||
v-model:value="listQuery['id']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="文件id,精准搜索"
|
||||
class="search-input-item"
|
||||
@ -49,7 +52,8 @@
|
||||
<a-button type="primary" @click="handleUpload">上传文件</a-button>
|
||||
<a-button type="primary" @click="handleRemoteDownload">远程下载</a-button>
|
||||
<a-button
|
||||
type="danger"
|
||||
type="primary"
|
||||
danger
|
||||
:disabled="!tableSelections || tableSelections.length <= 0"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
@ -57,62 +61,75 @@
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #tooltip slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip #id slot-scope="text, item" placement="topLeft" :title="text">
|
||||
<span v-if="item.status === 0 || item.status === 2">-</span>
|
||||
<span v-else>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-popover #name slot-scope="text, item" title="文件信息">
|
||||
<template #content>
|
||||
<p>文件名:{{ text }}</p>
|
||||
<p>文件描述:{{ item.description }}</p>
|
||||
<p v-if="item.status !== undefined">下载状态:{{ statusMap[item.status] || '未知' }}</p>
|
||||
<p v-if="item.progressDesc">状态描述:{{ item.progressDesc }}</p>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ (text || '').slice(0, 8) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'id'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span v-if="record.status === 0 || record.status === 2">-</span>
|
||||
<span v-else>{{ (text || '').slice(0, 8) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'name'">
|
||||
<a-popover title="文件信息">
|
||||
<template v-slot:content>
|
||||
<p>文件名:{{ text }}</p>
|
||||
<p>文件描述:{{ record.description }}</p>
|
||||
<p v-if="record.status !== undefined">下载状态:{{ statusMap[record.status] || '未知' }}</p>
|
||||
<p v-if="record.progressDesc">状态描述:{{ record.progressDesc }}</p>
|
||||
</template>
|
||||
<!-- {{ text }} -->
|
||||
<a-button type="link" style="padding: 0" @click="handleEdit(record)" size="small">{{ text }}</a-button>
|
||||
</a-popover>
|
||||
</template>
|
||||
<!-- {{ text }} -->
|
||||
<a-button type="link" style="padding: 0" @click="handleEdit(item)" size="small">{{ text }}</a-button>
|
||||
</a-popover>
|
||||
|
||||
<a-tooltip #renderSize slot-scope="text" placement="topLeft" :title="renderSize(text)">
|
||||
<span>{{ renderSize(text) }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip #source slot-scope="text" placement="topLeft" :title="`${sourceMap[text] || '未知'}`">
|
||||
<span>{{ sourceMap[text] || '未知' }}</span>
|
||||
</a-tooltip>
|
||||
<template v-else-if="column.dataIndex === 'size'">
|
||||
<a-tooltip placement="topLeft" :title="renderSize(text)">
|
||||
<span>{{ renderSize(text) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'source'">
|
||||
<a-tooltip placement="topLeft" :title="`${sourceMap[text] || '未知'}`">
|
||||
<span>{{ sourceMap[text] || '未知' }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template #exists slot-scope="text">
|
||||
<a-tag v-if="text" color="green">存在</a-tag>
|
||||
<a-tag v-else color="red">丢失</a-tag>
|
||||
</template>
|
||||
<template #global slot-scope="text">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<!-- <a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button> -->
|
||||
<a-button size="small" :disabled="!record.exists" type="primary" @click="handleDownloadUrl(record)"
|
||||
>下载</a-button
|
||||
>
|
||||
<a-button size="small" :disabled="!record.exists" type="primary" @click="handleReleaseFile(record)"
|
||||
>发布</a-button
|
||||
>
|
||||
<a-button type="danger" size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<template v-else-if="column.dataIndex === 'exists'">
|
||||
<a-tag v-if="text" color="green">存在</a-tag>
|
||||
<a-tag v-else color="red">丢失</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'workspaceId'">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<!-- <a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button> -->
|
||||
<a-button size="small" :disabled="!record.exists" type="primary" @click="handleDownloadUrl(record)"
|
||||
>下载</a-button
|
||||
>
|
||||
<a-button size="small" :disabled="!record.exists" type="primary" @click="handleReleaseFile(record)"
|
||||
>发布</a-button
|
||||
>
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 上传文件 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="uploadVisible"
|
||||
v-model:open="uploadVisible"
|
||||
:closable="!uploading"
|
||||
:footer="uploading ? null : undefined"
|
||||
:keyboard="false"
|
||||
:title="`上传文件`"
|
||||
@ok="handleUploadOk"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
>
|
||||
<a-form ref="form" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="选择文件" name="file">
|
||||
@ -127,9 +144,10 @@
|
||||
<a-upload
|
||||
:file-list="fileList"
|
||||
:disabled="!!percentage"
|
||||
:remove="
|
||||
@remove="
|
||||
(file) => {
|
||||
this.fileList = []
|
||||
return true
|
||||
}
|
||||
"
|
||||
:before-upload="
|
||||
@ -140,20 +158,20 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<a-icon type="loading" v-if="percentage" />
|
||||
<a-button v-else type="primary" icon="upload">选择文件</a-button>
|
||||
<LoadingOutlined v-if="percentage" />
|
||||
<a-button v-else type="primary"><UploadOutlined />选择文件</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
<a-form-item label="保留天数" name="keepDay">
|
||||
<a-input-number
|
||||
v-model="temp.keepDay"
|
||||
v-model:value="temp.keepDay"
|
||||
:min="1"
|
||||
style="width: 100%"
|
||||
placeholder="文件保存天数,默认 3650 天"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件共享" name="global">
|
||||
<a-radio-group v-model="temp.global">
|
||||
<a-radio-group v-model:value="temp.global">
|
||||
<a-radio :value="true"> 全局 </a-radio>
|
||||
<a-radio :value="false"> 当前工作空间 </a-radio>
|
||||
</a-radio-group>
|
||||
@ -161,7 +179,7 @@
|
||||
<a-form-item label="别名码" name="aliasCode" help="用于区别文件是否为同一类型,可以针对同类型进行下载管理">
|
||||
<a-input-search
|
||||
:maxLength="50"
|
||||
v-model="temp.aliasCode"
|
||||
v-model:value="temp.aliasCode"
|
||||
placeholder="请输入别名码"
|
||||
@search="
|
||||
() => {
|
||||
@ -169,38 +187,39 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #enterButton>
|
||||
<template v-slot:enterButton>
|
||||
<a-button type="primary"> 随机生成 </a-button>
|
||||
</template>
|
||||
</a-input-search>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件描述" name="description">
|
||||
<a-textarea v-model="temp.description" placeholder="请输入文件描述" />
|
||||
<a-textarea v-model:value="temp.description" placeholder="请输入文件描述" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 编辑文件 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:visible="editVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editVisible"
|
||||
:title="`修改文件`"
|
||||
@ok="handleEditOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="文件名" name="name">
|
||||
<a-input placeholder="文件名" v-model="temp.name" />
|
||||
<a-input placeholder="文件名" v-model:value="temp.name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="保留天数" name="keepDay">
|
||||
<a-input-number
|
||||
v-model="temp.keepDay"
|
||||
v-model:value="temp.keepDay"
|
||||
:min="1"
|
||||
style="width: 100%"
|
||||
placeholder="文件保存天数,默认 3650 天"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件共享" name="global">
|
||||
<a-radio-group v-model="temp.global">
|
||||
<a-radio-group v-model:value="temp.global">
|
||||
<a-radio :value="true"> 全局 </a-radio>
|
||||
<a-radio :value="false"> 当前工作空间 </a-radio>
|
||||
</a-radio-group>
|
||||
@ -208,7 +227,7 @@
|
||||
<a-form-item label="别名码" name="aliasCode" help="用于区别文件是否为同一类型,可以针对同类型进行下载管理">
|
||||
<a-input-search
|
||||
:maxLength="50"
|
||||
v-model="temp.aliasCode"
|
||||
v-model:value="temp.aliasCode"
|
||||
placeholder="请输入别名码"
|
||||
@search="
|
||||
() => {
|
||||
@ -216,38 +235,39 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #enterButton>
|
||||
<template v-slot:enterButton>
|
||||
<a-button type="primary"> 随机生成 </a-button>
|
||||
</template>
|
||||
</a-input-search>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件描述" name="description">
|
||||
<a-textarea v-model="temp.description" placeholder="请输入文件描述" />
|
||||
<a-textarea v-model:value="temp.description" placeholder="请输入文件描述" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!--远程下载 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="uploadRemoteFileVisible"
|
||||
v-model:open="uploadRemoteFileVisible"
|
||||
title="远程下载文件"
|
||||
@ok="handleRemoteUpload"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
>
|
||||
<a-form :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" :rules="rules" ref="remoteForm">
|
||||
<a-form-item label="远程下载URL" name="url">
|
||||
<a-input v-model="temp.url" placeholder="远程下载地址" />
|
||||
<a-input v-model:value="temp.url" placeholder="远程下载地址" />
|
||||
</a-form-item>
|
||||
<a-form-item label="保留天数" name="keepDay">
|
||||
<a-input-number
|
||||
v-model="temp.keepDay"
|
||||
v-model:value="temp.keepDay"
|
||||
:min="1"
|
||||
style="width: 100%"
|
||||
placeholder="文件保存天数,默认 3650 天"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件共享" name="global">
|
||||
<a-radio-group v-model="temp.global">
|
||||
<a-radio-group v-model:value="temp.global">
|
||||
<a-radio :value="true"> 全局 </a-radio>
|
||||
<a-radio :value="false"> 当前工作空间 </a-radio>
|
||||
</a-radio-group>
|
||||
@ -255,7 +275,7 @@
|
||||
<a-form-item label="别名码" name="aliasCode" help="用于区别文件是否为同一类型,可以针对同类型进行下载管理">
|
||||
<a-input-search
|
||||
:maxLength="50"
|
||||
v-model="temp.aliasCode"
|
||||
v-model:value="temp.aliasCode"
|
||||
placeholder="请输入别名码"
|
||||
@search="
|
||||
() => {
|
||||
@ -263,20 +283,20 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #enterButton>
|
||||
<template v-slot:enterButton>
|
||||
<a-button type="primary"> 随机生成 </a-button>
|
||||
</template>
|
||||
</a-input-search>
|
||||
</a-form-item>
|
||||
<a-form-item label="文件描述" name="description">
|
||||
<a-textarea v-model="temp.description" placeholder="请输入文件描述" />
|
||||
<a-textarea v-model:value="temp.description" placeholder="请输入文件描述" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 断点下载 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="triggerVisible"
|
||||
v-model:open="triggerVisible"
|
||||
title="断点/分片下载"
|
||||
width="50%"
|
||||
:footer="null"
|
||||
@ -284,42 +304,30 @@
|
||||
>
|
||||
<a-form ref="editTriggerForm" :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<a-tabs default-active-key="1">
|
||||
<template #tabBarExtraContent>
|
||||
<template v-slot:rightExtra>
|
||||
<a-tooltip title="重置下载 token 信息,重置后之前的下载 token 将失效">
|
||||
<a-button type="primary" size="small" @click="resetTrigger">重置</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tab-pane key="1" tab="断点/分片单文件下载">
|
||||
<a-space style="display: block" direction="vertical" align="baseline">
|
||||
<a-alert
|
||||
v-clipboard:copy="`${temp.triggerDownloadUrl}`"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
tempVue.prototype.$notification.success({ message: '复制成功' })
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
tempVue.prototype.$notification.error({ message: '复制失败' })
|
||||
}
|
||||
"
|
||||
type="info"
|
||||
:message="`下载地址(点击可以复制)`"
|
||||
>
|
||||
<template #description>
|
||||
<a-tag>GET</a-tag> <span>{{ `${temp.triggerDownloadUrl}` }} </span>
|
||||
<a-icon type="copy" />
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert type="info" :message="`下载地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-typography-paragraph :copyable="{ tooltip: false, text: temp.triggerDownloadUrl }">
|
||||
<a-tag>GET</a-tag>
|
||||
<span>{{ `${temp.triggerDownloadUrl}` }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a :href="temp.triggerDownloadUrl" target="_blank">
|
||||
<a-button size="small" icon="download" type="primary">立即下载</a-button>
|
||||
<a-button size="small" type="primary"><DownloadOutlined />立即下载</a-button>
|
||||
</a>
|
||||
</a-space>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="断点/分片别名下载" v-if="temp.triggerAliasDownloadUrl">
|
||||
<a-space style="display: block" direction="vertical" align="baseline">
|
||||
<a-tab-pane tab="断点/分片别名下载" v-if="temp.triggerAliasDownloadUrl">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<template #description>
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
<li>
|
||||
支持自定义排序字段:sort=createTimeMillis:desc
|
||||
@ -331,28 +339,16 @@
|
||||
</ul>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert
|
||||
v-clipboard:copy="`${temp.triggerAliasDownloadUrl}`"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
tempVue.prototype.$notification.success({ message: '复制成功' })
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
tempVue.prototype.$notification.error({ message: '复制失败' })
|
||||
}
|
||||
"
|
||||
type="info"
|
||||
:message="`下载地址(点击可以复制)`"
|
||||
>
|
||||
<template #description>
|
||||
<a-tag>GET</a-tag> <span>{{ `${temp.triggerAliasDownloadUrl}` }} </span>
|
||||
<a-icon type="copy" />
|
||||
<a-alert type="info" :message="`下载地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-typography-paragraph :copyable="{ tooltip: false, text: temp.triggerAliasDownloadUrl }">
|
||||
<a-tag>GET</a-tag>
|
||||
<span>{{ `${temp.triggerAliasDownloadUrl}` }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a :href="temp.triggerAliasDownloadUrl" target="_blank">
|
||||
<a-button size="small" icon="download" type="primary">立即下载</a-button>
|
||||
<a-button size="small" type="primary"><DownloadOutlined />立即下载</a-button>
|
||||
</a>
|
||||
</a-space>
|
||||
</a-tab-pane>
|
||||
@ -362,19 +358,45 @@
|
||||
<!-- 发布文件 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="releaseFileVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="releaseFileVisible"
|
||||
title="发布文件"
|
||||
width="50%"
|
||||
:maskClosable="false"
|
||||
@ok="
|
||||
() => {
|
||||
this.$refs.releaseFile?.tryCommit()
|
||||
}
|
||||
"
|
||||
@ok="releaseFileOk()"
|
||||
>
|
||||
<releaseFile ref="releaseFile" v-if="releaseFileVisible" @commit="handleCommitTask"></releaseFile>
|
||||
</a-modal>
|
||||
</div>
|
||||
<!-- 选择确认区域 -->
|
||||
<div style="padding-top: 50px" v-if="this.choose">
|
||||
<div
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e9e9e9',
|
||||
padding: '10px 16px',
|
||||
background: '#fff',
|
||||
textAlign: 'right',
|
||||
zIndex: 1
|
||||
}"
|
||||
>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.$emit('cancel')
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handerConfirm"> 确定 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -401,7 +423,7 @@ import {
|
||||
triggerUrl
|
||||
} from '@/api/file-manager/file-storage'
|
||||
import { uploadPieces } from '@/utils/upload-pieces'
|
||||
// import Vue from 'vue'
|
||||
import * as Vue from 'vue'
|
||||
import releaseFile from './releaseFile.vue'
|
||||
import { addReleaseTask } from '@/api/file-manager/release-task-log'
|
||||
|
||||
@ -409,49 +431,72 @@ export default {
|
||||
components: {
|
||||
releaseFile
|
||||
},
|
||||
props: {
|
||||
choose: {
|
||||
// "radio"
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
|
||||
list: [],
|
||||
columns: [
|
||||
{ title: '文件MD5', dataIndex: 'id', ellipsis: true, width: 100, scopedSlots: { customRender: 'id' } },
|
||||
{ title: '名称', dataIndex: 'name', ellipsis: true, width: 150, scopedSlots: { customRender: 'name' } },
|
||||
{
|
||||
title: '文件MD5',
|
||||
dataIndex: 'id',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '别名码',
|
||||
dataIndex: 'aliasCode',
|
||||
ellipsis: true,
|
||||
width: 100,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '大小',
|
||||
dataIndex: 'size',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'renderSize' },
|
||||
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: '后缀',
|
||||
dataIndex: 'extName',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' },
|
||||
tooltip: true,
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '共享',
|
||||
dataIndex: 'workspaceId',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'global' },
|
||||
|
||||
width: '90px'
|
||||
},
|
||||
{ title: '来源', dataIndex: 'source', ellipsis: true, scopedSlots: { customRender: 'source' }, width: '80px' },
|
||||
{
|
||||
title: '来源',
|
||||
dataIndex: 'source',
|
||||
ellipsis: true,
|
||||
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '过期天数',
|
||||
dataIndex: 'validUntil',
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
if (!text) {
|
||||
return '-'
|
||||
}
|
||||
@ -463,35 +508,35 @@ export default {
|
||||
title: '文件状态',
|
||||
dataIndex: 'exists',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'exists' },
|
||||
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' },
|
||||
tooltip: true,
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' },
|
||||
tooltip: true,
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
@ -499,7 +544,7 @@ export default {
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
fixed: 'right',
|
||||
width: '170px'
|
||||
}
|
||||
@ -522,7 +567,8 @@ export default {
|
||||
tempVue: null,
|
||||
triggerVisible: false,
|
||||
releaseFileVisible: false,
|
||||
tableSelections: []
|
||||
tableSelections: [],
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -534,7 +580,8 @@ export default {
|
||||
onChange: (selectedRowKeys) => {
|
||||
this.tableSelections = selectedRowKeys
|
||||
},
|
||||
selectedRowKeys: this.tableSelections
|
||||
selectedRowKeys: this.tableSelections,
|
||||
type: this.choose || 'checkbox'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -569,14 +616,10 @@ export default {
|
||||
// 上传文件
|
||||
handleUploadOk() {
|
||||
// 检验表单
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.$refs['form'].validate().then(() => {
|
||||
// 判断文件
|
||||
if (this.fileList.length === 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择文件'
|
||||
})
|
||||
return false
|
||||
@ -584,6 +627,7 @@ export default {
|
||||
this.percentage = 0
|
||||
this.percentageInfo = {}
|
||||
this.uploading = true
|
||||
this.confirmLoading = true
|
||||
uploadPieces({
|
||||
file: this.fileList[0],
|
||||
uploadBeforeAbrot: (md5) => {
|
||||
@ -594,13 +638,14 @@ export default {
|
||||
if (res.code === 200) {
|
||||
if (res.data) {
|
||||
//
|
||||
$notification.warning({
|
||||
this.$notification.warning({
|
||||
message: `当前文件已经存在啦,文件名:${res.data.name} ,是否共享:${
|
||||
res.data.workspaceId === 'GLOBAL' ? '是' : '否'
|
||||
}`
|
||||
})
|
||||
//
|
||||
this.uploading = false
|
||||
this.confirmLoading = false
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
@ -619,23 +664,31 @@ export default {
|
||||
if (res.code === 200) {
|
||||
this.fileList = []
|
||||
this.loadData()
|
||||
this.uploadVisible = false
|
||||
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.percentage = 0
|
||||
this.percentageInfo = {}
|
||||
this.uploadVisible = false
|
||||
}, 2000)
|
||||
this.uploading = false
|
||||
})
|
||||
.catch(() => {
|
||||
this.uploading = false
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
error: (msg) => {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: msg
|
||||
})
|
||||
this.uploading = false
|
||||
this.confirmLoading = false
|
||||
},
|
||||
uploadCallback: (formData) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -660,33 +713,40 @@ export default {
|
||||
},
|
||||
// 编辑
|
||||
handleEdit(item) {
|
||||
this.temp = { ...item, global: item.workspaceId === 'GLOBAL', workspaceId: '' }
|
||||
this.temp = {
|
||||
...item,
|
||||
global: item.workspaceId === 'GLOBAL',
|
||||
workspaceId: ''
|
||||
}
|
||||
this.editVisible = true
|
||||
this.$refs['editForm']?.resetFields()
|
||||
},
|
||||
// 编辑确认
|
||||
handleEditOk() {
|
||||
this.$refs['editForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
fileEdit(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editForm'].validate().then(() => {
|
||||
this.confirmLoading = true
|
||||
fileEdit(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除文件
|
||||
handleDelete(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除当前文件么?' + record.name,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -696,7 +756,7 @@ export default {
|
||||
id: record.id
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
@ -709,13 +769,14 @@ export default {
|
||||
// 批量删除
|
||||
handleBatchDelete() {
|
||||
if (!this.tableSelections || this.tableSelections.length <= 0) {
|
||||
$notification.warning({
|
||||
this.$notification.warning({
|
||||
message: '没有选择任何数据'
|
||||
})
|
||||
return
|
||||
}
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除这些文件么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -723,7 +784,7 @@ export default {
|
||||
// 删除
|
||||
delFile({ ids: this.tableSelections.join(',') }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
@ -744,21 +805,23 @@ export default {
|
||||
// 开始远程下载
|
||||
handleRemoteUpload() {
|
||||
//
|
||||
this.$refs['remoteForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
remoteDownload(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['remoteForm'].validate().then(() => {
|
||||
this.confirmLoading = true
|
||||
remoteDownload(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.uploadRemoteFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.uploadRemoteFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 下载地址
|
||||
@ -770,7 +833,9 @@ export default {
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.fillDownloadUrlResult(res)
|
||||
this.triggerVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.triggerVisible = true
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -781,7 +846,7 @@ export default {
|
||||
rest: 'rest'
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.fillDownloadUrlResult(res)
|
||||
@ -804,21 +869,51 @@ export default {
|
||||
},
|
||||
|
||||
handleCommitTask(data) {
|
||||
addReleaseTask({ ...data, fileId: this.temp.fileId }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.confirmLoading = true
|
||||
addReleaseTask({ ...data, fileId: this.temp.fileId, fileType: 1 })
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.releaseFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
this.releaseFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
releaseFileOk() {
|
||||
this.$refs.releaseFile?.tryCommit()
|
||||
},
|
||||
// 选择确认
|
||||
handerConfirm() {
|
||||
if (!this.tableSelections.length) {
|
||||
this.$notification.warning({
|
||||
message: '请选择要使用的文件'
|
||||
})
|
||||
return
|
||||
}
|
||||
const selectData = this.list.filter((item) => {
|
||||
return this.tableSelections.indexOf(item.id) > -1
|
||||
})
|
||||
if (!selectData.length) {
|
||||
this.$notification.warning({
|
||||
message: '请选择要使用的文件'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$emit('confirm', selectData)
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['cancel', 'confirm']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .ant-progress-text {
|
||||
width: auto;
|
||||
|
@ -8,11 +8,11 @@
|
||||
:wrapper-col="{ span: 20 }"
|
||||
>
|
||||
<a-form-item label="任务名" name="name">
|
||||
<a-input placeholder="请输入任务名" :maxLength="50" v-model="temp.name" />
|
||||
<a-input placeholder="请输入任务名" :maxLength="50" v-model:value="temp.name" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="发布方式" name="taskType">
|
||||
<a-radio-group v-model="temp.taskType" @change="taskTypeChange">
|
||||
<a-radio-group v-model:value="temp.taskType" @change="taskTypeChange">
|
||||
<a-radio :value="0"> SSH </a-radio>
|
||||
<a-radio :value="1"> 节点 </a-radio>
|
||||
</a-radio-group>
|
||||
@ -25,7 +25,7 @@
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
mode="multiple"
|
||||
v-model="temp.taskDataIds"
|
||||
v-model:value="temp.taskDataIds"
|
||||
placeholder="请选择SSH"
|
||||
>
|
||||
<a-select-option v-for="ssh in sshList" :key="ssh.id">
|
||||
@ -34,7 +34,7 @@
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="1" style="margin-left: 10px">
|
||||
<a-icon type="reload" @click="loadSshList" />
|
||||
<ReloadOutlined @click="loadSshList" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
@ -45,7 +45,7 @@
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
mode="multiple"
|
||||
v-model="temp.taskDataIds"
|
||||
v-model:value="temp.taskDataIds"
|
||||
placeholder="请选择节点"
|
||||
>
|
||||
<a-select-option v-for="ssh in nodeList" :key="ssh.id">
|
||||
@ -54,16 +54,15 @@
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="1" style="margin-left: 10px">
|
||||
<a-icon type="reload" @click="loadNodeList" />
|
||||
<ReloadOutlined @click="loadNodeList" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="releasePathParent" label="发布目录">
|
||||
<template #help>
|
||||
<a-tooltip title="需要配置授权目录(白名单才能正常使用发布),授权目录主要是用于确定可以发布到哪些目录中"
|
||||
<template v-slot:help>
|
||||
<a-tooltip title="需要配置授权目录(授权才能正常使用发布),授权目录主要是用于确定可以发布到哪些目录中"
|
||||
><a-button
|
||||
icon="info-circle"
|
||||
size="small"
|
||||
type="link"
|
||||
@click="
|
||||
@ -72,55 +71,68 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
配置目录
|
||||
<InfoCircleOutlined />配置目录
|
||||
</a-button>
|
||||
</a-tooltip></template
|
||||
>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-group compact>
|
||||
<a-select
|
||||
show-search
|
||||
allowClear
|
||||
style="width: 30%"
|
||||
v-model="temp.releasePathParent"
|
||||
v-model:value="temp.releasePathParent"
|
||||
placeholder="请选择发布的一级目录"
|
||||
>
|
||||
<a-select-option v-for="item in accessList" :key="item">
|
||||
<a-tooltip :title="item">{{ item }}</a-tooltip>
|
||||
</a-select-option>
|
||||
<a-icon #suffixIcon type="reload" @click="loadAccesList" />
|
||||
<template v-slot:suffixIcon>
|
||||
<ReloadOutlined @click="loadAccesList" />
|
||||
</template>
|
||||
</a-select>
|
||||
|
||||
<a-input style="width: 70%" v-model="temp.releasePathSecondary" placeholder="请填写发布的二级目录" />
|
||||
<a-form-item-rest>
|
||||
<a-input style="width: 70%" v-model:value="temp.releasePathSecondary" placeholder="请填写发布的二级目录" />
|
||||
</a-form-item-rest>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="执行脚本" name="releaseBeforeCommand">
|
||||
<a-tabs tabPosition="right">
|
||||
<a-tab-pane key="before" tab="上传前">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model="temp.beforeScript"
|
||||
:options="{ mode: temp.taskType === 0 ? 'shell' : '', tabSize: 2, theme: 'abcdef' }"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传前需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="after" tab="上传后">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model="temp.afterScript"
|
||||
:options="{ mode: temp.taskType === 0 ? 'shell' : '', tabSize: 2, theme: 'abcdef' }"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传成功后需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-form-item-rest>
|
||||
<a-tabs tabPosition="right">
|
||||
<a-tab-pane key="before" tab="上传前">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model:content="temp.beforeScript"
|
||||
:options="{
|
||||
mode: temp.taskType === 0 ? 'shell' : '',
|
||||
tabSize: 2,
|
||||
theme: 'abcdef'
|
||||
}"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传前需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="after" tab="上传后">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model:content="temp.afterScript"
|
||||
:options="{
|
||||
mode: temp.taskType === 0 ? 'shell' : '',
|
||||
tabSize: 2,
|
||||
theme: 'abcdef'
|
||||
}"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传成功后需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="configDir"
|
||||
v-model:value="configDir"
|
||||
:title="`配置授权目录`"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
@ -142,6 +154,7 @@
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSshListAll } from '@/api/ssh'
|
||||
import { getDispatchWhiteList } from '@/api/dispatch'
|
||||
@ -159,7 +172,13 @@ export default {
|
||||
releaseFileRules: {
|
||||
name: [{ required: true, message: '请输入文件任务名', trigger: 'blur' }],
|
||||
taskType: [{ required: true, message: '请选择发布方式', trigger: 'blur' }],
|
||||
releasePath: [{ required: true, message: '请选择发布的一级目录和填写二级目录', trigger: 'blur' }],
|
||||
releasePath: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择发布的一级目录和填写二级目录',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
taskDataIds: [{ required: true, message: '请选择发布的SSH', trigger: 'blur' }]
|
||||
},
|
||||
sshList: [],
|
||||
@ -185,14 +204,14 @@ export default {
|
||||
},
|
||||
// 创建任务
|
||||
tryCommit() {
|
||||
this.$refs['releaseFileForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$emit('commit', { ...this.temp, taskDataIds: this.temp.taskDataIds?.join(',') })
|
||||
this.$refs['releaseFileForm'].validate().then(() => {
|
||||
this.$emit('commit', {
|
||||
...this.temp,
|
||||
taskDataIds: this.temp.taskDataIds?.join(',')
|
||||
})
|
||||
})
|
||||
},
|
||||
// 加载项目白名单列表
|
||||
// 加载项目授权列表
|
||||
loadAccesList() {
|
||||
getDispatchWhiteList().then((res) => {
|
||||
if (res.code === 200) {
|
||||
@ -220,6 +239,7 @@ export default {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['commit']
|
||||
}
|
||||
</script>
|
||||
|
@ -22,8 +22,8 @@
|
||||
<a-form-item label="执行日志">
|
||||
<a-tabs :activeKey="activeKey" @change="tabCallback">
|
||||
<a-tab-pane v-for="item in temp.taskList" :key="item.id">
|
||||
<template #tab>
|
||||
<a-icon v-if="!logMap[item.id] || logMap[item.id].run" type="loading" />
|
||||
<template v-slot:tab>
|
||||
<LoadingOutlined v-if="!logMap[item.id] || logMap[item.id].run" type="loading" />
|
||||
<template v-if="temp.taskData && temp.taskData.taskType === 0">
|
||||
{{
|
||||
sshList.filter((item2) => {
|
||||
@ -45,7 +45,7 @@
|
||||
}}
|
||||
</template>
|
||||
<template>
|
||||
<a-tooltip v-if="item.statusMsg" :title="item.statusMsg"><a-icon type="info-circle" /></a-tooltip>
|
||||
<a-tooltip v-if="item.statusMsg" :title="item.statusMsg"><InfoCircleOutlined /></a-tooltip>
|
||||
</template>
|
||||
</template>
|
||||
<log-view :ref="`logView-${item.id}`" height="60vh" />
|
||||
@ -57,7 +57,7 @@
|
||||
<a-tab-pane key="before" tab="上传前">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
:code="temp.taskData && temp.taskData.beforeScript"
|
||||
:content="temp.taskData && temp.taskData.beforeScript"
|
||||
:options="{
|
||||
mode: temp.taskData && temp.taskData.taskType === 0 ? 'shell' : '',
|
||||
tabSize: 2,
|
||||
@ -70,7 +70,7 @@
|
||||
<a-tab-pane key="after" tab="上传后">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
:code="temp.taskData && temp.taskData.afterScript"
|
||||
:content="temp.taskData && temp.taskData.afterScript"
|
||||
:options="{
|
||||
mode: temp.taskData && temp.taskData.taskType === 0 ? 'shell' : '',
|
||||
tabSize: 2,
|
||||
@ -85,9 +85,10 @@
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskDetails, statusMap, taskLogInfoList } from '@/api/file-manager/release-task-log'
|
||||
import LogView from '@/components/logView'
|
||||
import LogView from '@/components/logView/index2'
|
||||
import codeEditor from '@/components/codeEditor'
|
||||
import { getSshListAll } from '@/api/ssh'
|
||||
import { getNodeListAll } from '@/api/node'
|
||||
@ -114,7 +115,7 @@ export default {
|
||||
nodeList: []
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
if (this.logTimerMap) {
|
||||
this.temp.taskList?.forEach((item) => {
|
||||
clearInterval(this.logTimerMap[item.id])
|
||||
@ -190,7 +191,7 @@ export default {
|
||||
taskLogInfoList(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (!res.data) {
|
||||
$notification.warning({
|
||||
this.$notification.warning({
|
||||
message: res.msg
|
||||
})
|
||||
if (res.data.status !== 0) {
|
||||
@ -226,7 +227,7 @@ export default {
|
||||
if (this.logTimerMap[key]) {
|
||||
return
|
||||
}
|
||||
nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
const data = this.temp.taskList?.filter((item1) => {
|
||||
return item1.id === key
|
||||
})[0]
|
||||
@ -236,4 +237,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<a-table
|
||||
size="middle"
|
||||
:data-source="commandList"
|
||||
@ -13,11 +13,14 @@
|
||||
}
|
||||
"
|
||||
rowKey="id"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="任务名"
|
||||
class="search-input-item"
|
||||
@ -25,7 +28,7 @@
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model="listQuery.status"
|
||||
v-model:value="listQuery.status"
|
||||
allowClear
|
||||
placeholder="状态"
|
||||
class="search-input-item"
|
||||
@ -35,7 +38,7 @@
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model="listQuery.taskType"
|
||||
v-model:value="listQuery.taskType"
|
||||
allowClear
|
||||
placeholder="发布类型"
|
||||
class="search-input-item"
|
||||
@ -47,45 +50,61 @@
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #tooltip slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template #fileId slot-scope="text, item">
|
||||
<a-button type="link" style="padding: 0" @click="handleViewFile(item)" size="small">{{ text }}</a-button>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'fileId'">
|
||||
<a-tooltip :title="text">
|
||||
<a-button type="link" style="padding: 0" @click="handleViewFile(record)" size="small">{{
|
||||
(text || '').slice(0, 10)
|
||||
}}</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template #status slot-scope="text">
|
||||
<a-tag v-if="text === 2" color="green">{{ statusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 0 || text === 1" color="orange">{{ statusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 4" color="blue"> {{ statusMap[text] || '未知' }} </a-tag>
|
||||
<a-tag v-else-if="text === 3" color="red">{{ statusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else>{{ statusMap[text] || '未知' }}</a-tag>
|
||||
</template>
|
||||
<template #taskType slot-scope="text">
|
||||
<span>{{ taskTypeMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<a-tag v-if="text === 2" color="green">{{ statusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 0 || text === 1" color="orange">{{ statusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else-if="text === 4" color="blue">
|
||||
{{ statusMap[text] || '未知' }}
|
||||
</a-tag>
|
||||
<a-tag v-else-if="text === 3" color="red">{{ statusMap[text] || '未知' }}</a-tag>
|
||||
<a-tag v-else>{{ statusMap[text] || '未知' }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'taskType'">
|
||||
<span>{{ taskTypeMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'fileType'">
|
||||
<span v-if="text == 2">静态文件</span>
|
||||
<span v-else>文件中心</span>
|
||||
</template>
|
||||
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="handleView(record)">查看</a-button>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="handleView(record)">查看</a-button>
|
||||
|
||||
<a-button type="primary" size="small" @click="handleRetask(record)">重建</a-button>
|
||||
<a-button
|
||||
type="danger"
|
||||
size="small"
|
||||
:disabled="!(record.status === 0 || record.status === 1)"
|
||||
@click="handleCancelTask(record)"
|
||||
>取消</a-button
|
||||
>
|
||||
<a-button
|
||||
type="danger"
|
||||
size="small"
|
||||
:disabled="record.status === 0 || record.status === 1"
|
||||
@click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</a-space>
|
||||
<a-button type="primary" size="small" @click="handleRetask(record)">重建</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
size="small"
|
||||
:disabled="!(record.status === 0 || record.status === 1)"
|
||||
@click="handleCancelTask(record)"
|
||||
>取消</a-button
|
||||
>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
size="small"
|
||||
:disabled="record.status === 0 || record.status === 1"
|
||||
@click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 任务详情 -->
|
||||
@ -93,19 +112,20 @@
|
||||
title="任务详情"
|
||||
placement="right"
|
||||
:width="'80vw'"
|
||||
:visible="detailsVisible"
|
||||
:open="detailsVisible"
|
||||
@close="
|
||||
() => {
|
||||
this.detailsVisible = false
|
||||
}
|
||||
"
|
||||
>
|
||||
<task-details-page v-if="detailsVisible" :taskId="temp.id" />
|
||||
<task-details-page v-if="detailsVisible" :taskId="this.temp.id" />
|
||||
</a-drawer>
|
||||
<!-- 重建任务 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="releaseFileVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="releaseFileVisible"
|
||||
title="发布文件"
|
||||
width="50%"
|
||||
:maskClosable="false"
|
||||
@ -119,11 +139,11 @@
|
||||
:wrapper-col="{ span: 20 }"
|
||||
>
|
||||
<a-form-item label="任务名" name="name">
|
||||
<a-input placeholder="请输入任务名" :maxLength="50" v-model="temp.name" />
|
||||
<a-input placeholder="请输入任务名" :maxLength="50" v-model:value="temp.name" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="发布方式" name="taskType">
|
||||
<a-radio-group v-model="temp.taskType" :disabled="true">
|
||||
<a-radio-group v-model:value="temp.taskType" :disabled="true">
|
||||
<a-radio :value="0"> SSH </a-radio>
|
||||
<a-radio :value="1"> 节点 </a-radio>
|
||||
</a-radio-group>
|
||||
@ -136,7 +156,7 @@
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
mode="multiple"
|
||||
v-model="temp.taskDataIds"
|
||||
v-model:value="temp.taskDataIds"
|
||||
placeholder="请选择SSH"
|
||||
>
|
||||
<a-select-option v-for="ssh in sshList" :key="ssh.id">
|
||||
@ -145,7 +165,7 @@
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="1" style="margin-left: 10px">
|
||||
<a-icon type="reload" @click="loadSshList" />
|
||||
<ReloadOutlined @click="loadSshList" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
@ -156,7 +176,7 @@
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
mode="multiple"
|
||||
v-model="temp.taskDataIds"
|
||||
v-model:value="temp.taskDataIds"
|
||||
placeholder="请选择节点"
|
||||
>
|
||||
<a-select-option v-for="ssh in nodeList" :key="ssh.id">
|
||||
@ -165,45 +185,55 @@
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="1" style="margin-left: 10px">
|
||||
<a-icon type="reload" @click="loadNodeList" />
|
||||
<ReloadOutlined @click="loadNodeList" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="releasePathParent" label="发布目录">
|
||||
<a-input placeholder="请输入发布目录" :disabled="true" v-model="temp.releasePath" />
|
||||
<a-input placeholder="请输入发布目录" :disabled="true" v-model:value="temp.releasePath" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="releasePathParent" label="文件id">
|
||||
<a-input placeholder="请输入发布的文件id" v-model="temp.fileId" />
|
||||
<a-input placeholder="请输入发布的文件id" v-model:value="temp.fileId" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="执行脚本" name="releaseBeforeCommand">
|
||||
<a-tabs tabPosition="right">
|
||||
<a-tab-pane key="before" tab="上传前">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model="temp.beforeScript"
|
||||
:options="{ mode: temp.taskType === 0 ? 'shell' : '', tabSize: 2, theme: 'abcdef' }"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传前需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="after" tab="上传后">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model="temp.afterScript"
|
||||
:options="{ mode: temp.taskType === 0 ? 'shell' : '', tabSize: 2, theme: 'abcdef' }"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传成功后需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-form-item-rest>
|
||||
<a-tabs tabPosition="right">
|
||||
<a-tab-pane key="before" tab="上传前">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model:content="temp.beforeScript"
|
||||
:options="{
|
||||
mode: temp.taskType === 0 ? 'shell' : '',
|
||||
tabSize: 2,
|
||||
theme: 'abcdef'
|
||||
}"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传前需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="after" tab="上传后">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model:content="temp.afterScript"
|
||||
:options="{
|
||||
mode: temp.taskType === 0 ? 'shell' : '',
|
||||
tabSize: 2,
|
||||
theme: 'abcdef'
|
||||
}"
|
||||
></code-editor>
|
||||
</div>
|
||||
<div style="margin-top: 10px">文件上传成功后需要执行的脚本(非阻塞命令)</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 查看文件 -->
|
||||
<a-modal destroyOnClose v-model:visible="viewFileVisible" :title="`查看文件`" :footer="null" :maskClosable="false">
|
||||
<a-modal destroyOnClose v-model:open="viewFileVisible" :title="`查看文件`" :footer="null" :maskClosable="false">
|
||||
<a-form :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="文件名" name="name">
|
||||
{{ temp.name }}
|
||||
@ -214,10 +244,10 @@
|
||||
<a-form-item label="文件大小" name="size">
|
||||
{{ renderSize(temp.size) }}
|
||||
</a-form-item>
|
||||
<a-form-item label="过期时间" name="keepDay">
|
||||
<a-form-item label="过期时间" name="validUntil" v-if="temp.validUntil">
|
||||
{{ parseTime(temp.validUntil) }}
|
||||
</a-form-item>
|
||||
<a-form-item label="文件共享" name="global">
|
||||
<a-form-item label="文件共享" name="global" v-if="temp.workspaceId">
|
||||
{{ temp.workspaceId === 'GLOBAL' ? '全局' : '工作空间' }}
|
||||
</a-form-item>
|
||||
<a-form-item label="文件描述" name="description">
|
||||
@ -244,7 +274,7 @@ import { getSshListAll } from '@/api/ssh'
|
||||
import codeEditor from '@/components/codeEditor'
|
||||
import { hasFile } from '@/api/file-manager/file-storage'
|
||||
import { getNodeListAll } from '@/api/node'
|
||||
|
||||
import { hasStaticFile } from '@/api/file-manager/static-storage'
|
||||
export default {
|
||||
components: {
|
||||
taskDetailsPage,
|
||||
@ -259,45 +289,66 @@ export default {
|
||||
statusMap,
|
||||
taskTypeMap,
|
||||
detailsVisible: false,
|
||||
confirmLoading: false,
|
||||
columns: [
|
||||
{ title: '任务名称', dataIndex: 'name', ellipsis: true, width: 150, scopedSlots: { customRender: 'tooltip' } },
|
||||
{
|
||||
title: '任务名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '分发类型',
|
||||
dataIndex: 'taskType',
|
||||
width: '100px',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'taskType' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '文件来源',
|
||||
dataIndex: 'fileType',
|
||||
width: '100px',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: '100px',
|
||||
ellipsis: true
|
||||
},
|
||||
{ title: '状态', dataIndex: 'status', width: '100px', ellipsis: true, scopedSlots: { customRender: 'status' } },
|
||||
|
||||
{
|
||||
title: '状态描述',
|
||||
dataIndex: 'statusMsg',
|
||||
ellipsis: true,
|
||||
width: 200,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '文件ID',
|
||||
dataIndex: 'fileId',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
},
|
||||
{ title: '文件ID', dataIndex: 'fileId', ellipsis: true, width: 150, scopedSlots: { customRender: 'fileId' } },
|
||||
{
|
||||
title: '发布目录',
|
||||
dataIndex: 'releasePath',
|
||||
width: '100px',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '执行人',
|
||||
dataIndex: 'modifyUser',
|
||||
width: '120px',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '任务时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
@ -305,7 +356,7 @@ export default {
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
|
||||
@ -313,7 +364,7 @@ export default {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
fixed: 'right',
|
||||
width: '230px'
|
||||
}
|
||||
@ -361,8 +412,9 @@ export default {
|
||||
|
||||
// 删除命令
|
||||
handleDelete(row) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除该执行记录吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -372,7 +424,7 @@ export default {
|
||||
id: row.id
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -430,27 +482,33 @@ export default {
|
||||
},
|
||||
// 创建任务
|
||||
handleReCrateTask() {
|
||||
this.$refs['releaseFileForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
reReleaseTask({ ...this.temp, taskDataIds: this.temp.taskDataIds?.join(',') }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.releaseFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
this.$refs['releaseFileForm'].validate().then(() => {
|
||||
this.confirmLoading = true
|
||||
reReleaseTask({
|
||||
...this.temp,
|
||||
taskDataIds: this.temp.taskDataIds?.join(',')
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.releaseFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 取消
|
||||
handleCancelTask(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的取消当前发布任务吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -458,7 +516,7 @@ export default {
|
||||
// 删除
|
||||
cancelReleaseTask({ id: record.id }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -469,22 +527,39 @@ export default {
|
||||
},
|
||||
// 查看文件
|
||||
handleViewFile(record) {
|
||||
hasFile({
|
||||
fileSumMd5: record.fileId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (res.data) {
|
||||
this.temp = res.data
|
||||
this.viewFileVisible = true
|
||||
} else {
|
||||
$notification.warning({
|
||||
message: '文件不存在啦'
|
||||
})
|
||||
if (record.fileType === 2) {
|
||||
//
|
||||
hasStaticFile({
|
||||
fileId: record.fileId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (res.data) {
|
||||
this.temp = res.data
|
||||
this.viewFileVisible = true
|
||||
} else {
|
||||
this.$notification.warning({
|
||||
message: '文件不存在啦'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
hasFile({
|
||||
fileSumMd5: record.fileId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (res.data) {
|
||||
this.temp = res.data
|
||||
this.viewFileVisible = true
|
||||
} else {
|
||||
this.$notification.warning({
|
||||
message: '文件不存在啦'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
609
web-vue3/src/pages/file-manager/staticFileStorage/list.vue
Normal file
609
web-vue3/src/pages/file-manager/staticFileStorage/list.vue
Normal file
@ -0,0 +1,609 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
size="middle"
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
@change="
|
||||
(pagination, filters, sorter) => {
|
||||
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter })
|
||||
this.loadData()
|
||||
}
|
||||
"
|
||||
bordered
|
||||
rowKey="id"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="文件名称"
|
||||
class="search-input-item"
|
||||
/>
|
||||
|
||||
<a-input
|
||||
v-model:value="listQuery['extName']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="后缀,精准搜索"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model:value="listQuery['id']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="文件id,精准搜索"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
|
||||
<a-button type="primary" :loading="loading" @click="loadData">搜索</a-button>
|
||||
</a-tooltip>
|
||||
<!-- <a-button type="primary" @click="handleUpload">上传文件</a-button> -->
|
||||
<a-button type="primary" @click="reScanner">扫描</a-button>
|
||||
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
:disabled="!tableSelections || tableSelections.length <= 0"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
批量删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, text, record, index }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'id'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span v-if="record.status === 0 || record.status === 2">-</span>
|
||||
<span v-else>{{ (text || '').slice(0, 8) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'name'">
|
||||
<a-popover title="文件信息">
|
||||
<template v-slot:content>
|
||||
<p>文件ID:{{ record.id }}</p>
|
||||
<p>文件名:{{ text }}</p>
|
||||
<p>文件描述:{{ record.description }}</p>
|
||||
</template>
|
||||
<!-- {{ text }} -->
|
||||
<a-button type="link" style="padding: 0" @click="handleEdit(record)" size="small">{{ text }}</a-button>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'size'">
|
||||
<a-tooltip placement="topLeft" :title="renderSize(text)">
|
||||
<span>{{ renderSize(text) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'source'">
|
||||
<a-tooltip placement="topLeft" :title="`${sourceMap[text] || '未知'}`">
|
||||
<span>{{ sourceMap[text] || '未知' }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<a-tag v-if="text === 1" color="green">存在</a-tag>
|
||||
<a-tag v-else color="red">丢失</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'type'">
|
||||
<a-tag v-if="text === 1">文件</a-tag>
|
||||
<a-tag v-else>文件夹</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<!-- <a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button> -->
|
||||
<a-button
|
||||
size="small"
|
||||
:disabled="!(record.status === 1 && record.type === 1)"
|
||||
type="primary"
|
||||
@click="handleDownloadUrl(record)"
|
||||
>
|
||||
下载</a-button
|
||||
>
|
||||
<a-button
|
||||
size="small"
|
||||
:disabled="!(record.status === 1 && record.type === 1)"
|
||||
type="primary"
|
||||
@click="handleReleaseFile(record)"
|
||||
>发布</a-button
|
||||
>
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 编辑文件 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:open="editVisible"
|
||||
:title="`修改文件`"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleEditOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="文件名" name="name">
|
||||
<a-input placeholder="文件名" :disabled="true" v-model:value="temp.name" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="文件描述" name="description">
|
||||
<a-textarea v-model:value="temp.description" placeholder="请输入文件描述" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 断点下载 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:open="triggerVisible"
|
||||
title="断点/分片下载"
|
||||
width="50%"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="editTriggerForm" :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<a-tabs default-active-key="1">
|
||||
<template v-slot:rightExtra>
|
||||
<a-tooltip title="重置下载 token 信息,重置后之前的下载 token 将失效">
|
||||
<a-button type="primary" size="small" @click="resetTrigger">重置</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tab-pane key="1" tab="断点/分片单文件下载">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert type="info" :message="`下载地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-typography-paragraph :copyable="{ text: temp.triggerDownloadUrl }">
|
||||
<a-tag>GET</a-tag>
|
||||
<span>{{ `${temp.triggerDownloadUrl}` }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a :href="temp.triggerDownloadUrl" target="_blank">
|
||||
<a-button size="small" type="primary"><DownloadOutlined />立即下载</a-button>
|
||||
</a>
|
||||
</a-space>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="断点/分片别名下载" v-if="temp.triggerAliasDownloadUrl">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
<li>
|
||||
支持自定义排序字段:sort=createTimeMillis:desc
|
||||
|
||||
<p>描述根据创建时间升序第一个</p>
|
||||
</li>
|
||||
<li>支持的字段可以通过接口返回的查看</li>
|
||||
<li>通用的字段有:createTimeMillis、modifyTimeMillis</li>
|
||||
</ul>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert type="info" :message="`下载地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-typography-paragraph :copyable="{ text: temp.triggerAliasDownloadUrl }">
|
||||
<a-tag>GET</a-tag>
|
||||
<span>{{ `${temp.triggerAliasDownloadUrl}` }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a :href="temp.triggerAliasDownloadUrl" target="_blank">
|
||||
<a-button size="small" type="primary"><DownloadOutlined />立即下载</a-button>
|
||||
</a>
|
||||
</a-space>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 发布文件 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:open="releaseFileVisible"
|
||||
title="发布文件"
|
||||
width="50%"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="releaseFileOk()"
|
||||
>
|
||||
<releaseFile ref="releaseFile" v-if="releaseFileVisible" @commit="handleCommitTask"></releaseFile>
|
||||
</a-modal>
|
||||
</div>
|
||||
<!-- 选择确认区域
|
||||
<div style="padding-top: 50px" v-if="this.choose">
|
||||
<div
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e9e9e9',
|
||||
padding: '10px 16px',
|
||||
background: '#fff',
|
||||
textAlign: 'right',
|
||||
zIndex: 1
|
||||
}"
|
||||
>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.$emit('cancel')
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handerConfirm"> 确定 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
CHANGE_PAGE,
|
||||
COMPUTED_PAGINATION,
|
||||
PAGE_DEFAULT_LIST_QUERY,
|
||||
parseTime,
|
||||
renderSize,
|
||||
formatDuration,
|
||||
randomStr
|
||||
} from '@/utils/const'
|
||||
// import { uploadFile, uploadFileMerge, hasFile } from "@/api/file-manager/file-storage";
|
||||
import { staticFileStorageList, delFile, triggerUrl, fileEdit, staticScanner } from '@/api/file-manager/static-storage'
|
||||
|
||||
import releaseFile from '@/pages/file-manager/fileStorage/releaseFile'
|
||||
import { addReleaseTask } from '@/api/file-manager/release-task-log'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
releaseFile
|
||||
},
|
||||
props: {
|
||||
choose: {
|
||||
// "radio"
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
|
||||
list: [],
|
||||
columns: [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '路径',
|
||||
dataIndex: 'absolutePath',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '大小',
|
||||
dataIndex: 'size',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
title: '后缀',
|
||||
dataIndex: 'extName',
|
||||
ellipsis: true,
|
||||
|
||||
tooltip: true,
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
ellipsis: true,
|
||||
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '文件状态',
|
||||
dataIndex: 'status',
|
||||
ellipsis: true,
|
||||
|
||||
width: '80px'
|
||||
},
|
||||
|
||||
{
|
||||
title: '文件修改时间',
|
||||
dataIndex: 'lastModified',
|
||||
sorter: true,
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
ellipsis: true,
|
||||
|
||||
fixed: 'right',
|
||||
width: '170px'
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
name: [{ required: true, message: '请输入文件名称', trigger: 'blur' }],
|
||||
url: [{ required: true, message: '请输入远程地址', trigger: 'blur' }]
|
||||
},
|
||||
|
||||
temp: {},
|
||||
|
||||
fileList: [],
|
||||
percentage: 0,
|
||||
percentageInfo: {},
|
||||
uploading: false,
|
||||
uploadVisible: false,
|
||||
editVisible: false,
|
||||
|
||||
tempVue: null,
|
||||
triggerVisible: false,
|
||||
releaseFileVisible: false,
|
||||
tableSelections: [],
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
rowSelection() {
|
||||
return {
|
||||
onChange: (selectedRowKeys) => {
|
||||
this.tableSelections = selectedRowKeys
|
||||
},
|
||||
selectedRowKeys: this.tableSelections,
|
||||
type: this.choose || 'checkbox'
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
randomStr,
|
||||
CHANGE_PAGE,
|
||||
renderSize,
|
||||
formatDuration,
|
||||
parseTime,
|
||||
// 加载数据
|
||||
loadData(pointerEvent) {
|
||||
this.loading = true
|
||||
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page
|
||||
staticFileStorageList(this.listQuery).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.list = res.data.result
|
||||
this.listQuery.total = res.data.total
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 编辑
|
||||
handleEdit(item) {
|
||||
this.temp = {
|
||||
...item,
|
||||
global: item.workspaceId === 'GLOBAL',
|
||||
workspaceId: ''
|
||||
}
|
||||
this.editVisible = true
|
||||
this.$refs['editForm']?.resetFields()
|
||||
},
|
||||
// 编辑确认
|
||||
handleEditOk() {
|
||||
this.$refs['editForm'].validate().then(() => {
|
||||
this.confirmLoading = true
|
||||
fileEdit(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.editVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除文件
|
||||
handleDelete(record) {
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除当前文件么?' + record.name,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
// 删除
|
||||
delFile({
|
||||
id: record.id,
|
||||
thorough: false
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 批量删除
|
||||
handleBatchDelete() {
|
||||
if (!this.tableSelections || this.tableSelections.length <= 0) {
|
||||
this.$notification.warning({
|
||||
message: '没有选择任何数据'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除这些文件么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
// 删除
|
||||
delFile({
|
||||
ids: this.tableSelections.join(','),
|
||||
thorough: false
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 下载地址
|
||||
handleDownloadUrl(record) {
|
||||
this.temp = Object.assign({}, record)
|
||||
this.tempVue = Vue
|
||||
triggerUrl({
|
||||
id: record.id
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.fillDownloadUrlResult(res)
|
||||
this.$nextTick(() => {
|
||||
this.triggerVisible = true
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 重置触发器
|
||||
resetTrigger() {
|
||||
triggerUrl({
|
||||
id: this.temp.id,
|
||||
rest: 'rest'
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.fillDownloadUrlResult(res)
|
||||
}
|
||||
})
|
||||
},
|
||||
fillDownloadUrlResult(res) {
|
||||
this.temp = {
|
||||
...this.temp,
|
||||
triggerDownloadUrl: `${location.protocol}//${location.host}${res.data.triggerDownloadUrl}`,
|
||||
triggerAliasDownloadUrl: res.data?.triggerAliasDownloadUrl
|
||||
? `${location.protocol}//${location.host}${res.data.triggerAliasDownloadUrl}?sort=createTimeMillis:desc`
|
||||
: ''
|
||||
}
|
||||
},
|
||||
// 发布文件
|
||||
handleReleaseFile(record) {
|
||||
this.releaseFileVisible = true
|
||||
this.temp = { fileId: record.id }
|
||||
},
|
||||
|
||||
handleCommitTask(data) {
|
||||
this.confirmLoading = true
|
||||
addReleaseTask({ ...data, fileId: this.temp.fileId, fileType: 2 })
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.releaseFileVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
releaseFileOk() {
|
||||
this.$refs.releaseFile?.tryCommit()
|
||||
},
|
||||
// 选择确认
|
||||
handerConfirm() {
|
||||
if (!this.tableSelections.length) {
|
||||
this.$notification.warning({
|
||||
message: '请选择要使用的文件'
|
||||
})
|
||||
return
|
||||
}
|
||||
const selectData = this.list.filter((item) => {
|
||||
return this.tableSelections.indexOf(item.id) > -1
|
||||
})
|
||||
if (!selectData.length) {
|
||||
this.$notification.warning({
|
||||
message: '请选择要使用的文件'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$emit('confirm', selectData)
|
||||
},
|
||||
// 扫描
|
||||
reScanner() {
|
||||
staticScanner({}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
emits: ['cancel', 'confirm']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .ant-progress-text {
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
@ -18,7 +18,7 @@
|
||||
<a-input-search
|
||||
placeholder="请输入工作空间备注,留空使用默认的名称"
|
||||
enter-button="确定"
|
||||
v-model="element.name"
|
||||
v-model:value="element.name"
|
||||
@search="editOk(element)"
|
||||
/>
|
||||
</template>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
@ -8,25 +8,27 @@
|
||||
:pagination="pagination"
|
||||
@change="changePage"
|
||||
bordered
|
||||
:rowKey="(record, index) => index"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="监控名称"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-select v-model="listQuery.status" allowClear placeholder="开启状态" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.status" allowClear placeholder="开启状态" class="search-input-item">
|
||||
<a-select-option :value="1">开启</a-select-option>
|
||||
<a-select-option :value="0">关闭</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="listQuery.autoRestart" allowClear placeholder="自动重启" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.autoRestart" allowClear placeholder="自动重启" class="search-input-item">
|
||||
<a-select-option :value="1">是</a-select-option>
|
||||
<a-select-option :value="0">否</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="listQuery.alarm" allowClear placeholder="报警状态" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.alarm" allowClear placeholder="报警状态" class="search-input-item">
|
||||
<a-select-option :value="1">报警中</a-select-option>
|
||||
<a-select-option :value="0">未报警</a-select-option>
|
||||
</a-select>
|
||||
@ -36,50 +38,35 @@
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #name slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-switch
|
||||
#status
|
||||
size="small"
|
||||
slot-scope="text"
|
||||
:checked="text"
|
||||
disabled
|
||||
checked-children="开启"
|
||||
un-checked-children="关闭"
|
||||
/>
|
||||
<a-switch
|
||||
#autoRestart
|
||||
size="small"
|
||||
slot-scope="text"
|
||||
:checked="text"
|
||||
disabled
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
/>
|
||||
<a-switch
|
||||
#alarm
|
||||
size="small"
|
||||
slot-scope="text"
|
||||
:checked="text"
|
||||
disabled
|
||||
checked-children="报警中"
|
||||
un-checked-children="未报警"
|
||||
/>
|
||||
<a-tooltip #parent slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="danger" size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<a-switch size="small" :checked="text" disabled checked-children="开启" un-checked-children="关闭" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'autoRestart'">
|
||||
<a-switch size="small" :checked="text" disabled checked-children="是" un-checked-children="否" />
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'alarm'">
|
||||
<a-switch size="small" :checked="text" disabled checked-children="报警中" un-checked-children="未报警" />
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑区 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="editMonitorVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editMonitorVisible"
|
||||
width="60%"
|
||||
title="编辑监控"
|
||||
@ok="handleEditMonitorOk"
|
||||
@ -87,54 +74,47 @@
|
||||
>
|
||||
<a-form ref="editMonitorForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="监控名称" name="name">
|
||||
<a-input v-model="temp.name" :maxLength="50" placeholder="监控名称" />
|
||||
<a-input v-model:value="temp.name" :maxLength="50" placeholder="监控名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="开启状态" name="status">
|
||||
<a-space size="large">
|
||||
<a-switch v-model="temp.status" checked-children="开" un-checked-children="关" />
|
||||
<a-switch v-model:checked="temp.status" checked-children="开" un-checked-children="关" />
|
||||
<div>
|
||||
自动重启:
|
||||
<a-switch v-model="temp.autoRestart" checked-children="开" un-checked-children="关" />
|
||||
<a-form-item-rest>
|
||||
<a-switch v-model:checked="temp.autoRestart" checked-children="开" un-checked-children="关" />
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
|
||||
<!-- <a-form-item label="自动重启" name="autoRestart">
|
||||
|
||||
</a-form-item> -->
|
||||
</a-form-item> -->
|
||||
|
||||
<!-- <a-form-item label="监控周期" name="cycle">
|
||||
<a-radio-group v-model="temp.cycle" name="cycle">
|
||||
<a-radio :value="1">1 分钟</a-radio>
|
||||
<a-radio :value="5">5 分钟</a-radio>
|
||||
<a-radio :value="10">10 分钟</a-radio>
|
||||
<a-radio :value="30">30 分钟</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item> -->
|
||||
<a-radio-group v-model="temp.cycle" name="cycle">
|
||||
<a-radio :value="1">1 分钟</a-radio>
|
||||
<a-radio :value="5">5 分钟</a-radio>
|
||||
<a-radio :value="10">10 分钟</a-radio>
|
||||
<a-radio :value="30">30 分钟</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item> -->
|
||||
|
||||
<a-form-item label="监控周期" name="execCron">
|
||||
<a-auto-complete
|
||||
v-model="temp.execCron"
|
||||
v-model:value="temp.execCron"
|
||||
placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])"
|
||||
option-label-prop="value"
|
||||
:options="CRON_DATA_SOURCE"
|
||||
>
|
||||
<template #dataSource>
|
||||
<a-select-opt-group v-for="group in cronDataSource" :key="group.title">
|
||||
<span #label>
|
||||
{{ group.title }}
|
||||
</span>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value">
|
||||
{{ opt.title }} {{ opt.value }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</template>
|
||||
<template #option="item"> {{ item.title }} {{ item.value }} </template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
<a-form-item label="监控项目" name="projects">
|
||||
<a-select
|
||||
option-label-prop="label"
|
||||
v-model="projectKeys"
|
||||
v-model:value="projectKeys"
|
||||
mode="multiple"
|
||||
placeholder="选择要监控的项目,file 类型项目不可以监控"
|
||||
show-search
|
||||
@ -151,19 +131,20 @@
|
||||
:disabled="!noFileModes.includes(project.runMode)"
|
||||
:key="project.id"
|
||||
>
|
||||
【{{ project.nodeName }}】{{ project.name }} - {{ project.runMode }}
|
||||
【{{ project.nodeName }}】{{ project.name }} -
|
||||
{{ project.runMode }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="notifyUser" class="jpom-notify">
|
||||
<template #label>
|
||||
联系人
|
||||
<a-tooltip v-show="!temp.id">
|
||||
<template #title>
|
||||
<template v-slot:label>
|
||||
<a-tooltip>
|
||||
联系人
|
||||
<template v-slot:title>
|
||||
如果这里的报警联系人无法选择,说明这里面的管理员没有设置邮箱,在右上角下拉菜单里面的用户资料里可以设置。
|
||||
</template>
|
||||
<question-circle-filled />
|
||||
<QuestionCircleOutlined v-show="!temp.id" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-transfer
|
||||
@ -179,25 +160,21 @@
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #render="item">
|
||||
<span>
|
||||
<span v-if="item.disabled">
|
||||
<a-tooltip title="如果不可以选择则表示对应的用户没有配置邮箱">
|
||||
<a-icon type="warning" theme="twoTone" />
|
||||
{{ item.name }}
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<span>{{ item.name }}</span>
|
||||
-
|
||||
{{ item.name }}
|
||||
</span>
|
||||
<template v-if="item.disabled">
|
||||
<a-tooltip title="如果不可以选择则表示对应的用户没有配置邮箱">
|
||||
<WarningTwoTone />
|
||||
{{ item.name }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else> {{ item.name }}</template>
|
||||
</template>
|
||||
</a-transfer>
|
||||
</a-form-item>
|
||||
<a-form-item name="webhook">
|
||||
<template #label>
|
||||
WebHooks
|
||||
<a-tooltip v-show="!temp.id">
|
||||
<template #title>
|
||||
<template v-slot:label>
|
||||
<a-tooltip>
|
||||
WebHooks
|
||||
<template v-slot:title>
|
||||
<ul>
|
||||
<li>发生报警时候请求</li>
|
||||
<li>
|
||||
@ -206,15 +183,16 @@
|
||||
<li>runStatus 值为 true 表示项目当前为运行中(异常恢复),false 表示项目当前未运行(发生异常)</li>
|
||||
</ul>
|
||||
</template>
|
||||
<question-circle-filled />
|
||||
<QuestionCircleOutlined v-show="!temp.id" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="temp.webhook" placeholder="接收报警消息,非必填,GET请求" />
|
||||
<a-input v-model:value="temp.webhook" placeholder="接收报警消息,非必填,GET请求" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deleteMonitor, editMonitor, getMonitorList } from '@/api/monitor'
|
||||
import { noFileModes } from '@/api/node-project'
|
||||
@ -234,7 +212,7 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
|
||||
cronDataSource: CRON_DATA_SOURCE,
|
||||
CRON_DATA_SOURCE,
|
||||
list: [],
|
||||
userList: [],
|
||||
nodeProjectList: [],
|
||||
@ -248,30 +226,50 @@ export default {
|
||||
temp: {},
|
||||
editMonitorVisible: false,
|
||||
columns: [
|
||||
{ title: '名称', dataIndex: 'name', ellipsis: true, scopedSlots: { customRender: 'name' } },
|
||||
{ title: '监控周期', dataIndex: 'execCron', ellipsis: true, scopedSlots: { customRender: 'execCron' } },
|
||||
{ title: '开启状态', dataIndex: 'status', ellipsis: true, scopedSlots: { customRender: 'status' }, width: 120 },
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '监控周期',
|
||||
dataIndex: 'execCron',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '开启状态',
|
||||
dataIndex: 'status',
|
||||
ellipsis: true,
|
||||
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '自动重启',
|
||||
dataIndex: 'autoRestart',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'autoRestart' },
|
||||
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '报警状态',
|
||||
dataIndex: 'alarm',
|
||||
ellipsis: true,
|
||||
|
||||
width: 120
|
||||
},
|
||||
{ title: '报警状态', dataIndex: 'alarm', ellipsis: true, scopedSlots: { customRender: 'alarm' }, width: 120 },
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '0') {
|
||||
return ''
|
||||
}
|
||||
@ -283,13 +281,20 @@ export default {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: 120
|
||||
fixed: 'right',
|
||||
width: '120px'
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
name: [{ required: true, message: 'Please input monitor name', trigger: 'blur' }]
|
||||
}
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入监控名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -302,24 +307,6 @@ export default {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
// 页面引导
|
||||
introGuide() {
|
||||
this.$store.dispatch('tryOpenGuide', {
|
||||
key: 'monitor',
|
||||
options: {
|
||||
hidePrev: true,
|
||||
steps: [
|
||||
{
|
||||
title: '导航助手',
|
||||
element: document.querySelector('.jpom-notify'),
|
||||
intro:
|
||||
'如果这里的报警联系人无法选择,说明这里面的管理员没有设置邮箱,在右上角下拉菜单里面的用户资料里可以设置。'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载数据
|
||||
loadData(pointerEvent) {
|
||||
this.loading = true
|
||||
@ -336,7 +323,7 @@ export default {
|
||||
loadUserList(fn) {
|
||||
getUserListAll().then((res) => {
|
||||
if (res.code === 200) {
|
||||
nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
this.userList = res.data.map((element) => {
|
||||
let canUse = element.email || element.dingDing || element.workWx
|
||||
return { key: element.id, name: element.name, disabled: !canUse }
|
||||
@ -387,11 +374,6 @@ export default {
|
||||
this.editMonitorVisible = true
|
||||
this.loadUserList()
|
||||
this.loadNodeProjectList()
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.introGuide()
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
// 修改
|
||||
handleEdit(record) {
|
||||
@ -425,10 +407,7 @@ export default {
|
||||
},
|
||||
handleEditMonitorOk() {
|
||||
// 检验表单
|
||||
this.$refs['editMonitorForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editMonitorForm'].validate().then(() => {
|
||||
let projects = this.nodeProjectList.filter((item) => {
|
||||
return this.projectKeys.includes(item.id)
|
||||
})
|
||||
@ -448,7 +427,7 @@ export default {
|
||||
.map((item) => item.key)
|
||||
|
||||
if (targetKeysTemp.length <= 0 && !this.temp.webhook) {
|
||||
$notification.warn({
|
||||
this.$notification.warn({
|
||||
message: '请选择一位报警联系人或者填写webhook'
|
||||
})
|
||||
return false
|
||||
@ -461,23 +440,29 @@ export default {
|
||||
projects: JSON.stringify(projects),
|
||||
notifyUser: JSON.stringify(targetKeysTemp)
|
||||
}
|
||||
editMonitor(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editMonitorForm'].resetFields()
|
||||
this.editMonitorVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.confirmLoading = true
|
||||
editMonitor(params)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editMonitorForm'].resetFields()
|
||||
this.editMonitorVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除监控么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -485,7 +470,7 @@ export default {
|
||||
// 删除
|
||||
deleteMonitor(record.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -502,4 +487,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<!-- <div ref="filter" class="filter"></div> -->
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
@ -8,60 +7,72 @@
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
bordered
|
||||
:rowKey="(record, index) => index"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
@change="change"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-select v-model="listQuery.nodeId" allowClear placeholder="请选择节点" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.nodeId" allowClear placeholder="请选择节点" class="search-input-item">
|
||||
<a-select-option v-for="(nodeName, key) in nodeMap" :key="key">{{ nodeName }}</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="listQuery.status" allowClear placeholder="报警状态" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.status" allowClear placeholder="报警状态" class="search-input-item">
|
||||
<a-select-option :value="1">正常</a-select-option>
|
||||
<a-select-option :value="0">异常</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="listQuery.notifyStatus" allowClear placeholder="通知状态" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.notifyStatus" allowClear placeholder="通知状态" class="search-input-item">
|
||||
<a-select-option :value="1">成功</a-select-option>
|
||||
<a-select-option :value="0">失败</a-select-option>
|
||||
</a-select>
|
||||
<a-range-picker
|
||||
class="search-input-item"
|
||||
:show-time="{ format: 'HH:mm:ss' }"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
@change="onchangeTime"
|
||||
/>
|
||||
<a-range-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" @change="onchangeTime" />
|
||||
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
|
||||
<a-button :loading="loading" type="primary" @click="loadData">搜索</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #nodeId slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ nodeMap[text] }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip #tooltip slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<span #status slot-scope="text">{{ text ? '正常' : '异常' }}</span>
|
||||
<template #notifyStyle slot-scope="text">
|
||||
{{ notifyStyle[text] || '未知' }}
|
||||
</template>
|
||||
<span #notifyStatus slot-scope="text">{{ text ? '成功' : '失败' }}</span>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-button size="small" type="primary" @click="handleDetail(record)">详情</a-button>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'nodeId'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ nodeMap[text] }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<span>{{ text ? '正常' : '异常' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'notifyStyle'">
|
||||
{{ notifyStyle[text] || '未知' }}
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'notifyStatus'">
|
||||
<span>{{ text ? '成功' : '失败' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-button size="small" type="primary" @click="handleDetail(record)">详情</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 详情区 -->
|
||||
<a-modal destroyOnClose v-model:visible="detailVisible" width="600px" title="详情信息" :footer="null">
|
||||
<a-modal destroyOnClose v-model:open="detailVisible" width="600px" title="详情信息" :footer="null">
|
||||
<a-list item-layout="horizontal" :data-source="detailData">
|
||||
<a-list-item #renderItem slot-scope="item">
|
||||
<a-list-item-meta :description="item.description">
|
||||
<h4 #title>{{ item.title }}</h4>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="item.description">
|
||||
<template v-slot:title>
|
||||
<h4>{{ item.title }}</h4>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMonitorLogList, notifyStyle } from '@/api/monitor'
|
||||
import { getNodeListAll } from '@/api/node'
|
||||
@ -79,52 +90,59 @@ export default {
|
||||
notifyStyle,
|
||||
detailData: [],
|
||||
columns: [
|
||||
{ title: '报警标题', dataIndex: 'title', ellipsis: true, scopedSlots: { customRender: 'tooltip' } },
|
||||
{ title: '节点名称', dataIndex: 'nodeId', width: 100, ellipsis: true, scopedSlots: { customRender: 'nodeId' } },
|
||||
{
|
||||
title: '报警标题',
|
||||
dataIndex: 'title',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '节点名称',
|
||||
dataIndex: 'nodeId',
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '项目 ID',
|
||||
dataIndex: 'projectId',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '报警状态',
|
||||
dataIndex: 'status',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '报警方式',
|
||||
dataIndex: 'notifyStyle',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'notifyStyle' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '报警时间',
|
||||
dataIndex: 'createTime',
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
width: 170
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '通知状态',
|
||||
dataIndex: 'notifyStatus',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'notifyStatus' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: 80
|
||||
fixed: 'right',
|
||||
width: '80px'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -186,12 +204,17 @@ export default {
|
||||
this.temp = Object.assign({}, record)
|
||||
this.detailData.push({ title: '标题', description: this.temp.title })
|
||||
this.detailData.push({ title: '内容', description: this.temp.content })
|
||||
this.detailData.push({ title: '通知对象', description: this.temp.notifyObject })
|
||||
this.detailData.push({
|
||||
title: '通知对象',
|
||||
description: this.temp.notifyObject
|
||||
})
|
||||
if (!this.temp.notifyStatus) {
|
||||
this.detailData.push({ title: '通知异常', description: this.temp.notifyError })
|
||||
this.detailData.push({
|
||||
title: '通知异常',
|
||||
description: this.temp.notifyError
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
size="middle"
|
||||
@ -8,17 +8,19 @@
|
||||
:pagination="pagination"
|
||||
@change="changePage"
|
||||
bordered
|
||||
:rowKey="(record, index) => index"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="loadData"
|
||||
placeholder="监控名称"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-select v-model="listQuery.status" allowClear placeholder="开启状态" class="search-input-item">
|
||||
<a-select v-model:value="listQuery.status" allowClear placeholder="开启状态" class="search-input-item">
|
||||
<a-select-option :value="1">开启</a-select-option>
|
||||
<a-select-option :value="0">关闭</a-select-option>
|
||||
</a-select>
|
||||
@ -28,33 +30,31 @@
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #name slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-switch
|
||||
#status
|
||||
size="small"
|
||||
slot-scope="text"
|
||||
:checked="text"
|
||||
checked-children="开启"
|
||||
un-checked-children="关闭"
|
||||
/>
|
||||
<!-- <a-switch #autoRestart slot-scope="text" :checked="text" checked-children="是" un-checked-children="否" /> -->
|
||||
<!-- <a-switch #alarm slot-scope="text" :checked="text" disabled checked-children="报警中" un-checked-children="未报警" /> -->
|
||||
<a-tooltip #parent slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<a-switch size="small" :checked="text" checked-children="开启" un-checked-children="关闭" />
|
||||
</template>
|
||||
<!-- <a-switch slot="autoRestart" slot-scope="text" :checked="text" checked-children="是" un-checked-children="否" /> -->
|
||||
<!-- <a-switch slot="alarm" slot-scope="text" :checked="text" disabled checked-children="报警中" un-checked-children="未报警" /> -->
|
||||
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑区 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="editOperateMonitorVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="editOperateMonitorVisible"
|
||||
width="50vw"
|
||||
title="编辑监控"
|
||||
@ok="handleEditOperateMonitorOk"
|
||||
@ -62,10 +62,10 @@
|
||||
>
|
||||
<a-form ref="editMonitorForm" :rules="rules" :model="temp" :label-col="{ span: 5 }" :wrapper-col="{ span: 17 }">
|
||||
<a-form-item label="监控名称" name="name">
|
||||
<a-input v-model="temp.name" :maxLength="50" placeholder="监控名称" />
|
||||
<a-input v-model:value="temp.name" :maxLength="50" placeholder="监控名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="开启状态" name="status">
|
||||
<a-switch v-model="temp.start" checked-children="开" un-checked-children="关" />
|
||||
<a-switch v-model:checked="temp.start" checked-children="开" un-checked-children="关" />
|
||||
</a-form-item>
|
||||
<a-form-item label="监控用户" name="monitorUser">
|
||||
<a-transfer
|
||||
@ -101,13 +101,13 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="notifyUser" class="jpom-monitor-notify">
|
||||
<template #label>
|
||||
报警联系人
|
||||
<a-tooltip v-show="!temp.id">
|
||||
<template #title>
|
||||
<template v-slot:label>
|
||||
<a-tooltip>
|
||||
报警联系人
|
||||
<template v-slot:title>
|
||||
如果这里的报警联系人无法选择,说明这里面的管理员没有设置邮箱,在右上角下拉菜单里面的用户资料里可以设置。
|
||||
</template>
|
||||
<question-circle-filled />
|
||||
<QuestionCircleOutlined v-show="!temp.id" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-transfer
|
||||
@ -124,6 +124,7 @@
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
deleteMonitorOperate,
|
||||
@ -152,14 +153,23 @@ export default {
|
||||
methodFeatureKeys: [],
|
||||
editOperateMonitorVisible: false,
|
||||
columns: [
|
||||
{ title: '名称', dataIndex: 'name', scopedSlots: { customRender: 'name' } },
|
||||
{ title: '开启状态', dataIndex: 'status', scopedSlots: { customRender: 'status' } },
|
||||
{ title: '修改人', dataIndex: 'modifyUser', scopedSlots: { customRender: 'modifyUser' } },
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: '开启状态',
|
||||
dataIndex: 'status'
|
||||
},
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser'
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '0') {
|
||||
return ''
|
||||
}
|
||||
@ -171,13 +181,20 @@ export default {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: 120
|
||||
fixed: 'right',
|
||||
width: '120px'
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
name: [{ required: true, message: 'Please input monitor name', trigger: 'blur' }]
|
||||
}
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入监控名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -191,23 +208,6 @@ export default {
|
||||
this.loadOptTypeData()
|
||||
},
|
||||
methods: {
|
||||
// 页面引导
|
||||
introGuide() {
|
||||
this.$store.dispatch('tryOpenGuide', {
|
||||
key: 'operate-log',
|
||||
options: {
|
||||
hidePrev: true,
|
||||
steps: [
|
||||
{
|
||||
title: '导航助手',
|
||||
element: document.querySelector('.jpom-monitor-notify'),
|
||||
intro: '这里面的报警联系人如果无法选择,您需要设置管理员的邮箱地址。'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载数据
|
||||
loadData(pointerEvent) {
|
||||
this.loading = true
|
||||
@ -263,11 +263,6 @@ export default {
|
||||
this.monitorUserKeys = []
|
||||
this.loadUserList()
|
||||
this.editOperateMonitorVisible = true
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.introGuide()
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
// 修改
|
||||
handleEdit(record) {
|
||||
@ -306,30 +301,27 @@ export default {
|
||||
// 提交
|
||||
handleEditOperateMonitorOk() {
|
||||
// 检验表单
|
||||
this.$refs['editMonitorForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editMonitorForm'].validate().then(() => {
|
||||
if (this.monitorUserKeys.length === 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择监控用户'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.methodFeatureKeys.length === 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择监控操作'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.classFeatureKeys.length === 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择监控的功能'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.notifyUserKeys.length === 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择报警联系人'
|
||||
})
|
||||
return false
|
||||
@ -340,23 +332,29 @@ export default {
|
||||
this.temp.monitorFeature = JSON.stringify(this.classFeatureKeys)
|
||||
this.temp.notifyUser = JSON.stringify(this.notifyUserKeys)
|
||||
this.temp.start ? (this.temp.status = 'on') : (this.temp.status = 'no')
|
||||
editMonitorOperate(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editMonitorForm'].resetFields()
|
||||
this.editOperateMonitorVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
this.confirmLoading = false
|
||||
editMonitorOperate(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.$refs['editMonitorForm'].resetFields()
|
||||
this.editOperateMonitorVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除操作监控么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -364,7 +362,7 @@ export default {
|
||||
// 删除
|
||||
deleteMonitorOperate(record.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -381,4 +379,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<workspaceEnv ref="workspaceEnv" :workspaceId="this.getWorkspaceId()" :global="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import workspaceEnv from '@/pages/system/workspace-env.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -15,7 +17,7 @@ export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: { ...mapGetters(['getWorkspaceId']) },
|
||||
computed: { ...mapState(useAppStore, ['getWorkspaceId']) },
|
||||
created() {},
|
||||
methods: {}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
@ -10,6 +10,9 @@
|
||||
bordered
|
||||
rowKey="id"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
@ -66,70 +69,72 @@
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'nodeId'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ nodeMap[text] }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.tooltip">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'workspaceId'">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<template v-if="mode === 'manage'">
|
||||
<a-button size="small" type="primary" @click="handleExec(record)">执行</a-button>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" @click="handleLog(record)">日志</a-button>
|
||||
<a-dropdown>
|
||||
<a @click="(e) => e.preventDefault()">
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a>
|
||||
<template v-slot:overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" @click="handleTrigger(record)">触发器</a-button>
|
||||
</a-menu-item>
|
||||
|
||||
<template v-slot:nodeId="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ nodeMap[text] }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:tooltip="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:global="text">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template v-slot:operation="text, record">
|
||||
<a-space>
|
||||
<template v-if="mode === 'manage'">
|
||||
<a-button size="small" type="primary" @click="handleExec(record)">执行</a-button>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" @click="handleLog(record)">日志</a-button>
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click="(e) => e.preventDefault()">
|
||||
更多
|
||||
<a-icon type="down" />
|
||||
</a>
|
||||
<template v-slot:overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" @click="handleTrigger(record)">触发器</a-button>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button
|
||||
size="small"
|
||||
type="primary"
|
||||
danger
|
||||
:disabled="!record.nodeIds"
|
||||
@click="handleUnbind(record)"
|
||||
>解绑</a-button
|
||||
>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
</template>
|
||||
</a-space>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button
|
||||
size="small"
|
||||
type="primary"
|
||||
danger
|
||||
:disabled="!record.nodeIds"
|
||||
@click="handleUnbind(record)"
|
||||
>解绑</a-button
|
||||
>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑区 -->
|
||||
<a-modal
|
||||
:zIndex="1009"
|
||||
destroyOnClose
|
||||
v-model:value="editScriptVisible"
|
||||
:zIndex="1009"
|
||||
v-model:open="editScriptVisible"
|
||||
title="编辑 Script"
|
||||
@ok="handleEditScriptOk"
|
||||
:maskClosable="false"
|
||||
width="80vw"
|
||||
:confirmLoading="confirmLoading"
|
||||
>
|
||||
<a-form ref="editScriptForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 19 }">
|
||||
<a-form-item v-if="temp.id" label="ScriptId" name="id">
|
||||
@ -139,41 +144,41 @@
|
||||
<a-input :maxLength="50" v-model:value="temp.name" placeholder="名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Script 内容" name="context">
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor
|
||||
v-model:value="temp.context"
|
||||
:options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }"
|
||||
></code-editor>
|
||||
</div>
|
||||
<a-form-item-rest>
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor v-model:content="temp.context" :options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }">
|
||||
</code-editor>
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label="默认参数" name="defArgs">
|
||||
<a-input v-model="temp.defArgs" placeholder="默认参数" />
|
||||
</a-form-item> -->
|
||||
<a-form-item label="默认参数">
|
||||
<div v-for="(item, index) in commandParams" :key="item.key">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-row v-for="(item, index) in commandParams" :key="item.key">
|
||||
<a-col :span="22">
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}描述`"
|
||||
v-model:value="item.desc"
|
||||
placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义"
|
||||
/>
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}值`"
|
||||
v-model:value="item.value"
|
||||
placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值"
|
||||
/>
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}描述`"
|
||||
v-model:value="item.desc"
|
||||
placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义" />
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}值`"
|
||||
v-model:value="item.value"
|
||||
placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值"
|
||||
/></a-space>
|
||||
</a-col>
|
||||
<a-col :span="2">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-col>
|
||||
<a-icon @click="() => commandParams.splice(index, 1)" type="minus-circle" style="color: #ff0000" />
|
||||
<MinusCircleOutlined @click="() => commandParams.splice(index, 1)" style="color: #ff0000" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider style="margin: 5px 0" />
|
||||
</div>
|
||||
</a-space>
|
||||
|
||||
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
|
||||
</a-form-item>
|
||||
@ -181,27 +186,15 @@
|
||||
<a-auto-complete
|
||||
v-model:value="temp.autoExecCron"
|
||||
placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])"
|
||||
option-label-prop="value"
|
||||
:options="CRON_DATA_SOURCE"
|
||||
>
|
||||
<template v-slot:dataSource>
|
||||
<a-select-opt-group v-for="group in cronDataSource" :key="group.title">
|
||||
<template v-slot:label>
|
||||
<span>
|
||||
{{ group.title }}
|
||||
</span>
|
||||
</template>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value">
|
||||
{{ opt.title }} {{ opt.value }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</template>
|
||||
<template #option="item"> {{ item.title }} {{ item.value }} </template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
<a-form-item label="描述" name="description">
|
||||
<a-input
|
||||
<a-textarea
|
||||
v-model:value="temp.description"
|
||||
:maxLength="200"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
style="resize: none"
|
||||
placeholder="详细描述"
|
||||
@ -246,7 +239,7 @@
|
||||
:title="drawerTitle"
|
||||
placement="right"
|
||||
width="85vw"
|
||||
:visible="drawerConsoleVisible"
|
||||
:open="drawerConsoleVisible"
|
||||
@close="onConsoleClose"
|
||||
>
|
||||
<script-console v-if="drawerConsoleVisible" :defArgs="temp.defArgs" :id="temp.id" />
|
||||
@ -254,12 +247,13 @@
|
||||
<!-- 同步到其他工作空间 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:value="syncToWorkspaceVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="syncToWorkspaceVisible"
|
||||
title="同步到其他工作空间"
|
||||
@ok="handleSyncToWorkspace"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<a-alert message="温馨提示" type="warning" show-icon>
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
<li>同步机制采用<b>脚本名称</b>确定是同一个脚本</li>
|
||||
@ -287,7 +281,7 @@
|
||||
<!-- 触发器 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:value="triggerVisible"
|
||||
v-model:open="triggerVisible"
|
||||
title="触发器"
|
||||
width="50%"
|
||||
:footer="null"
|
||||
@ -295,13 +289,13 @@
|
||||
>
|
||||
<a-form ref="editTriggerForm" :rules="rules" :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<a-tabs default-active-key="1">
|
||||
<template v-slot:tabBarExtraContent>
|
||||
<template v-slot:rightExtra>
|
||||
<a-tooltip title="重置触发器 token 信息,重置后之前的触发器 token 将失效">
|
||||
<a-button type="primary" size="small" @click="resetTrigger">重置</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tab-pane key="1" tab="执行">
|
||||
<a-space style="display: block" direction="vertical" align="baseline">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
@ -313,52 +307,18 @@
|
||||
</ul>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert
|
||||
v-clipboard:copy="temp.triggerUrl"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
tempVue.prototype.$notification.success({
|
||||
message: '复制成功'
|
||||
})
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
tempVue.prototype.$notification.error({
|
||||
message: '复制失败'
|
||||
})
|
||||
}
|
||||
"
|
||||
type="info"
|
||||
:message="`单个触发器地址(点击可以复制)`"
|
||||
>
|
||||
<a-alert type="info" :message="`单个触发器地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-tag>GET</a-tag> <span>{{ temp.triggerUrl }} </span>
|
||||
<a-icon type="copy" />
|
||||
<a-typography-paragraph :copyable="{ text: temp.triggerUrl }">
|
||||
<a-tag>GET</a-tag> <span>{{ temp.triggerUrl }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert
|
||||
v-clipboard:copy="temp.batchTriggerUrl"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
tempVue.prototype.$notification.success({
|
||||
message: '复制成功'
|
||||
})
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
tempVue.prototype.$notification.error({
|
||||
message: '复制失败'
|
||||
})
|
||||
}
|
||||
"
|
||||
type="info"
|
||||
:message="`批量触发器地址(点击可以复制)`"
|
||||
>
|
||||
<a-alert type="info" :message="`批量触发器地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-tag>POST</a-tag> <span>{{ temp.batchTriggerUrl }} </span>
|
||||
<a-icon type="copy" />
|
||||
<a-typography-paragraph :copyable="{ text: temp.batchTriggerUrl }">
|
||||
<a-tag>POST</a-tag> <span>{{ temp.batchTriggerUrl }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
</a-space>
|
||||
@ -370,8 +330,8 @@
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
title="脚本执行历史"
|
||||
width="50vw"
|
||||
:visible="drawerLogVisible"
|
||||
width="70vw"
|
||||
:open="drawerLogVisible"
|
||||
@close="
|
||||
() => {
|
||||
this.drawerLogVisible = false
|
||||
@ -380,7 +340,7 @@
|
||||
>
|
||||
<script-log v-if="drawerLogVisible" :scriptId="temp.id" />
|
||||
</a-drawer>
|
||||
<div style="padding-top: 50px" v-if="mode === 'choose'">
|
||||
<!-- <div style="padding-top: 50px" v-if="mode === 'choose'">
|
||||
<div
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
@ -407,7 +367,7 @@
|
||||
<a-button type="primary" @click="handerConfirm"> 确定 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -425,10 +385,12 @@ import codeEditor from '@/components/codeEditor'
|
||||
import { getNodeListAll } from '@/api/node'
|
||||
import ScriptConsole from '@/pages/script/script-console'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, CRON_DATA_SOURCE, PAGE_DEFAULT_LIST_QUERY, parseTime } from '@/utils/const'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import { getWorkSpaceListAll } from '@/api/workspace'
|
||||
import * as Vue from 'vue'
|
||||
|
||||
import ScriptLog from '@/pages/script/script-log'
|
||||
import { mapState } from 'pinia'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
export default {
|
||||
components: {
|
||||
ScriptConsole,
|
||||
@ -456,7 +418,7 @@ export default {
|
||||
// choose: this.choose,
|
||||
loading: false,
|
||||
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
|
||||
cronDataSource: CRON_DATA_SOURCE,
|
||||
CRON_DATA_SOURCE,
|
||||
list: [],
|
||||
temp: {},
|
||||
nodeList: [],
|
||||
@ -469,8 +431,8 @@ export default {
|
||||
dataIndex: 'id',
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
width: 50,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
@ -478,22 +440,22 @@ export default {
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '共享',
|
||||
dataIndex: 'workspaceId',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'global' },
|
||||
|
||||
width: '90px'
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
ellipsis: true,
|
||||
width: 300,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
width: 100,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '定时执行',
|
||||
@ -501,7 +463,7 @@ export default {
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
width: '100px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
@ -509,7 +471,7 @@ export default {
|
||||
sorter: true,
|
||||
width: '170px',
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text)
|
||||
customRender: ({ text }) => parseTime(text)
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
@ -517,20 +479,20 @@ export default {
|
||||
sorter: true,
|
||||
width: '170px',
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text)
|
||||
customRender: ({ text }) => parseTime(text)
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' },
|
||||
tooltip: true,
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' },
|
||||
tooltip: true,
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
@ -538,14 +500,14 @@ export default {
|
||||
dataIndex: 'lastRunUser',
|
||||
ellipsis: true,
|
||||
width: '120px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
this.mode === 'manage'
|
||||
? {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
fixed: 'right',
|
||||
width: '240px'
|
||||
}
|
||||
@ -553,7 +515,7 @@ export default {
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
fixed: 'right',
|
||||
width: '100px'
|
||||
}
|
||||
@ -567,11 +529,12 @@ export default {
|
||||
workspaceList: [],
|
||||
triggerVisible: false,
|
||||
commandParams: [],
|
||||
drawerLogVisible: false
|
||||
drawerLogVisible: false,
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getWorkspaceId']),
|
||||
...mapState(useAppStore, ['getWorkspaceId']),
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
@ -662,10 +625,7 @@ export default {
|
||||
// 提交 Script 数据
|
||||
handleEditScriptOk() {
|
||||
// 检验表单
|
||||
this.$refs['editScriptForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editScriptForm'].validate().then(() => {
|
||||
if (this.commandParams && this.commandParams.length > 0) {
|
||||
for (let i = 0; i < this.commandParams.length; i++) {
|
||||
if (!this.commandParams[i].desc) {
|
||||
@ -682,18 +642,23 @@ export default {
|
||||
// 提交数据
|
||||
this.temp.nodeIds = this.temp?.chooseNode?.join(',')
|
||||
delete this.temp.nodeList
|
||||
editScript(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.confirmLoading = true
|
||||
editScript(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.editScriptVisible = false
|
||||
this.loadData()
|
||||
this.$refs['editScriptForm'].resetFields()
|
||||
}
|
||||
})
|
||||
this.editScriptVisible = false
|
||||
this.loadData()
|
||||
this.$refs['editScriptForm'].resetFields()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
handleDelete(record) {
|
||||
@ -748,9 +713,9 @@ export default {
|
||||
this.$confirm({
|
||||
title: '危险操作!!!',
|
||||
zIndex: 1009,
|
||||
content: h('div', null, [h('p', { domProps: { innerHTML: html } }, null)]),
|
||||
okButtonProps: { props: { type: 'danger', size: 'small' } },
|
||||
cancelButtonProps: { props: { type: 'primary' } },
|
||||
content: h('div', null, [h('p', { innerHTML: html }, null)]),
|
||||
okButtonProps: { type: 'primary', danger: true, size: 'small' },
|
||||
cancelButtonProps: { type: 'primary' },
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
@ -793,24 +758,29 @@ export default {
|
||||
return false
|
||||
}
|
||||
// 同步
|
||||
this.confirmLoading = true
|
||||
syncToWorkspace({
|
||||
ids: this.tableSelections.join(','),
|
||||
toWorkspaceId: this.temp.workspaceId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
// 触发器
|
||||
handleTrigger(record) {
|
||||
this.temp = Object.assign({}, record)
|
||||
this.tempVue = Vue
|
||||
|
||||
getTriggerUrl({
|
||||
id: record.id
|
||||
}).then((res) => {
|
||||
@ -853,12 +823,12 @@ export default {
|
||||
return
|
||||
}
|
||||
if (this.choose === 'checkbox') {
|
||||
$emit(this, 'confirm', this.tableSelections.join(','))
|
||||
this.$emit('confirm', this.tableSelections.join(','))
|
||||
} else {
|
||||
const selectData = this.list.filter((item) => {
|
||||
return item.id === this.tableSelections[0]
|
||||
})[0]
|
||||
$emit(this, 'confirm', `${selectData.id}`)
|
||||
this.$emit('confirm', `${selectData.id}`)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1,21 +1,51 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<!-- 数据表格 -->
|
||||
<a-table :data-source="list" size="middle" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
|
||||
<a-table
|
||||
:data-source="list"
|
||||
size="middle"
|
||||
:columns="columns"
|
||||
@change="changePage"
|
||||
:pagination="pagination"
|
||||
bordered
|
||||
rowKey="id"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input v-model:value="listQuery['%scriptName%']" placeholder="名称" @pressEnter="loadData" allowClear class="search-input-item" />
|
||||
<a-select show-search option-filter-prop="children" v-model:value="listQuery.triggerExecType" allowClear placeholder="触发类型" class="search-input-item">
|
||||
<a-input
|
||||
v-model:value="listQuery['%scriptName%']"
|
||||
placeholder="名称"
|
||||
@pressEnter="loadData"
|
||||
allowClear
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model:value="listQuery.triggerExecType"
|
||||
allowClear
|
||||
placeholder="触发类型"
|
||||
class="search-input-item"
|
||||
>
|
||||
<a-select-option v-for="(val, key) in triggerExecTypeMap" :key="key">{{ val }}</a-select-option>
|
||||
</a-select>
|
||||
<a-select show-search option-filter-prop="children" v-model:value="listQuery.status" allowClear placeholder="状态" class="search-input-item">
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model:value="listQuery.status"
|
||||
allowClear
|
||||
placeholder="状态"
|
||||
class="search-input-item"
|
||||
>
|
||||
<a-select-option v-for="(val, key) in statusMap" :key="key">{{ val }}</a-select-option>
|
||||
</a-select>
|
||||
<a-range-picker
|
||||
v-model:value="listQuery['createTimeMillis']"
|
||||
allowClear
|
||||
inputReadOnly
|
||||
class="search-input-item"
|
||||
:show-time="{ format: 'HH:mm:ss' }"
|
||||
:placeholder="['执行时间开始', '执行时间结束']"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
@ -26,41 +56,43 @@
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template v-slot:scriptName="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:modifyUser="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:triggerExecTypeMap="text">
|
||||
<span>{{ triggerExecTypeMap[text] || "未知" }}</span>
|
||||
</template>
|
||||
<template v-slot:global="text">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template v-slot:createTimeMillis="text, record">
|
||||
<a-tooltip :title="`${parseTime(record.createTimeMillis)}`">
|
||||
<span>{{ parseTime(record.createTimeMillis) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:exitCode="text">
|
||||
<a-tag v-if="text == 0" color="green">成功</a-tag>
|
||||
<a-tag v-else color="orange">{{ text || "-" }}</a-tag>
|
||||
</template>
|
||||
<template v-slot:status="text">
|
||||
<span>{{ statusMap[text] || "" }}</span>
|
||||
</template>
|
||||
<template v-slot:operation="text, record">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="viewLog(record)">查看日志</a-button>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'scriptName'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'modifyUser'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'triggerExecType'">
|
||||
<span>{{ triggerExecTypeMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'workspaceId'">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'createTimeMillis'">
|
||||
<a-tooltip :title="`${parseTime(record.createTimeMillis)}`">
|
||||
<span>{{ parseTime(record.createTimeMillis) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'exitCode'">
|
||||
<a-tag v-if="text == 0" color="green">成功</a-tag>
|
||||
<a-tag v-else color="orange">{{ text || '-' }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<span>{{ statusMap[text] || '' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" @click="viewLog(record)">查看日志</a-button>
|
||||
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 日志 -->
|
||||
@ -70,7 +102,7 @@
|
||||
:visible="logVisible != 0"
|
||||
@close="
|
||||
() => {
|
||||
logVisible = 0;
|
||||
logVisible = 0
|
||||
}
|
||||
"
|
||||
:temp="temp"
|
||||
@ -79,20 +111,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getScriptLogList, scriptDel, triggerExecTypeMap } from "@/api/server-script";
|
||||
import ScriptLogView from "@/pages/script/script-log-view";
|
||||
import { statusMap } from "@/api/command";
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
|
||||
import { getScriptLogList, scriptDel, triggerExecTypeMap } from '@/api/server-script'
|
||||
import ScriptLogView from '@/pages/script/script-log-view'
|
||||
import { statusMap } from '@/api/command'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from '@/utils/const'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ScriptLogView,
|
||||
ScriptLogView
|
||||
},
|
||||
props: {
|
||||
scriptId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -105,123 +137,123 @@ export default {
|
||||
logVisible: 0,
|
||||
columns: [
|
||||
{
|
||||
title: "名称",
|
||||
dataIndex: "scriptName",
|
||||
title: '名称',
|
||||
dataIndex: 'scriptName',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: "scriptName" },
|
||||
scopedSlots: { customRender: 'scriptName' }
|
||||
},
|
||||
{
|
||||
title: "执行时间",
|
||||
dataIndex: "createTimeMillis",
|
||||
title: '执行时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
width: "160px",
|
||||
scopedSlots: { customRender: "createTimeMillis" },
|
||||
width: '160px',
|
||||
scopedSlots: { customRender: 'createTimeMillis' }
|
||||
},
|
||||
{
|
||||
title: "触发类型",
|
||||
dataIndex: "triggerExecType",
|
||||
title: '触发类型',
|
||||
dataIndex: 'triggerExecType',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: "triggerExecTypeMap" },
|
||||
scopedSlots: { customRender: 'triggerExecTypeMap' }
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: "status" },
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: "执行域",
|
||||
dataIndex: "workspaceId",
|
||||
title: '执行域',
|
||||
dataIndex: 'workspaceId',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: "global" },
|
||||
width: "90px",
|
||||
scopedSlots: { customRender: 'global' },
|
||||
width: '90px'
|
||||
},
|
||||
{
|
||||
title: "退出码",
|
||||
dataIndex: "exitCode",
|
||||
title: '退出码',
|
||||
dataIndex: 'exitCode',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: "exitCode" },
|
||||
scopedSlots: { customRender: 'exitCode' }
|
||||
},
|
||||
{
|
||||
title: "执行人",
|
||||
dataIndex: "modifyUser",
|
||||
title: '执行人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
width: "100px",
|
||||
scopedSlots: { customRender: "modifyUser" },
|
||||
width: '100px',
|
||||
scopedSlots: { customRender: 'modifyUser' }
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
dataIndex: "operation",
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
scopedSlots: { customRender: "operation" },
|
||||
width: "150px",
|
||||
},
|
||||
],
|
||||
};
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: '150px'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery);
|
||||
},
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
// 加载数据
|
||||
loadData(pointerEvent) {
|
||||
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page;
|
||||
this.listQuery.scriptId = this.scriptId;
|
||||
this.loading = true;
|
||||
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page
|
||||
this.listQuery.scriptId = this.scriptId
|
||||
this.loading = true
|
||||
getScriptLogList(this.listQuery).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.list = res.data.result;
|
||||
this.listQuery.total = res.data.total;
|
||||
this.list = res.data.result
|
||||
this.listQuery.total = res.data.total
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
parseTime,
|
||||
viewLog(record) {
|
||||
this.logVisible = new Date() * Math.random();
|
||||
this.temp = record;
|
||||
this.logVisible = new Date() * Math.random()
|
||||
this.temp = record
|
||||
},
|
||||
handleDelete(record) {
|
||||
this.$confirm({
|
||||
title: "系统提示",
|
||||
content: "真的要删除执行记录么?",
|
||||
title: '系统提示',
|
||||
content: '真的要删除执行记录么?',
|
||||
zIndex: 1009,
|
||||
okText: "确认",
|
||||
cancelText: "取消",
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
// 组装参数
|
||||
const params = {
|
||||
id: record.scriptId,
|
||||
executeId: record.id,
|
||||
};
|
||||
executeId: record.id
|
||||
}
|
||||
// 删除
|
||||
scriptDel(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg,
|
||||
});
|
||||
this.loadData();
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 分页、排序、筛选变化时触发
|
||||
changePage(pagination, filters, sorter) {
|
||||
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter });
|
||||
this.loadData();
|
||||
},
|
||||
},
|
||||
};
|
||||
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter })
|
||||
this.loadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<a-table
|
||||
size="middle"
|
||||
:data-source="commandList"
|
||||
@ -7,18 +7,20 @@
|
||||
bordered
|
||||
:pagination="pagination"
|
||||
@change="changePage"
|
||||
:rowKey="(record, index) => index"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%commandName%']"
|
||||
v-model:value="listQuery['%commandName%']"
|
||||
@pressEnter="getCommandLogData"
|
||||
placeholder="搜索命令名称"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model="listQuery['%sshName%']"
|
||||
v-model:value="listQuery['%sshName%']"
|
||||
@pressEnter="getCommandLogData"
|
||||
placeholder="搜索ssh名称"
|
||||
class="search-input-item"
|
||||
@ -26,7 +28,7 @@
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model="listQuery.status"
|
||||
v-model:value="listQuery.status"
|
||||
allowClear
|
||||
placeholder="状态"
|
||||
class="search-input-item"
|
||||
@ -36,7 +38,7 @@
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model="listQuery.triggerExecType"
|
||||
v-model:value="listQuery.triggerExecType"
|
||||
allowClear
|
||||
placeholder="触发类型"
|
||||
class="search-input-item"
|
||||
@ -48,39 +50,51 @@
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #sshName slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip #commandName slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #status slot-scope="text">
|
||||
<span>{{ statusMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template #triggerExecTypeMap slot-scope="text">
|
||||
<span>{{ triggerExecTypeMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'sshName'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'commandName'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'status'">
|
||||
<span>{{ statusMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'triggerExecType'">
|
||||
<span>{{ triggerExecTypeMap[text] || '未知' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'exitCode'">
|
||||
<a-tag v-if="text == 0" color="green">成功</a-tag>
|
||||
<a-tag v-else color="orange">{{ text || '-' }}</a-tag>
|
||||
</template>
|
||||
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" :disabled="!record.hasLog" @click="handleView(record)">查看</a-button>
|
||||
<a-button type="primary" size="small" :disabled="!record.hasLog" @click="handleDownload(record)"
|
||||
><a-icon type="download" />日志</a-button
|
||||
>
|
||||
<a-button type="danger" size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button type="primary" size="small" :disabled="!record.hasLog" @click="handleView(record)">查看</a-button>
|
||||
<a-button type="primary" size="small" :disabled="!record.hasLog" @click="handleDownload(record)"
|
||||
><DownloadOutlined />日志</a-button
|
||||
>
|
||||
<a-button type="primary" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 构建日志 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:width="'80vw'"
|
||||
v-model:visible="logVisible"
|
||||
:width="style.width"
|
||||
:bodyStyle="style.bodyStyle"
|
||||
:style="style.style"
|
||||
v-model:open="logVisible"
|
||||
title="执行日志"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<command-log v-if="logVisible" :temp="temp" />
|
||||
<command-log :height="style.bodyStyle.height" v-if="logVisible" :temp="temp" />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
@ -89,7 +103,8 @@
|
||||
import { deleteCommandLog, downloadLog, getCommandLogList, statusMap, triggerExecTypeMap } from '@/api/command'
|
||||
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from '@/utils/const'
|
||||
import CommandLog from './command-view-log'
|
||||
|
||||
import { mapState } from 'pinia'
|
||||
import { useGuideStore } from '@/stores/guide'
|
||||
export default {
|
||||
components: {
|
||||
CommandLog
|
||||
@ -104,58 +119,80 @@ export default {
|
||||
triggerExecTypeMap: triggerExecTypeMap,
|
||||
logVisible: false,
|
||||
columns: [
|
||||
{ title: 'ssh 名称', dataIndex: 'sshName', ellipsis: true, scopedSlots: { customRender: 'sshName' } },
|
||||
{ title: '命令名称', dataIndex: 'commandName', ellipsis: true, scopedSlots: { customRender: 'commandName' } },
|
||||
{ title: '状态', dataIndex: 'status', width: 100, ellipsis: true, scopedSlots: { customRender: 'status' } },
|
||||
{
|
||||
title: 'ssh 名称',
|
||||
dataIndex: 'sshName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '命令名称',
|
||||
dataIndex: 'commandName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '退出码',
|
||||
dataIndex: 'exitCode',
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '触发类型',
|
||||
dataIndex: 'triggerExecType',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'triggerExecTypeMap' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '执行时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
width: 170
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '结束时间',
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
width: 170
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '执行人',
|
||||
dataIndex: 'modifyUser',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: 200
|
||||
fixed: 'right',
|
||||
width: '200px'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(useGuideStore, ['getFullscreenViewLogStyle']),
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
style() {
|
||||
return this.getFullscreenViewLogStyle()
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
mounted() {
|
||||
this.getCommandLogData()
|
||||
},
|
||||
@ -184,8 +221,9 @@ export default {
|
||||
},
|
||||
// 删除命令
|
||||
handleDelete(row) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除该执行记录吗?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -193,7 +231,7 @@ export default {
|
||||
// 删除
|
||||
deleteCommandLog(row.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.getCommandLogData()
|
||||
@ -209,4 +247,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,27 +1,34 @@
|
||||
<template>
|
||||
<div style="margin-top: -10px">
|
||||
<div>
|
||||
<a-tabs :activeKey="activeKey" @change="tabCallback">
|
||||
<a-tab-pane v-for="item in logList" :key="item.id">
|
||||
<span #tab>
|
||||
<a-icon v-if="!logMap[item.id] || logMap[item.id].run" type="loading" />
|
||||
{{ item.sshName }}
|
||||
</span>
|
||||
<template v-slot:tab>
|
||||
<span>
|
||||
<LoadingOutlined v-if="!logMap[item.id] || logMap[item.id].run" />
|
||||
{{ item.sshName }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- <a-input :id="`build-log-textarea-${item.id}`" v-model="logMap[item.id].logText" type="textarea" class="console" readOnly style="resize: none; height: 60vh" /> -->
|
||||
<log-view :ref="`logView-${item.id}`" height="60vh" />
|
||||
<log-view1 :ref="`logView-${item.id}`" :height="`calc(${height} - 130px)`" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCommandLogBarchList, getCommandLogInfo } from '@/api/command'
|
||||
import LogView from '@/components/logView'
|
||||
import LogView1 from '@/components/logView/index2'
|
||||
export default {
|
||||
components: {
|
||||
LogView
|
||||
LogView1
|
||||
},
|
||||
props: {
|
||||
temp: {
|
||||
type: Object
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '60vh'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -32,7 +39,7 @@ export default {
|
||||
logMap: {}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
if (this.logTimerMap) {
|
||||
this.logList.forEach((item) => {
|
||||
clearInterval(this.logTimerMap[item.id])
|
||||
@ -79,7 +86,7 @@ export default {
|
||||
getCommandLogInfo(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (!res.data) {
|
||||
$notification.warning({
|
||||
this.$notification.warning({
|
||||
message: res.msg
|
||||
})
|
||||
this.logMap[item.id].tryCount = this.logMap[item.id].tryCount + 1
|
||||
@ -115,7 +122,7 @@ export default {
|
||||
this.logMap[item.id].line = res.data.line
|
||||
// if (lines.length) {
|
||||
// // 自动滚动到底部
|
||||
// nextTick(() => {
|
||||
// this.$nextTick(() => {
|
||||
// setTimeout(() => {
|
||||
// const textarea = document.getElementById("build-log-textarea-" + item.id);
|
||||
// if (textarea) {
|
||||
@ -135,7 +142,7 @@ export default {
|
||||
if (this.logTimerMap[key]) {
|
||||
return
|
||||
}
|
||||
nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
const index = this.logList
|
||||
.map((item1, index) => {
|
||||
return item1.id == key ? index : -1
|
||||
@ -147,6 +154,7 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* .console {
|
||||
padding: 5px;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<div>
|
||||
<a-table
|
||||
:data-source="commandList"
|
||||
:columns="columns"
|
||||
@ -9,23 +9,26 @@
|
||||
@change="changePage"
|
||||
:row-selection="rowSelection"
|
||||
rowKey="id"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
@pressEnter="getCommandData"
|
||||
placeholder="搜索命令"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model="listQuery['%desc%']"
|
||||
v-model:value="listQuery['%desc%']"
|
||||
@pressEnter="getCommandData"
|
||||
placeholder="描述"
|
||||
class="search-input-item"
|
||||
/>
|
||||
<a-input
|
||||
v-model="listQuery['%autoExecCron%']"
|
||||
v-model:value="listQuery['%autoExecCron%']"
|
||||
@pressEnter="getCommandData"
|
||||
placeholder="定时执行"
|
||||
class="search-input-item"
|
||||
@ -35,8 +38,8 @@
|
||||
</a-tooltip>
|
||||
<a-button type="primary" @click="createCommand">新建命令</a-button>
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click="(e) => e.preventDefault()"> 更多 <down-outlined /> </a>
|
||||
<template #overlay>
|
||||
<a @click="(e) => e.preventDefault()"> 更多 <DownOutlined /> </a>
|
||||
<template v-slot:overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-button
|
||||
@ -50,7 +53,7 @@
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-tooltip>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<div>命令模版是用于在线管理一些脚本命令,如初始化软件环境、管理应用程序等</div>
|
||||
|
||||
<div>
|
||||
@ -61,63 +64,76 @@
|
||||
命令文件、并自动加载环境变量:/etc/profile、/etc/bashrc、~/.bashrc、~/.bash_profile
|
||||
</li>
|
||||
<li>
|
||||
执行命令包含:<b>#disabled-template-auto-evn</b> 将取消自动加载环境变量(注意是整行不能包含空格)
|
||||
执行命令包含:<b>#disabled-template-auto-evn</b>
|
||||
将取消自动加载环境变量(注意是整行不能包含空格)
|
||||
</li>
|
||||
<li>命令文件将上传至 ${user.home}/.jpom/xxxx.sh 执行完成将自动删除</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<question-circle-filled />
|
||||
<QuestionCircleOutlined />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #name slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip #desc slot-scope="text" placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.dataIndex === 'name'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'desc'">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleExecute(record)">执行</a-button>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" @click="handleTrigger(record)">触发器</a-button>
|
||||
<a-button size="small" type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleExecute(record)">执行</a-button>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" @click="handleTrigger(record)">触发器</a-button>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑命令 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="editCommandVisible"
|
||||
v-model:open="editCommandVisible"
|
||||
width="80vw"
|
||||
title="编辑 命令"
|
||||
@ok="handleEditCommandOk"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
>
|
||||
<a-form ref="editCommandForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="命令名称" name="name">
|
||||
<a-input v-model="temp.name" :maxLength="100" placeholder="命令名称" />
|
||||
<a-input v-model:value="temp.name" :maxLength="100" placeholder="命令名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
name="command"
|
||||
help="脚本存放路径:${user.home}/.jpom/xxxx.sh,执行脚本路径:${user.home},执行脚本方式:bash ${user.home}/.jpom/xxxx.sh par1 par2"
|
||||
>
|
||||
<template #label>
|
||||
命令内容
|
||||
<a-tooltip v-show="!temp.id">
|
||||
<template #title>
|
||||
<template v-slot:label>
|
||||
<a-tooltip>
|
||||
命令内容
|
||||
<template v-slot:title>
|
||||
<ul>
|
||||
<li>可以引用工作空间的环境变量 变量占位符 ${xxxx} xxxx 为变量名称</li>
|
||||
</ul>
|
||||
</template>
|
||||
<question-circle-filled />
|
||||
<QuestionCircleOutlined v-show="!temp.id" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<div style="height: 40vh; overflow-y: scroll">
|
||||
<code-editor v-model="temp.command" :options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }"></code-editor>
|
||||
<a-form-item-rest>
|
||||
<code-editor
|
||||
v-model:content="temp.command"
|
||||
:options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }"
|
||||
></code-editor>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="SSH节点">
|
||||
@ -126,7 +142,7 @@
|
||||
option-filter-prop="children"
|
||||
placeholder="请选择SSH节点"
|
||||
mode="multiple"
|
||||
v-model="chooseSsh"
|
||||
v-model:value="chooseSsh"
|
||||
>
|
||||
<a-select-option v-for="item in sshList" :key="item.id" :value="item.id">
|
||||
{{ item.name }}
|
||||
@ -135,56 +151,49 @@
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="默认参数">
|
||||
<div v-for="(item, index) in commandParams" :key="item.key">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-col :span="22">
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}描述`"
|
||||
v-model="item.desc"
|
||||
placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义"
|
||||
/>
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}值`"
|
||||
v-model="item.value"
|
||||
placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="2">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-col>
|
||||
<a-icon @click="() => commandParams.splice(index, 1)" type="minus-circle" style="color: #ff0000" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider style="margin: 5px 0" />
|
||||
</div>
|
||||
<a-form-item-rest>
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-row v-for="(item, index) in commandParams" :key="item.key">
|
||||
<a-col :span="22">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}描述`"
|
||||
v-model:value="item.desc"
|
||||
placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义"
|
||||
/>
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}值`"
|
||||
v-model:value="item.value"
|
||||
placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值"
|
||||
/>
|
||||
</a-space>
|
||||
</a-col>
|
||||
|
||||
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
|
||||
<a-col :span="2">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-col>
|
||||
<MinusCircleOutlined @click="() => commandParams.splice(index, 1)" style="color: #ff0000" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
|
||||
</a-space>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
<a-form-item label="自动执行" name="autoExecCron">
|
||||
<a-auto-complete
|
||||
v-model="temp.autoExecCron"
|
||||
v-model:value="temp.autoExecCron"
|
||||
placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])"
|
||||
option-label-prop="value"
|
||||
:options="CRON_DATA_SOURCE"
|
||||
>
|
||||
<template #dataSource>
|
||||
<a-select-opt-group v-for="group in cronDataSource" :key="group.title">
|
||||
<span #label>
|
||||
{{ group.title }}
|
||||
</span>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value">
|
||||
{{ opt.title }} {{ opt.value }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</template>
|
||||
<template #option="item"> {{ item.title }} {{ item.value }} </template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
<a-form-item label="命令描述" name="desc">
|
||||
<a-input
|
||||
v-model="temp.desc"
|
||||
<a-textarea
|
||||
v-model:value="temp.desc"
|
||||
:maxLength="255"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
style="resize: none"
|
||||
placeholder="命令详细描述"
|
||||
@ -195,15 +204,16 @@
|
||||
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="executeCommandVisible"
|
||||
v-model:open="executeCommandVisible"
|
||||
width="600px"
|
||||
title="执行 命令"
|
||||
@ok="handleExecuteCommandOk"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
>
|
||||
<a-form :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item label="命令名称" name="name">
|
||||
<a-input v-model="temp.name" :disabled="true" placeholder="命令名称" />
|
||||
<a-input v-model:value="temp.name" :disabled="true" placeholder="命令名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="SSH节点" required>
|
||||
@ -211,7 +221,7 @@
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
mode="multiple"
|
||||
v-model="chooseSsh"
|
||||
v-model:value="chooseSsh"
|
||||
placeholder="请选择 SSH节点"
|
||||
>
|
||||
<a-select-option v-for="item in sshList" :key="item.id" :value="item.id">
|
||||
@ -228,30 +238,32 @@
|
||||
: ''
|
||||
}`"
|
||||
>
|
||||
<a-row v-for="(item, index) in commandParams" :key="item.key">
|
||||
<a-col :span="22">
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}值`"
|
||||
v-model="item.value"
|
||||
:placeholder="`参数值 ${item.desc ? ',' + item.desc : ''}`"
|
||||
>
|
||||
<template #suffix>
|
||||
<a-tooltip v-if="item.desc" :title="item.desc">
|
||||
<a-icon type="info-circle" style="color: rgba(0, 0, 0, 0.45)" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-row v-for="(item, index) in commandParams" :key="item.key">
|
||||
<a-col :span="22">
|
||||
<a-input
|
||||
:addon-before="`参数${index + 1}值`"
|
||||
v-model:value="item.value"
|
||||
:placeholder="`参数值 ${item.desc ? ',' + item.desc : ''}`"
|
||||
>
|
||||
<template v-slot:suffix>
|
||||
<a-tooltip v-if="item.desc" :title="item.desc">
|
||||
<InfoCircleOutlined />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
|
||||
<a-col v-if="!item.desc" :span="2">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-col>
|
||||
<a-icon type="minus-circle" style="color: #ff0000" @click="() => commandParams.splice(index, 1)" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
|
||||
<a-col v-if="!item.desc" :span="2">
|
||||
<a-row type="flex" justify="center" align="middle">
|
||||
<a-col>
|
||||
<MinusCircleOutlined style="color: #ff0000" @click="() => commandParams.splice(index, 1)" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
@ -259,7 +271,7 @@
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:width="'80vw'"
|
||||
v-model:visible="logVisible"
|
||||
v-model:open="logVisible"
|
||||
title="执行日志"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
@ -269,13 +281,14 @@
|
||||
<!-- 同步到其他工作空间 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="syncToWorkspaceVisible"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-model:open="syncToWorkspaceVisible"
|
||||
title="同步到其他工作空间"
|
||||
@ok="handleSyncToWorkspace"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<template #description>
|
||||
<a-alert message="温馨提示" type="warning" show-icon>
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
<li>同步机制采用<b>脚本名称</b>确定是同一个脚本</li>
|
||||
<li>当目标工作空间不存在对应的 脚本 时候将自动创建一个新的 脚本</li>
|
||||
@ -286,7 +299,12 @@
|
||||
<a-form :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
|
||||
<a-form-item> </a-form-item>
|
||||
<a-form-item label="选择工作空间" name="workspaceId">
|
||||
<a-select show-search option-filter-prop="children" v-model="temp.workspaceId" placeholder="请选择工作空间">
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model:value="temp.workspaceId"
|
||||
placeholder="请选择工作空间"
|
||||
>
|
||||
<a-select-option :disabled="getWorkspaceId() === item.id" v-for="item in workspaceList" :key="item.id">{{
|
||||
item.name
|
||||
}}</a-select-option>
|
||||
@ -298,7 +316,7 @@
|
||||
<!-- 触发器 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model:visible="triggerVisible"
|
||||
v-model:open="triggerVisible"
|
||||
title="触发器"
|
||||
width="50%"
|
||||
:footer="null"
|
||||
@ -306,15 +324,15 @@
|
||||
>
|
||||
<a-form ref="editTriggerForm" :rules="rules" :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<a-tabs default-active-key="1">
|
||||
<template #tabBarExtraContent>
|
||||
<template v-slot:tabBarExtraContent>
|
||||
<a-tooltip title="重置触发器 token 信息,重置后之前的触发器 token 将失效">
|
||||
<a-button type="primary" size="small" @click="resetTrigger">重置</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tab-pane key="1" tab="执行">
|
||||
<a-space style="display: block" direction="vertical" align="baseline">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<template #description>
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
<li>单个触发器地址中:第一个随机字符串为命令脚本ID,第二个随机字符串为 token</li>
|
||||
<li>
|
||||
@ -324,44 +342,18 @@
|
||||
</ul>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert
|
||||
v-clipboard:copy="temp.triggerUrl"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
tempVue.prototype.$notification.success({ message: '复制成功' })
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
tempVue.prototype.$notification.error({ message: '复制失败' })
|
||||
}
|
||||
"
|
||||
type="info"
|
||||
:message="`单个触发器地址(点击可以复制)`"
|
||||
>
|
||||
<template #description>
|
||||
<a-tag>GET</a-tag> <span>{{ temp.triggerUrl }} </span>
|
||||
<a-icon type="copy" />
|
||||
<a-alert type="info" :message="`单个触发器地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-typography-paragraph :copyable="{ tooltip: false, text: temp.triggerUrl }">
|
||||
<a-tag>GET</a-tag> <span>{{ temp.triggerUrl }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert
|
||||
v-clipboard:copy="temp.batchTriggerUrl"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
tempVue.prototype.$notification.success({ message: '复制成功' })
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
tempVue.prototype.$notification.error({ message: '复制失败' })
|
||||
}
|
||||
"
|
||||
type="info"
|
||||
:message="`批量触发器地址(点击可以复制)`"
|
||||
>
|
||||
<template #description>
|
||||
<a-tag>POST</a-tag> <span>{{ temp.batchTriggerUrl }} </span>
|
||||
<a-icon type="copy" />
|
||||
<a-alert type="info" :message="`批量触发器地址(点击可以复制)`">
|
||||
<template v-slot:description>
|
||||
<a-typography-paragraph :copyable="{ tooltip: false, text: temp.batchTriggerUrl }">
|
||||
<a-tag>POST</a-tag> <span>{{ temp.batchTriggerUrl }} </span>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</a-alert>
|
||||
</a-space>
|
||||
@ -378,16 +370,16 @@ import { CHANGE_PAGE, COMPUTED_PAGINATION, CRON_DATA_SOURCE, PAGE_DEFAULT_LIST_Q
|
||||
import { getSshListAll } from '@/api/ssh'
|
||||
import codeEditor from '@/components/codeEditor'
|
||||
import CommandLog from './command-view-log'
|
||||
import { mapState } from 'pinia'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { getWorkSpaceListAll } from '@/api/workspace'
|
||||
// import Vue from 'vue'
|
||||
|
||||
import { mapState } from 'pinia'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
export default {
|
||||
components: { codeEditor, CommandLog },
|
||||
data() {
|
||||
return {
|
||||
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
|
||||
cronDataSource: CRON_DATA_SOURCE,
|
||||
CRON_DATA_SOURCE,
|
||||
commandList: [],
|
||||
loading: false,
|
||||
editCommandVisible: false,
|
||||
@ -402,21 +394,30 @@ export default {
|
||||
command: [{ required: true, message: 'Please input command', trigger: 'blur' }]
|
||||
},
|
||||
columns: [
|
||||
{ title: '命令名称', dataIndex: 'name', ellipsis: true, width: 200, scopedSlots: { customRender: 'name' } },
|
||||
{ title: '命令描述', dataIndex: 'desc', ellipsis: true, width: 250, scopedSlots: { customRender: 'desc' } },
|
||||
{
|
||||
title: '命令名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: '命令描述',
|
||||
dataIndex: 'desc',
|
||||
ellipsis: true,
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '定时执行',
|
||||
dataIndex: 'autoExecCron',
|
||||
ellipsis: true,
|
||||
width: 120,
|
||||
scopedSlots: { customRender: 'autoExecCron' }
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
},
|
||||
width: '170px'
|
||||
@ -427,7 +428,7 @@ export default {
|
||||
width: '170px',
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
customRender: (text) => {
|
||||
customRender: ({ text }) => {
|
||||
return parseTime(text)
|
||||
}
|
||||
},
|
||||
@ -435,14 +436,13 @@ export default {
|
||||
title: '最后操作人',
|
||||
dataIndex: 'modifyUser',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' }
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
align: 'center',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
fixed: 'right',
|
||||
width: '240px'
|
||||
}
|
||||
@ -450,11 +450,12 @@ export default {
|
||||
tableSelections: [],
|
||||
syncToWorkspaceVisible: false,
|
||||
workspaceList: [],
|
||||
triggerVisible: false
|
||||
triggerVisible: false,
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getWorkspaceId']),
|
||||
...mapState(useAppStore, ['getWorkspaceId']),
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
@ -474,15 +475,11 @@ export default {
|
||||
methods: {
|
||||
// 编辑命令信息
|
||||
handleEditCommandOk() {
|
||||
this.$refs['editCommandForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.formLoading = true
|
||||
this.$refs['editCommandForm'].validate().then(() => {
|
||||
if (this.commandParams && this.commandParams.length > 0) {
|
||||
for (let i = 0; i < this.commandParams.length; i++) {
|
||||
if (!this.commandParams[i].desc) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请填写第' + (i + 1) + '个参数的描述'
|
||||
})
|
||||
return false
|
||||
@ -493,17 +490,21 @@ export default {
|
||||
this.temp.defParams = ''
|
||||
}
|
||||
this.temp.sshIds = this.chooseSsh.join(',')
|
||||
editCommand(this.temp).then((res) => {
|
||||
this.formLoading = false
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.editCommandVisible = false
|
||||
this.confirmLoading = true
|
||||
editCommand(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.editCommandVisible = false
|
||||
|
||||
this.getCommandData()
|
||||
}
|
||||
})
|
||||
this.getCommandData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取命令数据
|
||||
@ -561,8 +562,9 @@ export default {
|
||||
},
|
||||
// 删除命令
|
||||
handleDelete(row) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除“' + row.name + '”命令?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -570,7 +572,7 @@ export default {
|
||||
// 删除
|
||||
deleteCommand(row.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.getCommandData()
|
||||
@ -588,29 +590,33 @@ export default {
|
||||
|
||||
handleExecuteCommandOk() {
|
||||
if (!this.chooseSsh || this.chooseSsh.length <= 0) {
|
||||
$notification.error({
|
||||
this.$notification.error({
|
||||
message: '请选择执行节点'
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
this.confirmLoading = true
|
||||
executeBatch({
|
||||
id: this.temp.id,
|
||||
params: JSON.stringify(this.commandParams),
|
||||
nodes: this.chooseSsh.join(',')
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.executeCommandVisible = false
|
||||
this.temp = {
|
||||
commandId: this.temp.id,
|
||||
batchId: res.data
|
||||
}
|
||||
this.logVisible = true
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.executeCommandVisible = false
|
||||
this.temp = {
|
||||
commandId: this.temp.id,
|
||||
batchId: res.data
|
||||
}
|
||||
this.logVisible = true
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
// 加载工作空间数据
|
||||
loadWorkSpaceListAll() {
|
||||
@ -631,25 +637,30 @@ export default {
|
||||
//
|
||||
handleSyncToWorkspace() {
|
||||
if (!this.temp.workspaceId) {
|
||||
$notification.warn({
|
||||
this.$notification.warn({
|
||||
message: '请选择工作空间'
|
||||
})
|
||||
return false
|
||||
}
|
||||
// 同步
|
||||
this.confirmLoading = true
|
||||
syncToWorkspace({
|
||||
ids: this.tableSelections.join(','),
|
||||
toWorkspaceId: this.temp.workspaceId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
// 触发器
|
||||
handleTrigger(record) {
|
||||
@ -671,7 +682,7 @@ export default {
|
||||
rest: 'rest'
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.fillTriggerResult(res)
|
||||
@ -687,6 +698,7 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.config-editor {
|
||||
overflow-y: scroll;
|
||||
|
@ -10,17 +10,20 @@
|
||||
<template #title>
|
||||
<template v-if="sshData">
|
||||
<a-space>
|
||||
<div>{{ sshData.name }} ({{ sshData.host }})</div>
|
||||
<div>
|
||||
{{ sshData.name }}
|
||||
<template v-if="sshData.host">({{ sshData.host }})</template>
|
||||
</div>
|
||||
|
||||
<a-button size="small" type="primary" :disabled="!sshData.fileDirs" @click="handleFile()">文件</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template v-else>loading</template>
|
||||
</template>
|
||||
<template #extra>
|
||||
<template v-slot:extra>
|
||||
<a href="#"></a>
|
||||
</template>
|
||||
<terminal v-if="sshData" :sshId="sshData.id" />
|
||||
<terminal1 v-if="sshData" :sshId="sshData.id" />
|
||||
<template v-else>
|
||||
<a-result status="404" title="不能操作" sub-title="没有对应的SSH">
|
||||
<template #extra>
|
||||
@ -33,27 +36,23 @@
|
||||
</a-card>
|
||||
</a-spin>
|
||||
<!-- 文件管理 -->
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
v-if="sshData"
|
||||
:title="`${sshData.name} (${sshData.host}) 文件管理`"
|
||||
placement="right"
|
||||
width="90vw"
|
||||
:visible="drawerVisible"
|
||||
@close="onClose"
|
||||
>
|
||||
<ssh-file v-if="drawerVisible" :ssh="sshData" />
|
||||
<a-drawer destroyOnClose v-if="sshData" placement="right" width="90vw" :open="drawerVisible" @close="onClose">
|
||||
<template #title>
|
||||
{{ sshData.name }}<template v-if="sshData.host"> ({{ sshData.host }}) </template>文件管理
|
||||
</template>
|
||||
<ssh-file v-if="drawerVisible" :sshId="sshData.id" />
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import terminal from './terminal'
|
||||
import terminal1 from './terminal'
|
||||
import { getItem } from '@/api/ssh'
|
||||
import SshFile from '@/pages/ssh/ssh-file'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
terminal,
|
||||
terminal1,
|
||||
SshFile
|
||||
},
|
||||
|
||||
@ -72,7 +71,7 @@ export default {
|
||||
this.loadItemData()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {},
|
||||
beforeUnmount() {},
|
||||
methods: {
|
||||
loadItemData() {
|
||||
getItem({
|
||||
|
208
web-vue3/src/pages/ssh/ssh-tabs.vue
Normal file
208
web-vue3/src/pages/ssh/ssh-tabs.vue
Normal file
@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<a-layout style="padding: 5px 0; background: #fff">
|
||||
<a-layout-sider
|
||||
width="200"
|
||||
:style="{
|
||||
background: '#fff',
|
||||
height: `calc(100vh - 10px)`,
|
||||
borderRight: '1px solid #e8e8e8',
|
||||
overflowX: 'scroll'
|
||||
}"
|
||||
>
|
||||
<a-directory-tree
|
||||
v-if="treeList.length"
|
||||
multiple
|
||||
default-expand-all
|
||||
:treeData="treeList"
|
||||
:fieldNames="replaceFields"
|
||||
@select="select"
|
||||
>
|
||||
</a-directory-tree>
|
||||
<a-empty v-else></a-empty>
|
||||
</a-layout-sider>
|
||||
<a-layout-content :style="{ padding: '0 5px', height: `calc(100vh - 10px)` }">
|
||||
<a-tabs
|
||||
v-if="selectPanes.length"
|
||||
v-model:value="activeKey"
|
||||
type="editable-card"
|
||||
hide-add
|
||||
@edit="onEdit"
|
||||
@change="change"
|
||||
>
|
||||
<template v-slot:rightExtra>
|
||||
<a-button type="primary" :disabled="!activeKey" @click="changeFileVisible(activeKey, true)">
|
||||
文件管理
|
||||
</a-button>
|
||||
</template>
|
||||
<a-tab-pane
|
||||
v-for="pane in selectPanes"
|
||||
:key="pane.id"
|
||||
:tab="pane.name"
|
||||
:closable="true"
|
||||
:ref="`pene-${pane.id}`"
|
||||
>
|
||||
<div :id="`paneDom${pane.id}`">
|
||||
<div v-if="pane.open" :style="{ height: `calc(100vh - 70px) ` }">
|
||||
<terminal1 :sshId="pane.id" />
|
||||
</div>
|
||||
<a-result v-else status="warning" title="未开启当前终端">
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="open(pane.id)"> 打开终端 </a-button>
|
||||
</template>
|
||||
</a-result>
|
||||
<!-- 文件管理 -->
|
||||
<a-drawer
|
||||
v-if="pane.openFile"
|
||||
:getContainer="`#paneDom${pane.id}`"
|
||||
:title="`${pane.name}文件管理`"
|
||||
placement="right"
|
||||
width="90vw"
|
||||
:visible="pane.fileVisible"
|
||||
@close="changeFileVisible(pane.id, false)"
|
||||
>
|
||||
<ssh-file v-if="pane.openFile" :sshId="pane.id" />
|
||||
</a-drawer>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-empty v-else description="未选择ssh"></a-empty>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSshListTree } from '@/api/ssh'
|
||||
import terminal1 from './terminal'
|
||||
import SshFile from '@/pages/ssh/ssh-file'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
terminal1,
|
||||
SshFile
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeKey: '',
|
||||
selectPanes: [],
|
||||
treeList: [],
|
||||
replaceFields: {
|
||||
children: 'children',
|
||||
title: 'name',
|
||||
key: 'id'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
created() {
|
||||
this.listData()
|
||||
},
|
||||
methods: {
|
||||
findItemById(list, id) {
|
||||
// 每次进来使用find遍历一次
|
||||
let res = list.find((item) => item.id == id)
|
||||
|
||||
if (res) {
|
||||
return res
|
||||
} else {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].children instanceof Array && list[i].children.length > 0) {
|
||||
res = this.findItemById(list[i].children, id)
|
||||
|
||||
if (res) return res
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
},
|
||||
// 查询树
|
||||
listData() {
|
||||
getSshListTree().then((res) => {
|
||||
if (res.code == 200 && res.data) {
|
||||
this.treeList = res.data.children || []
|
||||
try {
|
||||
const cache = JSON.parse(localStorage.getItem('ssh-tabs-cache') || '{}')
|
||||
const cacheIds = (cache.selectPanes || []).map((item) => item.id)
|
||||
this.selectPanes =
|
||||
cacheIds
|
||||
.map((item) => {
|
||||
return this.findItemById(this.treeList, item)
|
||||
})
|
||||
.filter((item) => item)
|
||||
.map((item) => {
|
||||
// 默认关闭
|
||||
item.open = false
|
||||
return item
|
||||
}) || []
|
||||
|
||||
const activeKey = this.selectPanes.find((item) => item.id === cache.activeKey)
|
||||
if (activeKey) {
|
||||
this.activeKey = activeKey.id
|
||||
} else if (this.selectPanes.length) {
|
||||
this.activeKey = this.selectPanes[0].id
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 编辑 tabs
|
||||
onEdit(targetKey, action) {
|
||||
if (action === 'remove') {
|
||||
this.selectPanes = this.selectPanes.filter((pane) => pane.id !== targetKey)
|
||||
if (this.activeKey === targetKey) {
|
||||
this.activeKey = this.selectPanes[0] && this.selectPanes[0].id
|
||||
}
|
||||
this.cache()
|
||||
}
|
||||
},
|
||||
// 切换
|
||||
change() {
|
||||
this.cache()
|
||||
},
|
||||
open(activeKey) {
|
||||
this.selectPanes = this.selectPanes.map((item) => {
|
||||
if (item.id === activeKey) {
|
||||
item.open = true
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
select(selectedKeys, { node }) {
|
||||
if (!node.dataRef.isLeaf) {
|
||||
return
|
||||
}
|
||||
const findPane = this.selectPanes.find((item) => item.id === node.dataRef.id)
|
||||
if (findPane) {
|
||||
this.activeKey = findPane.id
|
||||
} else {
|
||||
const data = { ...node.dataRef, open: true }
|
||||
this.selectPanes.push(data)
|
||||
this.activeKey = node.dataRef.id
|
||||
}
|
||||
this.cache()
|
||||
},
|
||||
cache() {
|
||||
localStorage.setItem(
|
||||
'ssh-tabs-cache',
|
||||
JSON.stringify({
|
||||
activeKey: this.activeKey,
|
||||
selectPanes: this.selectPanes
|
||||
})
|
||||
)
|
||||
},
|
||||
// 文件管理状态切换
|
||||
changeFileVisible(activeKey, value) {
|
||||
this.selectPanes = this.selectPanes.map((item) => {
|
||||
if (item.id === activeKey) {
|
||||
item.fileVisible = value
|
||||
if (value && !item.openFile) {
|
||||
item.openFile = true
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<template v-if="useSuggestions">
|
||||
<div>
|
||||
<template v-if="this.useSuggestions">
|
||||
<a-result
|
||||
title="当前工作空间还没有SSH"
|
||||
sub-title="请到【系统管理】-> 【资产管理】-> 【SSH管理】添加SSH,或者将已添加的SSH授权关联、分配到此工作空间"
|
||||
@ -23,19 +23,22 @@
|
||||
bordered
|
||||
rowKey="id"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-input
|
||||
class="search-input-item"
|
||||
@pressEnter="loadData"
|
||||
v-model="listQuery['%name%']"
|
||||
v-model:value="listQuery['%name%']"
|
||||
placeholder="ssh名称"
|
||||
/>
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model="listQuery.group"
|
||||
v-model:value="listQuery.group"
|
||||
allowClear
|
||||
placeholder="分组"
|
||||
class="search-input-item"
|
||||
@ -49,8 +52,9 @@
|
||||
<a-button type="primary" :disabled="!tableSelections || !tableSelections.length" @click="syncToWorkspaceShow"
|
||||
>工作空间同步</a-button
|
||||
>
|
||||
<a-button type="primary" @click="toSshTabs">管理面板</a-button>
|
||||
<a-tooltip>
|
||||
<template #title>
|
||||
<template v-slot:title>
|
||||
<div>
|
||||
<ul>
|
||||
<li>关联节点数据是异步获取有一定时间延迟</li>
|
||||
@ -59,167 +63,202 @@
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<question-circle-filled />
|
||||
<QuestionCircleOutlined />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-tooltip #tooltip slot-scope="text" :title="text"> {{ text }}</a-tooltip>
|
||||
<a-tooltip
|
||||
#host
|
||||
slot-scope="text, record"
|
||||
:title="`${record.machineSsh && record.machineSsh.host}:${record.machineSsh && record.machineSsh.port}`"
|
||||
>
|
||||
{{ record.machineSsh && record.machineSsh.host }}:{{ record.machineSsh && record.machineSsh.port }}
|
||||
</a-tooltip>
|
||||
<template #status slot-scope="text, record">
|
||||
<a-tooltip :title="record.machineSsh && record.machineSsh.statusMsg">
|
||||
<a-tag :color="record.machineSsh && record.machineSsh.status === 1 ? 'green' : 'red'">{{
|
||||
record.machineSsh && record.machineSsh.status === 1 ? '正常' : '无法连接'
|
||||
}}</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-popover title="系统信息" #osName slot-scope="text, record">
|
||||
<template #content>
|
||||
<p>系统名:{{ record.machineSsh && record.machineSsh.osName }}</p>
|
||||
<p>系统版本:{{ record.machineSsh && record.machineSsh.osVersion }}</p>
|
||||
<p>CPU型号:{{ record.machineSsh && record.machineSsh.osCpuIdentifierName }}</p>
|
||||
<p>主机名:{{ record.machineSsh && record.machineSsh.hostName }}</p>
|
||||
<p>开机时间:{{ formatDuration(record.machineSsh && record.machineSsh.osSystemUptime) }}</p>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<template v-if="column.tooltip">
|
||||
<a-tooltip :title="text"> {{ text }}</a-tooltip>
|
||||
</template>
|
||||
{{ text || '未知' }}
|
||||
</a-popover>
|
||||
<a-tooltip
|
||||
#osOccupyMemory
|
||||
slot-scope="text, record"
|
||||
placement="topLeft"
|
||||
:title="`内存使用率:${formatPercent(
|
||||
record.machineSsh && record.machineSsh.osOccupyMemory
|
||||
)},总内存:${renderSize(record.machineSsh && record.machineSsh.osMoneyTotal)}`"
|
||||
>
|
||||
<span
|
||||
>{{ formatPercent(record.machineSsh && record.machineSsh.osOccupyMemory) }}/{{
|
||||
renderSize(record.machineSsh && record.machineSsh.osMoneyTotal)
|
||||
}}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip
|
||||
#osOccupyCpu
|
||||
slot-scope="text, record"
|
||||
placement="topLeft"
|
||||
:title="`CPU使用率:${formatPercent2Number(record.machineSsh && record.machineSsh.osOccupyCpu)}%,CPU数:${
|
||||
record.machineSsh && record.machineSsh.osCpuCores
|
||||
}`"
|
||||
>
|
||||
<span
|
||||
>{{ (formatPercent2Number(record.machineSsh && record.machineSsh.osOccupyCpu) || '-') + '%' }} /
|
||||
{{ record.machineSsh && record.machineSsh.osCpuCores }}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
|
||||
<a-popover title="硬盘信息" #osMaxOccupyDisk slot-scope="text, record">
|
||||
<template #content>
|
||||
<p>硬盘总量:{{ renderSize(record.machineSsh && record.machineSsh.osMoneyTotal) }}</p>
|
||||
<p>硬盘最大的使用率:{{ formatPercent(record.machineSsh && record.machineSsh.osMaxOccupyDisk) }}</p>
|
||||
<p>使用率最大的分区:{{ record.machineSsh && record.machineSsh.osMaxOccupyDiskName }}</p>
|
||||
</template>
|
||||
<span
|
||||
>{{ formatPercent(record.machineSsh && record.machineSsh.osMaxOccupyDisk) }} /
|
||||
{{ renderSize(record.machineSsh && record.machineSsh.osMoneyTotal) }}</span
|
||||
>
|
||||
</a-popover>
|
||||
|
||||
<template #nodeId slot-scope="text, record">
|
||||
<template v-if="record.linkNode">
|
||||
<a-tooltip placement="topLeft" :title="`节点名称:${record.linkNode.name}`">
|
||||
<a-button
|
||||
size="small"
|
||||
style="width: 90px; padding: 0 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||
type=""
|
||||
@click="toNode(record.linkNode)"
|
||||
>
|
||||
{{ record.linkNode.name }}
|
||||
</a-button>
|
||||
<template v-else-if="column.dataIndex === 'host'">
|
||||
<a-tooltip
|
||||
:title="`${record.machineSsh && record.machineSsh.host}:${record.machineSsh && record.machineSsh.port}`"
|
||||
>
|
||||
{{ record.machineSsh && record.machineSsh.host }}:{{ record.machineSsh && record.machineSsh.port }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else>-</template>
|
||||
</template>
|
||||
<template #operation slot-scope="text, record">
|
||||
<a-space>
|
||||
<a-dropdown>
|
||||
<a-button size="small" type="primary" @click="handleTerminal(record, false)">
|
||||
终端<down-outlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1">
|
||||
<a-button size="small" type="primary" icon="fullscreen" @click="handleTerminal(record, true)"
|
||||
>全屏终端</a-button
|
||||
>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2">
|
||||
<router-link
|
||||
target="_blank"
|
||||
:to="{ path: '/full-terminal', query: { id: record.id, wid: getWorkspaceId() } }"
|
||||
>
|
||||
<a-button size="small" type="primary" icon="fullscreen"> 新标签终端</a-button>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<template v-if="record.fileDirs">
|
||||
<a-button size="small" type="primary" @click="handleFile(record)">文件</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-tooltip
|
||||
placement="topLeft"
|
||||
title="如果按钮不可用,请去资产管理 ssh 列表的关联中添加当前工作空间允许管理的授权文件夹"
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('status')">
|
||||
<a-tooltip :title="record.machineSsh && record.machineSsh.statusMsg">
|
||||
<a-tag
|
||||
:color="
|
||||
statusMap[record.machineSsh && record.machineSsh.status] &&
|
||||
statusMap[record.machineSsh && record.machineSsh.status].color
|
||||
"
|
||||
>
|
||||
<a-button size="small" type="primary" :disabled="true">文件</a-button>
|
||||
{{
|
||||
(statusMap[record.machineSsh && record.machineSsh.status] &&
|
||||
statusMap[record.machineSsh && record.machineSsh.status].desc) ||
|
||||
'未知'
|
||||
}}
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('osName')">
|
||||
<a-popover title="系统信息">
|
||||
<template v-slot:content>
|
||||
<p>系统名:{{ record.machineSsh && record.machineSsh.osName }}</p>
|
||||
<p>系统版本:{{ record.machineSsh && record.machineSsh.osVersion }}</p>
|
||||
<p>CPU型号:{{ record.machineSsh && record.machineSsh.osCpuIdentifierName }}</p>
|
||||
<p>主机名:{{ record.machineSsh && record.machineSsh.hostName }}</p>
|
||||
<p>开机时间:{{ formatDuration(record.machineSsh && record.machineSsh.osSystemUptime) }}</p>
|
||||
</template>
|
||||
{{ text || '未知' }}
|
||||
</a-popover>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('osOccupyMemory')">
|
||||
<a-tooltip
|
||||
placement="topLeft"
|
||||
:title="`内存使用率:${formatPercent(
|
||||
record.machineSsh && record.machineSsh.osOccupyMemory
|
||||
)},总内存:${renderSize(record.machineSsh && record.machineSsh.osMoneyTotal)}`"
|
||||
>
|
||||
<span
|
||||
>{{ formatPercent(record.machineSsh && record.machineSsh.osOccupyMemory) }}/{{
|
||||
renderSize(record.machineSsh && record.machineSsh.osMoneyTotal)
|
||||
}}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('osOccupyCpu')">
|
||||
<a-tooltip
|
||||
placement="topLeft"
|
||||
:title="`CPU使用率:${formatPercent2Number(record.machineSsh && record.machineSsh.osOccupyCpu)}%,CPU数:${
|
||||
record.machineSsh && record.machineSsh.osCpuCores
|
||||
}`"
|
||||
>
|
||||
<span
|
||||
>{{ (formatPercent2Number(record.machineSsh && record.machineSsh.osOccupyCpu) || '-') + '%' }} /
|
||||
{{ record.machineSsh && record.machineSsh.osCpuCores }}</span
|
||||
>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex instanceof Array && column.dataIndex.includes('osMaxOccupyDisk')">
|
||||
<a-popover title="硬盘信息">
|
||||
<template v-slot:content>
|
||||
<p>内存总量:{{ renderSize(record.machineSsh && record.machineSsh.osMoneyTotal) }}</p>
|
||||
<p>硬盘最大的使用率:{{ formatPercent(record.machineSsh && record.machineSsh.osMaxOccupyDisk) }}</p>
|
||||
<p>使用率最大的分区:{{ record.machineSsh && record.machineSsh.osMaxOccupyDiskName }}</p>
|
||||
</template>
|
||||
<span
|
||||
>{{ formatPercent(record.machineSsh && record.machineSsh.osMaxOccupyDisk) }}
|
||||
/
|
||||
{{ renderSize(record.machineSsh && record.machineSsh.osMoneyTotal) }}</span
|
||||
>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.dataIndex === 'nodeId'">
|
||||
<template v-if="record.linkNode">
|
||||
<a-tooltip placement="topLeft" :title="`节点名称:${record.linkNode.name}`">
|
||||
<a-button
|
||||
size="small"
|
||||
style="width: 90px; padding: 0 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||
type="link"
|
||||
@click="toNode(record.linkNode)"
|
||||
>
|
||||
{{ record.linkNode.name }}
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click="(e) => e.preventDefault()">
|
||||
更多
|
||||
<down-outlined />
|
||||
</a>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="danger" @click="handleDelete(record)">删除</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" @click="handleViewLog(record)">终端日志</a-button>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<template v-else>-</template>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-dropdown>
|
||||
<a-button size="small" type="primary" @click="handleTerminal(record, false)"
|
||||
>终端<DownOutlined
|
||||
/></a-button>
|
||||
<template v-slot:overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1">
|
||||
<a-button size="small" type="primary" @click="handleTerminal(record, true)"
|
||||
><FullscreenOutlined />全屏终端</a-button
|
||||
>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2">
|
||||
<router-link
|
||||
target="_blank"
|
||||
:to="{
|
||||
path: '/full-terminal',
|
||||
query: { id: record.id, wid: getWorkspaceId() }
|
||||
}"
|
||||
>
|
||||
<a-button size="small" type="primary"> <FullscreenOutlined />新标签终端</a-button>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<template v-if="record.fileDirs">
|
||||
<a-button size="small" type="primary" @click="handleFile(record)">文件</a-button>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
<template v-else>
|
||||
<a-tooltip
|
||||
placement="topLeft"
|
||||
title="如果按钮不可用,请去资产管理 ssh 列表的关联中添加当前工作空间允许管理的授权文件夹"
|
||||
>
|
||||
<a-button size="small" type="primary" :disabled="true">文件</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-dropdown>
|
||||
<a @click="(e) => e.preventDefault()">
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a>
|
||||
<template v-slot:overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" danger @click="handleDelete(record)">删除</a-button>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-button size="small" type="primary" @click="handleViewLog(record)">终端日志</a-button>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 编辑区 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="editSshVisible"
|
||||
v-model:open="editSshVisible"
|
||||
width="600px"
|
||||
title="编辑 SSH"
|
||||
@ok="handleEditSshOk"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="confirmLoading"
|
||||
>
|
||||
<a-form ref="editSshForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<template v-if="this.getUserInfo && this.getUserInfo.systemUser">
|
||||
<a-alert type="info" show-icon style="width: 100%; margin-bottom: 10px">
|
||||
<template v-slot:message>
|
||||
<ul>
|
||||
<li>此编辑仅能编辑当前 SSH 在此工作空间的名称信息</li>
|
||||
<li>如果要配置 SSH 请到【系统管理】-> 【资产管理】-> 【SSH 管理】中去配置。</li>
|
||||
<li>
|
||||
当前 SSH 的授权目录(文件目录、文件后缀、禁止命令)需要请到 【系统管理】-> 【资产管理】-> 【SSH
|
||||
管理】-> 操作栏中->关联按钮->对应工作空间->操作栏中->配置按钮
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</a-alert>
|
||||
</template>
|
||||
<a-form-item label="SSH 名称" name="name">
|
||||
<a-input v-model="temp.name" :maxLength="50" placeholder="SSH 名称" />
|
||||
<a-input v-model:value="temp.name" :maxLength="50" placeholder="SSH 名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="分组名称" name="group">
|
||||
<custom-select
|
||||
v-model="temp.group"
|
||||
v-model:value="temp.group"
|
||||
:data="groupList"
|
||||
suffixIcon=""
|
||||
inputPlaceholder="添加分组"
|
||||
selectPlaceholder="选择分组名"
|
||||
>
|
||||
@ -231,7 +270,7 @@
|
||||
<!-- 文件管理 -->
|
||||
<a-drawer
|
||||
destroyOnClose
|
||||
:visible="drawerVisible"
|
||||
:open="drawerVisible"
|
||||
@close="
|
||||
() => {
|
||||
this.drawerVisible = false
|
||||
@ -246,29 +285,31 @@
|
||||
<!-- Terminal -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:dialogStyle="{
|
||||
:style="{
|
||||
maxWidth: '100vw',
|
||||
top: this.terminalFullscreen ? 0 : false,
|
||||
paddingBottom: 0
|
||||
}"
|
||||
:width="terminalFullscreen ? '100vw' : '80vw'"
|
||||
:width="this.terminalFullscreen ? '100vw' : '80vw'"
|
||||
:bodyStyle="{
|
||||
padding: '0 10px',
|
||||
padding: '0 5px',
|
||||
paddingTop: '10px',
|
||||
marginRight: '10px',
|
||||
height: `${this.terminalFullscreen ? 'calc(100vh - 56px)' : '70vh'}`
|
||||
height: `${this.terminalFullscreen ? 'calc(100vh - 80px)' : '70vh'}`
|
||||
}"
|
||||
v-model="terminalVisible"
|
||||
v-model:open="terminalVisible"
|
||||
:title="temp.name"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<terminal v-if="terminalVisible" :sshId="temp.id" />
|
||||
<div :style="`height: ${this.terminalFullscreen ? 'calc(100vh - 70px - 20px)' : 'calc(70vh - 20px)'}`">
|
||||
<terminal1 v-if="terminalVisible" :sshId="temp.id" />
|
||||
</div>
|
||||
</a-modal>
|
||||
<!-- 操作日志 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="viewOperationLog"
|
||||
v-model:open="viewOperationLog"
|
||||
title="操作日志"
|
||||
width="80vw"
|
||||
:footer="null"
|
||||
@ -279,13 +320,14 @@
|
||||
<!-- 同步到其他工作空间 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
v-model="syncToWorkspaceVisible"
|
||||
v-model:open="syncToWorkspaceVisible"
|
||||
title="同步到其他工作空间"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleSyncToWorkspace"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-alert message="温馨提示" type="warning">
|
||||
<template #description>
|
||||
<a-alert message="温馨提示" type="warning" show-icon>
|
||||
<template v-slot:description>
|
||||
<ul>
|
||||
<li>同步机制采用 IP+PORT+连接方式 确定是同一个服务器</li>
|
||||
<li>当目标工作空间不存在对应的 SSH 时候将自动创建一个新的 SSH</li>
|
||||
@ -296,7 +338,12 @@
|
||||
<a-form :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
|
||||
<a-form-item> </a-form-item>
|
||||
<a-form-item label="选择工作空间" name="workspaceId">
|
||||
<a-select show-search option-filter-prop="children" v-model="temp.workspaceId" placeholder="请选择工作空间">
|
||||
<a-select
|
||||
show-search
|
||||
option-filter-prop="children"
|
||||
v-model:value="temp.workspaceId"
|
||||
placeholder="请选择工作空间"
|
||||
>
|
||||
<a-select-option :disabled="getWorkspaceId() === item.id" v-for="item in workspaceList" :key="item.id">{{
|
||||
item.name
|
||||
}}</a-select-option>
|
||||
@ -306,10 +353,12 @@
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deleteSsh, editSsh, getSshList, syncToWorkspace, getSshGroupAll } from '@/api/ssh'
|
||||
import { statusMap } from '@/api/system/assets-ssh'
|
||||
import SshFile from '@/pages/ssh/ssh-file'
|
||||
import Terminal from '@/pages/ssh/terminal'
|
||||
import Terminal1 from '@/pages/ssh/terminal'
|
||||
import {
|
||||
CHANGE_PAGE,
|
||||
COMPUTED_PAGINATION,
|
||||
@ -323,13 +372,15 @@ import {
|
||||
import { getWorkSpaceListAll } from '@/api/workspace'
|
||||
|
||||
import { mapState } from 'pinia'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import OperationLog from '@/pages/system/assets/ssh/operation-log'
|
||||
import CustomSelect from '@/components/customSelect'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SshFile,
|
||||
Terminal,
|
||||
Terminal1,
|
||||
OperationLog,
|
||||
CustomSelect
|
||||
},
|
||||
@ -346,9 +397,7 @@ export default {
|
||||
workspaceList: [],
|
||||
tempNode: {},
|
||||
// fileList: [],
|
||||
sshAgentInfo: {},
|
||||
agentData: {},
|
||||
formLoading: false,
|
||||
statusMap,
|
||||
|
||||
drawerVisible: false,
|
||||
terminalVisible: false,
|
||||
@ -362,50 +411,47 @@ export default {
|
||||
sorter: true,
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
tooltip: true
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Host',
|
||||
dataIndex: 'machineSsh.host',
|
||||
dataIndex: ['machineSsh', 'host'],
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'host' }
|
||||
ellipsis: true
|
||||
},
|
||||
// { title: "Port", dataIndex: "machineSsh.port", sorter: true, width: 80, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'machineSsh.user',
|
||||
dataIndex: ['machineSsh', 'user'],
|
||||
width: '100px',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
title: '系统名',
|
||||
dataIndex: 'machineSsh.osName',
|
||||
dataIndex: ['machineSsh', 'osName'],
|
||||
width: 80,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'osName' }
|
||||
ellipsis: true
|
||||
},
|
||||
// { title: "系统版本", dataIndex: "machineSsh.osVersion", sorter: true, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
|
||||
{
|
||||
title: 'CPU',
|
||||
dataIndex: 'machineSsh.osOccupyCpu',
|
||||
dataIndex: ['machineSsh', 'osOccupyCpu'],
|
||||
width: 80,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'osOccupyCpu' }
|
||||
ellipsis: true
|
||||
// scopedSlots: { customRender: 'osOccupyCpu' }
|
||||
},
|
||||
{
|
||||
title: '内存',
|
||||
dataIndex: 'machineSsh.osOccupyMemory',
|
||||
dataIndex: ['machineSsh', 'osOccupyMemory'],
|
||||
width: 80,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'osOccupyMemory' }
|
||||
ellipsis: true
|
||||
// scopedSlots: { customRender: 'osOccupyMemory' }
|
||||
},
|
||||
{
|
||||
title: '硬盘',
|
||||
dataIndex: 'machineSsh.osMaxOccupyDisk',
|
||||
dataIndex: ['machineSsh', 'osMaxOccupyDisk'],
|
||||
width: 80,
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'osMaxOccupyDisk' }
|
||||
@ -413,17 +459,16 @@ export default {
|
||||
// { title: "编码格式", dataIndex: "charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
|
||||
{
|
||||
title: '连接状态',
|
||||
dataIndex: 'machineSsh.status',
|
||||
dataIndex: ['machineSsh', 'status'],
|
||||
ellipsis: true,
|
||||
align: 'center',
|
||||
width: '90px',
|
||||
scopedSlots: { customRender: 'status' }
|
||||
width: '90px'
|
||||
},
|
||||
// { title: "编码格式", dataIndex: "machineSsh.charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
|
||||
{
|
||||
title: '关联节点',
|
||||
dataIndex: 'nodeId',
|
||||
scopedSlots: { customRender: 'nodeId' },
|
||||
|
||||
width: '100px',
|
||||
ellipsis: true
|
||||
},
|
||||
@ -432,7 +477,7 @@ export default {
|
||||
dataIndex: 'createTimeMillis',
|
||||
ellipsis: true,
|
||||
sorter: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
@ -440,13 +485,13 @@ export default {
|
||||
dataIndex: 'modifyTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
customRender: ({ text }) => parseTime(text),
|
||||
width: '170px'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
|
||||
width: '200px',
|
||||
align: 'center',
|
||||
// ellipsis: true,
|
||||
@ -459,12 +504,13 @@ export default {
|
||||
name: [{ required: true, message: '请输入 SSH 名称', trigger: 'blur' }]
|
||||
},
|
||||
|
||||
groupList: []
|
||||
groupList: [],
|
||||
confirmLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getWorkspaceId', 'getUserInfo']),
|
||||
|
||||
...mapState(useUserStore, ['getUserInfo']),
|
||||
...mapState(useAppStore, ['getWorkspaceId']),
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
@ -537,23 +583,25 @@ export default {
|
||||
// 提交 SSH 数据
|
||||
handleEditSshOk() {
|
||||
// 检验表单
|
||||
this.$refs['editSshForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.$refs['editSshForm'].validate().then(() => {
|
||||
// 提交数据
|
||||
editSsh(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
//this.$refs['editSshForm'].resetFields();
|
||||
// this.fileList = [];
|
||||
this.editSshVisible = false
|
||||
this.loadData()
|
||||
this.loadGroupList()
|
||||
}
|
||||
})
|
||||
this.confirmLoading = true
|
||||
editSsh(this.temp)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
//this.$refs['editSshForm'].resetFields();
|
||||
// this.fileList = [];
|
||||
this.editSshVisible = false
|
||||
this.loadData()
|
||||
this.loadGroupList()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
// 进入终端
|
||||
@ -576,8 +624,9 @@ export default {
|
||||
},
|
||||
// 删除
|
||||
handleDelete(record) {
|
||||
$confirm({
|
||||
this.$confirm({
|
||||
title: '系统提示',
|
||||
zIndex: 1009,
|
||||
content: '真的要删除 SSH 么?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@ -585,7 +634,7 @@ export default {
|
||||
// 删除
|
||||
deleteSsh(record.id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.loadData()
|
||||
@ -597,7 +646,6 @@ export default {
|
||||
// 前往节点
|
||||
toNode(node) {
|
||||
const newpage = this.$router.resolve({
|
||||
name: 'node_' + node.id,
|
||||
path: '/node/list',
|
||||
query: {
|
||||
...this.$route.query,
|
||||
@ -618,7 +666,15 @@ export default {
|
||||
// },
|
||||
// });
|
||||
},
|
||||
|
||||
toSshTabs() {
|
||||
const newpage = this.$router.resolve({
|
||||
path: '/ssh-tabs',
|
||||
query: {
|
||||
wid: this.getWorkspaceId()
|
||||
}
|
||||
})
|
||||
window.open(newpage.href, '_blank')
|
||||
},
|
||||
// 分页、排序、筛选变化时触发
|
||||
changePage(pagination, filters, sorter) {
|
||||
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter })
|
||||
@ -644,27 +700,31 @@ export default {
|
||||
//
|
||||
handleSyncToWorkspace() {
|
||||
if (!this.temp.workspaceId) {
|
||||
$notification.warn({
|
||||
this.$notification.warn({
|
||||
message: '请选择工作空间'
|
||||
})
|
||||
return false
|
||||
}
|
||||
// 同步
|
||||
this.confirmLoading = true
|
||||
syncToWorkspace({
|
||||
ids: this.tableSelections.join(','),
|
||||
toWorkspaceId: this.temp.workspaceId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
this.tableSelections = []
|
||||
this.syncToWorkspaceVisible = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,345 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:data-source="list"
|
||||
size="middle"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
@change="
|
||||
(pagination, filters, sorter) => {
|
||||
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter })
|
||||
this.loadData()
|
||||
}
|
||||
"
|
||||
:row-selection="rowSelection"
|
||||
bordered
|
||||
rowKey="id"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<a-space>
|
||||
<a-space>
|
||||
<a-input
|
||||
allowClear
|
||||
class="search-input-item"
|
||||
@pressEnter="loadData"
|
||||
v-model:value="listQuery['%issuerDnName%']"
|
||||
placeholder="颁发者"
|
||||
/>
|
||||
<a-input
|
||||
allowClear
|
||||
class="search-input-item"
|
||||
@pressEnter="loadData"
|
||||
v-model:value="listQuery['%subjectDnName%']"
|
||||
placeholder="主题"
|
||||
/>
|
||||
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
|
||||
<a-button type="primary" :loading="loading" @click="loadData">搜索</a-button>
|
||||
</a-tooltip>
|
||||
<a-button type="primary" @click="handleAdd">导入证书</a-button>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</template>
|
||||
<template v-slot:tooltip="text">
|
||||
<a-tooltip placement="topLeft" :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-slot:name="text, item">
|
||||
<a-popover title="证书描述">
|
||||
<template v-slot:content>
|
||||
<p>描述:{{ item.description }}</p>
|
||||
</template>
|
||||
<!-- {{ text }} -->
|
||||
{{ text }}
|
||||
</a-popover>
|
||||
</template>
|
||||
<template v-slot:fileExists="text">
|
||||
<a-tag v-if="text" color="green">存在</a-tag>
|
||||
<a-tag v-else color="red">丢失</a-tag>
|
||||
</template>
|
||||
<template v-slot:global="text">
|
||||
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
|
||||
<a-tag v-else>工作空间</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
<!-- 导入 -->
|
||||
<a-modal
|
||||
destroyOnClose
|
||||
:zIndex="1009"
|
||||
v-model:value="editCertVisible"
|
||||
width="700px"
|
||||
title="导入证书"
|
||||
@ok="handleEditCertOk"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<a-form ref="importCertForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item
|
||||
label="证书文件"
|
||||
name="file"
|
||||
help="请上传 zip 压缩包,并且包里面必须包含:ca.pem、key.pem、cert.pem 三个文件"
|
||||
>
|
||||
<a-upload
|
||||
:file-list="uploadFileList"
|
||||
:remove="
|
||||
() => {
|
||||
uploadFileList = []
|
||||
}
|
||||
"
|
||||
:before-upload="
|
||||
(file) => {
|
||||
this.uploadFileList = [file]
|
||||
return false
|
||||
}
|
||||
"
|
||||
accept=".zip"
|
||||
>
|
||||
<a-button><a-icon type="upload" />选择文件</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<div style="padding: 40px">
|
||||
<div
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e9e9e9',
|
||||
padding: '10px 16px',
|
||||
background: '#fff',
|
||||
textAlign: 'right',
|
||||
zIndex: 1
|
||||
}"
|
||||
>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.$emit('cancel')
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handerConfirm"> 确定 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dockerImportTls } from '@/api/system/assets-docker'
|
||||
import { certListAll } from '@/api/tools/certificate'
|
||||
import { parseTime, CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY } from '@/utils/const'
|
||||
|
||||
export default {
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
|
||||
|
||||
list: [],
|
||||
uploadFileList: [],
|
||||
|
||||
temp: {},
|
||||
editCertVisible: false,
|
||||
|
||||
columns: [
|
||||
{
|
||||
title: '序列号 (SN)',
|
||||
dataIndex: 'serialNumberStr',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: '证书类型',
|
||||
dataIndex: 'keyType',
|
||||
ellipsis: true,
|
||||
width: '80px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
},
|
||||
{
|
||||
title: '文件状态',
|
||||
dataIndex: 'fileExists',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'fileExists' },
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '共享',
|
||||
dataIndex: 'workspaceId',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'global' },
|
||||
width: '90px'
|
||||
},
|
||||
{
|
||||
title: '颁发者',
|
||||
dataIndex: 'issuerDnName',
|
||||
ellipsis: true,
|
||||
width: 200,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
},
|
||||
{
|
||||
title: '主题',
|
||||
dataIndex: 'subjectDnName',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
},
|
||||
{
|
||||
title: '密钥算法',
|
||||
dataIndex: 'sigAlgName',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
},
|
||||
{
|
||||
title: '算法 OID',
|
||||
dataIndex: 'sigAlgOid',
|
||||
ellipsis: true,
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
},
|
||||
|
||||
{
|
||||
title: '生效时间',
|
||||
dataIndex: 'effectiveTime',
|
||||
customRender: (text) => parseTime(text),
|
||||
sorter: true,
|
||||
width: '160px'
|
||||
},
|
||||
{
|
||||
title: '到期时间',
|
||||
dataIndex: 'expirationTime',
|
||||
sorter: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
width: '160px'
|
||||
},
|
||||
{
|
||||
title: '版本号',
|
||||
dataIndex: 'certVersion',
|
||||
ellipsis: true,
|
||||
width: '80px',
|
||||
scopedSlots: { customRender: 'tooltip' }
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '修改人',
|
||||
dataIndex: 'modifyUser',
|
||||
ellipsis: true,
|
||||
scopedSlots: { customRender: 'modifyUser' },
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTimeMillis',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: (text) => parseTime(text),
|
||||
width: '160px'
|
||||
}
|
||||
],
|
||||
rules: {},
|
||||
tableSelections: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pagination() {
|
||||
return COMPUTED_PAGINATION(this.listQuery)
|
||||
},
|
||||
rowSelection() {
|
||||
return {
|
||||
onChange: (selectedRowKeys) => {
|
||||
this.tableSelections = selectedRowKeys
|
||||
},
|
||||
selectedRowKeys: this.tableSelections,
|
||||
type: 'radio'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
CHANGE_PAGE,
|
||||
// 加载数据
|
||||
loadData(pointerEvent) {
|
||||
this.loading = true
|
||||
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page
|
||||
this.loading = true
|
||||
certListAll(this.listQuery).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.list = res.data.result
|
||||
this.listQuery.total = res.data.total
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 添加
|
||||
handleAdd() {
|
||||
this.editCertVisible = true
|
||||
this.uploadFileList = []
|
||||
this.$refs['importCertForm']?.resetFields()
|
||||
},
|
||||
|
||||
// 提交 Cert 数据
|
||||
handleEditCertOk() {
|
||||
// 检验表单
|
||||
this.$refs['importCertForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
if (this.uploadFileList.length === 0) {
|
||||
this.$notification.error({
|
||||
message: '请选择证书文件'
|
||||
})
|
||||
return false
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('file', this.uploadFileList[0])
|
||||
|
||||
// 提交数据
|
||||
dockerImportTls(formData).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
this.$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
|
||||
this.editCertVisible = false
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 确认
|
||||
handerConfirm() {
|
||||
if (!this.tableSelections.length) {
|
||||
this.$notification.warning({
|
||||
message: '请选择要使用的证书'
|
||||
})
|
||||
return
|
||||
}
|
||||
const selectData = this.list.filter((item) => {
|
||||
return item.id === this.tableSelections[0]
|
||||
})[0]
|
||||
|
||||
$emit(this, 'confirm', `${selectData.serialNumberStr}:${selectData.keyType}`)
|
||||
}
|
||||
},
|
||||
emits: ['cancel', 'confirm']
|
||||
}
|
||||
</script>
|
@ -468,9 +468,11 @@
|
||||
this.certificateVisible = false
|
||||
}
|
||||
"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
>
|
||||
<certificate
|
||||
v-if="certificateVisible"
|
||||
ref="certificate"
|
||||
@confirm="
|
||||
(certInfo) => {
|
||||
this.temp = { ...this.temp, certInfo: certInfo }
|
||||
@ -483,6 +485,29 @@
|
||||
}
|
||||
"
|
||||
></certificate>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button
|
||||
@click="
|
||||
() => {
|
||||
this.chooseVisible = 0
|
||||
}
|
||||
"
|
||||
>
|
||||
取消
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="
|
||||
() => {
|
||||
this.$refs['certificate'].handerConfirm()
|
||||
}
|
||||
"
|
||||
>
|
||||
确认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
@ -507,7 +532,7 @@ import { getWorkSpaceListAll } from '@/api/workspace'
|
||||
import Console from '@/pages/docker/console'
|
||||
import SwarmConsole from '@/pages/docker/swarm/console.vue'
|
||||
|
||||
import certificate from './certificate.vue'
|
||||
import certificate from '@/pages/certificate/list.vue'
|
||||
import CustomSelect from '@/components/customSelect'
|
||||
|
||||
export default {
|
||||
|
@ -134,6 +134,11 @@ const children = [
|
||||
name: 'file-storage-release-task',
|
||||
component: () => import('../pages/file-manager/release-task/list.vue')
|
||||
},
|
||||
{
|
||||
path: '/file-manager/static-file-storage',
|
||||
name: 'static-file-storage',
|
||||
component: () => import('../pages/file-manager/staticFileStorage/list.vue')
|
||||
},
|
||||
{
|
||||
path: '/certificate/list',
|
||||
name: '/certificate-list',
|
||||
@ -160,7 +165,7 @@ const management = [
|
||||
{
|
||||
path: '/system/assets/repository-list',
|
||||
name: 'system-global-repository',
|
||||
component: () => import('../pages/repository/global-repository')
|
||||
component: () => import('../pages/repository/global-repository.vue')
|
||||
},
|
||||
{
|
||||
path: '/user/permission-group',
|
||||
@ -277,6 +282,11 @@ const router = createRouter({
|
||||
name: 'full-terminal',
|
||||
component: () => import('../pages/ssh/full-terminal.vue')
|
||||
},
|
||||
{
|
||||
path: '/ssh-tabs',
|
||||
name: 'ssh-tabs',
|
||||
component: () => import('../pages/ssh/ssh-tabs.vue')
|
||||
},
|
||||
{
|
||||
path: '/*',
|
||||
name: '404',
|
||||
|
Loading…
Reference in New Issue
Block a user