feat: 增加数据库页面

develop
时启龙 2024-09-02 16:00:51 +08:00
parent 99a1733bd8
commit e01feb840d
12 changed files with 2570 additions and 1 deletions

View File

@ -0,0 +1,113 @@
<template>
<el-dialog title="白名单列表" :close-on-click-modal="false" :visible.sync="dialog.visible" append-to-body width="800px">
<cb-form ref="addFormRef" :model="addData" label-width="120px">
<cb-advance-table ref="tableRef" row-key="id" :search-configs="searchConfigs" :data="list" :params="params" :columns="columns" :get-list="getList" :total="total" :loading="loading" @selection-change="handleSelectionChange">
<template #action>
<el-button type="primary" :disabled="!selectionIps.length" @click="handleDelete" icon="el-icon-delete">移除</el-button>
</template>
</cb-advance-table>
</cb-form>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="dialog.visible = false">取消</el-button>
<el-button type="primary" @click.native="addSubmit" :loading="loading">确定</el-button>
</div>
</el-dialog>
</template>
<script lang="ts">
import { computed, defineComponent, ref, watch } from 'vue'
import { Message } from 'element-ui'
import { getSecurityIps, modifySecurityIps } from '@/views/resource/ctstack/services/database/common'
import { handleSearchParam } from '@cmp/cmp-element'
import useTable from '@cmp/cmp-common/hooks/useTable'
import { columns, searchConfigs as searchConfig } from './config'
export default defineComponent({
props: {
dialog: {
type: Object,
default: () => {}
},
url: {
type: String,
required: true
}
},
setup(props: any, context: any) {
const { record } = props.dialog
const addData = ref({
...record
})
const { list, total, params, loading, getList } = useTable({
getService: getSecurityIps(props.url)
})
const addFormRef = ref(null)
function addSubmit() {
//
const { validate } = (addFormRef.value || context.refs.addFormRef) as HTMLFormElement
validate(async (valid: boolean) => {
if (valid) {
loading.value = true
const { instanceType, productType } = addData.value
// const data = await modifyRedisConfig({
// ...addData.value,
// storageType: productType === 'Tair' && instanceType === 'tair_essd' ? 'essd_pl1' : ''
// })
// loading.value = false
// if (data.success) {
// Message({
// message: data.message,
// type: 'success'
// })
// context.emit('getData')
// ;(props.dialog as Base.IDialog).visible = false
// }
;(props.dialog as Base.IDialog).visible = false
}
})
}
const searchConfigs = ref([...searchConfig]) as any
searchConfigs.value[0].initValue = addData.value.instanceId
async function handleDelete() {
const params: any = {
id: addData.value.id,
securityIps: selectionIps.value,
modifyMode: 'Delete'
}
if (total.value === params.securityIps.length) return Message.error('不能将所选IP地址置空')
const res = await modifySecurityIps(props.url, params)
if (res.success) {
Message.success(res.message)
;(context.refs.tableRef as any).clearSelection()
context.emit('getData')
getList()
}
}
const selectionIps = ref([])
function handleSelectionChange(selection: any) {
selectionIps.value = selection.map((item: any) => item.securityIp)
}
return {
loading,
list,
total,
params,
getList,
addFormRef,
addData,
addSubmit,
columns,
searchConfigs,
handleDelete,
selectionIps,
handleSelectionChange
}
}
})
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,14 @@
export const columns = [
{
type: 'selection',
reserveSelection: true
},
{
label: 'IP地址',
prop: 'securityIp'
}
]
export const searchConfigs: Base.ISearchConfig[] = [
{ type: 'Const', value: 'instanceId', initValue: '' },
{ type: 'Input', label: 'IP地址', value: 'securityIp' }
]

View File

@ -0,0 +1,42 @@
<template>
<div>
<el-dropdown-item :disabled="disabled" @click.native="handleOpen"> 白名单列表 </el-dropdown-item>
<Dialog v-if="addData.visible" :url="url" :dialog="addData" @getData="$emit('getData')"></Dialog>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Dialog from './List.vue'
export default defineComponent({
components: { Dialog },
props: {
disabled: {
type: Boolean,
default: false
},
record: {
type: Object,
default: () => {}
},
url: {
type: String,
required: true
}
},
setup(props: any, context: any) {
const addData = ref({
record: { ...props.record },
visible: false
})
function handleOpen() {
addData.value.visible = true
}
return {
addData,
handleOpen
}
}
})
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,135 @@
<template>
<el-dialog title="修改权限" :close-on-click-modal="false" :visible.sync="dialog.visible" width="1100px" append-to-body>
<cb-form ref="addFormRef" :model="addData" label-width="140px">
<cb-form-item label-width="0">
<el-transfer
style="text-align: left; display: inline-block"
v-model="addData.checkedUseRoleIds"
filterable
:titles="['未选择', '已选择']"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}'
}"
:props="{ key: 'name', label: 'name' }"
:data="list"
>
<span slot-scope="{ option }">
<span style="margin-right: 10px">{{ option.name }}</span>
<el-radio-group v-model="option.role" class="role-group">
<el-radio label="ReadWrite">读写</el-radio>
<el-radio label="ReadOnly">只读</el-radio>
<el-radio label="DDLOnly">DDL</el-radio>
<el-radio label="DMLOnly">DML</el-radio>
</el-radio-group>
</span>
</el-transfer>
</cb-form-item>
</cb-form>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="dialog.visible = false">取消</el-button>
<el-button type="primary" @click.native="addSubmit" :loading="loading">确定</el-button>
</div>
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { Message } from 'element-ui'
import { handleSearchParam } from '@cmp/cmp-element'
import { getRdsDbs, authRdsUser } from '@/views/resource/ctstack/services/database/rds'
export default defineComponent({
props: {
dialog: {
type: Object
}
},
setup(props: any, context: any) {
const { record } = props.dialog
const addData = ref({
...record,
checkedUseRoleIds: record.privileges && JSON.parse(record.privileges).map((item: any) => item.name),
privileges: record.privileges ? JSON.parse(record.privileges) : []
})
const list = ref([])
const { vendorId, rdsId } = addData.value
;(async () => {
const res = await getRdsDbs(rdsId, { params: handleSearchParam({ vendorId, rdsId }) })
if (res.success) {
const { checkedUseRoleIds, privileges } = addData.value
console.log(privileges)
list.value = res.data.rows.map((item: any) => {
if (checkedUseRoleIds.includes(item.name)) {
const { accountPrivilege = '' } = privileges.find((role: any) => role.name === item.name) || {}
return {
...item,
role: accountPrivilege
}
} else {
return {
role: 'ReadWrite',
...item
}
}
})
}
})()
const loading = ref(false)
const addFormRef = ref(null)
function addSubmit() {
//
const { validate } = (addFormRef.value || context.refs.addFormRef) as HTMLFormElement
validate(async (valid: boolean) => {
if (valid) {
loading.value = true
const { rdsId, id, checkedUseRoleIds: checkedUserIds, username } = addData.value
const userRoles = list.value
.filter((item: any) => checkedUserIds.includes(item.name))
.map(({ name, role }: any) => {
return {
name,
accountPrivilege: role
}
})
const data = await authRdsUser({ rdsId, userId: id, id, username, privileges: userRoles })
loading.value = false
if (data.success) {
Message({
message: data.message,
type: 'success'
})
context.emit('getData')
;(props.dialog as Base.IDialog).visible = false
}
}
})
}
return {
loading,
addFormRef,
addData,
addSubmit,
list
}
}
})
</script>
<style lang="scss" scoped>
.slider-wrapper {
display: flex;
::v-deep .el-slider__marks-text {
white-space: nowrap;
}
}
::v-deep .el-transfer-panel:nth-child(1) .role-group {
display: none;
}
::v-deep .el-transfer-panel:nth-child(3) {
width: 600px;
}
</style>

View File

