初始化

This commit is contained in:
砂糖
2025-11-08 10:38:36 +08:00
commit 3beeec7296
1626 changed files with 198488 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
import DataSetManagement from './src/index.vue'
DataSetManagement.install = function (Vue) {
Vue.component(DataSetManagement.name, DataSetManagement)
}
export default DataSetManagement

View File

@@ -0,0 +1,193 @@
<template>
<el-dialog
width="500px"
:title="title"
class="bs-dialog-wrap dialogClass bs-el-dialog"
:visible.sync="dialogFormVisible"
:append-to-body="true"
:before-close="handleClose"
>
<div style="margin: 20px;">
<el-form
ref="ruleForm"
:model="dataForm"
:rules="rules"
label-position="left"
label-width="90px"
class="bs-el-form"
>
<el-form-item
label="分组名称"
prop="name"
>
<el-input
v-model.trim="dataForm.name"
class="bs-el-input"
clearable
/>
</el-form-item>
</el-form>
</div>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="cancel"
>
取消
</el-button>
<el-button
type="primary"
@click="submitForm('ruleForm')"
>
确定
</el-button>
</span>
</el-dialog>
</template>
<script>
import { categoryAdd, categoryUpdate, categoryNameRepeat } from 'data-room-ui/js/utils/datasetConfigService'
export default {
name: 'CategroyEditForm',
props: {
appCode: {
type: String,
default: ''
}
},
data () {
const nameRepeatCheck = (rule, value, callback) => {
let parentId = ''
if (this.nodeFlag) {
// 新增节点
if (this.radio === 0) {
// 新增同级
parentId = this.nodeData.parentId
} else {
// 新增子级
parentId = this.nodeData.id
}
}
categoryNameRepeat({
...this.dataForm,
parentId
}).then(res => {
if (res) {
callback(new Error('分组名称已存在'))
} else {
callback()
}
})
}
return {
type: 'dataset',
dataForm: {
id: '',
name: '',
parentId: ''
},
title: '',
dialogFormVisible: false,
radio: 0,
nodeFlag: false,
rules: {
name: [
{ required: true, message: '分组名称不能为空', trigger: 'blur' },
{ validator: nameRepeatCheck, trigger: 'blur' }
]
},
nodeData: {}
}
},
watch: {
dialogFormVisible (val) {
if (!val) {
this.dataForm = {
id: '',
name: '',
parentId: ''
}
}
}
},
methods: {
init (row, flag) {
this.nodeFlag = flag
this.nodeData = row
if (!flag) {
this.dataForm.name = row.name
this.dataForm.id = row.id
}
},
cancel () {
this.dialogFormVisible = false
this.$nextTick(() => {
this.$parent.addOrUpdateTreeVisible = false
})
},
handleClose () {
this.dialogFormVisible = false
this.$parent.addOrUpdateTreeVisible = false
},
submitForm (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let id = ''
let parentId = ''
if (this.nodeFlag) {
// 新增节点
if (this.radio === 0) {
// 新增同级
parentId = this.nodeData.parentId
} else {
// 新增子级
parentId = this.nodeData.id
}
} else {
// 修改节点
id = this.dataForm.id
parentId = this.nodeData.parentId
}
const params = {
id: this.dataForm.id,
name: this.dataForm.name,
parentId: parentId,
type: this.type,
moduleCode: this.appCode
}
if (id) {
categoryUpdate(params).then((r) => {
// params.id = r
this.$message.success('保存成功')
this.cancel()
try {
this.$emit('addOrUpdateNode', params, this.nodeFlag)
} catch (error) {
this.$parent.initLazyDatasetTypeTree()
}
})
} else {
categoryAdd(params).then((r) => {
params.id = r
this.$message.success('保存成功')
this.cancel()
try {
this.$emit('addOrUpdateNode', params, this.nodeFlag)
} catch (error) {
this.$parent.initLazyDatasetTypeTree()
}
})
}
} else {
return false
}
})
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
<template>
<el-dialog title="新增" :visible.sync="dialogVisible" width="800px" :append-to-body="true" :close-on-click-modal="false"
class="bs-dialog-wrap bs-el-dialog">
<div class="type-wrap">
<el-row :gutter="20">
<el-col v-for="dataset in datasetTypeList.filter(item => item.datasetType !== '')" :key="dataset.datasetType"
:span="8" :xs="24" :sm="12" :md="8" style="minWidth: 200px;">
<el-card class="bs-el-card" shadow="hover">
<div class="type-item" @click="openAddForm(dataset.datasetType, dataset.componentName)">
<div style='
height: 50%;width: 100%;
display: flex;
align-items: center;
justify-content: center;
'>
<svg-icon :name="dataset.name" :icon-class='dataset.name' style="width: 20%; height: 100%;" />
</div><span>
{{ dataset.name }}
</span>
<p>
<span class="description">
{{ dataset.description }}
</span>
</p>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-dialog>
</template>
<script>
export default {
props: {
datasetTypeList: {
type: Array,
default: () => ([])
}
},
data() {
return {
dialogVisible: false
}
},
created() { },
mounted() { },
methods: {
// 选择新增类型
openAddForm(type, componentName) {
this.dialogVisible = false
this.$emit('openAddForm', type, componentName)
}
}
}
</script>
<style lang="scss" scoped>
.type-item {
height: 104px;
text-align: center;
font-size: 16px;
font-weight: 400;
cursor: pointer;
position: relative;
// padding-top: 5px;
color: var(--bs-el-text);
p {
width: 100%;
margin-top: 10px;
font-size: 14px;
line-height: 16px;
color: #909399;
}
&:hover {
color: var(--bs-el-color-primary);
}
}
::v-deep .el-dialog__body {
min-height: 130px !important;
}
.el-col {
margin-bottom: 10px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
<template>
<div>
<el-dialog
title="参数配置"
:visible.sync="dialogVisible"
width="1000px"
append-to-body
:close-on-click-modal="false"
:before-close="handleClose"
class="bs-dialog-wrap bs-el-dialog"
>
<el-form
ref="form"
:model="form"
:rules="formRules"
>
<div class="bs-table-box">
<el-table
ref="singleTable"
:data="form.params"
:border="true"
align="center"
class="bs-el-table"
>
<el-empty slot="empty" />
<el-table-column
prop="name"
label="参数名称"
align="center"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.name"
class="bs-el-input"
placeholder="请输入名称"
clearable
readonly
@change="checkParamsName(scope.row)"
/>
</template>
</el-table-column>
<el-table-column
prop="require"
label="是否必填"
align="center"
width="200"
filterable
>
<template slot-scope="scope">
<el-radio-group
v-model="scope.row.require"
:disabled="isUpdate"
>
<el-radio :label="1">
</el-radio>
<el-radio :label="0">
</el-radio>
</el-radio-group>
</template>
</el-table-column>
<el-table-column
prop="value"
label="参数值"
align="center"
>
<template slot-scope="scope">
<el-date-picker
v-if="scope.row.type === 'Date'"
v-model="scope.row.value"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期时间"
/>
<el-form-item
v-else
style="margin-bottom: 0"
:prop="'params.' + scope.$index + '.value'"
:rules="scope.row.require ?formRules.value:null"
>
<el-input
v-model="scope.row.value"
class="bs-el-input"
clearable
placeholder="请输入值"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
align="center"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.remark"
clearable
class="bs-el-input"
placeholder="请输入备注"
rows="2"
:readonly="isUpdate"
maxlength="200"
/>
</template>
</el-table-column>
</el-table>
</div>
</el-form>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="cancel"
>
取消
</el-button>
<el-button
type="primary"
@click="confirm"
>
确定
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import cloneDeep from 'lodash/cloneDeep'
export default {
name: 'HttpParamsSettingDialog',
props: {
paramsList: {
type: Array,
default: () => []
},
newParamsList: {
type: Array,
default: () => []
}
},
data () {
return {
isUpdate: false,
dialogVisible: false,
form: {
params: []
},
formRules: {
value: [{ required: true, message: '参数值不能为空', trigger: 'blur' }]
}
}
},
methods: {
open (isUpdate = false) {
this.$emit('getPramsList')
if (isUpdate) {
this.form.params = this.newParamsList
} else {
this.form.params = cloneDeep(this.paramsList)
}
this.isUpdate = isUpdate
this.dialogVisible = true
},
close () {
this.$refs.form.clearValidate()// 清空校验信息
this.dialogVisible = false
},
handleClose () {
this.$refs.form.clearValidate() // 清空校验信息
this.dialogVisible = false
},
checkParamsName (value) {
const checkList = this.form.params.filter(item => item.fieldName === value.name)
if (checkList.length) {
this.$message.warning('参数名称不可以与字段名相同!')
value.name = ''
}
},
cancel () {
this.$refs.form.clearValidate() // 清空校验信息
this.dialogVisible = false
},
confirm () {
this.$refs.form.validate((valid) => {
if (valid) {
if (!this.isUpdate) {
this.$emit('saveParams', cloneDeep(this.form.params))
} else {
this.$emit('saveNewParams', cloneDeep(this.form.params))
this.$emit('getData')
}
this.dialogVisible = false
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import '../../assets/style/bsTheme.scss';
</style>

View File

@@ -0,0 +1,81 @@
<template>
<div>
<!-- 字段填充方式 -->
<el-dialog
title="提示"
:visible.sync="dialogVisible"
width="420px"
append-to-body
:close-on-click-modal="false"
class="bs-dialog-wrap bs-el-dialog"
>
<p style="color:var(--bs-el-text);line-height: 24px;padding-left: 10px;display: flex;">
<i
class="el-icon-warning"
style="color: #E6A23C;font-size: 24px;margin-right: 5px;"
/>
存在字段描述信息为空请确认
</p>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="fieldDescFill"
>
使用字段名填充
</el-button>
<el-button
class="bs-el-button-default"
@click="fieldDescEdit"
>
进入编辑
</el-button>
<el-button
type="primary"
@click="toSave"
>
继续保存
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'FieldFillDialog',
data () {
return {
dialogVisible: false
}
},
methods: {
open () {
this.dialogVisible = true
},
close () {
this.dialogVisible = false
},
fieldDescFill () {
this.$emit('fieldDescFill')
},
fieldDescEdit () {
this.$emit('fieldDescEdit')
},
toSave () {
this.$emit('toSave')
}
}
}
</script>
<style lang="scss" scoped>
@import '../../../assets/style/bsTheme.scss';
::v-deep .el-dialog__body {
height: fit-content;
min-height: unset;
}
</style>

View File

@@ -0,0 +1,114 @@
<template>
<div>
<el-dialog
title="输出字段配置"
:visible.sync="dialogVisible"
width="1000px"
append-to-body
:close-on-click-modal="false"
:before-close="handleClose"
class="bs-dialog-wrap bs-el-dialog"
>
<div class="bs-table-box">
<el-table
:data="insideFieldList"
:border="true"
align="center"
class="bs-el-table"
>
<el-empty slot="empty" />
<el-table-column
align="left"
show-overflow-tooltip
prop="fieldName"
label="字段值"
/>
<el-table-column
align="center"
prop="fieldDesc"
label="字段描述"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.fieldDesc"
size="small"
class="labeldsc bs-el-input"
/>
</template>
</el-table-column>
<!-- 添加一个插槽供其他人可扩展表格列并把表格列的数据返回出去 -->
<slot name="output-field-table-column" />
</el-table>
</div>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="cancelField"
>
取消
</el-button>
<el-button
type="primary"
@click="setField"
>
确定
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import cloneDeep from 'lodash/cloneDeep'
export default {
name: 'OutputFieldDialog',
props: {
outputFieldList: {
type: Array,
default: () => []
}
},
data () {
return {
dialogVisible: false,
structurePreviewListCopy: [],
// 内部的输出字段列表 用于编辑
insideFieldList: []
}
},
methods: {
open () {
this.insideFieldList = cloneDeep(this.outputFieldList)
this.dialogVisible = true
},
close () {
this.dialogVisible = false
},
handleClose () {
this.dialogVisible = false
},
cancelField () {
this.dialogVisible = false
},
setField () {
if (this.insideFieldList.length) {
this.fieldDesc = {}
this.insideFieldList.forEach(key => {
this.fieldDesc[key.fieldName] = key.fieldDesc
})
this.$emit('setFieldList', this.insideFieldList)
} else {
this.fieldDesc = null
}
this.dialogVisible = false
}
}
}
</script>
<style lang="scss" scoped>
@import '../../../assets/style/bsTheme.scss';
</style>

View File

@@ -0,0 +1,228 @@
<template>
<div>
<el-dialog
title="参数配置"
:visible.sync="dialogVisible"
width="1000px"
append-to-body
:close-on-click-modal="false"
:before-close="handleClose"
class="bs-dialog-wrap bs-el-dialog"
>
<div class="bs-table-box">
<el-table
ref="singleTable"
:data="innerParamsList"
:border="true"
align="center"
class="bs-el-table"
>
<el-empty slot="empty" />
<el-table-column
prop="name"
label="参数名称"
align="center"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.name"
class="bs-el-input"
placeholder="请输入名称"
clearable
@change="checkParamsName(scope.row)"
/>
</template>
</el-table-column>
<!-- <el-table-column
prop="type"
label="参数类型"
align="center"
width="200"
filterable
>
<template slot-scope="scope">
<el-select
v-model="scope.row.type"
popper-class="bs-el-select"
class="bs-el-select"
placeholder="请选择"
>
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.value"
:value="item.value"
/>
</el-select>
</template>
</el-table-column> -->
<el-table-column
prop="require"
label="是否必填"
align="center"
width="200"
filterable
>
<template slot-scope="scope">
<el-radio-group v-model="scope.row.require">
<el-radio :label="1">
</el-radio>
<el-radio :label="0">
</el-radio>
</el-radio-group>
</template>
</el-table-column>
<el-table-column
prop="value"
label="参数值"
align="center"
>
<template slot-scope="scope">
<el-date-picker
v-if="scope.row.type === 'Date'"
v-model="scope.row.value"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期时间"
/>
<el-input
v-else
v-model="scope.row.value"
class="bs-el-input"
clearable
placeholder="请输入值"
/>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
align="center"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.remark"
clearable
class="bs-el-input"
placeholder="请输入备注"
rows="2"
maxlength="200"
/>
</template>
</el-table-column>
<el-table-column
label="操作"
width="105"
align="center"
>
<template slot="header">
<el-button
icon="el-icon-plus"
type="text"
class="no-border"
@click="addParam"
>
添加
</el-button>
</template>
<template slot-scope="scope">
<el-button
type="text"
style="color: #e47470;"
class="no-border"
@click="delRow(scope.$index)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="cancel"
>
取消
</el-button>
<el-button
type="primary"
@click="confirm"
>
确定
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import cloneDeep from 'lodash/cloneDeep'
export default {
name: 'ParamsSettingDialog',
props: {
paramsList: {
type: Array,
default: () => []
}
},
computed: {
innerParamsList () {
return cloneDeep(this.paramsList)
}
},
data () {
return {
dialogVisible: false
}
},
methods: {
open () {
this.dialogVisible = true
},
close () {
this.dialogVisible = false
},
handleClose () {
this.dialogVisible = false
},
addParam () {
this.innerParamsList.push({
name: '',
type: '',
value: '',
status: 1,
require: 0,
remark: ''
})
},
delRow (index) {
this.innerParamsList.splice(index, 1)
},
checkParamsName (value) {
const checkList = this.innerParamsList.filter(item => item.fieldName === value.name)
if (checkList.length) {
this.$message.warning('参数名称不可以与字段名相同!')
value.name = ''
}
},
cancel () {
this.dialogVisible = false
},
confirm () {
this.$emit('saveParams', this.innerParamsList)
this.dialogVisible = false
}
}
}
</script>
<style lang="scss" scoped>
@import '../../../assets/style/bsTheme.scss';
</style>

View File

@@ -0,0 +1,980 @@
<template>
<div
class="inner-container"
:element-loading-text="saveText"
>
<el-scrollbar class="data-set-scrollbar">
<div class="header">
<el-page-header class="bs-el-page-header">
<template slot="content">
<div class="page-header">
<div class="page-header-left">
{{ !isEdit ? 'JS数据集详情' : dataForm.id ? '编辑JS数据集' : '新增JS数据集' }}
</div>
<div class="page-header-right">
<el-button
class="bs-el-button-default"
@click="openNewWindow('https://www.yuque.com/chuinixiongkou/bigscreen/kv26b7ytvvbq7twi')"
>
帮助
</el-button>
<el-button
v-if="isEdit"
type="primary"
@click="save('form')"
>
保存
</el-button>
<el-button
class="bs-el-button-default"
@click="goBack"
>
返回
</el-button>
</div>
</div>
</template>
</el-page-header>
</div>
<el-row style="margin: 16px 16px 0;">
<el-col :span="isEdit ? 16 : 24">
<el-form
ref="form"
:model="dataForm"
:rules="rules"
label-width="120px"
style="padding: 16px 16px 0;"
class="bs-el-form"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="数据集名称"
prop="name"
>
<el-input
v-model="dataForm.name"
class="bs-el-input"
clearable
:disabled="!isEdit"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="分组"
prop="typeId"
>
<el-select
ref="selectParentName"
v-model="dataForm.typeId"
class="bs-el-select"
popper-class="bs-el-select"
placeholder="请选择分组"
clearable
:disabled="!isEdit"
filterable
:filter-method="selectorFilter"
@clear="clearType"
@visible-change="setCurrentNode"
>
<el-option
style="height: auto;padding: 0;"
:label="typeName"
:value="dataForm.typeId"
>
<div class="tree-box">
<el-tree
ref="categorySelectTree"
:data="categoryData"
node-key="id"
:indent="0"
:props="{ label: 'name', children: 'children' }"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
class="bs-el-tree"
:filter-node-method="treeFilter"
@node-click="selectParentCategory"
>
<span
slot-scope="{ data }"
class="custom-tree-node"
>
<span>
<i
:class="data.children && data.children.length ? 'el-icon el-icon-folder' : 'el-icon el-icon-document'"
/>
{{ data.name }}
</span>
</span>
</el-tree>
</div>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="备注"
prop="remark"
>
<el-input
v-model="dataForm.remark"
class="bs-el-input"
:disabled="!isEdit"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="标签"
prop="labelIds"
>
<LabelSelect
:dataset-id="datasetId"
:id-list="dataForm.labelIds"
@commit="(ids) =>{dataForm.labelIds = ids}"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div
v-if="isEdit"
class="sql-config"
>
<div>
<codemirror
ref="targetInSql"
v-model="dataForm.config.script"
:options="codemirrorOption"
style="margin-top: 2px"
/>
</div>
<div class="bs-codemirror-bottom-text">
<strong>请使用 return 将运行结果返回出来结果数据需要为数组格式</strong>
<br>
<strong>动态参数使用 使用${}将参数包裹例如let name = <span style="color: red;">${name}</span>
</strong>
</div>
<div style="text-align: center; padding: 16px 0;">
<el-button
type="primary"
@click="scriptExecute()"
>
解析并运行
</el-button>
</div>
</div>
</el-col>
<el-col
v-if="isEdit"
:span="8"
>
<div class="right-setting">
<div class="paramConfig">
<div class="title-style bs-title-style">
动态参数
<el-button
type="text"
style="float: right;border: none;margin-top: -4px;"
@click="$refs.paramsSettingDialog.open()"
>
配置
</el-button>
</div>
<div class="field-wrap bs-field-wrap bs-scrollbar">
<div
v-for="param in dataForm.config.paramsList"
:key="param.name"
class="field-item"
@click="$refs.paramsSettingDialog.open()"
>
<span>{{ param.name }}</span>&nbsp;<span
v-show="param.remark"
style="color: #909399;"
>
({{ param.remark }})
</span>
<el-button
class="edit_field"
type="text"
style="float: right;border: none;margin-top: 2px;"
@click="$refs.paramsSettingDialog.open()"
>
配置
</el-button>
</div>
</div>
</div>
<div class="structure">
<div class="title-style bs-title-style">
输出字段
<el-button
type="text"
style="float: right;border: none;margin-top: -4px;"
@click="$refs.outputFieldDialog.open()"
>
配置
</el-button>
</div>
<div class="field-wrap bs-field-wrap bs-scrollbar">
<div
v-for="(field, key) in outputFieldList"
:key="key"
class="field-item"
@click="$refs.outputFieldDialog.open()"
>
<span>{{ field.fieldName }}</span>&nbsp;
<span
v-show="field.fieldDesc"
style="color: #909399;"
>
({{ field.fieldDesc }})</span>
<el-button
class="edit_field"
type="text"
style="float: right;border: none;margin-top: 2px;"
@click="$refs.outputFieldDialog.open()"
>
配置
</el-button>
</div>
</div>
</div>
</div>
</el-col>
</el-row>
<div
v-if="isEdit"
class="dataPreView"
style="margin-top: 12px;"
>
<div class="result-view">
数据预览
</div>
<div
v-loading="tableLoading"
class="bs-table-box is-Edit bs-scrollbar"
>
<el-table
align="center"
:data="dataPreviewList"
max-height="400"
:border="true"
class="bs-el-table bs-scrollbar"
>
<el-table-column
v-for="(value, key) in dataPreviewList[0]"
:key="key"
:label="key"
align="center"
show-overflow-tooltip
:render-header="renderHeader"
>
<template slot-scope="scope">
<span>{{ scope.row[key] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div
v-if="!isEdit"
class="dataPreView"
>
<el-tabs v-model="activeName">
<el-tab-pane
v-loading="tableLoading"
label="数据预览"
name="data"
>
<div class="bs-table-box">
<el-table
align="center"
:data="dataPreviewList"
max-height="400"
class="bs-el-table"
>
<el-table-column
v-for="(value, key) in dataPreviewList[0]"
:key="key"
:label="key"
align="center"
show-overflow-tooltip
:render-header="renderHeader"
>
<template slot-scope="scope">
<span>{{ scope.row[key] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane
v-loading="tableLoading"
label="数据集结构"
name="structure"
>
<div class="bs-table-box">
<el-table
max-height="400"
:data="outputFieldList"
:border="true"
align="center"
>
<el-table-column
align="center"
show-overflow-tooltip
prop="fieldName"
label="字段值"
/>
<el-table-column
align="center"
prop="fieldDesc"
label="字段描述"
>
<template slot-scope="scope">
<el-input
v-if="isEdit"
v-model="scope.row.fieldDesc"
size="small"
class="labeldsc bs-el-input"
/>
<span v-else>{{ scope.row.fieldDesc }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</div>
<ParamsSettingDialog
ref="paramsSettingDialog"
:params-list="dataForm.config.paramsList"
@saveParams="saveParams"
/>
<OutputFieldDialog
ref="outputFieldDialog"
:output-field-list="outputFieldList"
@setFieldList="(list) => { outputFieldList = list }"
>
<template #output-field-table-column>
<slot name="output-field-table-column" />
</template>
</OutputFieldDialog>
</el-scrollbar>
<FieldFillDialog
ref="fieldFillDialog"
@fieldDescFill="fieldDescFill"
@fieldDescEdit="fieldDescEdit"
@toSave="toSave"
/>
</div>
</template>
<script>
import LabelSelect from 'data-room-ui/DataSetLabelManagement/src/LabelSelect.vue'
import ParamsSettingDialog from './JsComponents/ParamsSettingDialog.vue'
import OutputFieldDialog from './JsComponents/OutputFieldDialog.vue'
import FieldFillDialog from './JsComponents/FieldFillDialog.vue'
import { nameCheckRepeat, datasetAdd, datasetUpdate, getDataset, getCategoryTree } from 'data-room-ui/js/utils/datasetConfigService'
import { codemirror } from 'vue-codemirror'
import 'codemirror/mode/javascript/javascript'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/nord.css'
export default {
name: 'JsEditForm',
components: {
codemirror,
FieldFillDialog,
ParamsSettingDialog,
OutputFieldDialog,
LabelSelect
},
props: {
config: {
type: Object,
default: () => { }
},
isEdit: {
type: Boolean,
default: false
},
datasetId: {
type: String,
default: null
},
typeId: {
type: String,
default: ''
},
appCode: {
type: String,
default: ''
}
},
data () {
const validateName = (rule, value, callback) => {
nameCheckRepeat({
id: this.datasetId,
name: value,
moduleCode: this.appCode
}).then((r) => {
if (r) {
callback(new Error('数据集名称已存在'))
} else {
callback()
}
})
}
return {
autoFill: true,
dataForm: {
id: '',
name: '',
typeId: '',
remark: '',
labelIds: [],
config: {
script: '',
paramsList: []
}
},
rules: {
name: [
{ required: true, message: '请输入数据集名称', trigger: 'blur' },
{ validator: validateName, trigger: 'blur' }
],
typeId: [
{ required: true, message: '请选择分组', trigger: 'blur' }
]
},
codemirrorOption: {
mode: 'text/javascript',
lineNumbers: true,
lineWrapping: true,
theme: 'nord',
extraKey: { Ctrl: 'autocomplete' },
hintOptions: {
completeSingle: true
}
},
activeName: 'data',
dataPreviewList: [],
outputFieldList: [],
structurePreviewListCopy: [],
typeName: '',
categoryData: [],
// fieldDescVisible: false,
fieldsetVisible: false,
paramsVisible: false,
tableLoading: false,
saveloading: false,
saveText: '',
// paramsListCopy: [],
isSet: false, // 参数是否配置状态
passTest: false,
fieldDesc: null // 字段描述
}
},
watch: {
'dataForm.config.script' (val) {
if (!val) {
this.passTest = false
}
}
},
mounted () {
this.init()
},
methods: {
async init () {
this.categoryData = await getCategoryTree({ tableName: 'dataset', moduleCode: this.appCode })
if (this.typeId) {
this.dataForm.typeId = this.typeId
this.$nextTick(() => {
try {
this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
} catch (error) {
console.error(error)
}
})
}
if (this.datasetId) {
getDataset(this.datasetId).then(res => {
const { id, name, typeId, remark, datasetType, moduleCode, editable, sourceId, config } = res
const { script, paramsList, fieldDesc, fieldList } = config
this.dataForm = { id, name, typeId, remark, datasetType, moduleCode, editable, sourceId, config: { script, paramsList }, labelIds: this.dataForm.labelIds }
this.fieldDesc = fieldDesc
this.outputFieldList = fieldList
this.scriptExecute(true)
})
}
},
// 保存数据集
save (formName, nochecktosave = false) {
if (this.passTest === false) {
this.$message.error('请确保脚本不为空且执行通过')
return
}
if (!this.outputFieldList.length) {
this.$message.warning('该执行脚本未生成输出字段,请重新检查')
return
}
if (!nochecktosave) {
const temp = this.outputFieldList.some(item => {
return item.fieldDesc === '' || !item.hasOwnProperty('fieldDesc')
}) // true-存在为空
if (temp) {
this.$refs.fieldFillDialog.open()
// this.fieldDescVisible = true
return
}
}
this.$refs[formName].validate((valid) => {
if (valid) {
this.saveloading = true
this.saveText = '正在保存...'
const { datasetId, dataForm, appCode, fieldDesc, outputFieldList } = this
const form = {
id: datasetId,
name: dataForm.name,
typeId: dataForm.typeId,
remark: dataForm.remark,
datasetType: 'js',
moduleCode: appCode,
editable: appCode ? 1 : 0,
labelIds: dataForm.labelIds,
config: {
className: 'com.gccloud.dataset.entity.config.JsDataSetConfig',
script: dataForm.config.script,
fieldDesc,
paramsList: dataForm.config.paramsList,
fieldList: outputFieldList
}
}
const datasetSave = this.dataForm.id === '' ? datasetAdd : datasetUpdate
datasetSave(form).then(() => {
this.$message.success('操作成功')
this.$parent.init(false)
this.$parent.setType = null
this.saveloading = false
this.saveText = ''
this.goBack()
}).catch(() => {
this.saveloading = false
this.saveText = ''
})
}
})
},
saveParams (val) {
this.dataForm.config.paramsList = val
},
// 取消操作
// cancelField () {
// this.structurePreviewListCopy = cloneDeep(this.outputFieldList)
// this.fieldsetVisible = false
// },
// 设置输出字段
setField () {
// this.outputFieldList = cloneDeep(this.structurePreviewListCopy)
// if (this.outputFieldList.length) {
// this.fieldDesc = {}
// this.outputFieldList.forEach(key => {
// this.fieldDesc[key.fieldName] = key.fieldDesc
// })
// } else {
// this.fieldDesc = null
// }
// this.fieldsetVisible = false
},
// 字段值填充
fieldDescFill () {
this.fieldDesc = {}
this.outputFieldList.forEach(field => {
if (field.fieldDesc === '' || !field.hasOwnProperty('fieldDesc')) {
field.fieldDesc = field.fieldName
this.fieldDesc[field.fieldName] = field.fieldName
} else {
this.fieldDesc[field.fieldName] = field.fieldDesc
}
})
this.save('form')
this.$refs.fieldFillDialog.close()
// this.fieldDescVisible = false
},
// 进入编辑
fieldDescEdit () {
this.$refs.fieldFillDialog.close()
// this.fieldDescVisible = false
this.fieldsetVisible = true
},
// 继续保存
toSave () {
this.fieldDesc = {}
this.outputFieldList.forEach(field => {
this.fieldDesc[field.fieldName] = field.fieldDesc
})
this.save('form', true)
this.$refs.fieldFillDialog.close()
// this.fieldDescVisible = false
},
// 字段描述构建及同步
buildFieldDesc () {
const fieldDesc = {}
this.outputFieldList.forEach(field => {
if (this.fieldDesc.hasOwnProperty(field.fieldName)) {
field.fieldDesc = this.fieldDesc[field.fieldName]
}
fieldDesc[field.fieldName] = field.fieldDesc
})
this.fieldDesc = fieldDesc
},
// 脚本执行
scriptExecute (isInit = false) {
if (this.dataForm.config.script) {
const javascript = this.dataForm.config.script
let scriptMethod = null
try {
const scriptAfterReplacement = javascript.replace(/\${(.*?)}/g, (match, p) => {
const value = this.dataForm.config.paramsList.find(param => param.name === p).value
if (value === null || value === undefined || value === '') {
return "''"
} else if (!isNaN(value)) {
return value
} else {
return `'${value}'`
}
})
// eslint-disable-next-line no-new-func
scriptMethod = new Function(scriptAfterReplacement)
} catch (error) {
this.passTest = false
const javascriptParams = javascript.match(/\${(.*?)}/g)
// 取出${}中的参数名
let paramList = []
if (javascriptParams) {
javascriptParams.forEach(item => {
const name = item.replace(/\${(.*?)}/g, '$1')
const param = this.dataForm.config.paramsList.find(param => param.name === name)
if (!param) {
// 添加确认框,是否填充参数
paramList.push(name)
}
})
if (this.autoFill && paramList.length > 0) {
this.addParams(paramList)
paramList = []
}
} else {
console.info(error)
this.$message.error(`脚本执行错误,请检查脚本,具体错误:${error}`)
}
return
}
// 调用方法生成随机数据
const returnResult = scriptMethod()
// 检查数据是否为数组
if (!Array.isArray(returnResult)) {
this.passTest = false
this.$message.error('脚本执行结果不是数组,请检查脚本')
return
}
const keys = []
returnResult.forEach(item => {
Object.keys(item).forEach(key => {
if (!keys.includes(key)) {
keys.push(key)
}
})
})
if (this.outputFieldList.length === 0) {
this.outputFieldList = keys.map(item => {
return {
fieldName: item,
fieldDesc: ''
}
})
}
// 如果脚本有变化生成的keys和outputFieldList的长度不一致就重新生成outputFieldList仅删除或添加变化的那个字段其余的不变化
if (this.outputFieldList.length !== keys.length) {
const newOutputFieldList = []
keys.forEach(key => {
const field = this.outputFieldList.find(item => item.fieldName === key)
if (field) {
newOutputFieldList.push(field)
} else {
newOutputFieldList.push({
fieldName: key,
fieldDesc: ''
})
}
})
this.outputFieldList = newOutputFieldList
}
// 如果脚本有变化生成的keys和outputFieldList的长度一致仅字段名变化了就重新生成outputFieldList
if (this.outputFieldList.length === keys.length) {
const newOutputFieldList = []
keys.forEach(key => {
const field = this.outputFieldList.find(item => item.fieldName === key)
if (field) {
newOutputFieldList.push(field)
} else {
newOutputFieldList.push({
fieldName: key,
fieldDesc: ''
})
}
})
this.outputFieldList = newOutputFieldList
}
// 如果有字段描述,就同步
if (this.outputFieldList.length && this.fieldDesc && !isInit) {
this.buildFieldDesc()
}
// 如果有数据,就通过测试
if (this.outputFieldList.length > 0) {
// this.structurePreviewListCopy = cloneDeep(this.outputFieldList)
this.dataPreviewList = returnResult
this.passTest = true
if (!isInit) {
this.$message.success('脚本执行通过')
}
} else {
this.passTest = false
}
} else {
this.passTest = false
this.$message.error('请填写脚本')
}
},
addParams (paramList) {
paramList.forEach(name => {
this.dataForm.config.paramsList.push({
name,
type: '',
value: '',
status: 1,
require: 0,
remark: ''
})
})
this.$nextTick(() => {
this.$refs.paramsSettingDialog.open()
})
},
// 执行事件
// toExecute () {
// if (this.dataForm.config.paramsList.length) {
// this.isSet = false
// this.paramsVisible = true
// } else {
// 无参数,直接执行脚本
// this.scriptExecute()
// }
// },
// 清空分类
clearType () {
this.typeName = ''
this.dataForm.typeId = ''
},
// 分类展开高亮
setCurrentNode ($event) {
if ($event) {
const key = this.dataForm.typeId || null
this.$refs.categorySelectTree.setCurrentKey(key)
}
},
// openParamsSetting () {
// this.$refs.paramsSettingDialog.open()
// },
// 分类选择
selectParentCategory (value) {
this.dataForm.typeId = value.id
this.typeName = value.name
this.$refs.selectParentName.blur()
},
goBack () {
this.$emit('back')
},
renderHeader (h, { column, index }) {
const labelLong = column.label.length // 表头label长度
const size = 14 // 根据需要定义标尺,直接使用字体大小确定就行,也可以根据需要定义
column.minWidth = labelLong * size < 120 ? 120 : labelLong * size // 根据label长度计算该表头最终宽度
return h('span', { class: 'cell-content', style: { width: '100%' } }, [column.label])
},
openNewWindow (url) {
window.open(url, '_blank')
},
selectorFilter (value) {
this.$refs.categorySelectTree.filter(value)
},
treeFilter (value, data) {
if (!value) return true
return data.name.indexOf(value) !== -1
}
}
}
</script>
<style lang="scss" scoped>
@import '../../assets/style/bsTheme.scss';
.data-set-scrollbar {
height: 100%;
overflow-y: auto;
overflow-x: none;
.el-scrollbar__view {
height: 100%;
}
}
::v-deep .el-input__inner {
width: 100% !important;
}
.page-header {
display: flex;
position: relative;
.page-header-right {
position: absolute;
right: 16px;
}
}
.sql-config {
padding: 0 16px;
}
.operation {
::v-deep .el-select {
width: 200px !important;
margin-right: 16px;
}
display: flex;
}
::v-deep .CodeMirror {
height: 180px !important;
font-family: Helvetica, Tahoma;
}
.no-border {
border: 0;
}
::v-deep .fieldDescCheck {
.el-dialog__body {
height: fit-content !important;
min-height: unset !important;
}
}
.title-style {
padding: 8px 12px;
background-color: #f6f7fb;
border-left: 5px solid var(--bs-el-color-primary);
margin: 16px 16px 0 0;
}
.field-wrap {
// max-height: 110px;
overflow: auto;
margin-right: 16px;
cursor: pointer;
.field-item {
line-height: 32px;
padding: 0 12px 0 16px;
.edit_field {
display: none;
}
&:hover {
background-color: #f2f7fe;
.edit_field {
display: block;
}
}
}
}
.right-setting {
height: 358px;
overflow: hidden;
display: flex;
flex-direction: column;
.paramConfig {
max-height: 179px;
.field-wrap {
max-height: 127px;
}
}
.structure {
flex: 1;
overflow: hidden;
.field-wrap {
height: calc(100% - 40px);
}
}
}
.result-view {
font-size: 14px;
font-weight: 600;
color: var(--bs-el-text);
position: relative;
padding: 16px 0;
padding-left: 12px;
border-bottom: 1px solid var(--bs-background-1);
&::before {
content: "";
height: 14px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border-left: 4px solid var(--bs-el-color-primary);
}
}
::v-deep .bs-table-box.is-Edit .el-table {
max-height: unset !important;
.el-table__body-wrapper {
max-height: unset !important;
}
}
.bs-table-box {
padding: 0;
height: 100% !important;
margin-bottom: 0 !important;
}
.tree-box {
padding: 0;
}
.bs-el-select{
width: 100% !important;
}
::v-deep .el-input__inner{
width: 100% !important;
}
</style>

View File

@@ -0,0 +1,853 @@
<template>
<div
v-loading="saveLoading"
class="inner-container "
:element-loading-text="saveText"
>
<el-scrollbar class="data-set-scrollbar">
<div class="header">
<el-page-header class="bs-el-page-header">
<template slot="content">
<div class="page-header">
<div class="page-header-left">
{{ !isEdit ? 'JSON数据集详情' : dataForm.id ? '编辑JSON数据集' : '新增JSON数据集' }}
</div>
<div class="page-header-right">
<el-button
class="bs-el-button-default"
@click="openNewWindow('https://www.yuque.com/chuinixiongkou/bigscreen/json_dataset')"
>
帮助
</el-button>
<el-button
v-if="isEdit"
type="primary"
@click="save('form')"
>
保存
</el-button>
<el-button
class="bs-el-button-default"
@click="goBack"
>
返回
</el-button>
</div>
</div>
</template>
</el-page-header>
</div>
<el-row style="margin: 16px 16px 0;">
<el-col :span="isEdit ? 16 : 24">
<el-form
ref="form"
:model="dataForm"
:rules="rules"
label-width="120px"
style="padding: 16px 16px 0;"
class="bs-el-form"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="数据集名称"
prop="name"
>
<el-input
v-model="dataForm.name"
class="bs-el-input"
clearable
:disabled="!isEdit"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="分组"
prop="typeId"
>
<el-select
ref="selectParentName"
v-model="dataForm.typeId"
class="bs-el-select"
popper-class="bs-el-select"
placeholder="请选择分组"
clearable
:disabled="!isEdit"
filterable
:filter-method="selectorFilter"
@clear="clearType"
@visible-change="setCurrentNode"
>
<el-option
style="height: auto;padding: 0;"
:label="typeName"
:value="dataForm.typeId"
>
<div>
<el-tree
ref="categorySelectTree"
:data="categoryData"
node-key="id"
:indent="0"
:props="{ label: 'name', children: 'children' }"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
class="bs-el-tree"
:filter-node-method="treeFilter"
@node-click="selectParentCategory"
>
<span
slot-scope="{ data }"
class="custom-tree-node"
>
<span>
<i
:class="data.children && data.children.length ? 'el-icon el-icon-folder' : 'el-icon el-icon-document'"
/>
{{ data.name }}
</span>
</span>
</el-tree>
</div>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="备注"
prop="remark"
>
<el-input
v-model="dataForm.remark"
class="bs-el-input"
:disabled="!isEdit"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="标签"
prop="labelIds"
>
<LabelSelect
:dataset-id="datasetId"
:id-list="dataForm.labelIds"
@commit="(ids) =>{dataForm.labelIds = ids}"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="card-border">
<vue-json-editor
v-if="isEdit"
v-model="dataForm.json"
:show-btns="false"
mode="code"
@has-error="onError"
/>
<vue-json-viewer
v-else
:value="dataForm.json"
:expand-depth="5"
sort
/>
</div>
<div
v-if="isEdit"
style="text-align: center; padding: 16px 0;"
>
<el-button
type="primary"
@click="analysisJSON"
>
解析并运行
</el-button>
</div>
</el-col>
<el-col
v-if="isEdit"
:span="8"
>
<div class="structure">
<div class="title-style bs-title-style">
输出字段
<el-button
type="text"
style="float: right;border: none;margin-top: -4px;"
@click="fieldsetVisible = true"
>
配置
</el-button>
</div>
<div class="field-wrap bs-field-wrap">
<div
v-for="field in structurePreviewList"
:key="field.fieldName"
class="field-item"
@click="fieldsetVisible = true"
>
<span>{{ field.fieldName }}</span>&nbsp;<span
v-show="field.fieldDesc"
style="color: #909399;"
>({{
field.fieldDesc }})</span>
<el-button
class="edit_field"
type="text"
style="float: right;border: none;margin-top: 2px;"
@click="fieldsetVisible = true"
>
配置
</el-button>
</div>
</div>
</div>
</el-col>
</el-row>
<div
v-if="isEdit"
class="dataPreView"
style="margin-top: 12px;"
>
<div class="result-view">
数据预览
</div>
<div class="bs-table-box is-Edit">
<el-table
align="center"
:data="dataPreviewList"
max-height="400"
class="bs-el-table"
>
<el-table-column
v-for="(value, key) in dataPreviewList[0]"
:key="key"
:label="key"
align="center"
show-overflow-tooltip
:render-header="renderHeader"
>
<template slot-scope="scope">
<span>{{ scope.row[key] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 字段填充方式 -->
<el-dialog
title="提示"
:visible.sync="fieldDescVisible"
width="420px"
append-to-body
:close-on-click-modal="false"
custom-class="fieldDescCheck"
class="bs-dialog-wrap bs-el-dialog"
>
<p style="color:var(--bs-el-text);line-height: 24px;padding-left: 10px;display: flex;">
<i
class="el-icon-warning"
style="color: #E6A23C;font-size: 24px;margin-right: 5px;"
/>存在字段描述信息为空请确认
</p>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="fieldDescFill"
>使用字段名填充</el-button>
<el-button
class="bs-el-button-default"
@click="fieldDescEdit"
>进入编辑</el-button>
<el-button
type="primary"
@click="toSave"
>继续保存</el-button>
</span>
</el-dialog>
<!-- 字段填充 -->
<el-dialog
title="输出字段配置"
:visible.sync="fieldsetVisible"
width="1000px"
append-to-body
:close-on-click-modal="false"
:before-close="cancelField"
class="bs-dialog-wrap bs-el-dialog"
>
<div class="bs-table-box">
<el-table
:data="structurePreviewListCopy"
:border="true"
align="center"
class="bs-el-table"
>
<el-empty slot="empty" />
<el-table-column
align="left"
show-overflow-tooltip
prop="fieldName"
label="字段值"
/>
<el-table-column
align="center"
prop="fieldDesc"
label="字段描述"
>
<template slot-scope="scope">
<el-input
v-if="isEdit"
v-model="scope.row.fieldDesc"
size="small"
class="labeldsc bs-el-input"
/>
<span v-else>{{ scope.row.fieldDesc }}</span>
</template>
</el-table-column>
<!-- 添加一个插槽供其他人可扩展表格列并把表格列的数据返回出去 -->
<slot name="output-field-table-column" />
</el-table>
</div>
<span
slot="footer"
class="dialog-footer"
>
<el-button
class="bs-el-button-default"
@click="cancelField"
>
取消
</el-button>
<el-button
type="primary"
@click="setField"
>
确定
</el-button>
</span>
</el-dialog>
</el-scrollbar>
</div>
</template>
<script>
import LabelSelect from 'data-room-ui/DataSetLabelManagement/src/LabelSelect.vue'
import vueJsonEditor from 'vue-json-editor-fix-cn'
import vueJsonViewer from 'vue-json-viewer'
import { getCategoryTree, datasetAdd, datasetUpdate, getDataset, nameCheckRepeat } from 'data-room-ui/js/utils/datasetConfigService'
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import { datasetMixins } from 'data-room-ui/js/mixins/datasetMixin'
export default {
name: 'JsonEditForm',
components: {
vueJsonEditor,
vueJsonViewer,
LabelSelect
},
mixins: [datasetMixins],
data () {
const validateName = (rule, value, callback) => {
nameCheckRepeat({
id: this.datasetId,
name: value,
moduleCode: this.appCode
}).then((r) => {
if (r) {
callback(new Error('数据集名称已存在'))
} else {
callback()
}
})
}
return {
dataForm: {
id: '',
name: '',
typeId: '',
datasetType: 'json',
remark: '',
labelIds: [],
// 以下为config配置
json: '',
fieldDesc: {},
fieldList: []
},
rules: {
name: [
{ required: true, message: '请输入数据集名称', trigger: 'blur' },
{ validator: validateName, trigger: 'blur' }
],
typeId: [
{ required: true, message: '请选择分组', trigger: 'blur' }
]
},
passTest: false // 通过测试
}
},
mounted () {
this.init()
},
methods: {
/**
* 初始化
* 1. 获取分类树
* 2. 获取数据集详情
* 3. 分析JSON
*/
async init () {
this.categoryData = await getCategoryTree({ tableName: 'dataset', moduleCode: this.appCode })
if (this.typeId) {
this.dataForm.typeId = this.typeId
this.$nextTick(() => {
try {
this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
} catch (error) {
console.error(error)
}
})
}
if (!this.datasetId) {
this.dataForm.json = []
return
}
getDataset(this.datasetId).then(res => {
this.dataForm.id = res.id
this.dataForm.name = res.name
this.dataForm.typeId = res.typeId
this.dataForm.remark = res.remark
this.dataForm.datasetType = res.datasetType
this.dataForm.moduleCode = res.moduleCode
this.dataForm.editable = res.editable
this.dataForm.sourceId = res.sourceId
// config 配置
this.dataForm.fieldDesc = res.config.fieldDesc
this.dataForm.json = JSON.parse(res.config.json)
if (this.dataForm.typeId) {
this.$nextTick(() => {
try {
this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
} catch (error) {
console.error(error)
}
})
}
this.analysisJSON(null, true)
})
},
/**
* 保存数据集
* @param formName 表单名称
* @param noCheckToSave 保存时是否检查
*/
save (formName, noCheckToSave = false) {
if (!this.passTest) {
this.$message.error('请确保JSON不为空且解析通过')
return
}
if (!this.structurePreviewList.length) {
this.$message.warning('该JSON未生成输出字段请重新检查')
return
}
if (!noCheckToSave) {
const temp = this.structurePreviewList.some(item => {
return item.fieldDesc === '' || !item.hasOwnProperty('fieldDesc')
}) // true-存在为空
if (temp) {
this.fieldDescVisible = true
return
}
}
this.$refs[formName].validate((valid) => {
if (!valid) {
return
}
this.dataForm.fieldList = this.structurePreviewList.length ? this.structurePreviewList : []
let datasetSave = null
datasetSave = this.dataForm.id ? datasetUpdate : datasetAdd
const datasetParams = {
id: this.dataForm.id,
name: this.dataForm.name,
typeId: this.dataForm.typeId,
datasetType: 'json',
remark: this.dataForm.remark,
moduleCode: this.appCode,
editable: this.appCode ? 1 : 0,
labelIds: this.dataForm.labelIds,
config: {
className: 'com.gccloud.dataset.entity.config.JsonDataSetConfig',
json: JSON.stringify(this.dataForm.json),
fieldDesc: this.dataForm.fieldDesc,
fieldList: this.dataForm.fieldList
}
}
datasetSave(datasetParams).then(() => {
this.$message.success('保存成功')
this.$parent.init(false)
this.$parent.setType = null
this.saveLoading = false
this.saveText = ''
this.goBack()
}).catch(() => {
this.saveLoading = false
this.saveText = ''
})
})
},
/**
* 使用字段名作为字段描述
*/
fieldDescFill () {
this.dataForm.fieldDesc = {}
this.structurePreviewList.forEach(field => {
if (field.fieldDesc === '' || !field.hasOwnProperty('fieldDesc')) {
field.fieldDesc = field.fieldName
this.dataForm.fieldDesc[field.fieldName] = field.fieldName
} else {
this.dataForm.fieldDesc[field.fieldName] = field.fieldDesc
}
})
this.save('form')
this.fieldDescVisible = false
},
/**
* 跳过字段描述编辑直接保存
*/
toSave () {
this.dataForm.fieldDesc = {}
this.structurePreviewList.forEach(field => {
this.dataForm.fieldDesc[field.fieldName] = field.fieldDesc
})
this.save('form', true)
this.fieldDescVisible = false
},
/**
* 保存字段描述编辑
*/
setField () {
this.structurePreviewList = cloneDeep(this.structurePreviewListCopy)
if (this.structurePreviewList.length) {
this.dataForm.fieldDesc = {}
this.structurePreviewList.forEach(key => {
this.dataForm.fieldDesc[key.fieldName] = key.fieldDesc
})
} else {
this.dataForm.fieldDesc = null
}
this.fieldsetVisible = false
},
// json错误校验
onError () {
this.passTest = false
},
/**
* 解析JSON
* @param $event 事件
* @param initAnalysis 是否初始化解析
*/
analysisJSON ($event, initAnalysis = false) {
if (Object.prototype.toString.call(this.dataForm.json) === '[object Object]') {
// json为对象
this.structurePreviewList = Object.keys(this.dataForm.json).map(key => {
return {
fieldName: key,
fieldDesc: ''
}
})
this.dataPreviewList = [cloneDeep(this.dataForm.json)]
this.passTest = true
} else if (Object.prototype.toString.call(this.dataForm.json) === '[object Array]') {
// 为数组
if (Object.prototype.toString.call(this.dataForm.json[0]) === '[object Object]') {
this.structurePreviewList = Object.keys(this.dataForm.json[0]).map(key => {
return {
fieldName: key,
fieldDesc: ''
}
})
this.dataPreviewList = cloneDeep(this.dataForm.json)
this.passTest = true
} else {
try {
this.structurePreviewList = Object.keys(JSON.parse(this.dataForm.json[0])).map(key => {
return {
fieldName: key,
fieldDesc: ''
}
})
this.dataPreviewList = []
this.dataForm.json.forEach(item => {
this.dataPreviewList.push(JSON.parse(item))
})
this.passTest = true
} catch (error) {
this.passTest = false
this.$message.warning('JSON格式错误')
}
}
} else {
try {
const json = JSON.parse(this.dataForm.json)
if (Object.prototype.toString.call(json) === '[object Object]') {
// json为对象
this.structurePreviewList = Object.keys(json).map(key => {
return {
fieldName: key,
fieldDesc: ''
}
})
this.dataPreviewList = [cloneDeep(json)]
this.passTest = true
} else if (Object.prototype.toString.call(json) === '[object Array]') {
// 为数组
if (Object.prototype.toString.call(json[0]) === '[object Object]') {
this.structurePreviewList = Object.keys(json[0]).map(key => {
return {
fieldName: key,
fieldDesc: ''
}
})
this.dataPreviewList = cloneDeep(json)
this.passTest = true
} else {
try {
this.structurePreviewList = Object.keys(JSON.parse(json[0])).map(key => {
return {
fieldName: key,
fieldDesc: ''
}
})
this.dataPreviewList = []
json.forEach(item => {
this.dataPreviewList.push(JSON.parse(item))
})
this.passTest = true
} catch (error) {
this.passTest = false
this.$message.warning('JSON格式错误')
}
}
}
} catch (error) {
this.passTest = false
this.$message.warning('JSON格式错误')
}
}
if (this.structurePreviewList.length && this.dataForm.fieldDesc) {
this.buildFieldDesc()
}
if (this.passTest && !initAnalysis) {
this.$message.success('脚本执行通过')
}
this.structurePreviewListCopy = cloneDeep(this.structurePreviewList)
},
/**
* 构建字段描述
*/
buildFieldDesc () {
const fieldDesc = {}
this.structurePreviewList.forEach(field => {
if (this.dataForm.fieldDesc.hasOwnProperty(field.fieldName)) {
field.fieldDesc = this.dataForm.fieldDesc[field.fieldName]
}
fieldDesc[field.fieldName] = field.fieldDesc
})
this.dataForm.fieldDesc = fieldDesc
},
selectorFilter (value) {
this.$refs.categorySelectTree.filter(value)
},
treeFilter (value, data) {
if (!value) return true
return data.name.indexOf(value) !== -1
}
}
}
</script>
<style lang="scss" scoped>
@import '../../assets/style/bsTheme.scss';
.data-set-scrollbar {
height: 100%;
overflow-y: auto;
overflow-x: none;
}
// .tree-box {
// padding: 0;
// max-height: 270px;
// }
.page-header {
display: flex;
position: relative;
.page-header-right {
position: absolute;
right: 16px;
}
}
.no-border {
border: 0;
}
.title-tip {
line-height: 40px;
font-weight: 600;
padding-left: 16px;
position: relative;
&::before {
content: '';
width: 4px;
height: 20px;
background: #3478f6;
position: absolute;
left: 6px;
top: 10px;
}
}
.card-border {
margin: 0 16px;
border: 1px solid #e4e4e4;
max-height: 300px;
overflow: auto;
position: relative;
.test-btn {
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
}
.transfer-wrap {
width: fit-content;
margin: 16px;
max-height: 300px;
overflow: auto;
}
::v-deep .jsoneditor-poweredBy,
::v-deep .jsoneditor-modes {
display: none;
}
::v-deep .ace_editor.ace-jsoneditor {
min-height: 250px;
}
.tag-wrap {
.el-tag {
margin-right: 8px;
}
}
.title-style {
padding: 8px 12px;
background-color: #f6f7fb;
border-left: 5px solid var(--bs-el-color-primary);
margin: 16px 16px 0 0;
}
.field-wrap {
max-height: 410px;
overflow: auto;
margin-right: 16px;
.field-item {
line-height: 32px;
padding: 0 12px 0 16px;
cursor: pointer;
.edit_field {
display: none;
}
&:hover {
background-color: #f2f7fe;
.edit_field {
display: block;
}
}
}
}
::v-deep .fieldDescCheck {
.el-dialog__body {
height: fit-content !important;
min-height: unset !important;
}
}
.result-view {
font-size: 14px;
font-weight: 600;
color: var(--bs-el-text);
position: relative;
padding: 16px 0;
padding-left: 12px;
border-bottom: 1px solid var(--bs-background-1);
&::before {
content: "";
height: 14px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border-left: 4px solid var(--bs-el-color-primary);
}
}
::v-deep .ace_layer.ace_gutter-layer.ace_folding-enabled {
background: #f6f7fb;
}
::v-deep .jsoneditor-menu {
background: var(--bs-el-color-primary);
border-color: var(--bs-el-color-primary);
}
::v-deep .jsoneditor-mode-code {
border-color: var(--bs-el-color-primary);
}
.bs-table-box {
margin-bottom: 0;
}
::v-deep .bs-table-box.is-Edit .el-table {
max-height: unset !important;
.el-table__body-wrapper {
max-height: unset !important;
}
}
.bs-table-box {
height: 100% !important;
margin-bottom: 0 !important;
}
.bs-el-select{
width: 100% !important;
}
::v-deep .el-input__inner{
width: 100% !important;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,583 @@
<template>
<div class="bs-theme-wrap">
<div class="tree-box">
<div class="ztree-filter-container filter-container">
<el-input
v-model="queryForm.searchKey"
placeholder="请输入数据集分组"
clearable
class="ztree-input bs-el-input"
@keyup.enter.native="reSearch()"
@clear="reSearch()"
/>
<el-button
:loading="loading"
type="primary"
icon="el-icon-search"
@click="reSearch()"
>
查询
</el-button>
</div>
<br class="br">
<el-tabs v-model="activeName">
<el-tab-pane
label="分组"
name="group"
/>
<el-tab-pane
label="类型"
name="type"
/>
</el-tabs>
<div
v-show="activeName == 'group'"
class="left"
>
<div class="ztree ztree-box">
<el-scrollbar class="scroll">
<el-empty v-show="noData" />
<div
v-if="!categoryData.length"
style="text-align: center;"
>
<el-button
type="text"
icon="el-icon-plus"
@click="addRootNode"
>
<span style="color: var(--bs-el-color-primary);">新增根节点</span>
</el-button>
</div>
<ul
v-show="!noData"
id="datasetTypeTree"
class="ztree"
/>
<div
v-loading="loading"
class="loading"
element-loading-text="正在加载数据"
element-loading-spinner="el-icon-loading"
/>
</el-scrollbar>
</div>
<div
v-show="rightClickForm.visible"
class="ztreeNodeMenu"
@mouseleave="rightClickForm.visible = false"
@mouseenter="rightClickForm.visible = true"
>
<div class="node-bottom-menu">
<div class="triangle" />
<ul>
<li
v-if="!isBoth"
@click="menuClick(editTypeConstant.editOrg)"
>
<span slot="title">编辑</span>
</li>
<li
v-if="!isBoth"
@click="menuClick(editTypeConstant.deleteOrg)"
>
<span slot="title">删除</span>
</li>
<li @click="menuClick(editTypeConstant.addSiblingOrg)">
<span slot="title">新增同级</span>
</li>
<li
v-if="!isBoth"
@click="menuClick(editTypeConstant.addChildOrg)"
>
<span slot="title">新增子级</span>
</li>
</ul>
</div>
<div class="node-top-menu">
<ul>
<li
v-if="!isBoth"
@click="menuClick(editTypeConstant.addChildOrg)"
>
<span slot="title">新增子级</span>
</li>
<li @click="menuClick(editTypeConstant.addSiblingOrg)">
<span slot="title">新增同级</span>
</li>
<li
v-if="!isBoth"
@click="menuClick(editTypeConstant.deleteOrg)"
>
<span slot="title">删除</span>
</li>
<li
v-if="!isBoth"
@click="menuClick(editTypeConstant.editOrg)"
>
<span slot="title">编辑</span>
</li>
</ul>
<div class="triangle" />
</div>
</div>
</div>
<div
v-show="activeName == 'type'"
class="left"
>
<div class="left-tab-box">
<ul>
<li
v-for="(_type,index) in datasetTypeList"
:key="index"
:class="{ 'tab-style': true, 'tab-active': _type.datasetType == curType }"
@click="getTypeData(_type.datasetType)"
>
<span>{{ _type.name }}</span>
</li>
</ul>
</div>
</div>
<CategroyEditForm
v-if="editFormVisible"
ref="categroyEditForm"
:app-code="appCode"
@addOrUpdateNode="addOrUpdateNode"
/>
</div>
</div>
</template>
<script>
// eslint-disable-next-line no-unused-vars
import $ from 'jquery'
import 'ztree/js/jquery.ztree.core'
import 'ztree/js/jquery.ztree.exedit'
import 'ztree/js/jquery.ztree.exhide'
import 'data-room-ui/assets/style/zTree/metroStyle.css'
import 'data-room-ui/assets/style/zTree/zTree.scss'
import 'data-room-ui/assets/style/zTree/zTreeSelect.scss'
import { getCategoryTree, categoryRemove, categoryDele } from 'data-room-ui/js/utils/datasetConfigService'
import CategroyEditForm from './CategroyEditForm.vue'
export default {
name: 'DatasetTypeTreeIndex',
components: {
CategroyEditForm
},
props: {
datasetTypeList: {
type: Array,
default: () => (['original', 'custom', 'storedProcedure', 'json', 'script'])
},
appCode: {
type: String,
default: ''
}
},
data: function () {
return {
activeName: 'group',
categoryData: [],
curType: '-1',
noData: false,
loading: false,
rightTableIsTransition: '',
flatData: [],
queryForm: {
searchKey: ''
},
ztreeObj: '',
nodeData: '',
rightClickForm: {
visible: false,
org: {}
},
editTypeConstant: {
deleteOrg: 'deleteOrg',
editOrg: 'editOrg',
addChildOrg: 'addChildOrg',
addSiblingOrg: 'addSiblingOrg'
},
editFormVisible: false,
ztreeSetting: {
view: {
dblClickExpand: false,
showTitle: false,
addHoverDom: (treeId, treeNode) => {
const nodeObj = $('#' + treeNode.tId + '_a')
nodeObj.css({ 'background-color': '#f6f7fb', cursor: 'pointer' })
},
removeHoverDom: (treeId, treeNode) => {
const nodeObj = $('#' + treeNode.tId + '_a')
nodeObj.css({ 'background-color': 'rgba(0,0,0,0)' })
},
addDiyDom: this.addDiyDom
},
callback: {
onClick: this.getNodeData,
onExpand: this.expandNode
}
},
isBoth: false // 是否为全部
}
},
computed: { },
mounted () {
this.initLazyDatasetTypeTree()
},
methods: {
// 新增根节点
addRootNode () {
this.editFormVisible = true
this.$nextTick(() => {
this.$refs.categroyEditForm.tableName = 'r_dataset'
this.$refs.categroyEditForm.dialogFormVisible = true
this.$refs.categroyEditForm.init({ parentId: 0 }, true)
this.$refs.categroyEditForm.radio = 0
this.$refs.categroyEditForm.title = '新增分组'
})
},
addDiyDom (treeId, treeNode) {
if (this.activeName === 'type') return
if (treeNode.parentNode && treeNode.parentNode.id !== 2) return
const aObj = $('#' + treeNode.tId + '_a')
const editStr = "<span id='diyBtn_" + treeNode.id + "' class='treeAddNode'>⋮</span>"
aObj.after(editStr)
const btn = $('#diyBtn_' + treeNode.id)
if (btn) {
const _this = this
btn.bind('mouseenter', function () {
if (treeNode.name === '全部' && treeNode.id === '') {
_this.isBoth = true
} else {
_this.isBoth = false
}
_this.nodeData = treeNode
_this.rightClickForm.org = treeNode
_this.rightClickForm.visible = true
let hieght = 0
const nodeElObj = document.getElementById(treeNode.tId + '_a').getBoundingClientRect()
const clientHeight = document.documentElement.clientHeight
const clientY = nodeElObj.top + 34
if (clientHeight - clientY < 200) {
hieght = clientY - 200
document.querySelector('.node-bottom-menu').style.display = 'none'
const menu = document.querySelector('.node-top-menu')
/* 菜单定位基于鼠标点击位置 */
menu.style.display = ''
menu.style.left = nodeElObj.right - 92 + 'px'
menu.style.top = hieght + 20 + 25 + 'px'
menu.style.position = 'fixed' // 为新创建的DIV指定绝对定位
menu.style.width = 110 + 'px'
} else {
hieght = clientY
document.querySelector('.node-top-menu').style.display = 'none'
const menu = document.querySelector('.node-bottom-menu')
/* 菜单定位基于鼠标点击位置 */
menu.style.display = ''
menu.style.left = nodeElObj.right - 92 + 'px'
menu.style.top = hieght - 8 + 'px'
menu.style.position = 'fixed' // 为新创建的DIV指定绝对定位
menu.style.width = 110 + 'px'
}
})
btn.bind('mouseout', function () {
_this.rightClickForm.visible = false
})
}
},
// 初始化树节点
initLazyDatasetTypeTree () {
this.loading = true
getCategoryTree({ type: 'dataset', moduleCode: this.appCode }).then((res) => {
this.categoryData = res.map((item) => {
return { isParent: item.hasChildren, ...item }
})
this.categoryData.unshift({ name: '全部', id: '', parentId: '0' })
this.ztreeObj = $.fn.zTree.init($('#datasetTypeTree'), this.ztreeSetting, this.categoryData)
this.$emit('reCategory')
}).then((e) => {
this.loading = false
}).catch((e) => {
this.loading = false
})
},
// 节点展开
expandNode (event, tree, nodeData) {
if (nodeData.children) {
nodeData.children.forEach((item) => {
this.ztreeObj.showNode(item)
})
} else {
this.loading = true
}
},
// 未知
expandedNodes () {
if (this.rightTableIsTransition === 1) {
this.$emit('expandedNodes', 1)
} else {
this.$emit('expandedNodes', this.flatTreeData(this.ztreeObj.getNodes()).length)
}
this.flatData = []
},
// filter方法
filterNode (node) {
const selfMatch = node.name.includes(this.queryForm.searchKey)
const childMatch = false
// 过滤非匹配的值
return !(this.recursion(node, childMatch) || selfMatch)
},
// 子节点匹配 - 递归
recursion (node, childMatch) {
if (node.isParent) {
for (let i = 0; i < node.children.length; i++) {
if (node.children[i].name.includes(this.queryForm.searchKey)) {
childMatch = true
return childMatch
} else {
childMatch = this.recursion(node.children[i], childMatch)
}
}
}
return childMatch
},
// 查询
reSearch () {
this.activeName = 'group'
if (this.queryForm.searchKey) {
const treeObj = $.fn.zTree.getZTreeObj('datasetTypeTree')
const nodes = treeObj.getNodesByParam('isHidden', true)
treeObj.showNodes(nodes)
const hiddenNodes = treeObj.getNodesByFilter(this.filterNode)
treeObj.hideNodes(hiddenNodes)
treeObj.expandAll(true)
} else {
this.initLazyDatasetTypeTree()
}
},
// 节点点击事件
getNodeData (treeId, treeNode, nodeData, clickFlag) {
this.curType = '-1'
this.$emit('nodeClick', nodeData, this.activeName)
},
// 类型点击事件
getTypeData (datasetType) {
this.curType = datasetType
this.ztreeObj.cancelSelectedNode()
this.$emit('nodeClick', datasetType, this.activeName)
},
// 更多事件
menuClick (editType) {
// 删除节点
if (editType === this.editTypeConstant.deleteOrg) {
categoryDele(this.rightClickForm.org.id).then((res) => {
if (res == 0) {
this.deleteOrg(this.rightClickForm.org)
} else {
this.$message.error('当前节点下存在已定义数据集,无法删除')
}
})
return
}
// 编辑节点
this.editFormVisible = true
if (editType === this.editTypeConstant.editOrg) {
this.$nextTick(() => {
this.$refs.categroyEditForm.type = 'dataset'
this.$refs.categroyEditForm.dialogFormVisible = true
this.$refs.categroyEditForm.init(this.rightClickForm.org, false)
this.$refs.categroyEditForm.title = '编辑分组'
})
return
}
// 新增同级节点
if (editType === this.editTypeConstant.addSiblingOrg) {
this.$nextTick(() => {
this.$refs.categroyEditForm.type = 'dataset'
this.$refs.categroyEditForm.dialogFormVisible = true
this.$refs.categroyEditForm.init(this.rightClickForm.org, true, editType)
this.$refs.categroyEditForm.radio = 0
this.$refs.categroyEditForm.title = '新增分组'
})
return
}
// 新增子节点
if (editType === this.editTypeConstant.addChildOrg) {
this.$nextTick(() => {
this.$refs.categroyEditForm.type = 'dataset'
this.$refs.categroyEditForm.dialogFormVisible = true
this.$refs.categroyEditForm.init(this.rightClickForm.org, true, editType)
this.$refs.categroyEditForm.radio = 1
this.$refs.categroyEditForm.title = '新增分组'
})
}
},
flatTreeData (data) {
data.forEach(item => {
this.flatData.push(item)
if (item.children) {
this.flatTreeData(item.children)
}
})
return this.flatData
},
// 删除分类
deleteOrg (org) {
this.$confirm('删除数据集分组,确定进行删除操作?', '提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'bs-el-message-box'
}
).then(() => {
if (!org.id) {
return
}
categoryRemove(org.id).then((data) => {
this.$message.success('操作成功')
// this.ztreeObj.removeNode(org)
// this.initLazyOrgTree()
const removeNode = this.ztreeObj.getNodeByParam('id', org.id, null)
this.ztreeObj.removeNode(removeNode)
this.redrawTree()
// 刷新右侧表格
this.$emit('refreshData', org)
})
})
},
// 刷新右侧表格
refreshData (cbObj) {
// 刷新右侧表格
this.$emit('refreshData', cbObj)
},
// 新增或修改节点
addOrUpdateNode (params, isAdd) {
if (!isAdd) {
const editNode = this.ztreeObj.getNodeByParam('id', params.id, null)
editNode.name = params.name
this.ztreeObj.editName(editNode)
this.ztreeObj.setEditable(false)
} else {
// 新增
const parentNode = params.parentId === 0 ? null : this.ztreeObj.getNodeByParam('id', params.parentId, null)
this.ztreeObj.addNodes(parentNode, params)
}
this.redrawTree()
},
// 重新绘制树
redrawTree () {
// 重新绘制ztree
getCategoryTree({ type: 'dataset', moduleCode: this.appCode }).then((res) => {
this.categoryData = res.map((item) => {
return { isParent: item.hasChildren, ...item }
})
this.categoryData.unshift({ name: '全部', id: '', parentId: '0' })
}).then((e) => {
this.loading = false
}).catch((e) => {
this.loading = false
})
}
}
}
</script>
<style lang="scss" scoped>
@import '../../assets/style/bsTheme.scss';
::v-deep .el-tabs {
.el-tabs__header {
margin: 0;
.el-tabs__nav {
width: 100%;
.el-tabs__item {
color: var(--bs-el-text);
width: 50%;
text-align: center;
}
.el-tabs__active-bar {
display: none !important;
}
.el-tabs__item.is-active {
color: var(--bs-el-color-primary) !important;
border-bottom-color: var(--bs-el-border) !important;
}
}
}
}
::v-deep .ztree {
span {
color: var(--bs-el-text);
}
li:hover {
background: transparent !important;
background-color: transparent !important;
}
.curSelectedNode {
background: var(--bs-el-background-3) !important;
background-color: var(--bs-el-background-3) !important;
}
a:hover {
background: var(--bs-el-background-3) !important;
background-color: var(--bs-el-background-3) !important;
}
}
::v-deep .el-tabs__nav-wrap::after {
display: none !important;
}
.left-tab-box ul li {
font-size: 12px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
height: 34px;
line-height: 40px;
cursor: pointer;
padding-left: 20px;
margin: 2px 0;
&:hover,
&.tab-active {
background-color: #f2f7fe;
}
&.tab-active::before {
content: "";
height: 34px;
line-height: 40px;
position: absolute;
left: 0;
border-left: 4px solid var(--bs-el-color-primary);
}
}
.ztree-input {
margin-right: 10px !important;
}
.ztree-box {
height: 100%;
overflow: hidden;
}
.el-textarea__inner {
color: var(--bs-el-text);
background-color: var(--bs-el-background-1) !important;
}
</style>

View File

@@ -0,0 +1,899 @@
<template>
<div
id="box"
class="bs-container"
@mousemove="mousemoveOnBox"
@mouseup="mouseupOnBox"
>
<el-row
v-show="datasetType === null"
type="flex"
class="layout"
>
<div
id="left-box"
class="left-box"
:style="{ 'transition': transition + 's' }"
>
<div class="inner-container">
<TypeTree
ref="datasetsTypeTree"
:dataset-type-list="datasetTypeList"
:app-code="appCode"
@nodeClick="nodeClick"
@refreshData="refreshData"
@expandedNodes="expandedNodes"
@reCategory="getTreeList"
/>
</div>
</div>
<div
class="right-box inner-container"
:style="{ 'transition': transition + 's' }"
>
<div
id="resize"
class="resize pack-up-box"
@mousedown="mousedown"
@mouseup="mouseup"
@mousemove="mousemove"
>
<a
v-if="isPackUpTree"
@click="packUpTree"
@mousedown="resize = null"
@mouseup="resize = null"
@mousemove="resize = null"
>
<i :class="isPackUpTree === false ? 'el-icon-caret-left' : 'el-icon-caret-right'" />
</a>
<a
v-else
class="visible-pack-up"
>
<span>||</span>
</a>
</div>
<el-form
ref="queryForm"
:model="queryForm"
class="filter-container bs-el-form"
@submit.native.prevent
>
<el-form-item
class="filter-item"
prop="name"
>
<el-input
v-model="queryForm.name"
class="bs-el-input"
placeholder="请输入数据集名称"
clearable
@keyup.enter.native="handleSearch()"
@clear="handleSearch()"
/>
</el-form-item>
<el-form-item
class="filter-item"
prop="labelIds"
>
<el-select
v-model="queryForm.labelIds"
class="bs-el-select"
popper-class="bs-el-select"
clearable
filterable
multiple
collapse-tags
placeholder="请选择数据集关联标签"
@clear="handleSearch()"
>
<el-option
v-for="labelItem in labelList"
:key="labelItem.id"
:label="labelItem.labelName"
:value="labelItem.id"
/>
</el-select>
</el-form-item>
<el-form-item class="filter-item">
<el-button
:loading="dataListLoading"
type="primary"
icon="el-icon-search"
@click="handleSearch()"
>
查询
</el-button>
</el-form-item>
<el-form-item class="filter-item">
<el-button
v-if="toAdd"
class="bs-el-button-default"
@click="addDataset"
>
新增
</el-button>
</el-form-item>
</el-form>
<div class="bs-table-box">
<el-table
v-if="isDialog"
ref="userTable"
v-loading="dataListLoading"
class="bs-el-table bs-scrollbar"
:element-loading-text="loadingText"
:data="tableData"
:header-cell-style="sortStyle"
@sort-change="reSort"
@current-change="handleCurrentChange"
@select="selectDs"
@select-all="selectAll"
>
<el-empty slot="empty" />
<el-table-column
v-if="isDialog && multiple"
type="selection"
width="55"
/>
<el-table-column
prop="name"
label="数据集名称"
align="left"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-radio
v-if="isDialog && !multiple"
v-model="curRow"
:label="scope.row"
>
{{ scope.row.name }}
</el-radio>
<span v-else>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
prop="datasetType"
label="数据集类型"
align="center"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ datasetTypeList.find(type=>type.datasetType===scope.row.datasetType) ? datasetTypeList.find(type=>type.datasetType===scope.row.datasetType).name : '其他' }}</span>
</template>
</el-table-column>
<el-table-column
prop="labelIds"
label="标签"
align="center"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ getLabels(scope.row.labelIds).join(',') }}</span>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
align="left"
show-overflow-tooltip
/>
<!--操作栏-->
<el-table-column
v-if="doEdit||isDelete"
label="操作"
width="200"
align="center"
>
<template
v-if="showOperate(scope.row.datasetType)"
slot-scope="scope"
>
<el-button
v-if="doEdit"
class="bs-el-button-default"
:disabled="scope.row.editable === 1 && !appCode"
@click="toEdit(scope.row.id, scope.row.datasetType, scope.row.name, scope.row.typeId)"
>
编辑
</el-button>
<el-button
v-if="isDelete"
class="bs-el-button-default"
:loading="scope.row.loading"
:disabled="scope.row.editable === 1 && !appCode"
@click="delDataset(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-table
v-else
ref="userTable"
v-table
v-loading="dataListLoading"
height="0"
class="bs-el-table bs-scrollbar"
:element-loading-text="loadingText"
:data="tableData"
:header-cell-style="sortStyle"
@sort-change="reSort"
@current-change="handleCurrentChange"
@select="selectDs"
@select-all="selectAll"
>
<el-empty slot="empty" />
<el-table-column
v-if="isDialog && multiple"
type="selection"
width="55"
/>
<el-table-column
prop="name"
label="数据集名称"
align="left"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-radio
v-if="isDialog && !multiple"
v-model="curRow"
:label="scope.row"
>
{{ scope.row.name }}
</el-radio>
<span v-else>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column
prop="datasetType"
label="数据集类型"
align="center"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ datasetTypeList.find(type=>type.datasetType===scope.row.datasetType) ? datasetTypeList.find(type=>type.datasetType===scope.row.datasetType).name : '其他' }}</span>
</template>
</el-table-column>
<el-table-column
prop="labelIds"
label="标签"
align="center"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ getLabels(scope.row.labelIds).join(',') }}</span>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
align="left"
show-overflow-tooltip
/>
<!--操作栏-->
<el-table-column
v-if="doEdit||isDelete"
label="操作"
width="200"
align="center"
>
<template
v-if="showOperate(scope.row.datasetType)"
slot-scope="scope"
>
<el-button
v-if="doEdit"
class="bs-el-button-default"
:disabled="scope.row.editable === 1 && !appCode"
@click="toEdit(scope.row.id, scope.row.datasetType, scope.row.name, scope.row.typeId)"
>
编辑
</el-button>
<el-button
v-if="isDelete"
class="bs-el-button-default"
:loading="scope.row.loading"
:disabled="scope.row.editable === 1 && !appCode"
@click="delDataset(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="bs-pagination">
<el-pagination
class="bs-el-pagination"
popper-class="bs-el-pagination"
:current-page="current"
:page-sizes="[10, 20, 50, 100]"
:page-size="size"
:total="totalCount"
background
prev-text="上一页"
next-text="下一页"
layout="total, prev, pager, next, sizes"
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
/>
</div>
</div>
</el-row>
<!-- 新增-类型窗口 -->
<DatasetTypeDialog
ref="DatasetTypeDialog"
:dataset-type-list="datasetTypeList"
@openAddForm="openAddForm"
/>
<checkDatasource
ref="checkDatasource"
:reason-list="reasonList"
/>
<component
:is="componentData.component"
v-if="datasetType"
ref="EditForm"
:key="componentData.key"
:config="componentData.config"
:dataset-id="datasetId"
:dataset-name="datasetName"
:type-id="typeId"
:is-edit="isEdit"
:app-code="appCode"
@back="back"
>
<template #output-field-table-column>
<slot name="output-field-table-column" />
</template>
</component>
</div>
</template>
<script>
import TypeTree from './TypeTree.vue'
import JsEditForm from './JsEditForm.vue'
import JsonEditForm from './JsonEditForm.vue'
import table from 'data-room-ui/js/utils/table.js'
import ScriptEditForm from './ScriptEditForm.vue'
import checkDatasource from 'data-room-ui/DataSourceManagement/src/checkDatasource.vue'
import CustomEditForm from './CustomEditForm.vue'
import { pageMixins } from 'data-room-ui/js/mixins/page'
import OriginalEditForm from './OriginalEditForm.vue'
import HttpEditForm from './HttpEditForm.vue'
import DatasetTypeDialog from './DatasetTypeDialog.vue'
import StoredProcedureEditForm from './StoredProcedureEditForm.vue'
import { datasetPage, datasetRemove, datasetCheck } from 'data-room-ui/js/utils/datasetConfigService'
import { getLabelList } from 'data-room-ui/js/utils/LabelConfigService'
export default {
name: 'DataSetManagement',
directives: {
table // 注册自定义指令
},
components: {
TypeTree,
DatasetTypeDialog,
OriginalEditForm,
CustomEditForm,
JsonEditForm,
StoredProcedureEditForm,
ScriptEditForm,
JsEditForm,
checkDatasource,
HttpEditForm
},
mixins: [pageMixins],
props: {
multiple: {
type: Boolean,
default: false
},
isDialog: {
type: Boolean,
default: false
},
dsValue: {
type: [Array, Object],
default: null
},
appCode: {
type: String,
default: ''
},
isBorder: {
type: Boolean,
default: false
},
toAdd: {
type: Boolean,
default: true
},
doEdit: {
type: Boolean,
default: true
},
isDelete: {
type: Boolean,
default: true
}
},
data () {
return {
reasonList: [],
datasetType: null,
isEdit: false,
categoryData: [],
tableData: [], // 表格数据
queryForm: {
name: '',
datasetType: '',
typeId: '', // 分类id
labelIds: []
}, // 查询条件
// 数据集类型
datasetTypeList: [],
labelList: [],
isPackUpTree: false,
transition: 0.1,
loadingText: '正在加载数据',
dataListLoading: false,
leftBox: null,
rightBox: null,
startX: null,
event: null,
endX: null,
resize: null,
datasetId: '', // 数据集Id用于详情与编辑
datasetName: '', // 数据集名称
typeId: '', // 详情typeId
curRow: null,
multipleSelection: [],
// 远程组件
componentData: {
component: null,
config: null,
key: new Date().getTime()
}
}
},
computed: {
allType () {
return this.datasetTypeList.map(item => item.datasetType).filter(item => item !== '')
}
},
watch: {
datasetType (value) {
if (value === null) {
this.datasetId = ''
this.datasetName = ''
this.isEdit = false
}
}
},
mounted () {
this.init()
if (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)) {
// 当前浏览器为Safari浏览器
// 执行相关操作
document.addEventListener('mousemove', function (event) {
if (event.buttons === 1) {
event.preventDefault()
}
})
}
},
methods: {
getLabels (list) {
const arr = []
list?.forEach((item) => {
arr.push(this.labelList.filter(x => x.id === item)[0]?.labelName)
})
return arr
},
toggleRowSelection () {
this.$nextTick(() => {
const dsIds = this.multipleSelection.map(ds => ds.id)
this.$refs.userTable.clearSelection()
this.tableData.forEach(item => {
if (dsIds.includes(item.id)) {
this.$refs.userTable.toggleRowSelection(item, true)
}
})
})
},
// 全选
selectAll (selection) {
if (this.isDialog && this.multiple) {
if (selection.length) {
const dsIds = this.multipleSelection.map(ds => ds.id)
selection.forEach(ds => {
if (!dsIds.includes(ds.id)) {
this.multipleSelection.push(ds)
}
})
} else {
this.tableData.forEach(row => {
const dsIds = this.multipleSelection.map(ds => ds.id)
const i = dsIds.indexOf(row.id)
if (i > -1) this.multipleSelection.splice(i, 1)
})
}
}
},
// 手动勾选
selectDs (selection, row) {
if (this.isDialog && this.multiple) {
let dsIndex = null
const ds = this.multipleSelection.find((ds, index) => {
if (ds.id === row.id) {
dsIndex = index
return ds
}
})
if (!ds) {
this.multipleSelection.push(row)
} else {
this.multipleSelection.splice(dsIndex, 1)
}
}
},
// 获取选中数据集信息
getSelectDs () {
if (!this.isDialog) return
if (this.multiple) {
// 多选返回
return this.multipleSelection
} else {
// 单选返回
return this.curRow ? this.curRow : null
}
},
// 单选数据集
handleCurrentChange (currentRow) {
this.curRow = currentRow
},
// 删除数据集
delDataset (row) {
row.loading = true
datasetCheck(row.id).then((res) => {
row.loading = false
if (res.canDelete) {
this.$confirm('确定删除当前数据集吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'bs-el-message-box'
}).then(() => {
datasetRemove(row.id).then(res => {
this.init(false)
this.$message.success('删除成功')
})
}).catch(() => {
})
} else {
this.reasonList = res.reasons
this.$refs.checkDatasource.checkDatasourceVisible = true
}
})
},
// 详情
toPreview (id, type, name, typeId) {
this.datasetId = id
this.datasetType = type
this.datasetName = name
this.typeId = typeId
this.isEdit = false
},
toEdit (id, type, name, typeId) {
this.datasetId = id
this.datasetType = type
this.componentData = this.getComponents(this.datasetTypeList.find(item => item?.datasetType === type)?.componentName) ?? ''
this.datasetName = name
this.typeId = typeId
this.isEdit = true
},
// 回到管理端
back () {
this.datasetType = null
this.isEdit = false
},
// 新增数据集-类型
openAddForm (type, componentName) {
this.datasetType = type
this.componentData = this.getComponents(componentName)
this.typeId = this.queryForm.typeId
this.isEdit = true
},
showOperate (datasetType) {
return this.getComponents(this.datasetTypeList.find(type => type.datasetType === datasetType)?.componentName)?.config?.showOperate ?? true
},
getComponents (componentName) {
const components = Object.values(this.$options.components)
let remoteComponentData = null
if (window.BS_CONFIG?.customDatasetComponents && window.BS_CONFIG?.customDatasetComponents.length > 0) {
// 获取远程组件
remoteComponentData = window.BS_CONFIG?.customDatasetComponents.find(item => item.config.componentName === componentName)
}
return {
component: components.find(component => component.name === componentName) || remoteComponentData?.vueFile,
config: remoteComponentData?.config || null,
key: new Date().getTime()
}
},
// 初始化
init (temp = true) {
if (temp) {
this.getTreeList()
}
if (this.isDialog) {
if (this.multiple) {
this.multipleSelection = this.dsValue ? this.dsValue : []
} else {
this.curRow = this.dsValue
}
}
this.current = 1
const list = [
{ name: '全部', datasetType: '' },
{ name: '原始数据集', datasetType: 'original', componentName: 'OriginalEditForm', description: '直接查询某个数据库表' },
{ name: '自助数据集', datasetType: 'custom', componentName: 'CustomEditForm', description: '自定义SQL语句查询' },
{ name: '存储过程数据集', datasetType: 'storedProcedure', componentName: 'StoredProcedureEditForm', description: '调用数据库存储过程查询' },
{ name: 'JSON数据集', datasetType: 'json', componentName: 'JsonEditForm', description: '直接定义静态数据' },
{ name: 'JS数据集', datasetType: 'js', componentName: 'JsEditForm', description: '编写JS代码进行动态模拟数据创建' },
{ name: 'HTTP数据集', datasetType: 'http', componentName: 'HttpEditForm', description: '接入第三方HTTP服务查询' },
{ name: '脚本数据集', datasetType: 'script', componentName: 'ScriptEditForm', description: '支持ES、Mongodb、国产化数据库、自定义Java代码查询' },
// { name: 'MQTT数据集', datasetType: 'mqtt', componentName: 'ScriptEditForm', description: '编写JS代码进行MQTT数据创建' }
]
if (window.BS_CONFIG?.datasetTypeList && window.BS_CONFIG?.datasetTypeList?.length !== 0) {
this.datasetTypeList = [{ name: '全部', datasetType: '' }, ...list.filter(item => window.BS_CONFIG?.datasetTypeList.findIndex(x => x === item.datasetType) !== -1)]
} else {
this.datasetTypeList = [
...list
]
}
if (window.BS_CONFIG?.customDatasetComponents && window.BS_CONFIG?.customDatasetComponents.length > 0) {
// 将获得到的远程数据集进行组装
window.BS_CONFIG?.customDatasetComponents.forEach((item) => {
this.datasetTypeList.push({ name: item.config.name, datasetType: item.config.datasetType, componentName: item.config.componentName, description: item.config.description })
})
}
this.getDataList()
// getLabelList().then(res => {
// this.labelList = res
// })
},
// 新增数据集
addDataset () {
this.$refs.DatasetTypeDialog.dialogVisible = true
},
selectChange () {
this.getDataList()
},
getTreeList () {
},
// 获取表格数据
getDataList () {
getLabelList().then(res => {
this.labelList = res
})
this.dataListLoading = true
datasetPage({
current: this.current,
size: this.size,
moduleCode: this.appCode,
...this.queryForm,
datasetType: this.queryForm.datasetType === '' ? [...this.allType] : [this.queryForm.datasetType]
}).then((data) => {
this.tableData = data.list
this.tableData.forEach(r => {
this.$set(r, 'loading', false)
})
if (this.isDialog) {
if (this.multiple && this.multipleSelection.length) {
this.toggleRowSelection()
} else if (this.curRow.id) {
const ds = this.tableData.find(item => item.id === this.curRow.id)
if (ds) this.curRow = ds
}
}
this.totalCount = data.totalCount
this.dataListLoading = false
}).catch(() => {
this.dataListLoading = false
})
},
expandedNodes (data) {
if (data && data > 50) {
this.transition = 0
} else {
this.transition = 0.1
}
},
nodeClick (row, type) {
this.current = 1
if (type === 'group') {
this.queryForm.typeId = row.id
this.queryForm.datasetType = ''
} else if (type === 'type') {
this.queryForm.typeId = ''
this.queryForm.datasetType = row
}
this.getDataList()
},
refreshData (node) {
if (node && node.id === this.queryForm.typeId) {
this.queryForm.typeId = ''
}
this.getDataList()
},
handleSearch () {
this.current = 1
this.queryForm.typeId = ''
// 清除左侧机构树的选中状态
this.$refs.datasetsTypeTree.ztreeObj.cancelSelectedNode()
this.getDataList()
},
// 拖拽修改div宽度
mousedown (e) {
this.resize = document.getElementsByClassName('resize')[0]
this.resize.left = document.getElementById('left-box').getBoundingClientRect().width
this.leftBox = document.getElementsByClassName('left-box')[0]
this.rightBox = document.getElementsByClassName('right-box')[0]
this.startX = e.clientX
},
mousemove (e) {
this.event = e
if (this.resize) {
const boxWidth = document.getElementById('box').getBoundingClientRect().width
this.endX = e.clientX
const offset = document.getElementById('box').getBoundingClientRect().left > 300 ? 0 : 25
const moveLen = this.resize.left + (this.endX - this.startX) + offset
this.leftBox.style.width = moveLen / boxWidth * 100 + '%'
this.rightBox.style.width = ((1 - moveLen / boxWidth) * 100) + '%'
this.isPackUpTree = false
if (moveLen < 100 && this.endX - this.startX < 0) {
this.isPackUpTree = true
this.leftBox.style.width = '0%'
this.rightBox.style.width = '100%'
} else if (moveLen / boxWidth > 0.5) {
this.leftBox.style.width = '50%'
this.rightBox.style.width = '50%'
}
}
},
packUpTree () {
this.$refs.datasetsTypeTree.expandedNodes()
this.isPackUpTree = !this.isPackUpTree
if (!this.isPackUpTree) {
this.leftBox.style.width = '25%'
this.rightBox.style.width = '75%'
} else {
this.leftBox.style.width = '0%'
this.rightBox.style.width = '100%'
}
},
mouseup () {
if (this.resize) {
this.resize = null
if (this.endX < 350 && this.endX - this.startX > 0) {
this.isPackUpTree = false
this.leftBox.style.width = '25%'
this.rightBox.style.width = '75%'
}
}
},
mousemoveOnBox (e) {
this.mousemove(e)
},
mouseupOnBox () {
this.resize = null
if (this.endX < 350 && this.endX - this.startX > 0) {
this.isPackUpTree = false
this.leftBox.style.width = '25%'
this.rightBox.style.width = '75%'
}
this.resize = null
}
}
}
</script>
<style lang="scss" scoped>
@import '../../assets/style/bsTheme.scss';
@import '../../assets/style/zTree/treePackUp.scss';
::v-deep .big-screen-router-view-wrap{
padding-left: 16px !important;
}
::v-deep .bs-pagination {
margin-top: 4px;
padding-right: 16px;
// padding: 0 !important;
// right: 16px !important;
// bottom: 16px !important;
// position: absolute !important;
.el-input__inner {
width: 110px !important;
border:none;
background: var(--bs-el-background-1);
}
}
::v-deep .bs-container .inner-container .el-form .filter-item {
.el-input__inner {
width: 200px;
}
}
::v-deep .left-tab-box {
span {
color: var(--bs-el-text);
}
}
::v-deep .left-tab-box ul li.tab-active {
background-color: var(--bs-el-background-3);
}
::v-deep .left-tab-box ul li:hover {
background-color: var(--bs-el-background-3);
}
::v-deep .el-dialog {
.bs-container {
// max-height: calc(90vh - 236px) !important;
.el-table {
height: calc(100vh - 390px);
}
.ztree {
max-height: calc(90vh - 325px) !important;
}
.el-tabs__item.is-active {
border-bottom: none !important;
}
}
}
::v-deep .ztreeNodeMenu {
ul {
background-color: var(--bs-background-1);
}
li:hover {
background-color: var(--bs-el-background-3);
span {
color: var(--bs-el-color-primary);
}
}
span {
color: var(--bs-el-text);
}
.triangle {
background-color: var(--bs-background-1) !important;
}
}
.layout {
width: 100%;
height: 100%;
}
::v-deep .el-table__body-wrapper{
max-height: unset !important;
}
</style>