2024-08-20 12:11:31 +00:00
|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<cb-detail v-if="detaildialogVisible" :data="detail" @goBack="detaildialogVisible = false">
|
|
|
|
|
<template v-slot:item_container>
|
|
|
|
|
<cb-detail-item label="密钥名称">{{ detail.name }}</cb-detail-item>
|
|
|
|
|
<cb-detail-item label="所属平台">{{ detail.vendorName }}</cb-detail-item>
|
|
|
|
|
<cb-detail-item label="指纹">{{ detail.fingerprint }}</cb-detail-item>
|
|
|
|
|
<cb-detail-item label="可用区">{{ detail.regionName }}</cb-detail-item>
|
|
|
|
|
</template>
|
|
|
|
|
<el-tabs value="second">
|
|
|
|
|
<el-tab-pane label="公钥" name="second">
|
2024-08-21 01:17:14 +00:00
|
|
|
|
<div style="width: 100%; word-break: break-all">
|
2024-08-20 12:11:31 +00:00
|
|
|
|
{{ detail.publicKey }}
|
|
|
|
|
</div>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
</el-tabs>
|
|
|
|
|
</cb-detail>
|
|
|
|
|
<el-dialog title="新增密钥" :visible.sync="addFlag" width="40%" :before-close="handleClose">
|
|
|
|
|
<cb-form ref="addData" :model="addData">
|
|
|
|
|
<cb-form-item label="创建方式">
|
|
|
|
|
<div class="vm-region region-box" :class="{ true: 'region-active' }[row.isRegionActive]" @click="chooseRegion1(row)" v-for="row in createWays" :key="row.id">
|
|
|
|
|
<span class="region-txt">{{ row.name }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</cb-form-item>
|
2024-08-21 01:17:14 +00:00
|
|
|
|
<cb-form-item label="可用地域" prop="regionId" :rules="[required]">
|
|
|
|
|
<el-select v-model="addData.regionId" @change="getZone">
|
|
|
|
|
<el-option v-for="(item, index) in regionData" :key="index" :label="item.name" :value="item.regionId"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</cb-form-item>
|
|
|
|
|
<cb-form-item label="可用区域" prop="zoneId" :rules="[required]">
|
|
|
|
|
<el-select v-model="addData.zoneId">
|
|
|
|
|
<el-option v-for="(item, index) in zoneList" :key="index" :label="item.name" :value="item.zoneId"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</cb-form-item>
|
|
|
|
|
<cb-form-item label="名称" prop="name" :rules="[required, hicNoChinese]" required-message="请输入名称">
|
2024-08-20 12:11:31 +00:00
|
|
|
|
<el-input v-model="addData.name"></el-input>
|
|
|
|
|
</cb-form-item>
|
|
|
|
|
<cb-form-item label="公钥" v-if="way == 'daoru'" prop="publicKey" :rules="[required]" required-message="请输入公钥" :maxlength="10000">
|
|
|
|
|
<el-input type="textarea" v-model="addData.publicKey"></el-input>
|
|
|
|
|
</cb-form-item>
|
|
|
|
|
<cb-form-item label="读取文件" v-if="way == 'daoru'">
|
|
|
|
|
<input type="file" class="file" ref="file" @change="change" />
|
|
|
|
|
</cb-form-item>
|
|
|
|
|
</cb-form>
|
|
|
|
|
<div slot="footer" class="dialog-footer">
|
|
|
|
|
<el-button type="ghost" @click="cancel('addData')">取 消</el-button>
|
|
|
|
|
<el-button type="primary" @click="addOk('addData')">确 定</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
<el-card class="wrapper" v-if="!detaildialogVisible">
|
|
|
|
|
<cb-advance-table :card-border="false" title="秘钥列表" :search-configs="searchConfigs" :data="list" :params="params" :columns="columns" :get-list="getData" :total="total" :loading="loading" @selection-change="selectionChange">
|
|
|
|
|
<template v-slot:action>
|
2024-08-21 01:17:14 +00:00
|
|
|
|
<el-button type="primary" @click="add"> 新增 </el-button>
|
2024-08-20 12:11:31 +00:00
|
|
|
|
</template>
|
|
|
|
|
<template #name="val, record">
|
|
|
|
|
<span class="detail-href" @click="getDetail(record.id)">{{ record.name }}</span>
|
|
|
|
|
</template>
|
2024-08-21 01:17:14 +00:00
|
|
|
|
<template #operate="val, record">
|
2024-08-20 12:11:31 +00:00
|
|
|
|
<el-button :disabled="record.disabled" type="text" @click="dropdownClick({ id: record.id })"> <i class="el-icon-delete"></i> 删除 </el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</cb-advance-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
/* global $ */
|
|
|
|
|
import validate from '@/validate'
|
|
|
|
|
import { getRegion, getZone } from 'services/platform/index'
|
|
|
|
|
import { getKey, removeKey, createKey, detailKey, existKey, removeKeys } from 'views/resource/ctstack/services/key'
|
|
|
|
|
import { downloadFile } from '@cmp/cmp-common/utils/'
|
|
|
|
|
|
|
|
|
|
const detailSetting = {
|
|
|
|
|
type: 'miyao',
|
|
|
|
|
columns: [
|
|
|
|
|
[
|
|
|
|
|
{ name: '密钥名称', value: 'name' },
|
|
|
|
|
{ name: '所属平台', value: 'vendorName' },
|
|
|
|
|
{ name: '指纹', value: 'fingerprint' }
|
|
|
|
|
],
|
|
|
|
|
[{ name: '可用区', value: 'regionName' }]
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
export default {
|
|
|
|
|
props: {
|
|
|
|
|
platformObject: {
|
|
|
|
|
type: Object,
|
2024-08-21 01:17:14 +00:00
|
|
|
|
default: function () {
|
2024-08-20 12:11:31 +00:00
|
|
|
|
return {
|
|
|
|
|
vendorId: -1,
|
|
|
|
|
operate: -1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
required: validate.required,
|
|
|
|
|
hicNoChinese: validate.hicNoChinese,
|
|
|
|
|
detailSetting,
|
|
|
|
|
detaildialogVisible: false,
|
|
|
|
|
detail: {},
|
|
|
|
|
addFlag: false,
|
|
|
|
|
addData: {
|
|
|
|
|
name: '',
|
|
|
|
|
vendorId: this.platformObject.vendorId,
|
|
|
|
|
publicKey: ''
|
|
|
|
|
},
|
|
|
|
|
params: {
|
|
|
|
|
page: 1,
|
|
|
|
|
rows: 10
|
|
|
|
|
},
|
|
|
|
|
searchData: {
|
|
|
|
|
name: '',
|
|
|
|
|
regionId: ''
|
|
|
|
|
},
|
|
|
|
|
list: [],
|
|
|
|
|
total: 0,
|
|
|
|
|
createWays: [
|
|
|
|
|
{ name: '创建密钥', value: 'create', isRegionActive: true },
|
|
|
|
|
{
|
|
|
|
|
name: '导入密钥',
|
|
|
|
|
value: 'daoru',
|
|
|
|
|
isRegionActive: false
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
way: 'create',
|
|
|
|
|
regionData: [],
|
|
|
|
|
regionList: [],
|
|
|
|
|
idList: [],
|
|
|
|
|
selectList: [],
|
|
|
|
|
searchConfigs: [
|
|
|
|
|
{ type: 'Input', value: 'name', label: '名称' },
|
|
|
|
|
{ type: 'Select', data: [], value: 'regionId', label: '所属地域', props: { value: 'regionId' } },
|
|
|
|
|
{ type: 'Const', value: 'vendorId', initValue: this.platformObject.vendorId }
|
|
|
|
|
],
|
|
|
|
|
columns: [
|
|
|
|
|
{
|
|
|
|
|
label: '密钥名称',
|
|
|
|
|
prop: 'name',
|
|
|
|
|
scopedSlots: { customRender: 'name' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '所属平台',
|
|
|
|
|
prop: 'vendorName'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '指纹',
|
|
|
|
|
prop: 'fingerprint'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '操作',
|
|
|
|
|
disabled: true,
|
|
|
|
|
width: '100px',
|
|
|
|
|
scopedSlots: { customRender: 'operate' }
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
loading: false,
|
|
|
|
|
zoneList: []
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
selectionChange(val) {
|
|
|
|
|
this.selectList = val
|
|
|
|
|
},
|
|
|
|
|
handleDelete() {
|
|
|
|
|
this.refreshId()
|
|
|
|
|
const list = this.idList
|
|
|
|
|
if (list.length == 0) return this.$message.error('请选择VPC')
|
|
|
|
|
this.$confirm('此操作将删除所选密钥, 是否继续?', '提示', {
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
}).then(() => {
|
|
|
|
|
removeKeys(list).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.$message({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: data.message
|
|
|
|
|
})
|
|
|
|
|
this.getData()
|
|
|
|
|
this.selectList = []
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
refreshId() {
|
|
|
|
|
this.idList = []
|
|
|
|
|
this.selectList.forEach(item => {
|
|
|
|
|
this.idList.push(item.id)
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
// 单选
|
|
|
|
|
handleSelectItem(selection, row) {
|
|
|
|
|
this.refreshId()
|
|
|
|
|
if (this.idList.indexOf(row.id) > -1) {
|
|
|
|
|
for (let j = 0; j < this.selectList.length; j++) {
|
|
|
|
|
const item = this.selectList[j]
|
|
|
|
|
if (item.id == row.id) {
|
|
|
|
|
this.selectList.splice(j, 1)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.selectList.push(row)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 全选
|
|
|
|
|
handleSelectAll(selection) {
|
|
|
|
|
this.refreshId()
|
|
|
|
|
if (selection.length) {
|
|
|
|
|
// 全选情况下
|
|
|
|
|
selection.forEach(item => {
|
|
|
|
|
if (this.idList.indexOf(item.id) == -1) {
|
|
|
|
|
this.selectList.push(item)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
// 全不选情况下
|
|
|
|
|
this.list.forEach(item => {
|
|
|
|
|
if (this.idList.indexOf(item.id) > -1) {
|
|
|
|
|
for (let j = 0; j < this.selectList.length; j++) {
|
|
|
|
|
const row = this.selectList[j]
|
|
|
|
|
if (item.id == row.id) {
|
|
|
|
|
this.selectList.splice(j, 1)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 导入密钥
|
|
|
|
|
change() {
|
|
|
|
|
const that = this
|
|
|
|
|
const data = this.$refs.file.files[0]
|
|
|
|
|
// 对文件大小和类型进行过滤
|
|
|
|
|
const arr = data.name.split('.')
|
|
|
|
|
if (['pem', 'pub'].indexOf(arr[arr.length - 1]) == -1) {
|
|
|
|
|
this.$notify({
|
|
|
|
|
title: '提示',
|
|
|
|
|
message: '请上传此类型的文件,【.pem,.pub】',
|
|
|
|
|
type: 'error'
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (data.size > 1024 * 1024) {
|
|
|
|
|
this.$notify({
|
|
|
|
|
title: '提示',
|
|
|
|
|
message: '文件大小超过1M',
|
|
|
|
|
type: 'error'
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (data) {
|
|
|
|
|
// 将文件进行转码,转换为text
|
|
|
|
|
const reader = new FileReader()
|
|
|
|
|
reader.readAsText(data)
|
2024-08-21 01:17:14 +00:00
|
|
|
|
reader.onload = function (f) {
|
2024-08-20 12:11:31 +00:00
|
|
|
|
const data = this.result
|
|
|
|
|
that.addData.publicKey = data
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 详情
|
|
|
|
|
getDetail(id) {
|
|
|
|
|
this.detaildialogVisible = true
|
|
|
|
|
detailKey(id).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.detail = data.data
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
chooseRegion1(item) {
|
|
|
|
|
this.addData = {
|
|
|
|
|
name: this.addData.name,
|
|
|
|
|
vendorId: this.platformObject.vendorId,
|
|
|
|
|
regionId: this.addData.regionId,
|
|
|
|
|
zoneId: this.addData.zoneId,
|
|
|
|
|
publicKey: ''
|
|
|
|
|
}
|
|
|
|
|
this.way = item.value
|
|
|
|
|
this.createWays.forEach((data, index) => {
|
|
|
|
|
data.isRegionActive = false
|
|
|
|
|
if (data.name == item.name) {
|
|
|
|
|
data.isRegionActive = !item.isRegionActive
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
handleClose(done) {
|
|
|
|
|
this.cancel('addData')
|
|
|
|
|
},
|
|
|
|
|
add() {
|
|
|
|
|
this.addFlag = true
|
|
|
|
|
getRegion({ vendorId: this.platformObject.vendorId }).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.regionData = data.data
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
this.addData = {
|
|
|
|
|
name: '',
|
|
|
|
|
vendorId: this.platformObject.vendorId,
|
|
|
|
|
publicKey: ''
|
|
|
|
|
}
|
|
|
|
|
this.way = 'create'
|
|
|
|
|
this.createWays[0].isRegionActive = true
|
|
|
|
|
this.createWays[1].isRegionActive = false
|
|
|
|
|
},
|
|
|
|
|
addOk(formName) {
|
|
|
|
|
this.$refs[formName].validate(valid => {
|
|
|
|
|
if (valid) {
|
|
|
|
|
existKey({
|
|
|
|
|
condition: JSON.stringify({
|
|
|
|
|
condition: 'exist',
|
|
|
|
|
name: this.addData.name,
|
|
|
|
|
vendorId: this.platformObject.vendorId
|
|
|
|
|
})
|
|
|
|
|
}).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
if (this.way == 'create') {
|
|
|
|
|
createKey(this.addData).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.$message({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: data.message
|
|
|
|
|
})
|
|
|
|
|
this.cancel('addData')
|
|
|
|
|
this.getData()
|
|
|
|
|
downloadFile('/cmp/plugins/ctstack/v1/keypairs/download', {
|
|
|
|
|
id: data.data
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
createKey(this.addData).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.$message({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: data.message
|
|
|
|
|
})
|
|
|
|
|
this.getData()
|
|
|
|
|
this.cancel('addData')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
dropdownClick(command) {
|
|
|
|
|
this.$confirm('此操作将永久删除该密钥, 是否继续?', '提示', {
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
}).then(() => {
|
|
|
|
|
removeKey(command.id).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.$message({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: data.message
|
|
|
|
|
})
|
|
|
|
|
this.getData()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
getData() {
|
|
|
|
|
this.loading = true
|
2024-09-02 03:12:53 +00:00
|
|
|
|
getKey(this.params)
|
|
|
|
|
.then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.list = data.data.rows
|
|
|
|
|
|
|
|
|
|
this.selectList = []
|
|
|
|
|
this.total = data.data.total
|
|
|
|
|
this.list.forEach(item => {
|
|
|
|
|
if (item.projectVisibility && item.projectVisibility != 'GLOBAL_PROJECT') {
|
|
|
|
|
item.disabled = true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
2024-08-20 12:11:31 +00:00
|
|
|
|
this.loading = false
|
2024-09-02 03:12:53 +00:00
|
|
|
|
})
|
2024-08-20 12:11:31 +00:00
|
|
|
|
},
|
|
|
|
|
handleSizeChange(val) {
|
|
|
|
|
this.params.rows = val
|
|
|
|
|
this.getData()
|
|
|
|
|
},
|
|
|
|
|
cancel(formName) {
|
|
|
|
|
this.addFlag = false
|
|
|
|
|
this.$refs[[formName]].resetFields()
|
|
|
|
|
},
|
|
|
|
|
// 获取域
|
|
|
|
|
getRegion() {
|
|
|
|
|
getRegion({ vendorId: this.platformObject.vendorId }).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.regionList = data.data
|
|
|
|
|
this.searchConfigs[1].data = data.data
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
getZone() {
|
|
|
|
|
this.addData.zone = ''
|
|
|
|
|
getZone({
|
|
|
|
|
vendorId: this.platformObject.vendorId,
|
|
|
|
|
regionId: this.addData.regionId
|
|
|
|
|
}).then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
this.zoneList = data.data
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
this.getRegion()
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
platformObject: {
|
|
|
|
|
handler(newVal, oldVal) {
|
|
|
|
|
this.searchConfigs = [
|
|
|
|
|
{ type: 'Input', value: 'name', label: '名称' },
|
|
|
|
|
{ type: 'Select', data: [], value: 'regionId', label: '所属地域' },
|
|
|
|
|
{ type: 'Const', value: 'vendorId', initValue: newVal.vendorId }
|
|
|
|
|
]
|
|
|
|
|
this.getRegion()
|
|
|
|
|
},
|
|
|
|
|
deep: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.no-searchBox {
|
|
|
|
|
padding: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.vm-region {
|
|
|
|
|
height: 42px;
|
|
|
|
|
width: 110px;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
margin: 0 10px 0 0;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
line-height: 42px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: #666;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.vm-region:hover {
|
|
|
|
|
border-color: #43bfe3 !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.vm-region-text {
|
|
|
|
|
border-color: #43bfe3 !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.region-active {
|
|
|
|
|
background-color: deepskyblue;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
</style>
|