@ -0,0 +1,76 @@
/** * Created by HaijunZhang on 2019/4/28. */
<template>
<el-row :gutter="20">
<el-col :span="24">
<cb-form-item label="所属地域:" prop="regionId" validate="required">
<el-radio-group v-model="addData.regionId" @change="getZoneList">
<el-radio-button :label="item.regionId" v-for="item in regionList" :key="item.regionId">{{ item.name }}</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
<el-col :span="24">
<cb-form-item label="可用区:" prop="zone" validate="required">
<el-radio-group v-model="addData.zone" class="simple-radio">
<el-radio-button :label="item.zoneId" v-for="item in zoneList" :key="item.zoneId">{{ item.name }}</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
</el-row>
</template>
<script>
import { computed, defineComponent, reactive, ref, Ref, toRefs } from 'vue'
import { getRegion, getZone } from 'services/platform/index'
export default {
props: {
addData: {
type: Object,
required: true
}
},
setup(props, { root: { $set } }) {
const regionList = ref([])
const zoneList = ref([])
async function getRegionList() {
const res = await getRegion({ vendorId: props.addData.vendorId })
if (res.success) {
regionList.value = res.data
if (!props.addData.regionId) {
$set(props.addData, 'regionId', res.data[0].regionId)
}
getZoneList()
}
}
getRegionList()
async function getZoneList(rgId) {
// rgId
if (props.addData.zone && rgId) props.addData.zone = ''
const { vendorId, regionId } = props.addData
const res = await getZone({ vendorId, regionId })
if (res.success) {
zoneList.value = res.data
if (!props.addData.zone) {
$set(props.addData, 'zone', res.data[0].zoneId)
}
}
}
return {
regionList,
zoneList,
getZoneList
}
}
}
</script>
<style scoped>
.el-radio-button {
margin-bottom: 8px;
}
::v-deep .el-radio-button__inner {
border-left: solid 1px #dcdfe6;
}
.el-icon-question {
color: #bbbbbd;
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,525 @@
<template>
<el-dialog v-if="addData.dialog" title="新增RDS" :visible.sync="addData.dialog" width="950px">
<el-steps :active="active" finish-status="success">
<el-step title="基础资源"></el-step>
<el-step title="实例配置"></el-step>
</el-steps>
<el-row class="box" v-show="active === 0">
<cb-form ref="basicData" :model="addData.data" label-width="120px">
<cb-form-item label="计费方式: " prop="payType" :rules="[required]">
<el-radio-group v-model="addData.data.payType" @change="getFlavorList">
<el-radio-button label="Postpaid">后付费按量付费</el-radio-button>
<el-radio-button disabled label="Prepaid">预付费包年包月</el-radio-button>
</el-radio-group>
</cb-form-item>
<region-item :add-data="addData.data"></region-item>
<cb-form-item
label="实例名称:"
prop="name"
:rules="[{ pattern: /^[a-zA-Z\u4e00-\u9fa5?!((https?:)\/\/)][a-zA-Z0-9:_-]([a-zA-Z0-9:_-]{1,62})?$/, message: '由字母、数字、中文、下划线_、中划线-)冒号(:组成以字母或中文开头不能以http://或https://开头长度限制在2-64个字符。', trigger: null }]"
validate="required"
>
<el-input v-model="addData.data.name" placeholder="请输入实例名称" class="basic-cmp"></el-input>
</cb-form-item>
<!-- <el-col :span="12"> -->
<cb-form-item label="类型: " prop="dataStoreType" :rules="[required]">
<el-select v-model="addData.data.dataStoreType" clearable placeholder="请选择类型" @change="changeType">
<el-option v-for="(item, key) in typeList" :key="key" :value="key" :label="key"></el-option>
</el-select>
</cb-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12"> -->
<cb-form-item label="版本: " prop="dataStoreVersion" :rules="[required]">
<el-select v-model="addData.data.dataStoreVersion" clearable placeholder="请选择版本" @change="changeVersion">
<el-option v-for="item in versionList" :key="item.value" :value="item.value" :label="item.name"></el-option>
</el-select>
</cb-form-item>
<!-- </el-col> -->
<cb-form-item label="系列: " prop="series" :rules="[required]">
<el-select v-model="addData.data.series" clearable placeholder="请选择系列" @change="changeCategory">
<el-option v-for="(item, index) in categoryList" :key="index" :value="item.value" :label="item.name"></el-option>
</el-select>
</cb-form-item>
<cb-form-item label="存储类型: " prop="volType" :rules="[required]">
<el-select v-model="addData.data.volType" clearable placeholder="请选择类型" @change="getFlavorList">
<el-option v-for="(item, index) in volTypeList" :key="index" :value="item.value" :label="item.name"></el-option>
</el-select>
</cb-form-item>
<cb-form-item label="实例规格: " prop="flavorId" :rules="[required]" required-message="">
<cb-smart-table :data="specList" stripe>
<el-table-column show-overflow-tooltip label="规格代码" prop="flavorUuid">
<template v-slot="scope">
<el-radio v-model="addData.data.flavorId" :label="scope.row.id">{{ scope.row.flavorUuid }}</el-radio>
</template>
</el-table-column>
<el-table-column show-overflow-tooltip label="实例规格族" prop="typeLabel"></el-table-column>
<el-table-column show-overflow-tooltip label="CPU" prop="cpu"></el-table-column>
<el-table-column show-overflow-tooltip label="内存GB" prop="memory"></el-table-column>
<el-table-column show-overflow-tooltip label="最大连接数(个)" prop="maxConnections"></el-table-column>
<el-table-column show-overflow-tooltip label="最大IOPS次/秒)" prop="maxIops"></el-table-column>
<el-table-column show-overflow-tooltip label="最大IO带宽Mbps" prop="maxIombps" v-if="addData.data.volType !== 'local_ssd'"></el-table-column>
</cb-smart-table>
</cb-form-item>
<cb-form-item label="存储空间(GB): " prop="volSize" :rules="[required]">
<div style="padding: 0 10px">
<el-slider v-model="addData.data.volSize" :marks="marks" show-input :min="20" :max="6000"> </el-slider>
</div>
</cb-form-item>
</cb-form>
</el-row>
<cb-form v-show="active === 1" :model="addData.data" ref="data" label-width="120px">
<el-row>
<cb-form-item label="网络类型: " prop="instanceNetworkType" :rules="[required]">
<el-radio-group v-model="addData.data.instanceNetworkType" @change="changeNetworkType">
<el-radio-button label="Classic">经典网络</el-radio-button>
<el-radio-button label="VPC">专有网络</el-radio-button>
</el-radio-group>
</cb-form-item>
<div v-if="addData.data.instanceNetworkType == 'VPC'">
<cb-form-item label="VPC: " prop="vpcId" :rules="[required]">
<el-select v-model="addData.data.vpcId" clearable placeholder="请选择VPC" @change="changeVpc">
<el-option v-for="(item, index) in vpcList" :key="index" :value="item.value" :label="item.name"></el-option>
</el-select>
</cb-form-item>
<cb-form-item label="主节点交换机: " prop="vswitchId" :rules="[required]">
<el-select v-model="addData.data.vswitchId" clearable placeholder="请选择主节点交换机">
<el-option v-for="(item, index) in vswitchList" :key="index" :value="item.value" :label="item.name"></el-option>
</el-select>
</cb-form-item>
</div>
<!-- <cb-form-item label="参数模板: " prop="paramGroupId" :rules="[required]">
<el-select v-model="addData.data.paramGroupId" clearable placeholder="请选择参数模板">
<el-option v-for="(item, index) in templateList" :key="index" :value="item.id" :label="item.name"></el-option>
</el-select>
</cb-form-item> -->
<cb-form-item label="时区: " prop="timeZone" :rules="[required]">
<el-select filterable v-model="addData.data.timeZone" clearable placeholder="请选择时区">
<el-option v-for="(item, index) in timeList" :key="index" :value="item.value" :label="item.name"></el-option>
</el-select>
</cb-form-item>
<cb-form-item label="表名大小写: " prop="IgnoreCase" :rules="[required]">
<el-radio-group v-model="addData.data.IgnoreCase">
<el-radio label="1">不区分大小写默认</el-radio>
<el-radio label="0">区分大小写</el-radio>
</el-radio-group>
</cb-form-item>
<!-- <cb-form-item label="资源组: " prop="resourceId">
<el-select v-model="addData.data.resourceId" clearable filterable>
<el-option v-for="(item, index) in rgroupList" :key="index" :value="item.resourceGroupUuid" :label="item.displayName"></el-option>
</el-select>
</cb-form-item> -->
</el-row>
</cb-form>
<div slot="footer">
<el-button type="primary" v-if="active === 1" @click="next(1)"></el-button>
<el-button type="primary" v-if="active === 0" @click="next(0)"></el-button>
<el-button type="primary" v-if="active === 1" @click="ok"></el-button>
<el-button type="ghost" @click="addData.dialog = false">取消</el-button>
</div>
</el-dialog>
</template>
<script>
import validate from '@/validate'
import { getSubnet } from 'views/resource/ctstack/services/subnet.js'
import { getRegion, getFlavor, getParamGroup, createRds, getFlavorSpec } from 'views/resource/ctstack/services/database/rds.js'
import { getVpc } from 'views/resource/ctstack/services/vpcs.js'
import RegionItem from './RegionItem.vue'
import { handleSearchParam } from '@cmp/cmp-element'
export default {
components: { RegionItem },
props: {
addData: {
type: Object
}
},
data() {
return {
required: validate.required,
active: 0,
regions: [],
typeList: {
MySQL: ['5.5', '5.6', '5.7', '8.0']
// SQLServer: ['2008r2', '08r2_ent_ha', '2012', '2012_ent_ha', '2012_std_ha', '2012_web', '2014_std_ha', '2016_ent_ha', '2016_std_ha', '2016_web', '2017_std_ha', '2017_ent', '2019_std_ha', '2019_ent'],
// PostgreSQL: ['9.4', '10.0', '11.0', '12.0', '13.0'],
// MariaDB: ['10.3']
},
versionList: [],
categoryList: [],
categoryMap: {
5.5: [
{
name: '高可用版',
value: 'HighAvailability',
volType: [
{
name: '本地SSD云盘',
value: 'local_ssd'
}
]
}
],
5.6: [
{
name: '高可用版',
value: 'HighAvailability',
volType: [
{
name: '本地SSD云盘',
value: 'local_ssd'
}
]
},
{ name: '基础版', value: 'Basic', volType: [] }
],
5.7: [
{
name: '高可用版',
value: 'HighAvailability',
volType: [
{
name: '本地SSD云盘',
value: 'local_ssd'
},
{
name: 'ESSD PL1云盘',
value: 'cloud_essd'
},
{
name: 'ESSD PL2云盘',
value: 'cloud_essd2'
},
{
name: 'ESSD PL3云盘',
value: 'cloud_essd3'
},
{
name: 'SSD云盘',
value: 'cloud_ssd'
}
]
},
{
name: '基础版',
value: 'Basic',
volType: [
{
name: 'ESSD PL1云盘',
value: 'cloud_essd'
},
{
name: 'SSD云盘',
value: 'cloud_ssd'
}
]
},
{
name: '三节点企业版',
value: 'Finance',
volType: [
{
name: '本地SSD云盘',
value: 'local_ssd'
}
]
}
],
'8.0': [
{
name: '高可用版',
value: 'HighAvailability',
volType: [
{
name: '本地SSD云盘',
value: 'local_ssd'
},
{
name: 'ESSD PL1云盘',
value: 'cloud_essd'
},
{
name: 'ESSD PL2云盘',
value: 'cloud_essd2'
},
{
name: 'ESSD PL3云盘',
value: 'cloud_essd3'
},
{
name: 'SSD云盘',
value: 'cloud_ssd'
}
]
},
{
name: '基础版',
value: 'Basic',
volType: [
{
name: 'ESSD PL1云盘',
value: 'cloud_essd'
},
{
name: 'SSD云盘',
value: 'cloud_ssd'
}
]
},
{
name: '三节点企业版',
value: 'Finance',
volType: [
{
name: '本地SSD云盘',
value: 'local_ssd'
}
]
}
]
},
volTypeList: [],
specList: [],
vpcList: [],
vswitchList: [],
templateList: [],
timeList: [
{ name: 'UTC-12:00', value: '-12:00' },
{ name: 'UTC-11:00', value: '-11:00' },
{ name: 'UTC-10:00', value: '-10:00' },
{ name: 'UTC-09:00', value: '-9:00' },
{ name: 'UTC-08:00', value: '-8:00' },
{ name: 'UTC-07:00', value: '-7:00' },
{ name: 'UTC-06:00', value: '-6:00' },
{ name: 'UTC-05:00', value: '-5:00' },
{ name: 'UTC-04:00', value: '-4:00' },
{ name: 'UTC-03:00', value: '-3:00' },
{ name: 'UTC-02:00', value: '-2:00' },
{ name: 'UTC-01:00', value: '-1:00' },
{ name: 'UTC+00:00', value: '+0:00' },
{ name: 'UTC+01:00', value: '+1:00' },
{ name: 'UTC+02:00', value: '+2:00' },
{ name: 'UTC+03:00', value: '+3:00' },
{ name: 'UTC+04:00', value: '+4:00' },
{ name: 'UTC+05:00', value: '+5:00' },
{ name: 'UTC+05:30', value: '+5:30' },
{ name: 'UTC+06:00', value: '+6:00' },
{ name: 'UTC+07:00', value: '+7:00' },
{ name: 'UTC+08:00', value: '+8:00' },
{ name: 'UTC+09:00', value: '+9:00' },
{ name: 'UTC+10:00', value: '+10:00' },
{ name: 'UTC+11:00', value: '+11:00' },
{ name: 'UTC+12:00', value: '+12:00' },
{ name: 'UTC+13:00', value: '+13:00' }
],
rgroupList: [],
marks: {
20: '20GB',
1500: '1500GB',
3000: '3000GB'
}
}
},
created() {
this.changeType(this.addData.data.dataStoreType)
this.getVendorDetail()
},
mounted() {},
computed: {
regionId() {
return this.addData.data.regionId
}
},
watch: {
regionId(newVal) {
this.versionList = []
this.addData.data.dataStoreVersion = ''
this.getVersions()
}
},
methods: {
getVendorDetail() {
// detailCloudVendor(this.addData.data.vendorId).then(data => {
// if (data.success) {
// const authentication = data.data.authentication ? JSON.parse(data.data.authentication) : {}
// this.regions = authentication.regions || []
// }
// })
},
getFlavorList() {
const { dataStoreType, dataStoreVersion, series, vendorId, regionId, volType, zone } = this.addData.data
if (dataStoreType && dataStoreVersion && series && regionId && volType && zone) {
getFlavor({
series: series,
vendorId: vendorId,
regionId: regionId,
zone: zone,
engine: dataStoreType.toLowerCase(),
version: dataStoreVersion,
storageType: volType
}).then(data => {
if (data.success) {
this.specList = data.data
this.addData.data.flavorId = this.specList.length ? this.specList[0].id : ''
}
})
}
},
getVersions() {
const { vendorId, regionId } = this.addData.data
const params = {
type: 'version',
vendorId,
regionId,
engine: 'MySQL'
}
// const res = await getFlavorSpec(params)
// if (res) {
// this.versionList = res.data
// }
this.versionList = [
{ name: '8.0', value: '8.0' },
{ name: '5.7', value: '5.7' }
]
},
async getSeries(version) {
const { vendorId, regionId } = this.addData.data
const params = {
type: 'series',
vendorId,
regionId,
version,
engine: 'MySQL'
}
const res = await getFlavorSpec(params)
if (res) {
this.categoryList = res.data
}
},
async getStorageType(series) {
const { vendorId, regionId } = this.addData.data
const params = {
type: 'storageType',
vendorId,
regionId,
series,
engine: 'MySQL'
}
const res = await getFlavorSpec(params)
if (res) {
this.volTypeList = res.data
}
},
changeVersion(value) {
this.categoryList = []
this.$set(this.addData.data, 'series', '')
this.volTypeList = []
this.addData.data.volType = ''
this.specList = []
this.getSeries(value)
},
changeCategory(value) {
this.volTypeList = []
this.addData.data.volType = ''
this.specList = []
this.getStorageType(value)
},
changeNetworkType(value) {
if (value == 'VPC') {
this.getVpcList()
}
},
getVpcList() {
getVpc({
simple: true,
params: handleSearchParam({
vendorId: this.addData.data.vendorId,
regionId: this.addData.data.regionId
})
}).then(data => {
this.loading = false
if (data.success) {
this.vpcList = data.data.rows
}
})
},
changeVpc(value) {
let networkId = ''
this.vswitchList = []
this.addData.data.vswitchId = ''
this.vpcList.forEach(item => {
if (value == item.value) {
networkId = item.id
this.getSwitchList(networkId)
}
})
},
getSwitchList(networkId) {
getSubnet({
simple: true,
params: handleSearchParam({
networkId: networkId,
vendorId: this.addData.data.vendorId,
zone: this.addData.data.zone
})
}).then(data => {
if (data.success) {
this.vswitchList = data.data.rows
}
})
},
getTemplateList() {
getParamGroup({
regionId: this.addData.data.regionId,
vendorId: this.addData.data.vendorId,
engine: this.addData.data.dataStoreType.toLowerCase(),
version: this.addData.data.dataStoreVersion
}).then(data => {
if (data.success) {
this.templateList = data.data
}
})
},
getRgroup() {
// getResource({
// page: 1,
// rows: 99999,
// params: handleSearchParam({
// vendorId: this.addData.data.vendorId,
// 'status:ueq': 'PendingDelete'
// })
// }).then(data => {
// if (data.success) {
// this.rgroupList = data.data.rows
// }
// })
},
next(num) {
if (num === 0) {
this.$refs.basicData.validate(valid => {
if (valid) {
this.active = 1
this.getRgroup()
this.getTemplateList()
}
})
}
if (num === 1) this.active = 0
},
ok() {
this.$refs.data.validate(valid => {
if (!valid) {
return false
}
createRds(this.addData.data).then(data => {
const type = data.success ? 'success' : 'error'
this.$message[type](data.message)
if (data.success) {
this.$refs.data.resetFields()
this.addData.dialog = false
this.$emit('back')
}
})
})
}
}
}
</script>

View File

@ -0,0 +1,225 @@
<template>
<cb-card-layout title="授权数据库">
<el-row :gutter="10" class="m-b">
<el-col :span="8">
<el-card style="height: 408px">
<div slot="header">
<span>未授权数据库</span>
</div>
<el-form :inline="true">
<el-form-item>
<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>
<cb-table ref="userTable" :small="true" :data="list" :params="params" :get-list="getList" :total="total" @selection-change="selectionChange">
<el-table-column type="selection" :selectable="selectAble" width="60"> </el-table-column>
<el-table-column show-overflow-tooltip label="名称" prop="name"> </el-table-column>
<el-pagination class="pull-right m-t-sm" slot="pagination" background :page-size="params.rows" :pager-count="5" layout="prev, pager, next" @current-change="getList" :current-page.sync="params.page" :total="total"> </el-pagination>
</cb-table>
</el-card>
</el-col>
<el-col :span="2">
<div>
<div style="height: 204px; line-height: 330px">
<el-button type="primary" size="mini" :disabled="leftSlection.length === 0" @click="addUser"><i class="el-icon-right"></i></el-button>
</div>
<div style="height: 204px">
<el-button type="primary" size="mini" @click="removeUser" :disabled="rightSlection.length === 0"><i class="el-icon-back"></i></el-button>
</div>
</div>
</el-col>
<el-col :span="14">
<el-card style="height: 408px">
<div slot="header">
<span class="">已授权数据库</span>
<span class="pull-right">
全部设 <el-button class="m-r" type="text" @click="setAuth">{{ buttonName }}</el-button>
</span>
</div>
<cb-smart-table :data="selectUserList" :rows="5" @selection-change="selectionChangeCheck">
<el-table-column type="selection" width="60"> </el-table-column>
<el-table-column show-overflow-tooltip label="名称" prop="name"> </el-table-column>
<el-table-column show-overflow-tooltip label="权限" prop="name" width="400px">
<template slot-scope="scope">
<el-radio-group v-model="scope.row.accountPrivilege">
<el-radio label="ReadWrite">读写(DDL+DML)</el-radio>
<el-radio label="ReadOnly">只读</el-radio>
<el-radio label="DDLOnly">仅DDL</el-radio>
<el-radio label="DMLOnly">仅DML</el-radio>
</el-radio-group>
</template>
</el-table-column>
</cb-smart-table>
</el-card>
</el-col>
</el-row>
</cb-card-layout>
</template>
<script>
import { handleSearchParam } from '@cmp/cmp-element'
import { getRdsDbs } from 'views/resource/ctstack/services/database/rds.js'
export default {
props: {
dbIds: {
type: Array,
default: function () {
return []
}
},
rdsId: {
type: Number
},
flag: {
type: Number
}
},
data() {
return {
list: [],
total: 0,
listQuery: {
name: ''
},
params: {
page: 1,
rows: 5
},
leftSlection: [],
rightSlection: [],
selectUserList: [],
buttonName: '读写(DDL+DML)'
}
},
computed: {
userIds() {
const ids = []
for (const a of this.selectUserList) {
ids.push(a.name)
}
return ids
},
privileges() {
const arr = []
for (const a of this.selectUserList) {
const obj = {
name: a.name,
accountPrivilege: a.accountPrivilege
}
arr.push(obj)
}
return arr
}
},
created() {
this.getSelectUser()
},
watch: {
flag: {
handler(newVal, oldVal) {
this.getSelectUser()
}
}
},
methods: {
setAuth() {
if (this.buttonName == '读写(DDL+DML)') {
this.selectUserList.forEach(item => {
this.$set(item, 'accountPrivilege', 'ReadWrite')
})
this.buttonName = '只读'
} else if (this.buttonName == '只读') {
this.selectUserList.forEach(item => {
this.$set(item, 'accountPrivilege', 'ReadOnly')
})
this.buttonName = '仅DDL'
} else if (this.buttonName == '仅DDL') {
this.selectUserList.forEach(item => {
this.$set(item, 'accountPrivilege', 'DDLOnly')
})
this.buttonName = '仅DML'
} else if (this.buttonName == '仅DML') {
this.selectUserList.forEach(item => {
this.$set(item, 'accountPrivilege', 'DMLOnly')
})
this.buttonName = '读写(DDL+DML)'
}
},
getSelectUser() {
getRdsDbs(this.rdsId, {
page: 1,
rows: 999999,
params: handleSearchParam({
rdsId: this.rdsId
})
}).then(data => {
if (data.success) {
const list = data.data.rows
list.forEach(item => {
if (this.dbIds.indexOf(item.name) > -1) {
this.selectUserList.push(item)
}
})
}
})
this.handleSearch()
},
getList() {
getRdsDbs(this.rdsId, this.params).then(data => {
if (data.success) {
this.list = data.data.rows
this.total = data.data.total
//
this.$nextTick(() => {
this.list.forEach(item => {
if (this.userIds.indexOf(item.name) > -1) this.$refs.userTable.toggleRowSelection(item, true)
})
})
}
})
},
//
handleSearch() {
this.params.page = 1
this.params.params = handleSearchParam({
rdsId: this.rdsId,
'name:LK': this.listQuery.name
})
this.getList()
},
selectionChange(selection) {
this.leftSlection = selection
},
selectionChangeCheck(selection) {
this.rightSlection = selection
},
selectAble(row) {
return !this.userIds.includes(row.name)
},
addUser() {
const users = this.leftSlection.filter(item => {
this.$set(item, 'accountPrivilege', 'ReadWrite')
return !this.userIds.includes(item.name)
})
this.selectUserList.unshift(...users)
},
removeUser() {
const ids = []
this.rightSlection.forEach(item => {
ids.push(item.name)
})
for (let i = 0; i < this.selectUserList.length; i++) {
const item = this.selectUserList[i]
if (ids.includes(item.name)) {
this.selectUserList.splice(i, 1)
i--
}
}
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,894 @@
<template>
<div class="wrapper">
<cb-advance-table title="" :search-configs="searchConfigs" :data="tableData" :params="params" :columns="columns" :get-list="getData" :total="total" :loading="loading">
<template v-slot:action>
<el-button type="primary" @click="handleCreate()"></el-button>
</template>
<template #status="status">
<cb-status-icon :type="statusFilter[status]">
{{ filter[status] }}
</cb-status-icon>
</template>
<template #type="type">
{{ filter[type] }}
</template>
<template #instanceNetworkType="instanceNetworkType">
{{ filter[instanceNetworkType] }}
</template>
<template #payType="payType">
{{ filter[payType] }}
</template>
<template #name="val, record">
<span class="detail-href" @click="getDetail(record.id)">{{ (record.instanceId || '--') + ' / ' + val }}</span>
</template>
<template #operate="val, record">
<el-button type="text" @click="patch(record)" :disabled="record.status !== 'Running'"> 重启 </el-button>
<div class="action-divider"></div>
<el-button type="text" @click="removeAliRds(record)" :disabled="record.status !== 'Running'"> 释放实例 </el-button>
</template>
</cb-advance-table>
<add :add-data="addData" v-if="addData.dialog" @back="getData"></add>
<cb-detail v-if="detailFlag" :title="detailData.instanceId + ' / ' + detailData.name" @goBack="goBack">
<template v-slot:item_container>
<cb-detail-item label="名称">{{ detailData.name }}</cb-detail-item>
<cb-detail-item label="状态">{{ filter[detailData.status] }}</cb-detail-item>
<cb-detail-item label="版本">{{ detailData.dataStoreVersion }}</cb-detail-item>
<cb-detail-item label="实例类型">{{ filter[detailData.type] }}</cb-detail-item>
<cb-detail-item label="数据库类型">{{ detailData.dataStoreType }}</cb-detail-item>
<cb-detail-item label="网络类型">{{ filter[detailData.instanceNetworkType] }}</cb-detail-item>
<cb-detail-item label="付费类型">{{ filter[detailData.payType] }}</cb-detail-item>
<cb-detail-item label="规格">{{ detailData.flavorId }}</cb-detail-item>
<cb-detail-item label="可用区">{{ detailData.region }}</cb-detail-item>
</template>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="账号列表" name="account">
<el-button class="m-b" type="primary" @click="createAccount()"></el-button>
<cb-table :data="usersList" :params="paramsUsers" :total="totalUsers" :get-list="getUsersList">
<el-table-column prop="username" label="账号" show-overflow-tooltip></el-table-column>
<el-table-column prop="permission" label="类型" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.permission | typeFilter }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" show-overflow-tooltip>
<template slot-scope="scope">
<cb-status-icon :type="scope.row.status | statusFilter('color')">{{ scope.row.status | statusFilter('status') }} </cb-status-icon>
</template>
</el-table-column>
<el-table-column prop="db" label="所属数据库" show-overflow-tooltip>
<template slot-scope="scope">
<div v-for="item in JSON.parse(scope.row.privileges)" :key="item.name">{{ item.name }} {{ item.accountPrivilege | authFilter }}</div>
</template>
</el-table-column>
<el-table-column prop="remark" label="账号描述" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" show-overflow-tooltip width="250px">
<template slot-scope="scope">
<el-button type="text" @click="resetPasd(scope.row)"></el-button>
<div class="action-divider"></div>
<!-- <el-button type="text" v-if="scope.row.permission == 'Super'" @click="resetAuth(scope.row)"></el-button> -->
<el-button type="text" v-if="scope.row.permission == 'Normal'" @click="modifyAuth(scope.row)"></el-button>
<div class="action-divider" v-if="scope.row.permission == 'Normal'"></div>
<!-- <div class="action-divider" v-if="scope.row.permission == 'Super'"></div> -->
<el-button type="text" @click="deleteAccount(scope.row)"></el-button>
</template>
</el-table-column>
<!-- <div slot="pagination"></div> -->
</cb-table>
<el-dialog title="重置账号" append-to-body v-if="resetFlag" :visible.sync="resetFlag" :close-on-click-modal="false">
<cb-form ref="resetData" :model="resetData" label-width="120px">
<cb-form-item label="数据库账号:" prop="username">
{{ resetData.username }}
</cb-form-item>
<cb-form-item label="密码:" prop="password" :rules="[required, rdsDBPassword]">
<el-input type="password" v-model="resetData.password"></el-input>
</cb-form-item>
</cb-form>
<span slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="resetFlag = false"> </el-button>
<el-button type="primary" @click.native="submitReset"> </el-button>
</span>
</el-dialog>
<el-dialog title="重置账号密码" append-to-body v-if="resetPasdFlag" :visible.sync="resetPasdFlag" :close-on-click-modal="false">
<cb-form ref="resetData" :model="resetData" label-width="120px">
<cb-form-item label="密码:" prop="password" :rules="[required, rdsDBPassword]">
<el-input type="password" v-model="resetData.password" show-password></el-input>
</cb-form-item>
<cb-form-item label="确认密码:" prop="password2" :rules="[required, rdsDBPassword]">
<el-input type="password" v-model="resetData.password2" show-password></el-input>
</cb-form-item>
</cb-form>
<span slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="resetPasdFlag = false"> </el-button>
<el-button type="primary" @click.native="submitResetPasd"> </el-button>
</span>
</el-dialog>
<el-drawer append-to-body title="创建用户账号" :visible.sync="drawer" direction="rtl" size="40%">
<el-card>
<cb-form ref="accountData" :model="accountData" label-width="120px">
<cb-form-item label="数据库账号:" prop="username" maxlength="16" :rules="[required, rdsAccountName, { validator: validateKeyword, trigger: 'blur' }]">
<el-tooltip class="item" effect="dark" content="由小写字母、数字、下划线_组成以字母开头以字母或数字结尾最多16个字符" placement="top">
<el-input v-model="accountData.username" auto-complete="off"></el-input>
</el-tooltip>
</cb-form-item>
<cb-form-item class="m-t-lg" label="账号类型:" prop="permission" :rules="[required]">
<el-radio-group v-model="accountData.permission">
<el-radio label="Super" :disabled="Super">高权限账号</el-radio>
<el-radio label="Normal">普通账号</el-radio>
</el-radio-group>
</cb-form-item>
<cb-form-item class="m-t-lg" label="密码:" prop="password" :rules="[required, rdsDBPassword]">
<el-input type="password" v-model="accountData.password" auto-complete="off"></el-input>
</cb-form-item>
<cb-form-item class="m-t-lg" label="确认密码:" prop="password2" :rules="[required]">
<el-input type="password" v-model="accountData.password2" auto-complete="off"></el-input>
</cb-form-item>
<cb-form-item class="m-t-lg" label="备注:" prop="remark" maxlength="256">
<el-input type="textarea" v-model="accountData.remark"></el-input>
<div class="text_mine">请输入备注说明,最多256个字符</div>
</cb-form-item>
<el-button type="primary" @click="submitAccount()"></el-button>
<el-button type="ghost" @click="drawer = false">取消</el-button>
</cb-form>
</el-card>
</el-drawer>
<!-- 修改权限 -->
<ModifyRole :dialog="editDialog" v-if="editDialog.visible" @getData="getUsersList"></ModifyRole>
<!-- <el-drawer append-to-body :with-header="false" :visible.sync="drawerAuth" direction="rtl" size="1200px">
<el-card>
<cb-form :model="accountData" label-width="120px">
<cb-form-item label="数据库账号:" prop="username" maxlength="16">
{{accountData.username}}
</cb-form-item>
<modify-db class="m-b" ref="modifyDb" :rdsId="detailData.id" :dbIds="accountData.dbIds" :flag="flag"></modify-db>
<el-button type="primary" @click="submitAuth()"></el-button>
<el-button type="ghost" @click="drawerAuth = false">取消</el-button>
</cb-form>
</el-card>
</el-drawer> -->
</el-tab-pane>
<el-tab-pane label="数据库列表" name="database">
<el-button class="m-b" type="primary" @click="createDB"></el-button>
<cb-table :data="databaseList" :params="paramsDb" :total="totalDb" :get-list="getDbList">
<el-table-column prop="name" label="名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="状态" show-overflow-tooltip>
<!-- <template slot-scope="scope">
<cb-status-icon :type="scope.row.status | vmStatusColor">{{ scope.row.status | database }} </cb-status-icon>
</template> -->
</el-table-column>
<el-table-column prop="characterSet" label="字符集" show-overflow-tooltip></el-table-column>
<el-table-column prop="accounts" label="绑定账号" show-overflow-tooltip>
<template slot-scope="scope">
<div v-for="(item, index) in JSON.parse(scope.row.accounts)" :key="index">
{{ item.account }}
</div>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注说明" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" show-overflow-tooltip>
<template slot-scope="scope">
<el-button type="text" @click="deleteDB(scope.row)"></el-button>
</template>
</el-table-column>
</cb-table>
<el-drawer append-to-body title="创建数据库" :visible.sync="drawerDB" direction="rtl" size="40%">
<el-card>
<cb-form ref="databaseData" :model="databaseData" label-width="140px">
<cb-form-item
label="数据库(DB)名称:"
prop="name"
:rules="[
{
pattern: /^[a-z]([a-z0-9_-]{1,62})?([a-z0-9])$/,
message: '由小写字母、数字、下划线、中划线组成以字母开头字母或数字结尾最多64个字符',
trigger: null
},
{
validator: validateKeyword,
trigger: 'blur'
}
]"
validate="required"
>
<el-input v-model="databaseData.name" placeholder="请输入数据库名称" clearable></el-input>
</cb-form-item>
<cb-form-item class="m-t-lg" label="支持字符集:" prop="characterSet">
<el-select v-model="databaseData.characterSet" placeholder="请选择">
<el-option v-for="item in codeData" :key="item" :label="item" :value="item"> </el-option>
</el-select>
</cb-form-item>
<!-- <cb-form-item class="m-t-lg" label="授权账号:" prop="accountId">
<el-select v-model="databaseData.accountId" placeholder="请选择">
<el-option v-for="item in accounts" :key="item.id" :label="item.username" :value="item.id">
</el-option>
</el-select>
</cb-form-item> -->
<cb-form-item class="m-t-lg" label="账号类型:" prop="accountPrivilege" :rules="[required]" v-if="databaseData.accountId" key="accountPrivilege">
<el-radio-group v-model="databaseData.accountPrivilege">
<el-radio label="ReadWrite">读写 </el-radio>
<el-radio label="ReadOnly">只读 </el-radio>
<el-radio label="DDLOnly">仅DDL </el-radio>
<el-radio label="DMLOnly">仅DML </el-radio>
</el-radio-group>
</cb-form-item>
<cb-form-item class="m-t-lg" label="备注说明:" prop="remark" maxlength="256">
<el-input type="textarea" v-model="databaseData.remark"></el-input>
<div class="text_mine">请输入备注说明,最多256个字符</div>
</cb-form-item>
<el-button type="primary" @click="submitDB()"></el-button>
<el-button type="ghost" @click="drawerDB = false">取消</el-button>
</cb-form>
</el-card>
</el-drawer>
</el-tab-pane>
<el-tab-pane label="连接地址管理" name="connection">
<cb-table :data="connectionList" :params="paramsCo" :total="totalDo" :get-list="getCoList">
<el-table-column label="网络类型" show-overflow-tooltip>
<template slot-scope="scope">
{{ instanceNetworkTypeMap[scope.row.instanceNetworkType] + '(' + ipTypeMap[scope.row.ipType] + ')' }}
</template>
</el-table-column>
<el-table-column prop="connectionString" label="连接地址" show-overflow-tooltip></el-table-column>
<el-table-column prop="port" label="端口" show-overflow-tooltip></el-table-column>
</cb-table>
</el-tab-pane>
<!-- <el-tab-pane label="参数设置" name="params">
<el-tabs value="modify">
<el-tab-pane label="可修改参数" name="modify">
<cb-table :data="detailData.nodes">
<el-table-column prop="name" label="参数名" show-overflow-tooltip></el-table-column>
<el-table-column prop="type" label="参数默认值" show-overflow-tooltip></el-table-column>
<el-table-column prop="db" label="运行参数值" show-overflow-tooltip>
<template slot-scope="scope">
{{scope.row.params}} <el-button type="text" class="el-icon-edit"></el-button>
</template>
</el-table-column>
<el-table-column prop="remark" label="是否重启" show-overflow-tooltip></el-table-column>
<el-table-column prop="remark" label="参数值范围" show-overflow-tooltip></el-table-column>
<el-table-column prop="remark" label="参数描述" show-overflow-tooltip></el-table-column>
<div slot="pagination"></div>
</cb-table>
</el-tab-pane>
<el-tab-pane label="参数修改历史" name="history">
<cb-table :data="detailData.nodes">
<el-table-column prop="name" label="参数名" show-overflow-tooltip></el-table-column>
<el-table-column prop="type" label="变更前参数值" show-overflow-tooltip></el-table-column>
<el-table-column prop="db" label="变更后参数值" show-overflow-tooltip></el-table-column>
<el-table-column prop="remark" label="是否生效" show-overflow-tooltip></el-table-column>
<el-table-column prop="remark" label="变更时间" show-overflow-tooltip></el-table-column>
</cb-table>
</el-tab-pane>
</el-tabs>
</el-tab-pane> -->
</el-tabs>
</cb-detail>
</div>
</template>
<script>
import validate from '@/validate'
import { getAliRds, detailAliRds, getRdsCos, patchAliRds, removeAliRds, getRdsUsers, createRdsUsers, removeRdsUsers, resetRdsUserPasd, resetRdsUser, authRdsUser, getRdsDbs, createRdsDbs, removeRdsDbs } from 'views/resource/ctstack/services/database/rds.js'
import add from './add.vue'
import ModifyRole from './ModifyRole.vue'
import { keywords } from './validate'
import { handleSearchParam } from '@cmp/cmp-element'
const columns = [
{
label: '实例ID/名称',
prop: 'name',
scopedSlots: { customRender: 'name' }
},
{
label: '状态',
prop: 'status',
scopedSlots: { customRender: 'status' }
},
{
label: '版本',
prop: 'dataStoreVersion'
},
{
label: '规格',
prop: 'cpu',
customRender(cpu, { memory }) {
return cpu && memory ? `${cpu}C/${memory}GB` : '--'
}
},
{
label: '存储空间GB',
prop: 'volSize'
},
{
label: '创建时间',
prop: 'createDate'
},
{
label: '操作',
disabled: true,
width: '220px',
scopedSlots: { customRender: 'operate' }
}
]
const validateKeyword = (rule, value, callback) => {
const valid = keywords.find(item => item.toLocaleLowerCase() === value)
if (!valid) {
callback()
} else {
callback(new Error(`${valid.toLocaleLowerCase()}为关键字,不能输入`))
}
}
const instanceNetworkTypeMap = {
VPC: '专有网络',
Classic: '经典网络'
}
const ipTypeMap = {
Inner: '内网',
Public: '外网',
Private: '内网'
}
export default {
props: {
platformObject: {
type: Object
}
},
components: { add, ModifyRole },
filters: {
typeFilter(value) {
const obj = {
Normal: '普通账号',
Super: '高权限账号'
}
return obj[value]
},
statusFilter(value, type) {
const obj = {
Unavailable: {
status: '未激活',
color: 'info'
},
Available: {
status: ' 已激活',
color: 'success'
}
}
return obj[value] && obj[value][type]
},
authFilter(value) {
const obj = {
ReadWrite: '读写(DDL+DML)',
ReadOnly: '只读',
DDLOnly: '仅DDL',
DMLOnly: 'DMLOnly'
}
return obj[value]
}
},
data() {
return {
validateKeyword,
ipTypeMap,
instanceNetworkTypeMap,
required: validate.required,
rdsAccountName: { pattern: /^[a-z]([a-z0-9_-]{0,14}[a-z0-9])?$/, message: '由小写字母、数字、下划线_组成以字母开头以字母或数字结尾最多16个字符', trigger: null },
rdsDBName: { pattern: /^[a-z]([a-z0-9_-]{0,62}[a-z0-9])?$/, message: '由小写字母、数字、下划线_、中划线-组成以字母开头字母或数字结尾最多64个字符', trigger: null },
rdsDBPassword: { pattern: '^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_]+$)(?![a-z0-9]+$)(?![a-z\\W_]+$)(?![0-9\\W_]+$)[a-zA-Z0-9\\W_]{8,32}$', message: '必须包含三种及以上类型大小写字母、数字、特殊符号。长度为8-32位特殊字符包括! @ # $ % ^ & * () _ + - =', trigger: null },
searchConfigs: [
{ type: 'Input', label: '名称', value: 'name' },
{ type: 'Const', value: 'vendorId', initValue: this.platformObject.vendorId }
],
columns,
loading: false,
params: {
page: 1,
rows: 10
},
searchData: {
name: '',
regionId: '',
zone: ''
},
zoneList: [],
tableData: [],
total: 0,
detailFlag: false,
detailData: {},
filter: {
Primary: '主实例',
Readonly: '只读实例',
Guard: '灾备实例',
Temp: '临时实例',
VPC: 'VPC',
Classic: '经典网络',
Postpaid: '按量付费',
Prepaid: '包年包月',
Creating: '创建中',
Running: '使用中',
Deleting: '删除中',
Rebooting: '重启中',
RebootFailed: '重启失败',
DBInstanceClassChanging: '升降级中'
},
statusFilter: {
Creating: 'warning',
Running: 'success',
Deleting: 'warning',
Rebooting: 'warning',
RebootFailed: 'danger',
DBInstanceClassChanging: 'warning'
},
addData: {
dialog: false,
data: {}
},
activeName: 'account',
//
paramsUsers: {
page: 1,
rows: 10
},
totalUsers: 0,
usersList: [],
drawer: false,
accountData: {},
drawerAuth: false,
//
resetFlag: false,
resetPasdFlag: false,
resetData: {},
//
drawerDB: false,
databaseData: {},
codeData: [
'utf8mb4',
'utf8',
'gbk',
'gb18030',
'latin1',
'euckr',
'armscii8',
'ascii',
'big5',
'binary',
'cp1250',
'cp1251',
'cp1256',
'cp1257',
'cp850',
'cp852',
'cp866',
'cp932',
'dec8',
'eucjpms',
'gb2312',
'geostd8',
'greek',
'hebrew',
'hp8',
'keybcs2',
'koi8r',
'koi8u',
'latin2',
'latin5',
'latin7',
'macce',
'macroman',
'sjis',
'swe7',
'tis620',
'ucs2',
'ujis',
'utf16',
'utf16le',
'utf32'
],
accounts: [],
databases: [],
databaseList: [],
paramsDb: {
page: 1,
rows: 10
},
totalDb: 0,
flag: 0,
Super: false,
connectionList: [],
paramsCo: {
page: 1,
rows: 10
},
totalDo: 0,
editDialog: {
visible: false,
record: {}
}
}
},
methods: {
async getCoList(id) {
this.paramsCo.params = handleSearchParam({
vendorId: this.detailData.vendorId,
rdsId: this.detailData.id
})
const res = await getRdsCos(id, this.paramsCo)
if (res.success) {
this.totalDo = res.data.total
this.connectionList = res.data.rows
}
},
patch(data) {
this.$confirm('确定重启该实例吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
patchAliRds(data.id).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.getData()
}
})
})
},
removeAliRds(data) {
this.$confirm('确定释放该实例吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
removeAliRds(data.id).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.getData()
}
})
})
},
getData() {
this.loading = true
getAliRds(this.params).then(data => {
this.loading = false
if (data.success) {
this.tableData = data.data.rows
this.total = data.data.total
}
})
},
handleSearch() {
this.params.page = 1
this.params.params = handleSearchParam({
vendorId: this.platformObject.vendorId,
regionId: this.searchData.regionId,
zone: this.searchData.zone,
'name:lk': this.searchData.name
})
this.getData()
},
handleCreate() {
this.addData = {
dialog: true,
data: {
vendorId: this.platformObject.vendorId,
payType: 'Postpaid',
regionId: '',
type: 'MySQL',
dataStoreVersion: '',
category: '',
volType: '',
availZone: '',
flavorId: '',
volSize: 20,
instanceNetworkType: 'Classic',
vpcId: '',
vswitchId: '',
paramGroupId: '',
timeZone: '+8:00',
IgnoreCase: '1'
}
}
},
getDetail(id) {
this.Super = false
detailAliRds(id).then(data => {
if (data.success) {
this.detailData = data.data
this.detailFlag = true
this.getUsersList()
}
})
},
handleClick() {
switch (this.activeName) {
case 'account':
this.getUsersList()
break
case 'database':
this.getDbList()
break
case 'connection':
this.getCoList(this.detailData.id)
break
}
},
getUsersList() {
this.paramsUsers.params = handleSearchParam({
vendorId: this.detailData.vendorId,
rdsId: this.detailData.id
})
getRdsUsers(this.detailData.id, this.paramsUsers).then(data => {
if (data.success) {
this.usersList = data.data.rows
this.totalUsers = data.data.total
}
})
getRdsUsers(this.detailData.id, {
simple: true,
params: handleSearchParam({
vendorId: this.detailData.vendorId,
rdsId: this.detailData.id
})
}).then(data => {
if (data.success) {
const list = data.data.rows
list.forEach(item => {
if (item.permission == 'Super') {
this.Super = true
}
})
}
})
},
getDbList() {
this.paramsDb.params = handleSearchParam({
vendorId: this.detailData.vendorId,
rdsId: this.detailData.id
})
getRdsDbs(this.detailData.id, this.paramsDb).then(data => {
if (data.success) {
this.databaseList = data.data.rows
this.totalDb = data.data.total
}
})
},
goBack() {
this.detailFlag = false
},
//
createAccount() {
this.drawer = true
this.accountData = {
rdsId: this.detailData.id,
vendorId: this.detailData.vendorId
}
},
submitAccount() {
this.$refs.accountData.validate(valid => {
if (valid) {
if (this.accountData.password !== this.accountData.password2) {
return this.$message.error('两次密码输入不一致')
}
const { vendorId, username, password, rdsId, permission, remark } = this.accountData
createRdsUsers(this.accountData.rdsId, {
rdsId: rdsId,
vendorId: vendorId,
username: username,
permission: permission,
password: crypto.encrypt(password),
remark: remark
}).then(data => {
if (data.success) {
this.$message.success(data.message)
this.drawer = false
this.getUsersList()
}
})
}
})
},
//
deleteAccount(data) {
this.$confirm(`您确定要删除【${data.username}】吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
removeRdsUsers({
rdsId: this.detailData.id,
userId: data.id
}).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.getUsersList()
}
})
})
},
//
modifyAuth(data) {
// this.getDbList()
this.flag++
const privilege = data.privilege ? JSON.parse(data.privilege) : []
const arr = []
privilege.forEach(element => {
arr.push(element.name)
})
// this.accountData = {
// rdsId: this.detailData.id,
// userId: data.id,
// username: data.username,
// dbIds: arr
// }
this.editDialog = {
record: { ...data },
visible: true
}
},
submitAuth() {
const { rdsId, username, userId } = this.accountData
authRdsUser({
rdsId: rdsId,
userId: userId,
id: userId,
username: username,
privileges: this.$refs.modifyDb.privileges
}).then(data => {
if (data.success) {
this.$message.success(data.message)
this.getUsersList()
this.drawerAuth = false
}
})
},
//
resetAuth(data) {
this.resetData = {
rdsId: this.detailData.id,
userId: data.id,
password: '',
username: data.username
}
this.resetFlag = true
},
submitReset() {
this.$refs.resetData.validate(valid => {
if (valid) {
const { rdsId, userId, password } = this.resetData
resetRdsUser({
rdsId: rdsId,
userId: userId,
password: crypto.encrypt(password)
}).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.resetFlag = false
this.getUsersList()
}
})
}
})
},
resetPasd(data) {
//
this.resetData = {
rdsId: this.detailData.id,
userId: data.id,
username: data.username
}
this.resetPasdFlag = true
},
submitResetPasd() {
this.$refs.resetData.validate(valid => {
if (valid) {
if (this.resetData.password !== this.resetData.password2) {
return this.$message.error('两次密码输入不一致')
}
const { rdsId, userId, password } = this.resetData
resetRdsUserPasd({
rdsId: rdsId,
userId: userId,
password: crypto.encrypt(password)
}).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.resetPasdFlag = false
this.getUsersList()
}
})
}
})
},
createDB() {
this.drawerDB = true
getRdsUsers(this.detailData.id, {
page: 1,
rows: 99999,
params: handleSearchParam({
vendorId: this.detailData.vendorId,
rdsId: this.detailData.id,
permission: 'Normal'
})
}).then(data => {
if (data.success) {
this.accounts = data.data.rows
}
})
this.databaseData = {
name: '',
accountPrivilege: 'ReadWrite',
characterSet: 'utf8'
}
},
submitDB() {
this.$refs.databaseData.validate(valid => {
if (valid) {
createRdsDbs(this.detailData.id, this.databaseData).then(data => {
if (data.success) {
this.$message.success(data.message)
this.drawerDB = false
this.getDbList()
}
})
}
})
},
deleteDB(data) {
this.$confirm(`您确定要删除【${data.name}】吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
removeRdsDbs({
rdsId: this.detailData.id,
dbId: data.id
}).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.getDbList()
}
})
})
}
},
watch: {
platformObject: {
handler(newVal, oldVal) {
this.searchConfigs = [
{ type: 'Input', label: '名称', value: 'name' },
{ type: 'Const', value: 'vendorId', initValue: newVal.vendorId }
]
if (this.detailFlag) {
this.handleClick()
}
},
deep: true
}
}
}
</script>
<style scoped>
.tooltip {
font-size: 12px;
color: #666;
}
</style>

