平台增加适配 品高云和海云和华为云

x-20250106
chengmingrui 2025-06-03 17:17:02 +08:00
parent 94b3ccffeb
commit 0aca3945e8
10 changed files with 275 additions and 329 deletions

View File

@ -1,29 +1,24 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ['plugin:vue/essential', '@vue/standard', '@vue/typescript'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unused-vars': [
0,
{
vars: 'all',
args: 'none'
}
],
semi: 0,
eqeqeq: 0,
'one-var': 0,
camelcase: 0,
'no-case-declarations': 0,
'space-before-function-paren': 0,
'vue/no-parsing-error': [2, { "x-invalid-end-tag": false }],
'@typescript-eslint/indent': ['error', 2]
},
parserOptions: {
parser: '@typescript-eslint/parser'
}
};
module.exports = {
root: true,
env: {
node: true,
},
extends: ['plugin:vue/essential', '@vue/standard', '@vue/typescript'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unused-vars': [0, { vars: 'all', args: 'none' }],
semi: 0,
eqeqeq: 0,
'one-var': 0,
camelcase: 0,
'no-case-declarations': 0,
'space-before-function-paren': 0,
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
'@typescript-eslint/indent': ['error', 2],
'comma-dangle': ['error', 2],
},
parserOptions: {
parser: '@typescript-eslint/parser',
},
}

View File

@ -1,10 +1,9 @@
@media screen and(-ms-high-contrast:active),
(-ms-high-contrast:none) {
.el-table__header,
.el-table__body {
width: 100% !important;
}
/* .basic-form-item{
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.el-table__header,
.el-table__body {
width: 100% !important;
}
/* .basic-form-item{
height: 32.76px;
} */
}
}

View File

@ -1,3 +1,5 @@
import Vue from 'vue'
// 正反编译
export function booleanFilter(value) {
const obj = {
@ -6,7 +8,7 @@ export function booleanFilter(value) {
1: '是',
0: '否',
YES: '是',
NO: '否'
NO: '否',
}
return obj[value] || value
}
@ -29,7 +31,10 @@ export function vmStatusFilter(status) {
UNKNOWN: '已断开',
UNKNOWNON: '已断开',
UNKNOWNOFF: '已断开',
UNKNOWNSUSPEND: '已断开'
UNKNOWNSUSPEND: '已断开',
RECYCLING: '回收中',
MIGRATING: '迁移中',
FAULT_RESUMING: '故障恢复中',
}
return statusMap[status] || '未知'
}
@ -95,7 +100,10 @@ export function vmStatusColorFilter(status) {
DISABLED: 'danger',
DEALLOCATEING: 'warning',
PROTECTING: 'warning',
DOWN: 'info'
DOWN: 'info',
RECYCLING: 'warning',
MIGRATING: 'warning',
FAULT_RESUMING: 'warning',
}
return statusMap[status] || 'danger'
}
@ -104,12 +112,12 @@ export function taskStatusFilter(value, type) {
const statusMap = {
DEVELOPING: '开发中',
APPROVING: '发布中',
APPROVED: ' 已发布'
APPROVED: ' 已发布',
}
const colorMap = {
DEVELOPING: 'normal',
APPROVING: 'warning',
APPROVED: 'success'
APPROVED: 'success',
}
return type === 'color' ? colorMap[value] : statusMap[value]
}
@ -123,7 +131,7 @@ export function taskTypeFilter(value) {
RECOVERY: '文件恢复',
HTTP: 'HTTP请求',
DATABASE: '数据库',
POINT: '聚合节点'
POINT: '聚合节点',
}
return obj[value]
}
@ -140,20 +148,20 @@ export function applyStatusFilter(value, type) {
// },
APPROVED: {
name: '已发布',
color: 'success'
color: 'success',
},
DEVELOPING: {
name: '开发中',
color: 'normal'
color: 'normal',
},
UNAPPLY: {
name: '未发布',
color: 'primary'
color: 'primary',
},
CREATED: {
name: '新创建',
color: 'normal'
}
color: 'normal',
},
}
return obj[value] && obj[value][type]
}
@ -162,48 +170,62 @@ export function taskExeStatusFilter(value, type = 'name') {
const obj = {
CREATED: {
name: '未执行',
color: 'normal'
color: 'normal',
},
NOREADY: {
name: '已跳过',
color: 'warning'
color: 'warning',
},
READY: {
name: '准备中',
color: 'normal'
color: 'normal',
},
WAITTING: {
name: '等待执行',
color: 'primary'
color: 'primary',
},
CANCELING: {
name: '取消中',
color: 'primary'
color: 'primary',
},
RUNNING: {
name: '正在执行',
color: 'normal'
color: 'normal',
},
SUCCESS: {
name: '执行成功',
color: 'success'
color: 'success',
},
SUSPENDED: {
name: '已暂停',
color: 'warning'
color: 'warning',
},
FAILED: {
name: '执行失败',
color: 'danger'
color: 'danger',
},
CANCELED: {
name: '手动结束',
color: 'warning'
color: 'warning',
},
EXCEPTION: {
name: '执行异常',
color: 'danger'
}
color: 'danger',
},
}
return obj[value] && obj[value][type] // 容错处理(初始化值不存在)
}
const filters = {
booleanFilter,
vmStatusFilter,
vmStatusColorFilter,
taskStatusFilter,
taskTypeFilter,
applyStatusFilter,
taskExeStatusFilter,
}
// 注册全局过滤器
Object.keys(filters).forEach((key) => {
Vue.filter(key, filters[key])
})

