merge from primary branch

This commit is contained in:
jhnine 2022-03-23 15:41:27 +08:00
parent cdb24ade09
commit 2316e37d83
29 changed files with 1063 additions and 472 deletions

View File

@ -3,6 +3,6 @@ ENV = 'production'
# base api
VUE_APP_BASE_API = 'prod-api'
VUE_APP_FUNCTION_API = 'api'
VUE_APP_FUNCTION_API = 'apis/v1'
VUE_APP_PUBLIC_SOURCE_API = '/monitor'

View File

@ -92,7 +92,7 @@ export function putHostYaml(name, yaml) {
// 虚拟机管理-虚拟机列表
export function getVirtualMachines() {
return request({
url: '/virtual/v1/kubevirt.io.virtualmachine',
url: '/virtual/v1/harvester/kubevirt.io.virtualmachines',
method: 'get'
})
}
@ -223,6 +223,13 @@ export function deleteImage(name) {
method: 'delete'
})
}
export function editImage(name, data) {
return request({
url: '/virtual/v1/harvester/harvesterhci.io.virtualmachineimages/default/' + name,
method: 'put',
data: data
})
}
export function getImagesYaml(name) {
return request({
url: '/virtual/apis/harvesterhci.io/v1beta1/namespaces/default/virtualmachineimages/' + name,
@ -293,7 +300,7 @@ export function createVirtualMachine(data) {
export function virtualMachineACtion(name, action) {
return request({
url: '/virtual/v1/kubevirt.io.virtualmachines/default/' + name + '?action=' + action,
url: '/virtual/v1/harvester/kubevirt.io.virtualmachines/default/' + name + '?action=' + action,
method: 'post',
data: {}
})
@ -317,14 +324,14 @@ export function getVirtualNode() {
export function moveVirtualNode(id, data) {
return request({
url: `/virtual/v1/kubevirt.io.virtualmachines/default/${id}?action=migrate`,
url: `/virtual/v1/harvester/kubevirt.io.virtualmachines/default/${id}?action=migrate`,
method: 'Post',
data
})
}
export function deleteVirtualMachine(name, data) {
var newUrl = '/virtual/v1/kubevirt.io.virtualmachines/default/' + name + '?'
var newUrl = '/virtual/v1/harvester/kubevirt.io.virtualmachines/default/' + name + '?'
for (let i = 0; i < data.length; i++) {
newUrl += 'removedDisks=' + data[i] + '&'
}

View File

@ -223,6 +223,30 @@ export default {
})
},
// http://119.45.100.73:30880/api/v1/namespaces/jh-test1/configmaps
// http://119.45.100.73:30880/api/v1/namespaces/jh-test1/secrets
// 获取配置文件列表
getConfigmapList(clusterName, namespaces) {
return request({
url: process.env.VUE_APP_BASE_API + `/api${clusterName}/v1/namespaces/${namespaces}/configmaps`,
method: 'get',
headers: {
'Content-Type': 'application/json'
}
})
},
// 获取密钥列表
getSecretList(clusterName, namespaces) {
return request({
url: process.env.VUE_APP_BASE_API + `/api${clusterName}/v1/namespaces/${namespaces}/secrets`,
method: 'get',
headers: {
'Content-Type': 'application/json'
}
})
},
// 工作负载提交前check
getWorkloadCheck(clusterName, namespaces, type, name) {
// http://10.105.20.3:30880/apis/clusters/fedjcce-ten002/apps/v1/namespaces/test-zhangjie/deployments?labelSelector=app%3Dasd
@ -371,34 +395,40 @@ export default {
})
},
// 函数
checkFunctionName(functionName) {
return request({
url: process.env.VUE_APP_FUNCTION_API + `/jcc-faas-manager/function/exist/${functionName}`,
method: 'get'
})
},
createFunction(query) {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/createFunction',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/createFunction',
method: 'post',
data: query
})
},
deleteFunctionByName() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/delete/functionByName',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/delete/functionByName',
method: 'delete'
})
},
deleteFunction(functionId) {
return request({
url: process.env.VUE_APP_FUNCTION_API + `/function/deleteFunction/${functionId}`,
url: process.env.VUE_APP_FUNCTION_API + `/jcc-faas-manager/function/deleteFunction/${functionId}`,
method: 'delete'
})
},
getFunctionByFunctionName() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/delete/functionByName',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/delete/functionByName',
method: 'get'
})
},
invokeFunction(params, query) {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/invokeFunction',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/invokeFunction',
method: 'post',
params: params,
data: query
@ -406,7 +436,7 @@ export default {
},
listFunctions(query) {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/lists',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/lists',
method: 'get',
params: query || { page: 1, size: 10 }
})
@ -419,41 +449,41 @@ export default {
// },
getFunctionOverview() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/selectFunctionOverview',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/selectFunctionOverview',
method: 'get'
})
},
getFunctionMap() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/selectFunctionList',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/selectFunctionList',
method: 'get'
})
},
// 大屏总营收接口
getRevenue() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/queryRevenue',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-mall/order/queryRevenue',
method: 'get'
})
},
// 大屏地球查询
getMapArea() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/device/queryByArea',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryByArea',
method: 'get'
})
},
// 大屏云厂商服务器数量查询接口
getCloudServerCount() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/device/queryByManufacturer',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryByManufacturer',
method: 'get'
})
},
// 大屏服务器数量统计接口
getServerDeviceCount() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/function/device/queryDeviceCount',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryDeviceCount',
method: 'get'
})
},
@ -461,7 +491,7 @@ export default {
// 大屏服务器数量统计接口
getOrderStatus() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/countOrderStatus',
url: process.env.VUE_APP_FUNCTION_API + '/jcc-mall/order/countOrderStatus',
method: 'get'
})
},

View File