View File

@ -0,0 +1,439 @@
export const keywords = [
// RDS MySQL
'ACTION',
'ADD',
'ADMIN',
'ALL',
'ALTER',
'ANALYZE',
'AND',
'AS',
'ASC',
'ASENSITIVE',
'BEFORE',
'BETWEEN',
'BIGINT',
'BINARY',
'BLOB',
'BOTH',
'BY',
'CALL',
'CASCADE',
'CASE',
'CHANGE',
'CHAR',
'CHARACTER',
'CHECK',
'COLLATE',
'COLUMN',
'CONDITION',
'CONNECTION',
'CONSTRAINT',
'CONTINUE',
'CONVERT',
'CREATE',
'CROSS',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURSOR',
'DATABASE',
'DATABASES',
'DAY_HOUR',
'DAY_MICROSECOND',
'DAY_MINUTE',
'DAY_SECOND',
'DEC',
'DECIMAL',
'DECLARE',
'DEFAULT',
'DELAYED',
'DELETE',
'DESC',
'DESCRIBE',
'DETERMINISTIC',
'DISTINCT',
'DISTINCTROW',
'DIV',
'DOUBLE',
'DROP',
'DUAL',
'EACH',
'EAGLEYE',
'ELSE',
'ELSEIF',
'ENCLOSED',
'ESCAPED',
'EXISTS',
'EXIT',
'EXPLAIN',
'FALSE',
'FETCH',
'FLOAT',
'FLOAT4',
'FLOAT8',
'FOR',
'FORCE',
'FOREIGN',
'FROM',
'FULLTEXT',
'GOTO',
'GRANT',
'GROUP',
'GUEST',
'HAVING',
'HIGH_PRIORITY',
'HOUR_MICROSECOND',
'HOUR_MINUTE',
'HOUR_SECOND',
'IF',
'IGNORE',
'IN',
'INDEX',
'INFILE',
'INFORMATION_SCHEMA',
'INNER',
'INOUT',
'INSENSITIVE',
'INSERT',
'INT',
'INT1',
'INT2',
'INT3',
'INT4',
'INT8',
'INTEGER',
'INTERVAL',
'INTO',
'IS',
'ITERATE',
'JOIN',
'KEY',
'KEYS',
'KILL',
'LABEL',
'LEADING',
'LEAVE',
'LEFT',
'LIKE',
'LIMIT',
'LINEAR',
'LINES',
'LOAD',
'LOCALTIME',
'LOCALTIMESTAMP',
'LOCK',
'LONG',
'LONGBLOB',
'LONGTEXT',
'LOOP',
'LOW_PRIORITY',
'MATCH',
'MEDIUMBLOB',
'MEDIUMINT',
'MEDIUMTEXT',
'MIDDLEINT',
'MINUTE_MICROSECOND',
'MINUTE_SECOND',
'MOD',
'MODIFIES',
'MYSQL',
'NATURAL',
'NO_WRITE_TO_BINLOG',
'NOT',
'NULL',
'NUMERIC',
'ON',
'OPTIMIZE',
'OPTION',
'OPTIONALLY',
'OR',
'ORDER',
'OUT',
'OUTER',
'OUTFILE',
'PERFORMANCE_SCHEMA',
'PRECISION',
'PRIMARY',
'PROCEDURE',
'PURGE',
'RAID0',
'RANGE',
'READ',
'READS',
'REAL',
'REFERENCES',
'REGEXP',
'RELEASE',
'RENAME',
'REPEAT',
'REPLACE',
'REPLICATOR',
'REQUIRE',
'RESTRICT',
'RETURN',
'REVOKE',
'RIGHT',
'RLIKE',
'ROOT',
'SCHEMA',
'SCHEMAS',
'SECOND_MICROSECOND',
'SELECT',
'SENSITIVE',
'SEPARATOR',
'SET',
'SHOW',
'SMALLINT',
'SPATIAL',
'SPECIFIC',
'SQL',
'SQL_BIG_RESULT',
'SQL_CALC_FOUND_ROWS',
'SQL_SMALL_RESULT',
'SQLEXCEPTION',
'SQLSTATE',
'SQLWARNING',
'SSL',
'STARTING',
'STRAIGHT_JOIN',
'TABLE',
'TERMINATED',
'TEST',
'THEN',
'TINYBLOB',
'TINYINT',
'TINYTEXT',
'TO',
'TRAILING',
'TRIGGER',
'TRUE',
'UNDO',
'UNION',
'UNIQUE',
'UNLOCK',
'UNSIGNED',
'UPDATE',
'USAGE',
'USE',
'USING',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'VALUES',
'VARBINARY',
'VARCHAR',
'VARCHARACTER',
'VARYING',
'WHEN',
'WHERE',
'WHILE',
'WITH',
'WRITE',
'X509',
'XOR',
'XTRABAK',
'YEAR_MONTH',
'ZEROFILL',
// RDS SQL Server
'ADD',
'ADMIN',
'ADMINISTRATOR',
'ALL',
'ALTER',
'AND',
'ANY',
'AS',
'ASC',
'AURORA',
'AUTHORIZATION',
'BACKUP',
'BEGIN',
'BETWEEN',
'BREAK',
'BROWSE',
'BULK',
'BULKADMIN',
'BY',
'CASCADE',
'CASE',
'CHECK',
'CHECKPOINT',
'CLOSE',
'CLUSTERED',
'COALESCE',
'COLLATE',
'COLUMN',
'COMMIT',
'COMPUTE',
'CONSTRAINT',
'CONTAINS',
'CONTAINSTABLE',
'CONTINUE',
'CONVERT',
'CREATE',
'CROSS',
'CURRENT',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURSOR',
'DATABASE',
'DBCC',
'DBCREATOR',
'DEALLOCATE',
'DECLARE',
'DEFAULT',
'DELETE',
'DENY',
'DESC',
'DISK',
'DISKADMIN',
'DISTINCT',
'DISTRIBUTED',
'DISTRIBUTION',
'DOUBLE',
'DROP',
'DUMMY',
'DUMP',
'EAGLEYE',
'ELSE',
'END',
'ERRLVL',
'ESCAPE',
'EXCEPT',
'EXEC',
'EXECUTE',
'EXISTS',
'EXIT',
'FETCH',
'FILE',
'FILLFACTOR',
'FOR',
'FOREIGN',
'FREETEXT',
'FREETEXTTABLE',
'FROM',
'FULL',
'FUNCTION',
'GALAXY',
'GOTO',
'GRANT',
'GROUP',
'GUEST',
'HAVING',
'HOLDLOCK',
'IDENTITY',
'IDENTITY_INSERT',
'IDENTITYCOL',
'IF',
'IN',
'INDEX',
'INNER',
'INSERT',
'INTERSECT',
'INTO',
'IS',
'JOIN',
'KEY',
'KILL',
'LEFT',
'LIKE',
'LINENO',
'LOAD',
'MASTER',
'MODEL',
'MSDB',
'MSSQLD',
'MSSQLSYSTEMRESOURCE',
'NATIONAL',
'NOCHECK',
'NONCLUSTERED',
'NOT',
'NULL',
'NULLIF',
'OF',
'OFF',
'OFFSETS',
'ON',
'OPEN',
'OPENDATASOURCE',
'OPENQUERY',
'OPENROWSET',
'OPENXML',
'OPTION',
'OR',
'ORDER',
'OUTER',
'OVER',
'PERCENT',
'PLAN',
'PRECISION',
'PRIMARY',
'PRINT',
'PROC',
'PROCEDURE',
'PROCESSADMIN',
'PUBLIC',
'PUBLIC',
'RAISERROR',
'READ',
'READTEXT',
'RECONFIGURE',
'REFERENCES',
'REPLICATION',
'RESTORE',
'RESTRICT',
'RETURN',
'REVOKE',
'RIGHT',
'ROLLBACK',
'ROOT',
'ROWCOUNT',
'ROWGUIDCOL',
'RULE',
'SA',
'SAVE',
'SCHEMA',
'SECURITYADMIN',
'SELECT',
'SERVERADMIN',
'SESSION_USER',
'SET',
'SETUPADMIN',
'SETUSER',
'SHUTDOWN',
'SOME',
'STATISTICS',
'SYSADMIN',
'SYSTEM_USER',
'TABLE',
'TEMPDB',
'TEXTSIZE',
'THEN',
'TO',
'TOP',
'TRAN',
'TRANSACTION',
'TRIGGER',
'TRUNCATE',
'TSEQUAL',
'UNION',
'UNIQUE',
'UPDATE',
'UPDATETEXT',
'USE',
'USER',
'VALUES',
'VARYING',
'VIEW',
'WAITFOR',
'WHEN',
'WHERE',
'WHILE',
'WITH',
'WRITETEXT'
]