View File

@ -21,21 +21,22 @@ import './icons'
import VueCompositionAPI from '@vue/composition-api'
import rules from '@/validate/index'
import actions from './shared/action'
import './filters'
Vue.use(ElementUI, { size: 'small' });
Vue.use(ElementUI, { size: 'small' })
Vue.use(VueCompositionAPI)
Vue.use(CmpElement, { rules })
Vue.use(CmpEcharts)
Vue.config.productionTip = false
let instance: any = null
function render(props:any) {
const { appPath = '', container } = props;
function render(props: any) {
const { appPath = '', container } = props
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app');
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app')
store.commit('SET_APP_PATH', appPath)
}
if (!(window as any).__POWERED_BY_QIANKUN__) {
@ -48,8 +49,8 @@ export async function bootstrap() {
}
export async function mount(props: any) {
console.log('cop app mounted');
const getStyle = window.getComputedStyle;
console.log('cop app mounted')
const getStyle = window.getComputedStyle
// @ts-ignore
// window.getComputedStyle = (element, property) => {
// if (!element || element.nodeType === 9) return {};
@ -57,10 +58,10 @@ export async function mount(props: any) {
// };
render(props)
actions.init(props, (state: any) => {
const { permissions, userData } = state;
userData && store.commit('SET_USERDATA', userData);
const { permissions, userData } = state
userData && store.commit('SET_USERDATA', userData)
if (!store.getters.addRoutes && permissions) {
store.dispatch('permission/GenerateRoutes');
store.dispatch('permission/GenerateRoutes')
}
})
}

View File

@ -83,9 +83,7 @@
</template>
<el-table-column label="状态">
<template slot-scope="scope">
<status-icon :type="vmStatusColorFilter(scope.row.status)">
{{ vmStatusFilter(scope.row.status) }}
</status-icon>
<status-icon :type="scope.row.status | vmStatusColorFilter"> {{ scope.row.status | vmStatusFilter }} </status-icon>
</template>
</el-table-column>
<el-table-column label="平台名称" prop="vendorName"></el-table-column>
@ -110,36 +108,34 @@ export default {
mixins: [show],
props: {
itemData: {
type: Object
type: Object,
},
osCategory: {
type: String,
default: ''
default: '',
},
hasResource: {
type: Boolean,
default: false
}
default: false,
},
},
data() {
return {
vmStatusFilter,
vmStatusColorFilter,
list: null,
total: null,
listQuery: {
name: '',
privateIps: ''
privateIps: '',
},
params: {
page: 1,
rows: 10
rows: 10,
},
dialogServerVisible: false,
vendorOptions: '',
selectList: [],
idList: [],
http: getResource
http: getResource,
}
},
created() {
@ -148,12 +144,12 @@ export default {
computed: {
path() {
return this.$route.path
}
},
},
watch: {
path(val) {
this.http = val.includes('PatchDeploy') ? getHosts : getResource
}
},
},
methods: {
clearCustomParams() {
@ -183,7 +179,7 @@ export default {
this.listQuery = {
name: '',
privateIps: '',
vendorId: ''
vendorId: '',
}
this.params.page = 1
this.selectList = []
@ -196,7 +192,7 @@ export default {
host: data.host,
username: data.user,
password: data.pasd,
osCategory: data.category
osCategory: data.category,
})
})
this.dialogServerVisible = true
@ -206,7 +202,7 @@ export default {
this.$confirm('您确定要清空所选主机吗?', '提示', {
confirmButtonText: '清空',
confirmButtonClass: 'el-button--danger',
type: 'warning'
type: 'warning',
}).then(() => {
this.clearCustomParams()
this.itemData.hostList = []
@ -223,7 +219,7 @@ export default {
},
getVendor() {
getCloudVendor({
simple: true
simple: true,
}).then((data) => {
if (data.success) {
this.vendorOptions = data.data.rows
@ -305,7 +301,7 @@ export default {
category: this.path.includes('PatchDeploy') ? '' : 'Computer',
catalog: this.path.includes('PatchDeploy') ? 'Computer' : '',
osCategory: this.osCategory,
vendorId: this.listQuery.vendorId
vendorId: this.listQuery.vendorId,
})
this.getList()
},
@ -326,7 +322,7 @@ export default {
pasd: data.password || getPassword(data),
port: data.osCategory.toUpperCase() === 'WINDOWS' ? 5986 : 22,
category: data.osCategory,
privateIps: this.path.includes('PatchDeploy') ? '' : data.privateIps
privateIps: this.path.includes('PatchDeploy') ? '' : data.privateIps,
}
if (data.paramsList) {
tmp.paramsList = data.paramsList
@ -339,13 +335,13 @@ export default {
this.itemData.resourceList = this.itemData.hostList.slice()
this.itemData.resourceList.unshift({
id: 0,
rawName: '选择全部'
rawName: '选择全部',
})
}
this.clearCustomParams()
this.dialogServerVisible = false
}
}
},
},
}
</script>
<style scoped>

View File

@ -15,32 +15,24 @@
<el-col :span="24">
<el-form-item label="">
<smart-table :data="itemData.targets" class="target-table">
<el-table-column label="主机名称" prop="name" show-overflow-tooltip>
</el-table-column>
<el-table-column label="主机名称" prop="name" show-overflow-tooltip> </el-table-column>
<el-table-column label="IP">
<template slot-scope="scope">
<el-select v-model="scope.row.host" placeholder="请选择">
<el-option
v-for="(item, index) in scope.row.privateIps"
:key="index"
:label="item"
:value="item">
</el-option>
<el-option v-for="(item, index) in scope.row.privateIps" :key="index" :label="item" :value="item"> </el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="端口">
<template slot-scope="scope">
<el-form-item class="m-b-none" :prop="'targets.'+scope.row.indexKey+'.port'"
:rules="{required: true, message: '请填写端口号'}">
<el-form-item class="m-b-none" :prop="'targets.' + scope.row.indexKey + '.port'" :rules="{ required: true, message: '请填写端口号' }">
<el-input v-model="scope.row.port" type="number"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="用户名">
<template slot-scope="scope">
<el-form-item class="m-b-none" :prop="'targets.'+scope.row.indexKey+'.user'"
:rules="{required: true, message: '请填写用户名'}">
<el-form-item class="m-b-none" :prop="'targets.' + scope.row.indexKey + '.user'" :rules="{ required: true, message: '请填写用户名' }">
<el-input v-model="scope.row.user"></el-input>
</el-form-item>
</template>
@ -61,47 +53,37 @@
</smart-table>
</el-form-item>
</el-col>
<el-dialog width="70%" title="服务器列表" :close-on-click-modal="false" :visible.sync="dialogServerVisible"
append-to-body>
<el-dialog width="70%" title="服务器列表" :close-on-click-modal="false" :visible.sync="dialogServerVisible" append-to-body>
<el-form :inline="true">
<el-form-item>
<el-select clearable v-model="listQuery.vendorId" placeholder="所属平台">
<el-option v-for="item in vendorOptions" :key="item.id" :label="item.name" :value="item.id">
</el-option>
<el-option v-for="item in vendorOptions" :key="item.id" :label="item.name" :value="item.id"> </el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input placeholder="IP地址" v-model="listQuery.ip">
</el-input>
<el-input placeholder="IP地址" v-model="listQuery.ip"> </el-input>
</el-form-item>
<el-form-item>
<el-input placeholder="主机名" v-model="listQuery.name">
</el-input>
<el-input placeholder="主机名" v-model="listQuery.name"> </el-input>
</el-form-item>
<el-form-item>
<el-button type="ghost" icon="el-icon-search" @click="handleSearch"></el-button>
</el-form-item>
</el-form>
<basic-table :data="list" :params="params" :get-list="getList" :total="total" ref="serverTable1"
@select="handleSelectItem" @select-all="handleSelectAll">
<el-table-column type="selection" :selectable="selectAble" width="60">
</el-table-column>
<el-table-column label="主机名" prop="name">
</el-table-column>
<basic-table :data="list" :params="params" :get-list="getList" :total="total" ref="serverTable1" @select="handleSelectItem" @select-all="handleSelectAll">
<el-table-column type="selection" :selectable="selectAble" width="60"> </el-table-column>
<el-table-column label="主机名" prop="name"> </el-table-column>
<el-table-column label="IP">
<template slot-scope="scope">
<div v-for="(item, index) in scope.row.privateIps" :key="index">{{item}}</div>
<div v-for="(item, index) in scope.row.privateIps" :key="index">{{ item }}</div>
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">
<status-icon :type="vmStatusColorFilter(scope.row.status)">
{{vmStatusFilter(scope.row.status)}}
</status-icon>
<status-icon :type="scope.row.status | vmStatusColorFilter"> {{ scope.row.status | vmStatusFilter }} </status-icon>
</template>
</el-table-column>
<el-table-column label="平台名称" prop="vendorName">
</el-table-column>
<el-table-column label="平台名称" prop="vendorName"> </el-table-column>
</basic-table>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="dialogServerVisible = false">取消</el-button>
@ -118,47 +100,42 @@ import { vmStatusFilter, vmStatusColorFilter } from '@/filters/index'
export default {
props: {
itemData: {
type: Object
}
type: Object,
},
},
data () {
data() {
return {
vmStatusFilter,
vmStatusColorFilter,
list: null,
total: null,
listQuery: {
name: '',
privateIps: '',
catalog: ''
catalog: '',
},
params: {
page: 1,
rows: 10
rows: 10,
},
dialogServerVisible: false,
vendorOptions: '',
selectList: [],
idList: []
idList: [],
}
},
created () {
},
created() {},
methods: {
selectAble (row) {
selectAble(row) {
if (row.osCategory && row.osCategory.toLowerCase() == 'linux' && row.status && row.status.toLowerCase() == 'running') {
if (row.catalog.toLowerCase() == 'physical') {
const server = JSON.parse(row.inventory)
if ((server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) ||
(server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if ((server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) || (server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if (server.powerState.toLowerCase() == 'running') {
return true
}
}
} else if (row.catalog.toLowerCase() == 'logical') {
const cloud_server = JSON.parse(row.inventory)
if ((cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) ||
(cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if ((cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) || (cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if (cloud_server.status.toLowerCase() == 'running') {
return true
}
@ -167,41 +144,41 @@ export default {
}
return false
},
selectServer () {
selectServer() {
this.getVendor()
//
this.listQuery = {
name: '',
privateIps: '',
catalog: '',
vendorId: ''
vendorId: '',
}
this.params.page = 1
this.selectList = []
//
this.itemData.targets.forEach(data => {
this.itemData.targets.forEach((data) => {
this.selectList.push({
id: data.resourceId,
name: data.name,
host: data.host,
username: data.user,
password: data.pasd ? crypto.encrypt(data.pasd) : data.pasd,
osCategory: data.category
osCategory: data.category,
})
})
this.dialogServerVisible = true
this.handleSearch()
},
clearServer () {
clearServer() {
this.$confirm('您确定要清空所选主机吗?', '提示', {
confirmButtonText: '清空',
confirmButtonClass: 'el-button--danger',
type: 'warning'
type: 'warning',
}).then(() => {
this.itemData.targets = []
})
},
delServer (id) {
delServer(id) {
for (let i = 0, len = this.itemData.targets.length; i < len; i++) {
if (this.itemData.targets[i].id === id) {
this.itemData.targets.splice(i, 1)
@ -209,16 +186,16 @@ export default {
}
}
},
getVendor () {
getVendor() {
getCloudVendor({
simple: true
}).then(data => {
simple: true,
}).then((data) => {
if (data.success) {
this.vendorOptions = data.data.rows
}
})
},
handleSelectItem (selection, row) {
handleSelectItem(selection, row) {
this.refreshId()
if (this.idList.indexOf(row.id) > -1) {
for (let j = 0; j < this.selectList.length; j++) {
@ -232,16 +209,18 @@ export default {
this.selectList.push(row)
}
},
handleSelectAll (selection) {
handleSelectAll(selection) {
this.refreshId()
if (selection.length) { //
this.list.forEach(item => {
if (selection.length) {
//
this.list.forEach((item) => {
if (this.idList.indexOf(item.id) == -1 && item.privateIps.length && item.status != 'STOPPED') {
this.selectList.push(item)
}
})
} else { //
this.list.forEach(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]
@ -254,13 +233,13 @@ export default {
})
}
},
refreshId () {
refreshId() {
this.idList = []
this.selectList.forEach(item => {
this.selectList.forEach((item) => {
this.idList.push(item.id)
})
},
getList () {
getList() {
const self = this
this.refreshId()
const handleData = function (data) {
@ -271,7 +250,7 @@ export default {
})
})
}
getResource(this.params).then(data => {
getResource(this.params).then((data) => {
if (data.success) {
this.list = data.data.rows
this.total = data.data.total
@ -279,20 +258,20 @@ export default {
}
})
},
handleSearch () {
handleSearch() {
this.params.page = 1
this.params.params = this.$tools.handleSearchParam({
vendorId: this.listQuery.vendorId,
category: 'Computer',
catalog: this.listQuery.catalog,
'name:LK': this.listQuery.name,
'privateIps:LK': this.listQuery.ip
'privateIps:LK': this.listQuery.ip,
})
this.getList()
},
ok () {
ok() {
this.itemData.targets = []
this.selectList.forEach(data => {
this.selectList.forEach((data) => {
this.itemData.targets.push({
resourceId: data.id,
name: data.name,
@ -302,17 +281,16 @@ export default {
pasd: data.password ? crypto.decrypt(data.password) : data.password,
category: data.osCategory,
sudo: true,
privateIps: data.privateIps
privateIps: data.privateIps,
})
})
this.dialogServerVisible = false
}
}
},
},
}
</script>
<style scoped>
.target-table .el-form-item.is-error {
margin-bottom: 15px !important;
}
.target-table .el-form-item.is-error {
margin-bottom: 15px !important;
}
</style>

View File

@ -11,32 +11,24 @@
<el-col :span="24">
<el-form-item label="">
<smart-table :data="itemData.targets" class="target-table">
<el-table-column label="主机名称" prop="name" show-overflow-tooltip>
</el-table-column>
<el-table-column label="主机名称" prop="name" show-overflow-tooltip> </el-table-column>
<el-table-column label="IP">
<template slot-scope="scope">
<el-select v-model="scope.row.host" placeholder="请选择">
<el-option
v-for="(item, index) in scope.row.privateIps"
:key="index"
:label="item"
:value="item">
</el-option>
<el-option v-for="(item, index) in scope.row.privateIps" :key="index" :label="item" :value="item"> </el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="端口">
<template slot-scope="scope">
<el-form-item class="m-b-none" :prop="'targets.'+scope.row.indexKey+'.port'"
:rules="{required: true, message: '请填写端口号'}">
<el-form-item class="m-b-none" :prop="'targets.' + scope.row.indexKey + '.port'" :rules="{ required: true, message: '请填写端口号' }">
<el-input v-model="scope.row.port" type="number"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="用户名">
<template slot-scope="scope">
<el-form-item class="m-b-none" :prop="'targets.'+scope.row.indexKey+'.user'"
:rules="{required: true, message: '请填写用户名'}">
<el-form-item class="m-b-none" :prop="'targets.' + scope.row.indexKey + '.user'" :rules="{ required: true, message: '请填写用户名' }">
<el-input v-model="scope.row.user"></el-input>
</el-form-item>
</template>
@ -57,22 +49,18 @@
</smart-table>
</el-form-item>
</el-col>
<el-dialog width="70%" title="服务器列表" :close-on-click-modal="false" :visible.sync="dialogServerVisible"
append-to-body>
<el-dialog width="70%" title="服务器列表" :close-on-click-modal="false" :visible.sync="dialogServerVisible" append-to-body>
<el-form :inline="true">
<el-form-item>
<el-select clearable v-model="listQuery.vendorId" placeholder="所属平台">
<el-option v-for="item in vendorOptions" :key="item.id" :label="item.name" :value="item.id">
</el-option>
<el-option v-for="item in vendorOptions" :key="item.id" :label="item.name" :value="item.id"> </el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input placeholder="IP地址" v-model="listQuery.ip">
</el-input>
<el-input placeholder="IP地址" v-model="listQuery.ip"> </el-input>
</el-form-item>
<el-form-item>
<el-input placeholder="主机名" v-model="listQuery.name">
</el-input>
<el-input placeholder="主机名" v-model="listQuery.name"> </el-input>
</el-form-item>
<el-form-item>
<el-button type="ghost" icon="el-icon-search" @click="handleSearch"></el-button>
@ -82,24 +70,21 @@
<el-table-column label="主机名称">
<template slot-scope="scope">
<el-radio :label="scope.row.id" v-model="targetId" :disabled="selectAble(scope.row)" @change.native="getCurrentRow(scope.row)">
<span style="font-size: 12px;">{{scope.row.name}}</span>
<span style="font-size: 12px">{{ scope.row.name }}</span>
</el-radio>
</template>
</el-table-column>
<el-table-column label="IP">
<template slot-scope="scope">
<div v-for="(item, index) in scope.row.privateIps" :key="index">{{item}}</div>
<div v-for="(item, index) in scope.row.privateIps" :key="index">{{ item }}</div>
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">
<status-icon :type="vmStatusColorFilter(scope.row.status)">
{{vmStatusFilter(scope.row.status)}}
</status-icon>
<status-icon :type="scope.row.status | vmStatusColorFilter"> {{ scope.row.status | vmStatusFilter }} </status-icon>
</template>
</el-table-column>
<el-table-column label="平台名称" prop="vendorName">
</el-table-column>
<el-table-column label="平台名称" prop="vendorName"> </el-table-column>
</basic-table>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="dialogServerVisible = false">取消</el-button>
@ -116,48 +101,43 @@ import crypto from 'utils/crypto.js'
export default {
props: {
itemData: {
type: Object
}
type: Object,
},
},
data () {
data() {
return {
vmStatusFilter,
vmStatusColorFilter,
list: null,
total: null,
listQuery: {
name: '',
privateIps: ''
privateIps: '',
},
params: {
page: 1,
rows: 10
rows: 10,
},
dialogServerVisible: false,
groupOptions: '',
vendorOptions: '',
selectList: [],
idList: [],
targetId: null
targetId: null,
}
},
created () {
},
created() {},
methods: {
selectAble (row) {
selectAble(row) {
if (row.osCategory && row.osCategory.toLowerCase() == 'linux' && row.status.toLowerCase() == 'running') {
if (row.catalog.toLowerCase() == 'physical') {
const server = JSON.parse(row.inventory)
if ((server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) ||
(server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if ((server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) || (server.config && server.config.osName && server.config.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if (server.powerState.toLowerCase() == 'running') {
return false
}
}
} else if (row.catalog.toLowerCase() == 'logical') {
const cloud_server = JSON.parse(row.inventory)
if ((cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) ||
(cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if ((cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('centos') > -1) || (cloud_server.osName && cloud_server.osName.trim().replace(/\s*/g, '').toLowerCase().indexOf('redhat') > -1)) {
if (cloud_server.status.toLowerCase() == 'running') {
return false
}
@ -166,51 +146,51 @@ export default {
}
return true
},
selectServer () {
selectServer() {
this.getVendor()
//
this.listQuery = {
name: '',
privateIps: '',
vendorId: ''
vendorId: '',
}
this.params.page = 1
this.selectList = []
//
this.itemData.targets.forEach(data => {
this.itemData.targets.forEach((data) => {
this.selectList.push({
id: data.resourceId,
name: data.name,
host: data.host,
username: data.user,
password: data.pasd ? crypto.encrypt(data.pasd) : data.pasd,
osCategory: data.category
osCategory: data.category,
})
})
this.dialogServerVisible = true
this.handleSearch()
},
delServer () {
this.selectList = [];
this.itemData.targets = [];
this.targetId = null;
delServer() {
this.selectList = []
this.itemData.targets = []
this.targetId = null
},
getVendor () {
getVendor() {
getCloudVendor({
simple: true
}).then(data => {
simple: true,
}).then((data) => {
if (data.success) {
this.vendorOptions = data.data.rows
}
})
},
refreshId () {
refreshId() {
this.idList = []
this.selectList.forEach(item => {
this.selectList.forEach((item) => {
this.idList.push(item.id)
})
},
getList () {
getList() {
const self = this
this.refreshId()
const handleData = function (data) {
@ -218,7 +198,7 @@ export default {
item.privateIps = JSON.parse(item.privateIps)
})
}
getResource(this.params).then(data => {
getResource(this.params).then((data) => {
if (data.success) {
this.list = data.data.rows
this.total = data.data.total
@ -226,22 +206,22 @@ export default {
}
})
},
handleSearch () {
handleSearch() {
this.params.page = 1
this.params.params = this.$tools.handleSearchParam({
vendorId: this.listQuery.vendorId,
category: 'Computer',
'name:LK': this.listQuery.name,
'privateIps:LK': this.listQuery.ip
'privateIps:LK': this.listQuery.ip,
})
this.getList()
},
getCurrentRow (data) {
this.selectList = [data];
getCurrentRow(data) {
this.selectList = [data]
},
ok () {
ok() {
this.itemData.targets = []
this.selectList.forEach(data => {
this.selectList.forEach((data) => {
this.itemData.targets.push({
resourceId: data.id,
name: data.name,
@ -251,17 +231,16 @@ export default {
pasd: data.password ? crypto.decrypt(data.password) : data.password,
category: data.osCategory,
sudo: true,
privateIps: data.privateIps
privateIps: data.privateIps,
})
})
this.dialogServerVisible = false
}
}
},
},
}
</script>
<style scoped>
.target-table .el-form-item.is-error {
margin-bottom: 15px !important;
}
.target-table .el-form-item.is-error {
margin-bottom: 15px !important;
}
</style>

View File

@ -2,9 +2,7 @@
<div>
<AdvanceTable :title="`${title}主机列表`" :data="tableData" :searchConfigs="searchConfigs" :params="params" :total="total" :columns="columns" :get-list="getData" :loading="loading" :key="path">
<template v-slot:action>
<el-button type="primary" @click="handleCreate(null, 1)" icon="el-icon-plus">
新增
</el-button>
<el-button type="primary" @click="handleCreate(null, 1)" icon="el-icon-plus"> 新增 </el-button>
<el-button type="primary" icon="el-icon-refresh" @click="handleSynchro(type)"></el-button>
<!-- <el-button type="primary" icon="el-icon-refresh" @click="handleSynchro('RIS')">RIS</el-button> -->
<el-button icon="el-icon-upload2" @click="importData"> </el-button>
@ -127,7 +125,7 @@ export default {
} else {
callback(new Error('请输入正确的IP地址'))
}
}
},
},
columns,
vmSearchConfigs,
@ -138,11 +136,11 @@ export default {
params: {
page: 1,
rows: 20,
sorter: JSON.stringify({ gmtCreate: '1' })
sorter: JSON.stringify({ gmtCreate: '1' }),
},
setData: {
dialog: false,
data: {}
data: {},
},
type: 'VM',
searchConfigs: vmSearchConfigs,
@ -151,7 +149,7 @@ export default {
//
textMap2: {
update: '主机修改',
create: '新增主机'
create: '新增主机',
},
addHostVisible: false,
addHostData: {
@ -164,7 +162,7 @@ export default {
ips: [],
// ip: '',
username: '',
password: ''
password: '',
},
typeName: 'ansible',
statusList: [
@ -174,54 +172,29 @@ export default {
{ id: 'PAUSED', name: '已停止' },
{ id: 'UNKNOWN', name: '已断开' },
{ id: 'EXCEPTION', name: '异常' },
{ id: 'UNKNOWSTATUS', name: '未知' }
{ id: 'UNKNOWSTATUS', name: '未知' },
],
segmentIdList: [],
flag: true,
detail: {},
importDialog: {
visible: false
visible: false,
},
idx: '',
userConfig: {
visible: false
}
visible: false,
},
}
},
computed: {
path() {
return this.$route.path
}
},
},
watch: {
path(val) {
this.judgeType(val)
}
},
filters: {
vmStatusFilter(val) {
const statusMap = {
RUNNING: '运行中',
STOPPED: '已关机',
SUSPENDED: '已挂起',
PAUSED: '已停止',
UNKNOWN: '已断开',
EXCEPTION: '异常',
UNKNOWSTATUS: '未知'
}
return statusMap[val] || '未知'
},
vmStatusColorFilter(val) {
const statusMap = {
RUNNING: 'success',
STOPPED: 'danger',
SUSPENDED: 'danger',
PAUSED: 'danger',
EXCEPTION: 'warning',
UNKNOWN: 'warning'
}
return statusMap[val] || 'warning'
}
},
created() {
this.judgeType(this.path)
@ -241,7 +214,7 @@ export default {
//
importData() {
this.importDialog = {
visible: true
visible: true,
}
},
async getsegmentIdList() {
@ -256,7 +229,7 @@ export default {
addIp() {
this.addHostData.ipList.push({
value: '',
key: Date.now()
key: Date.now(),
})
this.$forceUpdate()
},
@ -286,14 +259,14 @@ export default {
category: this.type == 'VM' ? 'LOGICAL' : 'PHYSICAL',
ipList: [
{
value: ''
}
value: '',
},
],
ips: [],
// ip: '',
username: '',
password: '',
updateMode: 'MANUAL'
updateMode: 'MANUAL',
}
break
case 2:
@ -308,7 +281,7 @@ export default {
ips.split(' ').forEach((item) => {
this.addHostData.ipList.push({
value: item,
key: Date.now()
key: Date.now(),
})
})
break
@ -340,7 +313,7 @@ export default {
if (data.success) {
this.$message({
message: data.message,
type: 'success'
type: 'success',
})
that.addHostVisible = false
that.getData()
@ -354,13 +327,13 @@ export default {
dialog: true,
data: {
...row,
ips: row.ips.includes('[') ? JSON.parse(row.ips).join('') : row.ips
}
ips: row.ips.includes('[') ? JSON.parse(row.ips).join('') : row.ips,
},
}
},
handleSynchro(category) {
this.$confirm('执行同步操作前,请先在【分组管理—>网段管理】页面创建网段数据,以免影响正常使用,您确定要同步?', '提示', {
type: 'warning'
type: 'warning',
}).then(() => {
syncHost(this.type == 'VM' ? 'LOGICAL' : 'PHYSICAL').then((res) => {
const type = res.success ? 'success' : 'error'
@ -371,7 +344,7 @@ export default {
},
settingAllow(row) {
this.$confirm('您确定要设置HostsAllow配置嘛?', '提示', {
type: 'warning'
type: 'warning',
}).then(() => {
HostsAllow(row.id).then((res) => {
const type = res.success ? 'success' : 'error'
@ -382,7 +355,7 @@ export default {
},
handleDelete(row) {
this.$confirm('您确定要删除该主机吗嘛?', '提示', {
type: 'warning'
type: 'warning',
}).then(() => {
deleteBsmHost(row.id).then((res) => {
const type = res.success ? 'success' : 'error'
@ -402,7 +375,7 @@ export default {
if (document.querySelector('#inspect')) document.querySelector('#inspect').checked = true
this.$confirm(`<div>您确定要对主机[${row.ip}]进行纳管巡检吗?<div> <div><input type="checkbox" id="inspect" autocomplete="off" checked>是否更新密码再进行纳管巡检</div>`, '提示', {
type: 'warning',
dangerouslyUseHTMLString: true
dangerouslyUseHTMLString: true,
}).then(() => {
this.submitInspect({ id: row.id, force: document.querySelector('#inspect').checked })
})
@ -425,7 +398,7 @@ export default {
OC = osCategory.toLocaleUpperCase()
}
this.$router.push({ name: 'serverConfig', params: { type: 'pm', osCategory: OC, host: data.ip, segmentId: data.segmentId } })
}
}
},
},
}
</script>

View File

@ -47,7 +47,7 @@
<el-dialog title="服务器列表" :close-on-click-modal="false" :visible.sync="dialogServerVisible" append-to-body width="1200px">
<AdvanceTable :data="list" :card-border="false" row-key="id" :searchConfigs="searchConfigs" :params="params" :total="total" :columns="columns" :get-list="getList" :loading="loading" ref="tableRef" @select="handleSelectItem" @select-all="handleSelectAll">
<template #status="status">
<status-icon :type="vmStatusColorFilter(status, 'color')">{{ vmStatusFilter(status) }}</status-icon>
<status-icon :type="status | vmStatusColorFilter">{{ status | vmStatusFilter }}</status-icon>
</template>
</AdvanceTable>
<div slot="footer" class="dialog-footer">
@ -69,24 +69,24 @@ import { vmStatusFilter, vmStatusColorFilter } from '@/filters/index'
import { ref, nextTick, unref, computed, reactive, watch, defineComponent, Ref } from '@vue/composition-api'
import { Message, MessageBox } from 'element-ui'
interface IHost {
id: number,
name: string,
ip: string,
port: number,
segmentId: number,
osCategory: string,
username: string,
pasd: string,
id: number
name: string
ip: string
port: number
segmentId: number
osCategory: string
username: string
pasd: string
password: string
}
interface ITargetHost {
id: number,
rawName: string,
host: string,
port: number,
segmentId: number,
category: string,
user: string,
id: number
rawName: string
host: string
port: number
segmentId: number
category: string
user: string
pasd: string
}
function formatHostData(list: IHost[]) {
@ -100,23 +100,24 @@ function formatHostData(list: IHost[]) {
segmentId,
category,
user,
pasd: pasd && encrypt(pasd)
pasd: pasd && encrypt(pasd),
}
})
}
export default defineComponent({
props: {
itemData: {
type: Object
type: Object,
},
osCategory: { // osCategorylinux/windows
osCategory: {
// osCategorylinux/windows
type: String,
default: ''
default: '',
},
disabled: {
type: Boolean,
default: false
}
default: false,
},
},
setup(props, context) {
const hostData = reactive({
@ -131,14 +132,17 @@ export default defineComponent({
username,
pasd: password,
password,
osCategory
osCategory,
}
})
}),
})
//
watch(() => hostData.list, () => {
context.emit('host-change', formatHostData(hostData.list))
});
watch(
() => hostData.list,
() => {
context.emit('host-change', formatHostData(hostData.list))
}
)
const { list, total, params, loading, getList } = useTable({
getService: getHosts,
async afterGetList() {
@ -148,7 +152,7 @@ export default defineComponent({
unref(tableRef).toggleRowSelection(item, true)
}
})
}
},
})
const selectList: Ref<IHost[]> = ref([])
function handleSelectItem(selection: IHost[], row: IHost) {
@ -188,7 +192,7 @@ export default defineComponent({
...others,
osCategory,
pasd: pasd || (password && decrypt(password)),
port: port || (osCategory.toUpperCase() === 'WINDOWS' ? 5986 : 22)
port: port || (osCategory.toUpperCase() === 'WINDOWS' ? 5986 : 22),
}
})
dialogServerVisible.value = false
@ -211,7 +215,7 @@ export default defineComponent({
MessageBox.confirm('您确定要清空所选主机吗?', '提示', {
confirmButtonText: '清空',
confirmButtonClass: 'el-button--danger',
type: 'warning'
type: 'warning',
}).then(() => {
hostData.list = []
})
@ -220,7 +224,7 @@ export default defineComponent({
MessageBox.confirm(`您确定要删除主机【${name}】吗?`, '提示', {
confirmButtonText: '清空',
confirmButtonClass: 'el-button--danger',
type: 'warning'
type: 'warning',
}).then(() => {
const index = hostData.list.findIndex((item: IHost) => item.id === id)
hostData.list.splice(index, 1)
@ -246,8 +250,6 @@ export default defineComponent({
total,
params,
loading,
vmStatusFilter,
vmStatusColorFilter,
getList,
handleSelectItem,
handleSelectAll,
@ -259,9 +261,9 @@ export default defineComponent({
selectServer,
clearServer,
delServer,
getPostData
getPostData,
}
}
},
})
</script>
<style scoped>

View File

@ -14,6 +14,7 @@
"allowJs": true,
"baseUrl": ".",
"types": ["webpack-env"],
"outDir": "dist",
// "typeRoots": ["./node_modules/@types/", "./types"],
"paths": {
"@/*": ["src/*"],