749 lines
20 KiB
Vue
749 lines
20 KiB
Vue
<template xmlns:el-col="http://www.w3.org/1999/html">
|
|
<el-col class="tree-style">
|
|
<el-col>
|
|
<el-row v-show="showView === 'Datasource'" class="title-css">
|
|
<el-col class="title-text" :span="12">
|
|
{{ $t('commons.datasource') }}
|
|
</el-col>
|
|
<el-col class="title-operate" :span="12">
|
|
<el-tooltip
|
|
class="item"
|
|
effect="dark"
|
|
:content="$t('driver.mgm')"
|
|
placement="top"
|
|
>
|
|
<i
|
|
v-show="user.isAdmin"
|
|
class="el-icon-setting"
|
|
@click="driverMgm"
|
|
/>
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
class="item"
|
|
effect="dark"
|
|
:content="$t('datasource.create')"
|
|
placement="top"
|
|
>
|
|
<i class="el-icon-plus" @click="addFolder" />
|
|
</el-tooltip>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-input
|
|
v-model="key"
|
|
size="small"
|
|
:placeholder="$t('chart.search')"
|
|
prefix-icon="el-icon-search"
|
|
clearable
|
|
class="main-area-input"
|
|
/>
|
|
|
|
<el-col class="custom-tree-container de-tree">
|
|
<div class="block">
|
|
<div v-if="!tData.length && !treeLoading" class="no-tdata">
|
|
{{ showView === 'Driver' ? '暂无驱动' : '暂无数据源' }}
|
|
<span @click="() => addFolder()" class="no-tdata-new">{{
|
|
$t('deDataset.create')
|
|
}}</span>
|
|
</div>
|
|
<el-tree
|
|
v-else
|
|
ref="myDsTree"
|
|
:default-expanded-keys="expandedArray"
|
|
:data="tData"
|
|
node-key="id"
|
|
:expand-on-click-node="true"
|
|
:filter-node-method="filterNode"
|
|
@node-click="nodeClick"
|
|
>
|
|
<span slot-scope="{ data }" class="custom-tree-node-list">
|
|
<span style="display: flex; width: calc(100% - 20px)">
|
|
<span
|
|
v-if="
|
|
data.type !== 'folder' &&
|
|
data.status !== 'Error' &&
|
|
data.status !== 'Warning'
|
|
"
|
|
>
|
|
<svg-icon
|
|
:icon-class="showView === 'Driver' ? 'driver-de' : 'db-de'"
|
|
/>
|
|
</span>
|
|
<span v-if="data.status === 'Error'">
|
|
<svg-icon
|
|
icon-class="de-ds-error"
|
|
class="ds-icon-scene"
|
|
/>
|
|
</span>
|
|
<span v-if="data.status === 'Warning'">
|
|
<svg-icon
|
|
icon-class="exclamationmark2"
|
|
class="ds-icon-scene"
|
|
/>
|
|
</span>
|
|
<span v-if="data.type === 'folder'">
|
|
<svg-icon icon-class="scene" class="ds-icon-scene" />
|
|
</span>
|
|
<span
|
|
style="
|
|
margin-left: 6px;
|
|
width: 100%;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
"
|
|
>
|
|
<el-tooltip
|
|
v-if="['Error', 'Warning'].includes(data.status)"
|
|
effect="dark"
|
|
:content="
|
|
data.status === 'Warning'
|
|
? $t('datasource.warning')
|
|
: $t('datasource.in_valid')
|
|
"
|
|
placement="right"
|
|
>
|
|
<span>
|
|
{{ data.name }}
|
|
</span>
|
|
</el-tooltip>
|
|
<template v-else>
|
|
{{ data.name }}
|
|
</template>
|
|
</span>
|
|
</span>
|
|
<span class="child">
|
|
<el-tooltip
|
|
v-if="data.type === 'folder'"
|
|
class="item"
|
|
effect="dark"
|
|
:content="
|
|
$t(
|
|
showView === 'Driver'
|
|
? 'driver.add'
|
|
: 'datasource.add_data_source'
|
|
)
|
|
"
|
|
placement="top"
|
|
>
|
|
<i
|
|
class="el-icon-plus"
|
|
@click.stop
|
|
@click="addFolderWithType(data)"
|
|
/>
|
|
</el-tooltip>
|
|
|
|
<el-dropdown
|
|
v-else-if="hasDataPermission('manage', data.privileges)"
|
|
size="medium"
|
|
trigger="click"
|
|
@command="(type) => handleCommand(type, data)"
|
|
>
|
|
<i class="el-icon-more" @click.stop />
|
|
<el-dropdown-menu slot="dropdown" class="de-card-dropdown">
|
|
<slot>
|
|
<el-dropdown-item command="edit">
|
|
<i class="el-icon-edit" />
|
|
{{ $t('chart.edit') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item command="delete">
|
|
<i class="el-icon-delete" />
|
|
{{ $t('chart.delete') }}
|
|
</el-dropdown-item>
|
|
</slot>
|
|
</el-dropdown-menu>
|
|
</el-dropdown>
|
|
</span>
|
|
</span>
|
|
</el-tree>
|
|
</div>
|
|
</el-col>
|
|
|
|
<el-dialog
|
|
v-dialogDrag
|
|
:title="dialogTitle"
|
|
class="de-dialog-form"
|
|
:visible.sync="editDriver"
|
|
width="600px"
|
|
append-to-body
|
|
>
|
|
<el-form
|
|
ref="driverForm"
|
|
:model="driverForm"
|
|
class="de-form-item"
|
|
label-position="right"
|
|
label-width="100px"
|
|
size="small"
|
|
:rules="rule"
|
|
>
|
|
<el-form-item :label="$t('datasource.driver_name')" prop="name">
|
|
<el-input
|
|
v-model="driverForm.name"
|
|
:placeholder="$t('fu.search_bar.please_input')"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item :label="$t('datasource.drive_type')" prop="type">
|
|
<el-select
|
|
v-model="driverForm.type"
|
|
:placeholder="$t('fu.search_bar.please_select')"
|
|
class="de-select"
|
|
:disabled="!!driverForm.id"
|
|
filterable
|
|
>
|
|
<el-option
|
|
v-for="item in dsTypesForDriver"
|
|
:key="item.type"
|
|
:label="item.name"
|
|
:value="item.type"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item :label="$t('commons.description')">
|
|
<deTextarea v-model="driverForm.desc" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<div slot="footer" class="dialog-footer">
|
|
<deBtn secondary @click="close()">{{ $t('commons.cancel') }}</deBtn>
|
|
<deBtn
|
|
type="primary"
|
|
size="mini"
|
|
@click="saveDriver(driverForm)"
|
|
>{{ $t('commons.save') }}
|
|
</deBtn>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<el-dialog
|
|
v-dialogDrag
|
|
:title="$t('datasource.create')"
|
|
:visible.sync="dsTypeRelate"
|
|
width="1200px"
|
|
class="de-dialog-form"
|
|
append-to-body
|
|
>
|
|
<el-tabs v-model="tabActive">
|
|
<el-tab-pane :label="$t('datasource.all')" name="all" />
|
|
<el-tab-pane
|
|
:label="$t('datasource.relational_database')"
|
|
name="RDBMS"
|
|
/>
|
|
<el-tab-pane
|
|
:label="$t('datasource.non_relational_database')"
|
|
name="NORDBMS"
|
|
/>
|
|
<el-tab-pane :label="$t('datasource.other')" name="OTHER" />
|
|
</el-tabs>
|
|
<div class="db-container">
|
|
<div
|
|
v-for="(db, index) in databaseList"
|
|
:key="db.type"
|
|
class="db-card"
|
|
:class="[{ marLeft: index % 4 === 0 }]"
|
|
@click="addDb(db)"
|
|
>
|
|
<img
|
|
v-if="!db.isPlugin"
|
|
:src="require('../../../assets/datasource/' + db.type + '.jpg')"
|
|
alt=""
|
|
>
|
|
<img
|
|
v-if="db.isPlugin"
|
|
:src="`/api/pluginCommon/staticInfo/${db.type}/jpg`"
|
|
>
|
|
<p class="db-name">{{ db.name }}</p>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
</el-col>
|
|
</el-col>
|
|
</template>
|
|
<script>
|
|
import { mapGetters } from 'vuex'
|
|
import i18n from '@/lang'
|
|
import {
|
|
listDatasource,
|
|
listDatasourceByType,
|
|
delDs,
|
|
listDatasourceType,
|
|
listDrivers,
|
|
addDriver,
|
|
delDriver,
|
|
listDriverByType,
|
|
updateDriver
|
|
} from '@/api/system/datasource'
|
|
import deTextarea from '@/components/deCustomCm/deTextarea.vue'
|
|
import msgCfm from '@/components/msgCfm'
|
|
export default {
|
|
name: 'DsTree',
|
|
components: { deTextarea },
|
|
mixins: [msgCfm],
|
|
props: {
|
|
datasource: {
|
|
type: Object,
|
|
default: null
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
tabActive: 'all',
|
|
dsTypeRelate: false,
|
|
expandedArray: [],
|
|
tData: [],
|
|
treeLoading: false,
|
|
dsTypes: [],
|
|
dsTypesForDriver: [],
|
|
showSearchInput: false,
|
|
key: '',
|
|
showView: 'Datasource',
|
|
dialogTitle: '',
|
|
editDriver: false,
|
|
driverForm: {
|
|
name: '',
|
|
desc: '',
|
|
type: ''
|
|
},
|
|
params: {},
|
|
rule: {
|
|
name: [
|
|
{
|
|
required: true,
|
|
message: i18n.t('datasource.input_name'),
|
|
trigger: 'blur'
|
|
},
|
|
{
|
|
min: 2,
|
|
max: 50,
|
|
message: i18n.t('datasource.input_limit_2_25', [2, 25]),
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
desc: [
|
|
{
|
|
required: true,
|
|
message: i18n.t('datasource.input_name'),
|
|
trigger: 'blur'
|
|
},
|
|
{
|
|
min: 2,
|
|
max: 200,
|
|
message: i18n.t('datasource.input_limit_2_25', [2, 25]),
|
|
trigger: 'blur'
|
|
}
|
|
],
|
|
type: [
|
|
{
|
|
required: true,
|
|
message: i18n.t('datasource.please_choose_type'),
|
|
trigger: 'blur'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
key(val) {
|
|
this.$refs.myDsTree.filter(val)
|
|
}
|
|
},
|
|
computed: {
|
|
...mapGetters(['user']),
|
|
databaseList() {
|
|
if (this.tabActive === 'all') {
|
|
return this.dsTypes
|
|
}
|
|
return this.dsTypes.filter(
|
|
(ele) => ele.databaseClassification === this.tabActive
|
|
)
|
|
}
|
|
},
|
|
created() {
|
|
this.queryTreeDatas()
|
|
this.datasourceTypes()
|
|
},
|
|
methods: {
|
|
filterNode(value, data) {
|
|
if (!value) return true
|
|
return data.name.indexOf(value) !== -1
|
|
},
|
|
showSearchWidget() {
|
|
this.showSearchInput = true
|
|
},
|
|
closeSearchWidget() {
|
|
this.key = ''
|
|
this.showSearchInput = false
|
|
},
|
|
queryTreeDatas() {
|
|
this.treeLoading = true
|
|
if (this.showView === 'Datasource') {
|
|
listDatasource().then((res) => {
|
|
this.tData = this.buildTree(res.data)
|
|
}).finally(() => {
|
|
this.treeLoading = false
|
|
})
|
|
}
|
|
if (this.showView === 'Driver') {
|
|
listDrivers().then((res) => {
|
|
this.tData = this.buildTree(res.data)
|
|
}).finally(() => {
|
|
this.treeLoading = false
|
|
})
|
|
}
|
|
},
|
|
datasourceTypes() {
|
|
listDatasourceType().then((res) => {
|
|
this.dsTypes = res.data
|
|
this.dsTypes.forEach((item) => {
|
|
if (item.isJdbc) {
|
|
this.dsTypesForDriver.push(item)
|
|
}
|
|
})
|
|
})
|
|
},
|
|
refreshType(datasource) {
|
|
const method =
|
|
this.showView === 'Datasource' ? listDatasourceByType : listDriverByType
|
|
let typeData = []
|
|
method(datasource.type).then((res) => {
|
|
typeData = this.buildTree(res.data)
|
|
if (typeData.length === 0) {
|
|
const index = this.tData.findIndex((item) => {
|
|
if (item.id === datasource.type) {
|
|
return true
|
|
}
|
|
})
|
|
this.tData.splice(index, 1)
|
|
} else {
|
|
let find = false
|
|
for (let index = 0; index < this.tData.length; index++) {
|
|
if (typeData[0].id === this.tData[index].id) {
|
|
this.tData[index].children = typeData[0].children
|
|
for (let i = 0; i < this.tData[index].children.length; i++) {
|
|
if (this.tData[index].children[i].id === datasource.id) {
|
|
this.showInfo({ data: this.tData[index].children[i] })
|
|
}
|
|
}
|
|
find = true
|
|
}
|
|
}
|
|
if (!find) {
|
|
this.tData.push(typeData[0])
|
|
}
|
|
}
|
|
})
|
|
},
|
|
buildTree(array = []) {
|
|
const types = {}
|
|
const newArr = []
|
|
for (let index = 0; index < array.length; index++) {
|
|
const element = array[index]
|
|
if (this.msgNodeId) {
|
|
if (element.id === this.msgNodeId) {
|
|
element.msgNode = true
|
|
}
|
|
}
|
|
if (!(element.type in types)) {
|
|
types[element.type] = []
|
|
newArr.push({
|
|
id: element.type,
|
|
name: element.typeDesc,
|
|
type: 'folder',
|
|
children: types[element.type]
|
|
})
|
|
}
|
|
types[element.type].push(element)
|
|
}
|
|
return newArr
|
|
},
|
|
addFolder() {
|
|
if (this.showView === 'Driver') {
|
|
this.dialogTitle = this.$t('datasource.add_driver')
|
|
this.editDriver = true
|
|
this.switchMain('DriverForm', {}, this.tData, this.dsTypes)
|
|
} else {
|
|
this.dsTypeRelate = true
|
|
}
|
|
},
|
|
addDriver() {
|
|
this.dialogTitle = this.$t('datasource.add_driver')
|
|
this.editDriver = true
|
|
},
|
|
driverMgm() {
|
|
this.$emit('switch-main', {})
|
|
this.$emit('switch-mgm', 'driverMgm')
|
|
this.showView = 'Driver'
|
|
this.expandedArray = []
|
|
this.tData = []
|
|
this.queryTreeDatas()
|
|
},
|
|
dsMgm() {
|
|
this.$emit('switch-main', {})
|
|
this.showView = 'Datasource'
|
|
this.expandedArray = []
|
|
this.tData = []
|
|
this.queryTreeDatas()
|
|
},
|
|
addDb({ type }) {
|
|
this.$router.push({
|
|
path: '/ds-form',
|
|
query: { type }
|
|
})
|
|
},
|
|
addFolderWithType(data) {
|
|
if (this.showView === 'Driver') {
|
|
this.driverForm.type = data.id
|
|
this.dialogTitle = this.$t('datasource.add_driver')
|
|
this.editDriver = true
|
|
} else {
|
|
this.$router.push({
|
|
path: '/ds-form',
|
|
query: { type: data.id }
|
|
})
|
|
}
|
|
},
|
|
nodeClick(node, data) {
|
|
if (node.type === 'folder') return
|
|
this.showInfo(data)
|
|
},
|
|
clickFileMore(param) {
|
|
const { optType, data } = param
|
|
switch (optType) {
|
|
case 'edit':
|
|
this.edit(data)
|
|
break
|
|
case 'delete':
|
|
this._handleDelete(data)
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
},
|
|
showInfo(row) {
|
|
if (this.showView === 'Driver') {
|
|
const param = { ...row.data, ...{ showModel: 'show' }}
|
|
this.switchMain(
|
|
this.showView === 'Datasource' ? 'DsForm' : 'DriverForm',
|
|
param,
|
|
this.tData,
|
|
this.dsTypes
|
|
)
|
|
} else {
|
|
this.switchMain('dsTable', row.data)
|
|
}
|
|
},
|
|
handleCommand(type, data) {
|
|
switch (type) {
|
|
case 'edit':
|
|
this._handleEditer(data)
|
|
break
|
|
case 'delete':
|
|
this._handleDelete(data)
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
},
|
|
_handleEditer(row) {
|
|
if (this.showView === 'Datasource') {
|
|
const param = { ...row, ...{ showModel: 'show' }}
|
|
this.switchMain('DsForm', param, this.tData, this.dsTypes)
|
|
return
|
|
}
|
|
this.editDriver = true
|
|
this.dialogTitle = this.$t('datasource.edit_driver')
|
|
this.driverForm = row
|
|
},
|
|
_handleDelete(datasource) {
|
|
const params = {
|
|
title:
|
|
this.showView === 'Datasource'
|
|
? 'datasource.this_data_source'
|
|
: 'datasource.delete_this_driver',
|
|
cb: () => {
|
|
let method = delDriver
|
|
let parma = { type: datasource.type, id: datasource.id }
|
|
if (this.showView === 'Datasource') {
|
|
method = delDs
|
|
parma = datasource.id
|
|
}
|
|
method(parma).then((res) => {
|
|
if (res.success) {
|
|
this.openMessageSuccess('commons.delete_success')
|
|
this.switchMain('', {}, this.tData, this.dsTypes)
|
|
this.refreshType(datasource)
|
|
} else {
|
|
this.openMessageSuccess(res.message, 'error')
|
|
}
|
|
})
|
|
}
|
|
}
|
|
this.handlerConfirm(params)
|
|
},
|
|
switchMain(component, componentParam, tData, dsTypes) {
|
|
if (component === 'DsForm') {
|
|
const { id, type, showModel } = componentParam
|
|
this.$router.push({
|
|
path: '/ds-form',
|
|
query: {
|
|
id,
|
|
type,
|
|
showModel,
|
|
msgNodeId: this.msgNodeId
|
|
}
|
|
})
|
|
return
|
|
}
|
|
this.$emit('switch-main', {
|
|
component,
|
|
componentParam,
|
|
tData,
|
|
dsTypes
|
|
})
|
|
},
|
|
markInvalid(msgParam) {
|
|
const param = JSON.parse(msgParam)
|
|
const msgNodeId = param.id
|
|
this.msgNodeId = msgNodeId
|
|
this.$nextTick(() => {
|
|
this.tData.forEach((folder) => {
|
|
const nodes = folder.children
|
|
nodes.forEach((node) => {
|
|
if (node.id === msgNodeId) {
|
|
node.msgNode = true
|
|
}
|
|
})
|
|
})
|
|
})
|
|
},
|
|
close() {
|
|
this.$refs['driverForm'].resetFields()
|
|
this.editDriver = false
|
|
this.driverForm = {
|
|
name: '',
|
|
desc: '',
|
|
type: ''
|
|
}
|
|
},
|
|
saveDriver(driverForm) {
|
|
this.$refs['driverForm'].validate((valid) => {
|
|
if (valid) {
|
|
const req = driverForm.id ? updateDriver : addDriver
|
|
req(driverForm).then((res) => {
|
|
this.openMessageSuccess('dataset.save_success')
|
|
this.refreshType(driverForm)
|
|
this.close()
|
|
})
|
|
} else {
|
|
return false
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.custom-tree-container {
|
|
margin-top: 16px;
|
|
.no-tdata {
|
|
text-align: center;
|
|
margin-top: 80px;
|
|
font-family: PingFang SC;
|
|
font-size: 14px;
|
|
color: var(--deTextSecondary, #646a73);
|
|
font-weight: 400;
|
|
.no-tdata-new {
|
|
cursor: pointer;
|
|
color: var(--primary, #3370ff);
|
|
}
|
|
}
|
|
}
|
|
.custom-tree-node {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 14px;
|
|
padding-right: 8px;
|
|
width: 100%;
|
|
}
|
|
.custom-tree-node-list {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 14px;
|
|
padding: 0 8px;
|
|
width: calc(100% - 40px);
|
|
.child {
|
|
/*display: none;*/
|
|
visibility: hidden;
|
|
}
|
|
&:hover .child {
|
|
/*display: inline;*/
|
|
visibility: visible;
|
|
}
|
|
}
|
|
.tree-list ::v-deep .el-tree-node__expand-icon.is-leaf {
|
|
display: none;
|
|
}
|
|
.tree-style {
|
|
padding: 16px 24px;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
.title-text {
|
|
line-height: 26px;
|
|
color: #1f2329;
|
|
margin-bottom: 16px;
|
|
}
|
|
.title-operate {
|
|
text-align: right;
|
|
i {
|
|
margin-left: 22.91px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
<style lang="scss">
|
|
.db-container {
|
|
width: 100%;
|
|
max-height: 674px;
|
|
overflow-y: auto;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
margin-top: -3px;
|
|
position: relative;
|
|
z-index: 10;
|
|
.db-card {
|
|
height: 193px;
|
|
width: 270px;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
background: #ffffff;
|
|
border: 1px solid #dee0e3;
|
|
border-radius: 4px;
|
|
margin-bottom: 24px;
|
|
margin-left: 22px;
|
|
img {
|
|
width: 100%;
|
|
height: 154.58px;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
}
|
|
.db-name {
|
|
width: 100%;
|
|
margin: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 8px 12px;
|
|
border-top: 1px solid rgba(#1f2329, 0.15);
|
|
}
|
|
&:hover {
|
|
box-shadow: 0px 6px 24px rgba(31, 35, 41, 0.08);
|
|
}
|
|
}
|
|
|
|
.marLeft {
|
|
margin-left: 0;
|
|
}
|
|
}
|
|
</style>
|