View File

@ -17,5 +17,8 @@ export default {
ctstackCreateCluster: () => import('views/resource/ctstack/page/container/creatCluster/index.vue'),
ctstackInstanceAdd: () => import('views/resource/ctstack/page/container/instance/create/index.vue'),
ctstackAddBareMetal: () => import('views/resource/ctstack/page/bareMetal/addBareMetal.vue'),
ctstackBareMetal: () => import('views/resource/ctstack/page/bareMetal/index.vue')
ctstackBareMetal: () => import('views/resource/ctstack/page/bareMetal/index.vue'),
// 数据库
ctstackRds: () => import('views/resource/ctstack/page/database/rds/index.vue')
// ctstackPgSQL: () => import('views/resource/ctstack/page/database/pgsql/cluster/index.vue')
}

View File

@ -0,0 +1,24 @@
import { request } from '@cmp/cmp-element'
import { wrapperParams } from 'utils'
// 查询实例IP白名单列表
export function getSecurityIps(url) {
return function (params) {
return request.get(`/cmp/plugins/ctstack/v1/${url}/security/ips`, {
params
})
}
}
// 修改MongoDB实例IP白名单
export function modifySecurityIps(url, params) {
// 设置modifyMode传Cover 删除modifyMode传Delete
return request.put(`/cmp/plugins/ctstack/v1/${url}/${params.id}/security/ips`, wrapperParams(params))
}
export function getUserApps(id) {
return request.get(`/sms/v1/users/${id}/apps`)
}
export function getIps(params) {
return request.get('/cmp/plugins/ctstack/v1/vms/ips', { params })
}