@ -0,0 +1,120 @@
// 大屏云厂商服务器数量查询接口
import request from '@/utils/request'
export function getTotalName() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryByManufacturer',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 大屏服务器数量统计接口
export function getTotalNum() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryDeviceCount',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 大屏地球地区查询接口
export function getEarthRegion() {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryByArea',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 大屏地球各个区域服务器资源详细接口
export function getEarthDetails(area) {
return request({
url: process.env.VUE_APP_FUNCTION_API + '/jcc-faas-manager/function/device/queryResDetailByArea?area=' + area,
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// CPU平均使用率接口
export function getCpuAverage(start, end, step) {
return request({
url: '/monitoringscreen/api/v1/query_range?query=avg(1%20-%20avg(rate(node_cpu_seconds_total%7Borigin_prometheus%3D~%22%22%2Cjob%3D~%22node-exporter%22%2Cmode%3D%22idle%22%7D%5B2m%5D))%20by%20(instance))%20*%20100' + '&start=' + start + '&end=' + end + '&step=' + step + '&_=1642578431700',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 内存平均使用率接口
export function getRamAverage(start, end, step) {
return request({
url: '/monitoringscreen/api/v1/query_range?query=(sum(node_memory_MemTotal_bytes%7Borigin_prometheus%3D~%22%22%2Cjob%3D~%22node-exporter%22%7D%20-%20node_memory_MemAvailable_bytes%7Borigin_prometheus%3D~%22%22%2Cjob%3D~%22node-exporter%22%7D)%20%2F%20sum(node_memory_MemTotal_bytes%7Borigin_prometheus%3D~%22%22%2Cjob%3D~%22node-exporter%22%7D))*100' + '&start=' + start + '&end=' + end + '&step=' + step + '&_=1642578431700',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 计量计费接口总价格
export function getMeasure(start, end, step) {
return request({
url: process.env.VUE_APP_BASE_API + '/kapis/clusters/host/metering.kubesphere.io/v1alpha1/cluster' + '?start=' + start + '&end=' + end + '&step=' + step + 's&metrics_filter=meter_cluster_cpu_usage%7Cmeter_cluster_memory_usage%7Cmeter_cluster_net_bytes_transmitted%7Cmeter_cluster_net_bytes_received%7Cmeter_cluster_pvc_bytes_total&resources_filter=host',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 内存整体负载接口
export function getRamLoad(start, end, step) {
return request({
url: '/monitoringscreen/api/v1/query_range?query=sum(node_memory_MemTotal_bytes%7Borigin_prometheus%3D~%22%22%2Cjob%3D~%22node-exporter%22%7D%20-%20node_memory_MemAvailable_bytes%7Borigin_prometheus%3D~%22%22%2Cjob%3D~%22node-exporter%22%7D)&start=' + start + '&end=' + end + '&step=' + step + '&_=1642669327729',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// 计量计费列表接口
export function getMeteringList(start, end, step) {
return request({
url: process.env.VUE_APP_BASE_API + '/kapis/clusters/host/metering.kubesphere.io/v1alpha1/cluster' + '?start=' + start + '&end=' + end + '&step=' + step + 's&metrics_filter=meter_cluster_cpu_usage%7Cmeter_cluster_memory_usage%7Cmeter_cluster_net_bytes_transmitted%7Cmeter_cluster_net_bytes_received%7Cmeter_cluster_pvc_bytes_total&resources_filter=host',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}
// CPU整体负载接口
export function getCpuAllload(start, end) {
return request({
url: '/monitoringscreen/api/v1/query_range?query=node_load15%7Binstance%3D~%22k8s-master%22%7D&start=' + start + '&end=' + end + '&step=900&_=1645409802467',
method: 'get',
data: JSON,
headers: {
'Content-Type': 'application/json'
}
})
}

View File

@ -1,7 +1,7 @@
<template>
<!-- 添加容器镜像 -->
<!-- 初始容器 其他 -->
<el-dialog v-if="dialogVisible" width="80%" :title="(!createContainerForm.name?'添加':'编辑')+'容器'" :visible.sync="dialogVisible">
<el-dialog v-if="dialogVisible" width="80%" :title="(!createContainerForm.name?'添加':'编辑')+'容器'" :visible.sync="dialogVisible" append-to-body>
<el-form ref="createContainerForm" :model="createContainerForm">
<div class="border">

View File

@ -1,10 +1,16 @@
<template>
<!-- 添加存储卷 -->
<el-dialog v-if="dialogVisible" width="80%" title="存储卷" :visible.sync="dialogVisible">
<el-form ref="createVolumesForm" :model="createVolumesForm">
<el-dialog v-if="dialogVisible" width="80%" title="存储卷" :visible.sync="dialogVisible" append-to-body>
<el-form ref="volumeData" :model="volumeData">
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="已有存储卷" name="first">
<el-select v-model="createVolumesForm.volumes" class="selectPro" placeholder="选择已有存储卷">
<el-form-item
v-if="activeName==='first'"
prop="firstName"
label=""
required
>
<el-select v-model="volumeData.firstName" class="selectPro" placeholder="选择已有存储卷">
<el-option
v-for="item in volumesList"
:key="item.label"
@ -16,14 +22,16 @@
<span style="width:40%; display:block;float:left; text-align:right">访问模式{{ item.accessModes }}</span>
</el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="临时存储卷" name="second">
<el-form-item
prop="volumesName"
v-if="activeName==='second'"
prop="secondName"
label="存储卷名称"
required
>
<el-input v-model="createVolumesForm.volumesName" :maxlength="63" />
<el-input v-model="volumeData.secondName" :maxlength="63" />
<span class="tips">最长 63 个字符只能包含小写字母数字及分隔符("-")且必须以小写字母或数字开头及结尾</span>
</el-form-item>
</el-tab-pane>
@ -33,19 +41,21 @@
type="warning"
/>
<el-form-item
prop="volumesName"
v-if="activeName==='third'"
prop="thirdName"
label="存储卷名称"
required
>
<el-input v-model="createVolumesForm.volumesName" :maxlength="63" />
<el-input v-model="volumeData.thirdName" :maxlength="63" />
<span class="tips">最长 63 个字符只能包含小写字母数字及分隔符("-")且必须以小写字母或数字开头及结尾</span>
</el-form-item>
<el-form-item
v-if="activeName==='third'"
prop="hostPath"
label="HostPath"
required
>
<el-input v-model="createVolumesForm.volumesName" :maxlength="63" />
<el-input v-model="volumeData.path" :maxlength="63" />
<span class="tips">最长 63 个字符只能包含小写字母数字及分隔符("-")且必须以小写字母或数字开头及结尾</span>
</el-form-item>
</el-tab-pane>
@ -53,9 +63,9 @@
<table v-if="formData.spec.template.spec.containers">
<tr v-for="(item, index) in formData.spec.template.spec.containers" :key="index" class="dataList">
<td>容器</td>
<td>{{ item }}</td>
<td>{{ item.name }}</td>
<td>
<el-select v-model="createVolumesForm.readOnly">
<el-select v-model="createVolumesForm[index].readOnly">
<el-option
v-for="it in mountOptions"
:key="it.value"
@ -64,7 +74,7 @@
/>
</el-select>
</td>
<td><el-input v-model="createVolumesForm.mountPath" :disabled="!createVolumesForm.readOnly || createVolumesForm.readOnly === 'null'" placeholder="容器挂载路径, 例如: /data" /></td>
<td><el-input v-model="createVolumesForm[index].mountPath" :disabled="createVolumesForm[index].readOnly == null" placeholder="容器挂载路径, 例如: /data" /></td>
</tr>
</table>
</el-form>
@ -77,6 +87,7 @@
<script>
import { mountOptions } from '@/utils/map'
import generate from 'nanoid/generate'
export default {
props: {
value: {
@ -94,7 +105,13 @@ export default {
},
data() {
return {
createVolumesForm: {},
createVolumesForm: [],
volumeData: {
firstName: '',
secondName: '',
thirdName: '',
hostPath: ''
},
volumesList: [],
activeName: 'first',
mountOptions
@ -126,6 +143,15 @@ export default {
// }
},
watch: {
'formData.spec.template.spec.containers': {
handler(val) {
this.createVolumesForm = []
for (let i = 0; i < this.formData.spec.template.spec.containers.length; i++) {
this.createVolumesForm.push({ readOnly: null, mountPath: '' })
}
},
immediate: true
}
// code(val) {
// this.codeData = val
// }
@ -141,22 +167,49 @@ export default {
})
},
ok() {
const val = {}
this.$refs.volumeData.validate((valid) => {
if (valid) {
//
// if(this.createVolumesForm.readOnly !== 'null'){
// this.formData.spec.template.spec.containers.forEach(e=>{
// })
// }
const newFormData = Object.assign({}, this.formData)
let newSpecVolume = {}
switch (this.activeName) {
case 'first':
newSpecVolume = {
name: `volume-${generate('0123456789abcdefghijklmnopqrstuvwxyz', 6)}`,
persistentVolumeClaim: { claimName: this.volumeData[this.activeName + 'Name'] }
}
break
case 'second':
newSpecVolume = {
name: this.volumeData[this.activeName + 'Name'],
emptyDir: {}
}
break
case 'third':
newSpecVolume = {
name: this.volumeData[this.activeName + 'Name'],
hostPath: {
path: this.volumeData.hostPath
}
}
break
}
newFormData.spec.template.spec.containers.forEach((e, index) => {
if (this.createVolumesForm[index]?.readOnly !== null) {
if (!e.volumeMounts) { e.volumeMounts = [] }
e.volumeMounts.push({
name: newSpecVolume.name,
readOnly: this.createVolumesForm[index]?.readOnly,
mountPath: this.createVolumesForm[index]?.mountPath
})
}
})
newFormData.spec.template.spec.volumes.push(newSpecVolume)
return this.$emit('addVolumes', val)
this.$emit('addVolumes', newFormData)
this.dialogVisible = false
}
})
},
handleClick() {}
}

View File

@ -0,0 +1,201 @@
<template>
<!-- 配置文件和密钥 -->
<el-dialog v-if="dialogVisible" width="80%" title="配置文件和密钥" :visible.sync="dialogVisible" append-to-body>
<el-form ref="settingForm" :model="settingForm">
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="配置" name="first">
<el-form-item
v-if="activeName==='first'"
prop="firstName"
label=""
required
>
<el-select v-model="settingForm.firstName" class="selectPro" placeholder="请选择配置文件">
<el-option
v-for="item in configmapList"
:key="item.label"
:label="item.label"
:value="item.label"
>
{{ item.label }}
</el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="密钥" name="second">
<el-form-item
v-if="activeName==='second'"
prop="secondName"
label=""
required
>
<el-select v-model="settingForm.secondName" class="selectPro" placeholder="请选择配置文件">
<el-option
v-for="item in secretList"
:key="item.label"
:label="item.label"
:value="item.label"
>
{{ item.label }}
</el-option>
</el-select>
</el-form-item>
</el-tab-pane>
</el-tabs>
<table v-if="formData.spec.template.spec.containers">
<tr v-for="(item, index) in formData.spec.template.spec.containers" :key="index" class="dataList">
<td>容器</td>
<td>{{ item.name }}</td>
<td>
<el-select v-model="createVolumesForm[index].readOnly">
<el-option
v-for="it in settingMountOptions"
:key="it.value"
:label="it.label"
:value="it.value"
/>
</el-select>
</td>
<td><el-input v-model="createVolumesForm[index].mountPath" :disabled="createVolumesForm[index].readOnly == null" placeholder="容器挂载路径, 例如: /data" /></td>
</tr>
</table>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button icon="el-icon-close" circle @click="dialogVisible = false" />
<el-button icon="el-icon-check" circle @click="ok" />
</div>
</el-dialog>
</template>
<script>
import { settingMountOptions } from '@/utils/map'
import generate from 'nanoid/generate'
export default {
props: {
value: {
type: Boolean,
default: false
},
formData: {
type: Object,
default: () => {}
},
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
createVolumesForm: [],
settingForm: {
firstName: '',
secondName: ''
},
configmapList: [],
secretList: [],
activeName: 'first',
settingMountOptions
}
},
computed: {
clusterName() {
if (localStorage.getItem('clusterName') === 'default') {
return ''
} else {
return '/clusters/' + localStorage.getItem('clusterName')
}
},
dialogVisible: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
}
// codeData: {
// get() {
// return this.code + ''
// },
// set(value) {
// this.$emit('code', value)
// }
// }
},
watch: {
'formData.spec.template.spec.containers': {
handler(val) {
this.createVolumesForm = []
for (let i = 0; i < this.formData.spec.template.spec.containers.length; i++) {
this.createVolumesForm.push({ readOnly: null, mountPath: '' })
}
},
immediate: true
}
// code(val) {
// this.codeData = val
// }
},
mounted() {
this.getConfigmapList()
this.getSecretList()
},
methods: {
getConfigmapList() {
this.$Api.getConfigmapList(this.clusterName, this.formData.metadata.namespace).then(res => {
const arr = res.items.map(e => { return { label: e.metadata.name } })
this.configmapList = arr
})
},
getSecretList() {
this.$Api.getSecretList(this.clusterName, this.formData.metadata.namespace).then(res => {
const arr = res.items.map(e => { return { label: e.metadata.name } })
this.secretList = arr
})
},
ok() {
this.$refs.settingForm.validate((valid) => {
if (valid) {
//
const newFormData = Object.assign({}, this.formData)
let newSpecVolume = {}
switch (this.activeName) {
case 'first':
newSpecVolume = {
name: `volume-${generate('0123456789abcdefghijklmnopqrstuvwxyz', 6)}`,
configMap: { name: this.settingForm[this.activeName + 'Name'] }
}
break
case 'second':
newSpecVolume = {
name: `volume-${generate('0123456789abcdefghijklmnopqrstuvwxyz', 6)}`,
secret: { secretName: this.settingForm[this.activeName + 'Name'] }
}
break
}
newFormData.spec.template.spec.containers.forEach((e, index) => {
if (this.createVolumesForm[index]?.readOnly !== null) {
if (!e.volumeMounts) { e.volumeMounts = [] }
e.volumeMounts.push({
name: newSpecVolume.name,
readOnly: this.createVolumesForm[index]?.readOnly,
mountPath: this.createVolumesForm[index]?.mountPath
})
}
})
newFormData.spec.template.spec.volumes.push(newSpecVolume)
this.$emit('addSettingOrKey', newFormData)
this.dialogVisible = false
}
})
},
handleClick() {}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -265,10 +265,7 @@ export default {
},
methods: {
addImage(data) {
console.log(this.editInfoForm.spec.template.spec.containers)
this.editInfoForm.spec.template.spec.containers.push(data)
console.log(this.editInfoForm.spec.template.spec.containers.length)
// console.log(data)
this.containerForm = undefined
},
editImage(data) {
@ -303,8 +300,10 @@ export default {
border: 1px solid #DCDFE6;
margin-top: 10px;
padding:10px;
line-height: 1.4em;
.el-button{
float:right;
margin-right: 10px;
}
}
.selectRadio{

View File

@ -11,7 +11,7 @@
<mountVolumes v-if="stepNum===2" v-model="metaData" />
<advancedSettings v-if="stepNum===3" v-model="metaData" />
<div slot="footer" class="dialog-footer">
<el-button @click="createFormVisible = false"> </el-button>
<el-button @click="createFormVisible = false;transformYaml()"> </el-button>
<el-button v-if="stepNum!==0" type="primary" @click="prev">上一步</el-button>
<el-button v-if="stepNum!==3" type="primary" @click="next">下一步</el-button>
<el-button v-else type="primary" @click="createWorkload">创建</el-button>
@ -111,6 +111,7 @@ spec:
this.$Api.postWorkload(this.clusterName, this.metaData.metadata.namespace, this.classification, this.metaData).then(() => {
this.$message.success('创建成功!')
this.createFormVisible = false
this.transformYaml()
this.$emit('getList')
})
})
@ -118,6 +119,7 @@ spec:
})
},
transformYaml() {
this.stepNum = 0
this.metaData = JSON.parse(JSON.stringify(yaml.load(this.baseYaml), null, 2))
},
prev() {

View File

@ -1,30 +1,51 @@
<template>
<div>
<div class="mountVolumes">
<p>挂载存储</p>
<!-- dataList -->
<!-- <i class="el-icon-receiving" />
<i class="el-icon-setting" />
<i class="el-icon-key" /> -->
<div v-if="editInfoForm.spec.template.spec.volumes">
<div v-for="(item, index) in editInfoForm.spec.template.spec.volumes" :key="index" class="dataList">
<el-button icon="el-icon-delete" circle @click.prevent="removeVolume(index)" />
<el-button icon="el-icon-edit" circle @click.prevent="editVolume(item)" />
<div><i class="el-icon-receiving" />{{ item.name }}</div>
<div>类型{{ item.emptyDir ? ' 临时存储卷': item.hostPath ? 'HostPath' : item.configMap ? '配置' : item.secret ? '密钥' : '已有存储卷' }}</div>
<div v-for="(it, ind) in editInfoForm.spec.template.spec.containers" :key="ind">
<div v-if="it.volumeMounts">
<div v-for="(ii, dd) in it.volumeMounts" :key="dd">
<div v-if="ii.name === item.name" class="volumeList">
<span><i class="el-icon-c-scale-to-original" />{{ it.name }}</span>
<span><i class="el-icon-reading" />{{ ii.mountPath }}({{ ii.readOnly==='true'?'只读':'读写' }})</span>
</div>
</div>
</div>
</div>
</div>
</div>
<el-button-group>
<el-button ton @click="createFormVisible=true">
添加存储卷
<span class="tips">支持临时存储卷以及持久化存储卷</span>
</el-button>
<el-button>挂载配置文件或密钥
<el-button @click="mountSettingFormVisible=true">
挂载配置文件或密钥
<span class="tips">将配置文件或密钥挂载至指定目录</span>
</el-button>
</el-button-group>
<addVolumesForm v-model="createFormVisible" :form-data="editInfoForm" @addVolumes="addVolumes" />
<addVolumesForm v-if="createFormVisible" v-model="createFormVisible" :form-data="editInfoForm" @addVolumes="addVolumes" />
<mountSettingForm v-if="mountSettingFormVisible" v-model="mountSettingFormVisible" :form-data="editInfoForm" @addSettingOrKey="addSettingOrKey" />
</div>
</template>
<script>
import addVolumesForm from '@/components/Actions/addVolumesForm.vue'
import mountSettingForm from '@/components/Actions/mountSettingOrKey.vue'
export default {
components: { addVolumesForm },
components: { addVolumesForm, mountSettingForm },
props: {
value: {
type: Object,
@ -34,8 +55,7 @@ export default {
data() {
return {
createFormVisible: false,
createVolumesForm: {},
formData: {}
mountSettingFormVisible: false
}
},
computed: {
@ -49,12 +69,53 @@ export default {
}
},
methods: {
addVolumes() {}
addSettingOrKey(e) {
this.editInfoForm = e
},
addVolumes(e) {
this.editInfoForm = e
},
editVolume() {},
removeVolume(index) {
const name = this.editInfoForm.spec.template.spec.volumes[index].name
this.editInfoForm.spec.template.spec.containers.forEach(e => {
for (let i = 0; i < e.volumeMounts?.length || 0; i++) {
if (e.volumeMounts[i].name === name) {
e.volumeMounts.splice(i, 1)
}
}
})
this.editInfoForm.spec.template.spec.volumes.splice(index, 1)
}
}
}
</script>
<style lang="scss">
.mountVolumes{
.dataList{
border: 1px solid #DCDFE6;
margin-top: 10px;
margin-bottom: 10px;
padding:10px;
line-height: 1.4em;
.el-button{
float:right;
margin-right: 10px;
}
.volumeList{
padding: 5px;
background-color: #eeeeee;
margin-top:5px;
span{
i {display: inline-block; margin-right:10px}
display:inline-block;
width: 50%;
}
}
}
}
.selectPro{
width: 100%;

View File

@ -2,7 +2,7 @@ import { getVolume } from '@/api/one-class-page/storageManagement'
import { getNodeMessage, getNodeStatus } from '@/api/one-class-page/nodeManagement'
import { getByValue } from '@/utils/data-process'
import { getDaemonSets, getDeployments, getPods, getServices, getStatefulSets } from '@/api/one-class-page/workloadManagement'
import { getUsageStatus, getVirtualHosts, getVirtualMachinesInstances, getDataVolumeNew, getImages } from '@/api/one-class-page/virtualMachine'
import { getUsageStatus, getVirtualHosts, getVirtualMachinesInstances, getVirtualMachines, getDataVolumeNew, getImages, getDashboardInfo, getStorageStatus } from '@/api/one-class-page/virtualMachine'
// import { getImages } from '@/api/one-class-page/virtualMachine'
import {
getBriefSystemProject,
@ -11,6 +11,8 @@ import {
getDetailUserProject
} from '@/api/one-class-page/projectManagement'
import { getHostNum, getMemberNum } from '@/api/one-class-page/cluster'
import { parseSi } from '@/utils'
// import { strToNumber } from '@/utils/data-process'
function getStatus(readyReplicas, replicas) {
@ -273,12 +275,25 @@ const getHostVirtualMachineList = (clusterName, params) => {
}
const getVirtualMachineList = (clusterName, params) => {
return new Promise(function(resolve) {
const listResult = []
getVirtualMachinesInstances().then((res) => {
for (let item = 0; item < res.data.length; item++) {
const obj = {}
obj.index = item
obj.name = res.data[item].metadata.fields[0]
let listResult = []
Promise.all([
getVirtualMachines(),
getVirtualMachinesInstances()
]).then(res => {
const vm = res[0]
const vmIn = res[1]
listResult = vm.data.map((i, index) => ({
index: index,
name: i.metadata.fields[0],
state: i.metadata.annotations['harvesterhci.io/migrationState'] || i.metadata.fields[2],
operator: i.metadata.fields[5],
aliveTime: i.metadata.fields[1].replace('d', '天').replace('h', '小时'),
cpu: i.spec.template.spec.domain.resources.limits.cpu,
memory: i.spec.template.spec.domain.resources.limits.memory,
ipAddress: vmIn.data.filter(n => n.id === i.id)?.[0]?.metadata?.fields[3] || '',
node: vmIn.data.filter(n => n.id === i.id)?.[0]?.metadata?.fields[4] || '',
label: i.metadata.labels['harvesterhci.io/creator']
})
// TODO: 虚拟机状态显示
// var running = res.data[item].spec.running
// if (running) {
@ -286,21 +301,10 @@ const getVirtualMachineList = (clusterName, params) => {
// } else {
// obj.state = 'Off'
// }
obj.state = res.data[item].metadata.annotations['harvesterhci.io/migrationState'] || res.data[item].metadata.fields[2]
obj.operator = res.data[item].metadata.fields[5]
obj.aliveTime = res.data[item].metadata.fields[1]
obj.aliveTime = obj.aliveTime.replace('d', '天')
obj.aliveTime = obj.aliveTime.replace('h', '小时')
obj.aliveTime = obj.aliveTime.replace('h', '小时')
obj.cpu = res.data[item].spec.domain.cpu.cores
obj.memory = res.data[item].spec.domain.memory.guest
obj.ipAddress = res.data[item].metadata.fields[3]
obj.node = res.data[item].metadata.fields[4]
obj.label = res.data[item].metadata.labels['harvesterhci.io/creator']
// obj.memoryused = res.data[item].status.capacity.ephemeral-storage - res.data[item].status.allocatable.memory
// obj.memoryall = res.data[item].status.capacity.memory
listResult[item] = obj
}
// listResult[item] = obj
)
resolve({ total: 10, rows: listResult })
})
})
@ -309,38 +313,77 @@ const getVirtualMachineList = (clusterName, params) => {
const getVirtualHostList = () => {
return new Promise(function(resolve) {
const listResult = []
getUsageStatus().then(usage => {
getVirtualHosts().then((res) => {
for (let item = 0; item < res.data.length; item++) {
Promise.all([
getDashboardInfo(), // cpu 内存 总量
getUsageStatus(), // cpu 内存 使用量
getStorageStatus(),
getVirtualHosts()]).then(res => {
for (let item = 0; item < res[3].data.length; item++) {
let cpuAll = 0
let memoryAll = 0
let storageAll = 0
let cpuUsed = 0
let memoryUsed = 0
let storageUsage = 0
res[0].data.forEach(element => {
if (res[3].data[item].metadata.name === element.id) {
cpuAll += Number(element.status.capacity.cpu)
memoryAll += Number(element.status.capacity.memory.replace('Ki', ''))
}
})
res[1].data.forEach(element => {
if (res[3].data[item].metadata.name === element.spec.nodeName) {
const containers = element.spec.containers || []
containers.forEach(e => {
memoryUsed += parseSi(e?.resources?.requests?.memory || '0m', { increment: 1024 })
cpuUsed += parseSi(e?.resources?.requests?.cpu || '0m')
})
}
})
res[2].data.forEach(element => {
if (res[3].data[item].metadata.name === element.spec.name) {
const diskStatus = element.status?.diskStatus || {}
Object.values(diskStatus).map((disk) => {
if (disk?.conditions?.Schedulable?.status === 'True' && disk?.storageAvailable && disk?.storageMaximum) {
storageUsage += (disk.storageMaximum - disk.storageAvailable)
}
if (disk?.storageMaximum) {
storageAll += disk.storageMaximum
}
})
}
})
var obj = {}
obj.index = item
obj.customName = res.data[item].metadata.annotations['harvesterhci.io/host-custom-name']
obj.name = res.data[item].metadata.name
obj.state = res.data[item].metadata.state.name
obj.customName = res[3].data[item].metadata.annotations['harvesterhci.io/host-custom-name']
obj.name = res[3].data[item].metadata.name
obj.state = res[3].data[item].metadata.state.name
obj.state = obj.state.charAt(0).toUpperCase() + obj.state.slice(1)
obj.hostIP = res.data[item].metadata.fields[5]
obj.aliveTime = res.data[item].metadata.fields[3]
obj.hostIP = res[3].data[item].metadata.fields[5]
obj.aliveTime = res[3].data[item].metadata.fields[3]
obj.aliveTime = obj.aliveTime.replace('d', '天')
obj.aliveTime = obj.aliveTime.replace('h', '小时')
obj.cpuUsed = parseFloat((parseInt(usage?.data[item]?.usage?.cpu || 0) / 1000000000).toFixed(2))
obj.cpuAll = parseInt(res.data[item].status.capacity.cpu)
obj.cpuUsePercentage = parseFloat((obj.cpuUsed / obj.cpuAll).toFixed(2)) * 100
obj.memoryUsed = parseFloat((parseInt(usage.data[item].usage.memory) / 1024 / 1024).toFixed(2))
obj.memoryAll = parseFloat((parseFloat(res.data[item].status.capacity.memory) / 1024 / 1024).toFixed(2))
obj.memoryUsePercentage = parseFloat((obj.memoryUsed / obj.memoryAll).toFixed(2)) * 100
obj.storageUsed = parseFloat((parseFloat(res.data[item].status.capacity['ephemeral-storage']) / 1024 / 1024 / 1024 - parseFloat(res.data[item].status.allocatable['ephemeral-storage']) / 1024 / 1024 / 1024 / 1024).toFixed(2))
obj.storageAll = parseFloat((parseFloat(res.data[item].status.capacity['ephemeral-storage']) / 1024 / 1024 / 1024).toFixed(2))
obj.storageUsePercentage = parseFloat((obj.storageUsed / obj.storageAll).toFixed(2)) * 100
// obj.memoryall = res.data[item].status.capacity.memory
obj.cpuUsed = cpuUsed.toFixed(2)
obj.cpuAll = cpuAll
obj.cpuUsePercentage = (parseFloat((obj.cpuUsed / obj.cpuAll)) * 100).toFixed(0)
obj.memoryUsed = (memoryUsed / 1024 / 1024 / 1024).toFixed(2)
obj.memoryAll = (memoryAll / 1024 / 1024).toFixed(0)
obj.memoryUsePercentage = (parseFloat((obj.memoryUsed / obj.memoryAll)) * 100).toFixed(1)
obj.storageUsed = (storageUsage / 1024 / 1024 / 1024 / 1024).toFixed(2)
obj.storageAll = (storageAll / 1024 / 1024 / 1024 / 1024).toFixed(2)
obj.storageUsePercentage = (parseFloat((obj.storageUsed / obj.storageAll)) * 100).toFixed(1)
// obj.memoryall = hostRes.data[item].status.capacity.memory
var n, e
obj.isMaintenance = (((n = res.data[item].metadata) === null || void 0 === n || (e = n.annotations) === null || void 0 === e ? void 0 : e['harvesterhci.io/maintain-status']) === 'completed')
obj.isMaintenance = (((n = res[3].data[item].metadata) === null || void 0 === n || (e = n.annotations) === null || void 0 === e ? void 0 : e['harvesterhci.io/maintain-status']) === 'completed')
if (obj.isMaintenance) {
obj.state = 'Maintenance mode'
}
listResult[item] = obj
}
resolve({ total: res.data.length, rows: listResult })
})
resolve({ total: res[3].data.length, rows: listResult })
})
})
}

View File

@ -360,3 +360,58 @@ export function newEval(str) {
var Fn = Function
return new Fn('return ' + str)()
}
const UNITS = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
const FRACTIONAL = ['', 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y'] // milli micro nano pico femto
export function parseSi(inValue, opt) {
opt = opt || {}
let increment = opt.increment
const allowFractional = opt.allowFractional !== false
if (!inValue || typeof inValue !== 'string' || !inValue.length) {
return NaN
}
inValue = inValue.replace(/,/g, '')
// eslint-disable-next-line prefer-const
let [, valStr, unit, incStr] = inValue.match(/^([0-9.-]+)\s*([^0-9.-]?)([^0-9.-]?)/)
const val = parseFloat(valStr)
if (!unit) {
return val
}
// micro "mu" symbol -> u
if (unit.charCodeAt(0) === 181) {
unit = 'u'
}
const divide = FRACTIONAL.includes(unit)
const multiply = UNITS.includes(unit.toUpperCase())
if (!increment) {
// Automatically handle 1 KB = 1000B, 1 KiB = 1024B if no increment set
if ((multiply || divide) && incStr === 'i') {
increment = 1024
} else {
increment = 1000
}
}
if (divide && allowFractional) {
const exp = FRACTIONAL.indexOf(unit)
return val / (increment ** exp)
}
if (multiply) {
const exp = UNITS.indexOf(unit.toUpperCase())
return val * (increment ** exp)
}
// Unrecognized unit character
return val
}

View File

@ -17,9 +17,13 @@ const policysTypeOptions = [
]
const mountOptions = [
{ label: '读写', value: 'false' },
{ label: '只读', value: 'true' },
{ label: '不挂载', value: 'null' }
{ label: '读写', value: false },
{ label: '只读', value: true },
{ label: '不挂载', value: null }
]
const settingMountOptions = [
{ label: '只读', value: true },
{ label: '不挂载', value: null }
]
const imagePullPolicyOptions = [
@ -57,6 +61,7 @@ export {
policysOptions,
policysTypeOptions,
mountOptions,
settingMountOptions,
imagePullPolicyOptions,
protocolOptions
}

View File

@ -6,7 +6,7 @@
<el-tab-pane label="证书查验" name="1" />
<el-tab-pane label="身份查验" name="2" />
</el-tabs>
<el-input v-model="content" placeholder="请输入您想要校验的内容">
<el-input v-model="content" :placeholder="`${activeName === '1' ? '请输入您想要校验的内容' : '请输入身份指纹'}`">
<el-button slot="append" icon="el-icon-search" type="primary" @click="search">校验查询</el-button>
</el-input>
<List
@ -22,7 +22,7 @@
<el-dialog width="70%" title="交易证书" :visible.sync="dialogCertVisible">
<div class="certification">
<p>兹证明<br>
申请人 {{ certificationData.buyerName }} {{ certificationData.transTime }} 通过 {{ certificationData.sellerName }} 平台提交了以下电子数据及信息
申请人 {{ certificationData.buyerName }} {{ new Date(certificationData.transTime).toLocaleString() }} 通过 {{ certificationData.sellerName }} 平台提交了以下电子数据及信息
</p>
<FormData :column="1" :data="certificationData" :data-map="certDataMap" />
</div>
@ -47,7 +47,9 @@ export default {
columns: [
{ prop: 'certNum', label: '证书编号' },
{ prop: 'fingerPrint', label: '身份指纹' },
{ prop: 'createDate', label: '创建时间' },
{ prop: 'createDate', label: '创建时间', formatter: (row) => {
return <div>{new Date(row.createDate).toLocaleString() || '-'}</div>
} },
{ prop: 'more', label: '操作', formatter: (row) => {
return <div>
<el-button onClick={() => this.viewCredential(row)} type='text' size='small'>查看</el-button>

View File

@ -1,7 +1,7 @@
<template>
<div>
<el-card shadow="never">
<el-page-header content="区块列表" @back="goBack" />
<el-page-header content="区块列表(最近一个月的数据)" @back="goBack" />
<List
v-if="listData.length>=1"
ref="multipleTable"

View File

@ -1,6 +1,6 @@
<template>
<div class="functionList">
<el-button class="opr-btn" size="middle" type="primary" @click="dialogCreateVisible=true">创建函数</el-button>
<el-button class="opr-btn" size="middle" type="primary" @click="form={runEnv: 'python3'};dialogCreateVisible=true">创建函数</el-button>
<el-row :gutter="20">
<el-col v-for="(item,index) in functionList" :key="index" :span="6">
<el-card shadow="never" class="functionCard">
@ -31,6 +31,7 @@
background
:hide-on-single-page="true"
:current-page="page"
:page-size="size"
layout="prev, pager, next"
:total="total"
@current-change="currentChange"
@ -91,15 +92,15 @@
<td>Function Name:</td>
<td :colspan="3">{{ form.functionName }}</td>
<td>Created Time:</td>
<td>{{ form.createTime }}</td>
<td>{{ form.createdTime }}</td>
</tr>
<tr>
<td>Env:</td>
<td>{{ form.runEnv }}</td>
<td>Memory Size:</td>
<td>{{ form.memorySize }}</td>
<td>{{ form.memorySize }}MB</td>
<td>Timeout:</td>
<td>{{ form.timeout }}</td>
<td>{{ form.timeout }}s</td>
</tr>
<tr>
<td>Code Size:</td>
@ -107,7 +108,7 @@
<td>Code Checksum:</td>
<td :colspan="3">{{ form.codeChecksum }}</td>
</tr>
<tr>
<!-- <tr>
<td>Description</td>
<td :colspan="5">{{ form.description }}</td>
</tr>
@ -115,13 +116,8 @@
<td>EnviromentVariable:</td>
<td :colspan="2">{{ form.envVar }}</td>
<td :colspan="3">
<!-- Enable Native Serverless:
<el-switch
v-model="form.enableNS"
size="small">
</el-switch> -->
</td>
</tr>
</tr> -->
</table>
<el-row style="margin-bottom:30px" :gutter="30">
<el-col :span="9">
@ -263,10 +259,16 @@ export default {
// this.form = val;
// this.activeName = '2';
this.form = val
this.result = ''
this.dialogExecuteVisible = true
},
createFunc() {
//
this.$Api.checkFunctionName(this.form.functionName).then(res => {
if (res.data.exist) {
this.$message.warning('名称已存在')
} else {
const funcForm = { code: { zipFile: this.form.runEnv === 'java8' ? this.fileList[0].code : this.transBase64(this.code) }, ...this.form }
this.$Api.createFunction(funcForm).then((res) => {
if (res) {
@ -275,6 +277,8 @@ export default {
this.getList()
}
})
}
})
},
transBase64(code) {
return Buffer.from(code).toString('base64')

View File

@ -55,13 +55,11 @@
</template>
<script>
const UNITS = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
const FRACTIONAL = ['', 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y'] // milli micro nano pico femto
import List from '@/components/list'
import * as echarts from 'echarts'
import { getDashboardCounts, getDashboardEvents, getDashboardInfo, getUsageStatus, getStorageStatus } from '@/api/one-class-page/virtualMachine'
import moment from 'moment'
import { parseSi } from '@/utils'
export default {
components: { List },
data() {
@ -132,8 +130,8 @@ export default {
let memoryCounts = 0
let cpuCounts = 0
containers.forEach(e => {
memoryCounts += this.parseSi(e?.resources?.requests?.memory || '0m', { increment: 1024 })
cpuCounts += this.parseSi(e?.resources?.requests?.cpu || '0m')
memoryCounts += parseSi(e?.resources?.requests?.memory || '0m', { increment: 1024 })
cpuCounts += parseSi(e?.resources?.requests?.cpu || '0m')
})
this.memoryUsage += memoryCounts
this.cpuUsage += cpuCounts
@ -156,57 +154,6 @@ export default {
})
},
methods: {
parseSi(inValue, opt) {
opt = opt || {}
let increment = opt.increment
const allowFractional = opt.allowFractional !== false
if (!inValue || typeof inValue !== 'string' || !inValue.length) {
return NaN
}
inValue = inValue.replace(/,/g, '')
// eslint-disable-next-line prefer-const
let [, valStr, unit, incStr] = inValue.match(/^([0-9.-]+)\s*([^0-9.-]?)([^0-9.-]?)/)
const val = parseFloat(valStr)
if (!unit) {
return val
}
// micro "mu" symbol -> u
if (unit.charCodeAt(0) === 181) {
unit = 'u'
}
const divide = FRACTIONAL.includes(unit)
const multiply = UNITS.includes(unit.toUpperCase())
if (!increment) {
// Automatically handle 1 KB = 1000B, 1 KiB = 1024B if no increment set
if ((multiply || divide) && incStr === 'i') {
increment = 1024
} else {
increment = 1000
}
}
if (divide && allowFractional) {
const exp = FRACTIONAL.indexOf(unit)
return val / (increment ** exp)
}
if (multiply) {
const exp = UNITS.indexOf(unit.toUpperCase())
return val * (increment ** exp)
}
// Unrecognized unit character
return val
},
formatDate(t) {
return moment(t).local().format('YYYY-MM-DD hh:mm:ss')
},
@ -214,7 +161,7 @@ export default {
const cpuGauge = echarts.init(this.$refs.cpuGauge)
cpuGauge.setOption(this.returnGaugeOption((this.cpuUsage / this.cpuTotal).toFixed(4) * 100))
const memoryGauge = echarts.init(this.$refs.memoryGauge)
memoryGauge.setOption(this.returnGaugeOption((parseInt(this.memoryUsage / 1024 / 1024 / 1024) / parseInt(this.memoryTotal / 1024 / 1024)).toFixed(4) * 100))
memoryGauge.setOption(this.returnGaugeOption(((parseInt(this.memoryUsage / 1024 / 1024 / 1024) / parseInt(this.memoryTotal / 1024 / 1024)) * 100).toFixed(2)))
const storageGauge = echarts.init(this.$refs.storageGauge)
storageGauge.setOption(this.returnGaugeOption(((this.storageUsage / 1024 / 1024 / 1024 / 1024).toFixed(2) / (this.storageTotal / 1024 / 1024 / 1024 / 1024).toFixed(2)).toFixed(4) * 100))
},

View File

@ -1,7 +1,7 @@
<template>
<div>
<div class="formcontainer">
<el-form ref="formData" :rules="rules" :model="formData" label-position="top">
<el-form ref="formData" :model="formData" label-position="top">
<el-row :gutter="10">
<el-col :span="12">
<el-form-item label="名称"><el-input
@ -78,37 +78,11 @@
</template>
<script>
import { getImages, getDataVolumeNew, editDataVolume } from '@/api/one-class-page/virtualMachine'
import { createDataVolume } from '@/api/one-class-page/virtualMachine'
import { getImages, getDataVolumeNew, editDataVolume, createDataVolume } from '@/api/one-class-page/virtualMachine'
export default {
data() {
return {
volumeData: {
type: 'cdi.kubevirt.io.datavolume',
metadata: {
namespace: 'default',
annotations: {
'field.cattle.io/description': ''
// 'harvesterhci.io/imageId': null
},
labels: {},
name: ''
},
spec: {
pvc: {
resources: {
requests: {
storage: ''
}
},
volumeMode: 'Block',
accessModes: ['ReadWriteMany']
},
source: {
blank: {}
}
}
},
volumeData: {},
Data: {},
tagNum: 0,
disableConfig: true,
@ -198,15 +172,31 @@ export default {
this.$router.push({ path: '/virtual/dataVolume' })
},
create() {
this.volumeData.metadata.annotations['field.cattle.io/description'] = this.formData.description
if (this.formData.resourceType === 'VM Image') this.volumeData.metadata.annotations['harvesterhci.io/imageId'] = this.formData.imageId
for (let item = 0; item < this.formData.tag.length; item++) {
this.volumeData.metadata.labels[this.formData.tag[item].key] = this.formData.tag[item].value
this.volumeData = {
apiVersion: 'v1',
kind: 'PersistentVolumeClaim',
metadata: {
annotations: {
'field.cattle.io/description': this.formData.description || '',
'harvesterhci.io/imageId': this.formData.resourceType === 'VM Image' ? this.formData.imageId : undefined
},
name: this.formData.name || '',
namespace: 'default'
},
spec: {
accessModes: ['ReadWriteMany'],
resources: {
requests: {
storage: this.formData.size + 'Gi'
}
},
storageClassName: this.formData.resourceType === 'VM Image' ? `longhorn-${this.formData.imageId.replace('default/', '')}` : 'longhorn',
volumeMode: 'Block',
volumeName: ''
},
type: 'persistentvolumeclaim'
}
this.volumeData.metadata.name = this.formData.name
this.volumeData.spec.pvc.resources.requests.storage = this.formData.size + 'Gi'
createDataVolume(this.volumeData).then(res => {
console.log(res)
this.$message.success('创建成功')
this.goBack()
})

View File

@ -62,7 +62,7 @@
<script>
import {
getHostYaml,
getHostNetwork,
// getHostNetwork,
putHostYaml,
putHostNetwork,
getNodeNetworks
@ -139,11 +139,11 @@ export default {
this.formData.customName = res.metadata.annotations['harvesterhci.io/host-custom-name']
this.yamlData = res
})
getHostNetwork(this.$route.query.name).then(res => {
this.formData.type = res.spec.type
this.formData.physicalNIC = res.spec.nic
this.vlanData = res
})
// getHostNetwork(this.$route.query.name).then(res => {
// this.formData.type = res.spec.type
// this.formData.physicalNIC = res.spec.nic
// this.vlanData = res
// })
getNodeNetworks().then(res => {
this.physicalNICList = res.data[this.$route.query.index].status.physicalNICs
})

View File

@ -194,15 +194,15 @@ export default {
this.formData.lastUpdateTime = res.data[this.formData.index].status.conditions[0].lastUpdateTime
this.formData.uuid = res.data[this.formData.index].status.nodeInfo.systemUUID
this.formData.cpuUsed = parseFloat((parseInt(usage.usage.cpu) / 1000000000).toFixed(2))
this.formData.cpuAll = parseInt(res.data[this.formData.index].status.capacity.cpu)
this.formData.cpuUsePercentage = parseFloat((this.formData.cpuUsed / this.formData.cpuAll).toFixed(2)) * 100
this.formData.memoryUsed = parseFloat((parseInt(usage.usage.memory) / 1024 / 1024).toFixed(2))
this.formData.memoryAll = parseFloat((parseFloat(res.data[this.formData.index].status.capacity.memory) / 1024 / 1024).toFixed(2))
this.formData.memoryUsePercentage = parseFloat((this.formData.memoryUsed / this.formData.memoryAll).toFixed(2)) * 100
this.formData.storageUsed = parseFloat((parseFloat(res.data[this.formData.index].status.capacity['ephemeral-storage']) / 1024 / 1024 / 1024 - parseFloat(res.data[this.formData.index].status.allocatable['ephemeral-storage']) / 1024 / 1024 / 1024 / 1024).toFixed(2))
this.formData.storageAll = parseFloat((parseFloat(res.data[this.formData.index].status.capacity['ephemeral-storage']) / 1024 / 1024 / 1024).toFixed(2))
this.formData.storageUsePercentage = parseFloat((this.formData.storageUsed / this.formData.storageAll).toFixed(2)) * 100
this.formData.cpuUsed = this.$route.query.cpuUsed
this.formData.cpuAll = this.$route.query.cpuAll
this.formData.cpuUsePercentage = this.$route.query.cpuUsePercentage
this.formData.memoryUsed = this.$route.query.memoryUsed
this.formData.memoryAll = this.$route.query.memoryAll
this.formData.memoryUsePercentage = this.$route.query.memoryUsePercentage
this.formData.storageUsed = this.$route.query.storageUsed
this.formData.storageAll = this.$route.query.storageAll
this.formData.storageUsePercentage = this.$route.query.storageUsePercentage
})
})
},

View File

@ -22,7 +22,7 @@
:columns="columns"
func-name="getImagesList"
:cluster-name="clusterName"
:pagination="true"
:pagination="false"
tooltip-effect="dark"
/>
</el-card>
@ -78,7 +78,6 @@ export default {
<el-dropdown-menu slot='dropdown'>
<span onClick={() => this.clickOption(row, 1)}> <el-dropdown-item><i class='el-icon-edit'></i> 编辑配置 </el-dropdown-item> </span>
<span onClick={() => this.clickOption(row, 2)}> <el-dropdown-item><i class='el-icon-tickets'></i> 克隆 </el-dropdown-item> </span>
<span onClick={() => this.clickOption(row, 3)}> <el-dropdown-item><i class='el-icon-document'></i> 创建虚拟机 </el-dropdown-item> </span>
<span onClick={() => this.clickOption(row, 4)}> <el-dropdown-item><i class='el-icon-download'></i> 下载YAML </el-dropdown-item> </span>
<span onClick={() => this.clickOption(row, 5)}> <el-dropdown-item divided><i class='el-icon-delete'></i> 删除 </el-dropdown-item> </span>
</el-dropdown-menu>
@ -132,7 +131,7 @@ export default {
this.$router.push({ path: `virtual-machine/create`, query: row })
break
case 4:
this.downloadHostYaml(row.name)
this.downloadHostYaml(row)
break
case 5:
this.deleteDialogVisible = true
@ -151,8 +150,8 @@ export default {
viewVMDetail(row) {
this.$router.push({ path: `virtual-machine/detail`, query: row })
},
downloadHostYaml(name) {
getImagesYaml(name).then(res => {
downloadHostYaml(row) {
getImagesYaml(row.name).then(res => {
var data = JSON.stringify(res)
// encodeURIComponent
const uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(data)
@ -160,7 +159,7 @@ export default {
const link = document.createElement('a')
link.href = uri
//
link.download = name + '.json'
link.download = row.displayName + '.json'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)

View File

@ -1,7 +1,7 @@
<template>
<div>
<div class="formcontainer">
<el-form ref="formData" :rules="rules" :model="formData" label-position="top">
<el-form ref="formData" :model="formData" label-position="top">
<el-row :gutter="10">
<el-col :span="12">
<el-form-item label="名称"><el-input
@ -22,26 +22,24 @@
<el-tab-pane label="基本信息">
<h4>基本信息</h4>
<el-form-item label="镜像地址">
<el-select v-model="formData.resourceType" :disabled="!clone">
<el-option
v-for="item in options1"
:key="item.value"
:label="item.label"
:value="item.value"
<el-input
v-model="formData.url"
:hide-required-asterisk="true"
:disabled="!clone"
placeholder="镜像地址"
/>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="标签">
<h4>标签</h4>
<div v-for="(item, index) in formData.tag" :key="item.id">
<div v-for="(item, i) in formData.tag" :key="item.id">
<el-form-item>
<el-row v-if="index=='0'" :gutter="20"><el-col :span="6"><h5>键</h5></el-col><el-col :span="6"><h5></h5></el-col></el-row>
<el-row v-if="i=='0'" :gutter="20"><el-col :span="6"><h5>键</h5></el-col><el-col :span="6"><h5></h5></el-col></el-row>
<el-row :gutter="20">
<el-col :span="6"><el-input v-model="item.key" placeholder="例如test" /></el-col>
<el-col :span="6"> <el-input v-model="item.value" placeholder="例如test" /></el-col>
<el-col :span="5"><el-button type="danger" icon="el-icon-delete" @click="removeTag(index)" /></el-col>
<el-col v-if="!isDisabled" :span="5"><el-button type="danger" icon="el-icon-delete" @click="removeTag(i)" /></el-col>
</el-row>
</el-form-item>
</div>
@ -54,25 +52,28 @@
<el-row type="flex" justify="end">
<el-col :span="2.5">
<el-button v-if="!isDisabled" type="info" @click="goBack">取消</el-button>
<el-button v-if="!isDisabled" type="primary">创建</el-button>
<el-button v-if="clone" type="primary" @click="create">创建</el-button>
<el-button v-if="!clone && !isDisabled" type="primary" @click="edit">保存</el-button>
</el-col>
</el-row>
</div>
</template>
<script>
import { getImages, editImage, createImages } from '@/api/one-class-page/virtualMachine'
export default {
data() {
return {
tagNum: 0,
index: '',
disableConfig: true,
clone: false,
currentData: '',
formData: {
name: '',
description: '',
size: '',
resourceType: 'https://github.com/rancher/k3os/releases/download/v0.11.1/k3os-amd64.iso',
image: '',
tag: []
}
@ -83,6 +84,9 @@ export default {
return this.disableConfig && !this.clone
}
},
created() {
this.index = this.$route.query.index
},
mounted() {
this.getFormData()
if (this.$route.query.disableConfig != null && typeof (this.$route.query.disableConfig) !== 'undefined') {
@ -94,10 +98,22 @@ export default {
},
methods: {
getFormData() {
this.formData.name = this.$route.query.name
this.formData.size = this.$route.query.size.slice(0, -2)
// this.formData.description = this.$route.query.description,
// this.formData.resourceType = this.$route.query.resourceType
getImages().then(res => {
var data = res.data[this.index]
this.currentData = data
this.formData = {
name: this.$route.query.displayName,
description: data.metadata.annotations['field.cattle.io/description'],
size: Number((data.status.size / 1024 / 1024).toString().match(/^\d+(?:\.\d{0,2})?/)) + 'MB',
url: data.spec.url,
image: data.spec.url
}
const arry = []
for (const i in data.metadata.labels) {
arry.push({ 'key': i, 'value': data.metadata.labels[i] })
}
this.formData.tag = arry
})
},
addTag() {
this.formData.tag.push(
@ -108,11 +124,53 @@ export default {
}
)
},
create() {
this.imagesData = {
'apiVersion': this.currentData.apiVersion,
'kind': this.currentData.kind,
'metadata': {
annotations: {
'field.cattle.io/description': this.formData.description
},
finalizers: this.currentData.metadata.finalizers,
generateName: 'image-',
labels: {},
name: '',
namespace: 'default'
},
'spec': {
'displayName': this.formData.name,
'url': this.formData.url,
checksum: '',
pvcName: '',
pvcNamespace: '',
sourceType: 'download'
},
'type': this.currentData.type
}
for (let item = 0; item < this.formData.tag.length; item++) {
this.imagesData.metadata.labels[this.formData.tag[item].key] = this.formData.tag[item].value
}
createImages(this.imagesData).then(res => {
this.$message.success('创建成功')
this.goBack()
})
},
edit() {
this.currentData.metadata.annotations['field.cattle.io/description'] = this.formData.description
for (let item = 0; item < this.formData.tag.length; item++) {
this.currentData.metadata.labels[this.formData.tag[item].key] = this.formData.tag[item].value
}
editImage(this.$route.query.name, this.currentData).then(res => {
this.$message.success('修改成功')
this.goBack()
})
},
removeTag(index) {
this.formData.tag.splice(index, 1)
},
goBack() {
this.$router.push({ path: '/virtual/images.vue' })
this.$router.push({ path: '/virtual/images' })
}
}
}

View File

@ -15,6 +15,7 @@
<script>
import { FormData } from '@/components/FormData'
import { getImages } from '@/api/one-class-page/virtualMachine'
export default {
components: { FormData },
data() {
@ -23,7 +24,7 @@ export default {
formData1: {
description: ' ',
size: this.$route.query.size,
resourceType: 'https://github.com/rancher/k3os/releases/download/v0.11.1/k3os-amd64.iso'
resourceType: ''
},
dataMap1: {
description: '描述',
@ -64,7 +65,16 @@ export default {
this.getFormData()
},
methods: {
getFormData() {}
getFormData() {
getImages().then(res => {
var data = res.data[this.index]
this.formData1 = {
description: data.metadata.annotations['field.cattle.io/description'],
size: Number((data.status.size / 1024 / 1024).toString().match(/^\d+(?:\.\d{0,2})?/)) + 'MB',
resourceType: data.spec.url
}
})
}
}
}
</script>

View File

@ -33,14 +33,14 @@
width="30%"
>
<p>您试图删除 虚拟机 {{ deleteName }} .</p>
<p>Select the volume you want to delete:</p>
<p>选择您需要删除的卷:</p>
<el-checkbox-group v-model="selectDeleteVolumeList">
<el-checkbox
v-for="data in deleteVolumeList"
:key="data.name"
:label="data.name"
:key="data"
:label="data"
>
{{ data.name }}
{{ data }}
</el-checkbox>
</el-checkbox-group>
@ -103,6 +103,8 @@ export default {
formatter: row => {
if (row.state === 'Running') {
return <el-tag disable-transitions type='success'>{row.state}</el-tag>
} else if (row.state === 'Stopped') {
return <el-tag disable-transitions type='info'>Off</el-tag>
} else {
return <span>
<el-tag disable-transitions>{row.state}</el-tag>
@ -169,10 +171,9 @@ export default {
// },
deleteVirtualMachine() {
this.deleteDialogVisible = false
console.log(this.selectDeleteVolumeList)
deleteVirtualMachine(this.deleteName, this.selectDeleteVolumeList).then(res => {
alert('删除成功')
location.reload()
this.$message.success('删除成功')
this.$refs.multipleTable.getList()
})
},
createVirtualMachine() {
@ -219,9 +220,9 @@ export default {
getVirtualMachines().then(res => {
var data = res.data[row.index]
this.deleteVolumeList = []
for (let i = 0; i < data.spec.template.spec.volumes.length; i++) {
if (data.spec.template.spec.volumes[i].dataVolume != null) {
this.deleteVolumeList.push(data.spec.template.spec.volumes[i])
for (let i = 0; i < data.status?.volumeSnapshotStatuses?.length; i++) {
if (data.status.volumeSnapshotStatuses[i].name !== 'cloudinitdisk') {
this.deleteVolumeList.push(data.status.volumeSnapshotStatuses[i].name)
}
}
this.deleteName = row.name

View File

@ -137,7 +137,7 @@
</template>
<script>
import { getImages, postSSHKey, getDataVolume, getSSHKey } from '@/api/one-class-page/virtualMachine'
import { getImages, postSSHKey, getDataVolumeNew, getSSHKey } from '@/api/one-class-page/virtualMachine'
import ConfigVolume from './virtualMachineConfig/configVolume'
import ConfigImage from './virtualMachineConfig/configImage'
import ConfigExistedVolume from './virtualMachineConfig/configExistedVolume'
@ -347,7 +347,7 @@ export default {
this.getSSHKeys()
getImages().then(res => {
this.imageList = res.data
getDataVolume().then(res => {
getDataVolumeNew().then(res => {
this.volumeList = res.data
this.show = true
})
@ -363,15 +363,14 @@ export default {
getImages().then(res => {
images = res.data
this.imageList = res.data
getDataVolume().then(res => {
getDataVolumeNew().then(res => {
dataVolumeData = res.data
this.volumeList = dataVolumeData
// getVirtualMachines().then(res => {
var data = this.instanceData // res.data[this.$route.query.index]
var dataImageName = null
for (let item = 0; item < data.spec.template.spec.volumes.length; item++) {
var dataVolumeTemplate = data.spec.template.spec.volumes[item]
var volumeName = dataVolumeTemplate.name
data.spec.template.spec.volumes.forEach(item => {
var volumeName = item.name
dataImageName = null
var bus = null
var bootOrder = null
@ -380,16 +379,14 @@ export default {
var componentType = null
var dataVolumeName = null
var imageId = null
if (dataVolumeTemplate.dataVolume != null) {
dataVolumeName = dataVolumeTemplate.dataVolume.name
} else if (dataVolumeTemplate.containerDisk != null) {
dataVolumeName = dataVolumeTemplate.containerDisk.image
if (item.dataVolume != null) {
dataVolumeName = item.dataVolume.name
} else if (item.containerDisk != null) {
dataVolumeName = item.containerDisk.image
componentType = 'Container'
} else if (dataVolumeTemplate.cloudInitNoCloud != null) {
this.formData.userData = dataVolumeTemplate.cloudInitNoCloud.userData
continue
} else {
continue
} else if (item.cloudInitNoCloud != null) {
this.formData.userData = item.cloudInitNoCloud.userData
return
}
for (let item1 = 0; item1 < dataVolumeData.length; item1++) {
if (dataVolumeName === dataVolumeData[item1].metadata.fields[0]) {
@ -412,7 +409,7 @@ export default {
break
}
}
for (let item1 = 0; item1 < data.spec.dataVolumeTemplates.length; item1++) {
for (let item1 = 0; item1 < data.spec?.dataVolumeTemplates?.length; item1++) {
if (data.spec.dataVolumeTemplates[item1].metadata.name !== dataVolumeName) {
continue
}
@ -436,7 +433,7 @@ export default {
// Name,type,Image,Size,Bus,BootOrder,docker,isCreate, disableConfig, volumeList, imageId
this.childLists.push([volumeName, type, dataImageName, size, bus, bootOrder, dataVolumeName, null, true, this.volumeList, imageId])
this.allComponents.push(componentType)
}
})
this.formData.name = data.metadata.name
this.formData.description = data.metadata.annotations['field.cattle.io/description']
this.formData.customName = data.metadata.annotations['harvesterhci.io/host-custom-name']

View File

@ -19,7 +19,7 @@
<h4>网络</h4>
<VirtualMachineDetailCard v-for="(item, ind) in allNetworkCard" :key="ind" card-name="网络" :data-map="dataMap5" :form-data="item" />
</el-tab-pane>
<el-tab-pane key="4" label="SSH 钥">
<el-tab-pane key="4" label="SSH 钥">
<h4>SSH Keys</h4>
<div v-for="(item,index) in sshkeyLists" :key="index">
{{ item.name }}
@ -72,7 +72,7 @@
<script>
import { FormData } from '@/components/FormData'
import List from '@/components/list'
import { getVirtualMachines, getImages, getDataVolume, getSSHKey } from '@/api/one-class-page/virtualMachine'
import { getVirtualMachines, getImages, getDataVolumeNew, getSSHKey, getVirtualMachinesInstances } from '@/api/one-class-page/virtualMachine'
import VirtualMachineDetailCard from './virtualMachineConfig/virtualMachineDetailCard'
export default {
name: 'VirtualMachineDetail',
@ -125,8 +125,8 @@ export default {
dataMap: {
name: '名称',
image: '镜像',
hostname: '主机名',
node: '节点',
node: '节点名称',
hostname: '主机',
IPAddress: 'IP地址',
created: '创建时间'
},
@ -175,7 +175,7 @@ export default {
dataMap8: {
Name: '名称',
Type: '类型',
Volume: '卷名称',
// Volume: '',
Size: '大小',
Bus: '总线',
BootOrder: '驱动顺序'
@ -207,15 +207,17 @@ export default {
// TODO Basics Image, Node IP Address
var images = null
var dataVolumeData = null
getImages().then(res => {
images = res.data
getDataVolume().then(res => {
dataVolumeData = res.data
getVirtualMachines().then(res => {
var data = res.data[this.$route.query.index]
for (let item = 0; item < data.spec.template.spec.volumes.length; item++) {
var dataVolumeTemplate = data.spec.template.spec.volumes[item]
var volumeName = dataVolumeTemplate.name
Promise.all([
getImages(),
getDataVolumeNew(),
getVirtualMachines(),
getVirtualMachinesInstances()]).then(res => {
images = res[0].data
dataVolumeData = res[1].data
var data = res[2].data[this.index]
var currentVm = res[3].data.filter(n => n.id === data.id)?.[0]
data.spec?.template?.spec?.volumes?.forEach(item => {
var volumeName = item.name
var dataImageName = null
var bus = null
var bootOrder = null
@ -223,16 +225,14 @@ export default {
var size = null
var componentType = null
var dataVolumeName = null
if (dataVolumeTemplate.dataVolume != null) {
dataVolumeName = dataVolumeTemplate.dataVolume.name
} else if (dataVolumeTemplate.containerDisk != null) {
dataVolumeName = dataVolumeTemplate.containerDisk.image
if (item.dataVolume != null) {
dataVolumeName = item.dataVolume.name
} else if (item.containerDisk != null) {
dataVolumeName = item.containerDisk.image
componentType = '容器镜像'
} else if (dataVolumeTemplate.cloudInitNoCloud != null) {
this.formData.userData = dataVolumeTemplate.cloudInitNoCloud.userData
continue
} else {
continue
} else if (item.cloudInitNoCloud != null) {
this.formData.userData = item.cloudInitNoCloud.userData
return
}
for (let item1 = 0; item1 < dataVolumeData.length; item1++) {
if (dataVolumeName === dataVolumeData[item1].metadata.fields[0]) {
@ -255,7 +255,7 @@ export default {
break
}
}
for (let item1 = 0; item1 < data.spec.dataVolumeTemplates.length; item1++) {
for (let item1 = 0; item1 < data.spec?.dataVolumeTemplates?.length; item1++) {
if (data.spec.dataVolumeTemplates[item1].metadata.name !== dataVolumeName) {
continue
}
@ -312,7 +312,7 @@ export default {
}
}
this.allDetailCard.push(itemObjct)
}
})
this.allNetworkCard = this.getNetworkRows(data.spec)
this.formData.name = data.metadata.name
@ -322,8 +322,12 @@ export default {
this.formData.cpu = data.spec.template.spec.domain.cpu.cores
this.formData.memory = data.spec.template.spec.domain.resources.requests.memory
this.formData.memory = this.formData.memory.replace('Gi', '')
this.formData.hostname = data.spec.template.spec.hostname
data = res.data[this.index]
this.formData.hostname = currentVm?.metadata.fields[4]
this.formData.IPAddress = currentVm?.metadata.fields[3]
this.formData.node = currentVm?.metadata.fields[0]
this.formData.KernelRelease = currentVm?.status?.guestOSInfo?.kernelRelease
this.formData.OperatingSystem = currentVm?.status?.guestOSInfo?.prettyName
// data = res.data[this.index]
this.formData.name = data.metadata.fields[0]
this.formData.created = data.metadata.creationTimestamp
this.formData.MachineType = data.spec.template.spec.domain.machine.type
@ -349,27 +353,25 @@ export default {
this.formData.bootOrder += i + '. ' + disksarr[i] + ' \n'
}
}
var cpucore = data.spec.template.spec.domain.cpu.cores
var memory = data.spec.template.spec.domain.resources.requests.memory
this.formData.Flavor += cpucore + ' vCPU, ' + memory + ' Memory'
var cpucore = data.spec.template.spec.domain.resources.limits.cpu
var memory = data.spec.template.spec.domain.resources.limits.memory
this.formData.Flavor += cpucore + ' vCPU, ' + memory + ' 内存'
this.formData2.Name = data.spec.template.spec.domain.devices.interfaces[0].name
this.formData2.Model = data.spec.template.spec.domain.devices.interfaces[0].model
var str = data.spec.template.metadata.annotations['harvesterhci.io/sshNames']
var arr = JSON.parse(str)
getSSHKey().then(res => {
getSSHKey().then(n => {
this.sshkeyLists = []
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < res.data.length; j++) {
console.log(res.data[j])
if (res.data[j].metadata.name === arr[i]) {
this.sshkeyLists.push({ name: arr[i], sshKey: res.data[j].spec.publicKey })
for (let j = 0; j < n.data.length; j++) {
console.log(n.data[j])
if (n.data[j].id === arr[i]) {
this.sshkeyLists.push({ name: arr[i], sshKey: n.data[j].spec.publicKey })
}
}
}
})
})
})
})
},
methods: {
getNetworkRows(n) {

View File

@ -108,7 +108,7 @@ export default {
</span>
} },
{ prop: 'project', label: '项目' },
{ prop: 'updateTime', label: '更新时间', formatter: (row) => new Date(row.updateTime).toLocaleString() },
{ prop: 'updateTime', label: '更新时间', formatter: (row) => new Date(row.data.status.conditions[1].lastUpdateTime).toLocaleString() || '-' },
{ prop: 'more', label: '操作', formatter: (row) => {
return <div>
<el-dropdown>

View File

@ -99,9 +99,14 @@ module.exports = {
changeOrigin: true,
secure: false
},
'^/function': {
'^/jcc-faas-manager': {
ws: false,
target: 'https://jointcloud.net/api/'
target: 'https://jointcloud.net/apis/v1'
// changeOrigin: true,
},
'^/jcc-mall': {
ws: false,
target: 'https://jointcloud.net/apis/v1'
// changeOrigin: true,
},
'/kapis/terminal.kubesphere.io': {