View File

@ -0,0 +1,79 @@
import { request } from '@cmp/cmp-element'
import { wrapperParams, downloadFile } from 'utils'
const url = '/cmp/plugins/ctstack/v1/rds'
export function getRegion(params) {
return request.get(`${url}/mysql/regions`, { params })
}
export function getFlavor(params) {
return request.get(`${url}/mysql/flavors`, { params })
}
export function getParamGroup(params) {
return request.get(`${url}/mysql/param/groups`, { params })
}
export function createRds(params) {
return request.post(`${url}/mysql`, params)
}
export function getRdsDbs(id, params) {
return request.get(`${url}/mysql/${id}/dbs`, { params })
}
export function getRdsCos(id, params) {
return request.get(`${url}/mysql/${id}/nets`, { params })
}
export function getAliRds(params) {
return request.get(`${url}/mysql`, { params })
}
export function detailAliRds(id) {
return request.get(`${url}/mysql/${id}`)
}
export function patchAliRds(id) {
return request.patch(`${url}/mysql/${id}`)
}
export function removeAliRds(id) {
return request.delete(`${url}/mysql/${id}`)
}
export function getRdsUsers(id, params) {
return request.get(`${url}/mysql/${id}/users`, { params })
}
export function createRdsUsers(id, params) {
return request.post(`${url}/mysql/${id}/users`, params)
}
export function removeRdsUsers(params) {
return request.delete(`${url}/mysql/${params.rdsId}/users/${params.userId}`)
}
export function resetRdsUserPasd(params) {
return request.put(`${url}/mysql/${params.rdsId}/users/${params.userId}/resetpassword`, wrapperParams(params))
}
export function resetRdsUser(params) {
return request.put(`${url}/mysql/${params.rdsId}/users/${params.userId}/resetaccount`, wrapperParams(params))
}
export function authRdsUser(params) {
return request.put(`${url}/mysql/${params.rdsId}/users/${params.userId}/dbs`, wrapperParams(params))
}
export function createRdsDbs(id, params) {
return request.post(`${url}/mysql/${id}/dbs`, params)
}
export function removeRdsDbs(params) {
return request.delete(`${url}/mysql/${params.rdsId}/dbs/${params.dbId}`)
}
export function getFlavorSpec(params) {
return request.get(`${url}/mysql/flavors/spec`, { params })
}