更新前端src/views/visualized目录文件

This commit is contained in:
limengnan 2025-06-24 11:55:42 +08:00
parent 13e7d15784
commit 598dcd07b2
25 changed files with 2335 additions and 555 deletions

View File

@ -3,7 +3,7 @@ import dvPreviewDownload from '@/assets/svg/icon_download_outlined.svg'
import deDelete from '@/assets/svg/de-delete.svg' import deDelete from '@/assets/svg/de-delete.svg'
import icon_fileExcel_colorful from '@/assets/svg/icon_file-excel_colorful.svg' import icon_fileExcel_colorful from '@/assets/svg/icon_file-excel_colorful.svg'
import icon_refresh_outlined from '@/assets/svg/icon_refresh_outlined.svg' import icon_refresh_outlined from '@/assets/svg/icon_refresh_outlined.svg'
import { ref, h, onUnmounted, computed } from 'vue' import { ref, h, onUnmounted, computed, reactive } from 'vue'
import { EmptyBackground } from '@/components/empty-background' import { EmptyBackground } from '@/components/empty-background'
import { ElButton, ElMessage, ElMessageBox, ElTabPane, ElTabs } from 'element-plus-secondary' import { ElButton, ElMessage, ElMessageBox, ElTabPane, ElTabs } from 'element-plus-secondary'
import { RefreshLeft } from '@element-plus/icons-vue' import { RefreshLeft } from '@element-plus/icons-vue'
@ -12,7 +12,9 @@ import {
exportRetry, exportRetry,
exportDelete, exportDelete,
exportDeleteAll, exportDeleteAll,
exportDeletePost exportDeletePost,
exportTasksRecords,
generateDownloadUri
} from '@/api/dataset' } from '@/api/dataset'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
@ -22,6 +24,13 @@ import { useLinkStoreWithOut } from '@/store/modules/link'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAppStoreWithOut } from '@/store/modules/app'
const { t } = useI18n() const { t } = useI18n()
const state = reactive({
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
}
})
const tableData = ref([]) const tableData = ref([])
const drawerLoading = ref(false) const drawerLoading = ref(false)
const drawer = ref(false) const drawer = ref(false)
@ -59,7 +68,6 @@ const handleClose = () => {
} }
const { wsCache } = useCache() const { wsCache } = useCache()
const openType = wsCache.get('open-backend') === '1' ? '_self' : '_blank' const openType = wsCache.get('open-backend') === '1' ? '_self' : '_blank'
const xpack = wsCache.get('xpack-model-distributed')
const desktop = wsCache.get('app.desktop') const desktop = wsCache.get('app.desktop')
onUnmounted(() => { onUnmounted(() => {
@ -77,46 +85,29 @@ const handleClick = tab => {
description.value = t('data_export.no_task') description.value = t('data_export.no_task')
} }
drawerLoading.value = true drawerLoading.value = true
exportTasks(activeName.value) exportTasksRecords().then(res => {
.then(res => {
tabList.value.forEach(item => { tabList.value.forEach(item => {
if (item.name === 'ALL') { if (item.name === 'ALL') {
item.label = t('data_set.all') + '(' + res.data.length + ')' item.label = t('data_set.all') + '(' + res.data.ALL + ')'
} }
if (item.name === 'IN_PROGRESS') { if (item.name === 'IN_PROGRESS') {
item.label = item.label = t('data_set.exporting') + '(' + res.data.IN_PROGRESS + ')'
t('data_set.exporting') +
'(' +
res.data.filter(task => task.exportStatus === 'IN_PROGRESS').length +
')'
} }
if (item.name === 'SUCCESS') { if (item.name === 'SUCCESS') {
item.label = item.label = t('data_set.success') + '(' + res.data.SUCCESS + ')'
t('data_set.success') +
'(' +
res.data.filter(task => task.exportStatus === 'SUCCESS').length +
')'
} }
if (item.name === 'FAILED') { if (item.name === 'FAILED') {
item.label = item.label = t('data_set.fail') + '(' + res.data.FAILED + ')'
t('data_set.fail') +
'(' +
res.data.filter(task => task.exportStatus === 'FAILED').length +
')'
} }
if (item.name === 'PENDING') { if (item.name === 'PENDING') {
item.label = item.label = t('data_set.waiting') + '(' + res.data.PENDING + ')'
t('data_set.waiting') +
'(' +
res.data.filter(task => task.exportStatus === 'PENDING').length +
')'
} }
}) })
if (activeName.value === 'ALL') { })
tableData.value = res.data exportTasks(state.paginationConfig.currentPage, state.paginationConfig.pageSize, activeName.value)
} else { .then(res => {
tableData.value = res.data.filter(task => task.exportStatus === activeName.value) state.paginationConfig.total = res.data.total
} tableData.value = res.data.records
}) })
.finally(() => { .finally(() => {
drawerLoading.value = false drawerLoading.value = false
@ -131,45 +122,32 @@ const init = params => {
handleClick() handleClick()
timer = setInterval(() => { timer = setInterval(() => {
if (activeName.value === 'IN_PROGRESS') { if (activeName.value === 'IN_PROGRESS') {
exportTasks(activeName.value).then(res => { exportTasksRecords().then(res => {
tabList.value.forEach(item => { tabList.value.forEach(item => {
if (item.name === 'ALL') { if (item.name === 'ALL') {
item.label = t('data_set.all') + '(' + res.data.length + ')' item.label = t('data_set.all') + '(' + res.data.ALL + ')'
} }
if (item.name === 'IN_PROGRESS') { if (item.name === 'IN_PROGRESS') {
item.label = item.label = t('data_set.exporting') + '(' + res.data.IN_PROGRESS + ')'
t('data_set.exporting') +
'(' +
res.data.filter(task => task.exportStatus === 'IN_PROGRESS').length +
')'
} }
if (item.name === 'SUCCESS') { if (item.name === 'SUCCESS') {
item.label = item.label = t('data_set.success') + '(' + res.data.SUCCESS + ')'
t('data_set.success') +
'(' +
res.data.filter(task => task.exportStatus === 'SUCCESS').length +
')'
} }
if (item.name === 'FAILED') { if (item.name === 'FAILED') {
item.label = item.label = t('data_set.fail') + '(' + res.data.FAILED + ')'
t('data_set.fail') +
'(' +
res.data.filter(task => task.exportStatus === 'FAILED').length +
')'
} }
if (item.name === 'PENDING') { if (item.name === 'PENDING') {
item.label = item.label = t('data_set.waiting') + '(' + res.data.PENDING + ')'
t('data_set.waiting') +
'(' +
res.data.filter(task => task.exportStatus === 'PENDING').length +
')'
} }
}) })
if (activeName.value === 'ALL') { })
tableData.value = res.data exportTasks(
} else { state.paginationConfig.currentPage,
tableData.value = res.data.filter(task => task.exportStatus === activeName.value) state.paginationConfig.pageSize,
} activeName.value
).then(res => {
state.paginationConfig.total = res.data.total
tableData.value = res.data.records
}) })
} }
}, 5000) }, 5000)
@ -242,13 +220,17 @@ const callbackExportSuc = () => {
const downLoadAll = () => { const downLoadAll = () => {
if (multipleSelection.value.length === 0) { if (multipleSelection.value.length === 0) {
tableData.value.forEach(item => { tableData.value.forEach(item => {
generateDownloadUri(item.id).then(() => {
window.open(PATH_URL + '/exportCenter/download/' + item.id) window.open(PATH_URL + '/exportCenter/download/' + item.id)
}) })
})
return return
} }
multipleSelection.value.map(ele => { multipleSelection.value.map(ele => {
generateDownloadUri(ele.id).then(() => {
window.open(PATH_URL + '/exportCenter/download/' + ele.id) window.open(PATH_URL + '/exportCenter/download/' + ele.id)
}) })
})
} }
const showMsg = item => { const showMsg = item => {
msg.value = '' msg.value = ''
@ -262,8 +244,11 @@ const timestampFormatDate = value => {
return new Date(value).toLocaleString() return new Date(value).toLocaleString()
} }
import { PATH_URL } from '@/config/axios/service' import { PATH_URL } from '@/config/axios/service'
import GridTable from '../../../../components/grid-table/src/GridTable.vue'
const downloadClick = item => { const downloadClick = item => {
generateDownloadUri(item.id).then(() => {
window.open(PATH_URL + '/exportCenter/download/' + item.id, openType) window.open(PATH_URL + '/exportCenter/download/' + item.id, openType)
})
} }
const retry = item => { const retry = item => {
@ -294,6 +279,19 @@ const handleSelectionChange = val => {
multipleSelection.value = val multipleSelection.value = val
} }
const pageChange = index => {
if (typeof index !== 'number') {
return
}
state.paginationConfig.currentPage = index
handleClick()
}
const sizeChange = size => {
state.paginationConfig.currentPage = 1
state.paginationConfig.pageSize = size
handleClick()
}
const delAll = () => { const delAll = () => {
if (multipleSelection.value.length === 0) { if (multipleSelection.value.length === 0) {
ElMessageBox.confirm(t('data_export.sure_del_all'), { ElMessageBox.confirm(t('data_export.sure_del_all'), {
@ -344,7 +342,7 @@ defineExpose({
<template> <template>
<el-drawer <el-drawer
v-loading="drawerLoading" v-loading="drawerLoading"
custom-class="de-export-excel" modal-class="de-export-excel"
:title="$t('data_export.export_center')" :title="$t('data_export.export_center')"
v-model="drawer" v-model="drawer"
direction="rtl" direction="rtl"
@ -386,11 +384,13 @@ defineExpose({
>{{ $t('commons.delete') }} >{{ $t('commons.delete') }}
</el-button> </el-button>
<div class="table-container" :class="!tableData.length && 'hidden-bottom'"> <div class="table-container" :class="!tableData.length && 'hidden-bottom'">
<el-table <GridTable
ref="multipleTable" ref="multipleTable"
:data="tableData" :pagination="state.paginationConfig"
height="100%" :table-data="tableData"
style="width: 100%" class="popper-max-width"
@current-change="pageChange"
@size-change="sizeChange"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="50" /> <el-table-column type="selection" width="50" />
@ -482,7 +482,7 @@ defineExpose({
<template #empty> <template #empty>
<empty-background :description="description" img-type="noneWhite" /> <empty-background :description="description" img-type="noneWhite" />
</template> </template>
</el-table> </GridTable>
</div> </div>
</el-drawer> </el-drawer>

View File

@ -384,6 +384,7 @@ const emits = defineEmits(['update:item', 'del'])
size="small" size="small"
@change="filterTypeChange" @change="filterTypeChange"
v-model="item.filterType" v-model="item.filterType"
class="w181"
:placeholder="t('auth.select')" :placeholder="t('auth.select')"
> >
<el-option <el-option
@ -586,11 +587,15 @@ const emits = defineEmits(['update:item', 'del'])
} }
.w100.ed-select { .w100.ed-select {
width: 100px; width: 100px !important;
}
.w181.ed-select {
width: 181px !important;
} }
.w70 { .w70 {
width: 70px; width: 70px !important;
} }
.mar5 { .mar5 {
@ -676,11 +681,12 @@ const emits = defineEmits(['update:item', 'del'])
} }
} }
:deep(.ed-input__wrapper) { :deep(.ed-input__wrapper),
:deep(.ed-select__wrapper) {
background-color: #f8f8fa; background-color: #f8f8fa;
border: none; border: none;
border-radius: 0; border-radius: 0;
box-shadow: none; box-shadow: none !important;
height: 26px; height: 26px;
font-family: var(--de-custom_font, 'PingFang'); font-family: var(--de-custom_font, 'PingFang');
word-wrap: break-word; word-wrap: break-word;

View File

@ -24,7 +24,9 @@ import {
onBeforeUnmount, onBeforeUnmount,
shallowRef, shallowRef,
computed, computed,
h inject,
h,
Ref
} from 'vue' } from 'vue'
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -100,7 +102,7 @@ const state = reactive({
}) })
const datasourceTableData = shallowRef([]) const datasourceTableData = shallowRef([])
const isCross = inject<Ref>('isCross')
const paginationConfig = reactive({ const paginationConfig = reactive({
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
@ -203,18 +205,25 @@ const insertFieldToCodeMirror = (value: string) => {
const setNameIdTrans = (from, to, originName, name2Auto?: string[]) => { const setNameIdTrans = (from, to, originName, name2Auto?: string[]) => {
let name2Id = originName let name2Id = originName
const ids = [...builtInList.value, ...fieldFormList.value].map(item => item.id)
const names = [...builtInList.value, ...fieldFormList.value].map(item => item.name)
const nameIdMap = [...builtInList.value, ...fieldFormList.value].reduce((pre, next) => { const nameIdMap = [...builtInList.value, ...fieldFormList.value].reduce((pre, next) => {
pre[next[from]] = next[to] pre[next[from]] = next[to]
return pre return pre
}, {}) }, {})
const on = originName.match(/\[(.+?)\]/g) const on = originName.match(/\$f2cde\[(.+?)\]/g)
if (on) { if (on) {
on.forEach(itm => { on.forEach(itm => {
const ele = itm.slice(1, -1) const ele = itm.slice(7, -1)
if (name2Auto) { if (name2Auto) {
name2Auto.push(nameIdMap[ele]) name2Auto.push(nameIdMap[ele])
} }
name2Id = name2Id.replace(`[${ele}]`, `[${nameIdMap[ele]}]`) if (from === 'id' && ids.includes(ele)) {
name2Id = name2Id.replace(`$f2cde[${ele}]`, `$f2cde[${nameIdMap[ele]}]`)
}
if (from === 'name' && names.includes(ele)) {
name2Id = name2Id.replace(`$f2cde[${ele}]`, `$f2cde[${nameIdMap[ele]}]`)
}
}) })
} }
return name2Id return name2Id
@ -253,7 +262,13 @@ const getNodeField = ({ datasourceId, tableName }) => {
table: tableName, table: tableName,
sql: '' sql: ''
} }
getTableField({ datasourceId, info: JSON.stringify(info), tableName, type: 'db' }) getTableField({
datasourceId,
info: JSON.stringify(info),
tableName,
type: 'db',
isCross: isCross.value
})
.then(res => { .then(res => {
gridData.value = res as unknown as Field[] gridData.value = res as unknown as Field[]
}) })
@ -263,7 +278,6 @@ const getNodeField = ({ datasourceId, tableName }) => {
} }
const getDatasource = () => { const getDatasource = () => {
debugger
getDatasourceList().then(res => { getDatasourceList().then(res => {
const _list = (res as unknown as DataSource[]) || [] const _list = (res as unknown as DataSource[]) || []
if (_list && _list.length > 0 && _list[0].id === '0') { if (_list && _list.length > 0 && _list[0].id === '0') {
@ -412,6 +426,7 @@ const getSQLPreview = () => {
parseVariable() parseVariable()
dataPreviewLoading.value = true dataPreviewLoading.value = true
getPreviewSql({ getPreviewSql({
isCross: isCross.value,
sql: Base64.encode((sql = setNameIdTrans('name', 'id', codeCom.value.state.doc.toString()))), sql: Base64.encode((sql = setNameIdTrans('name', 'id', codeCom.value.state.doc.toString()))),
datasourceId: sqlNode.value.datasourceId, datasourceId: sqlNode.value.datasourceId,
sqlVariableDetails: JSON.stringify(state.variables) sqlVariableDetails: JSON.stringify(state.variables)
@ -427,7 +442,9 @@ const getSQLPreview = () => {
let tableList = [] let tableList = []
watch(searchTable, val => { watch(searchTable, val => {
datasourceTableData.value = tableList.filter(ele => ele.tableName.includes(val)) datasourceTableData.value = tableList.filter(ele =>
ele.tableName.toLowerCase().includes(val.toLowerCase())
)
}) })
const getIconName = (type: string) => { const getIconName = (type: string) => {
@ -489,15 +506,19 @@ const mouseupDrag = () => {
const parseVariable = () => { const parseVariable = () => {
state.variablesTmp = [] state.variablesTmp = []
const reg = new RegExp('\\${(.*?)}', 'gim') const variableReg = new RegExp('\\$DE_PARAM{(.*?)}', 'gim')
const match = codeCom.value.state.doc.toString().match(reg) const variableMatch = codeCom.value.state.doc.toString().match(variableReg)
if (variableMatch !== null) {
const names = [] const names = []
const reg = new RegExp('\\$\\[[^\\]]+\\]', 'gim')
for (let index = 0; index < variableMatch.length; index++) {
let sqlItem = variableMatch[index].substring(10, variableMatch[index].length - 1)
const match = sqlItem.match(reg)
if (match !== null) { if (match !== null) {
for (let index = 0; index < match.length; index++) { for (let matchIndex = 0; matchIndex < match.length; matchIndex++) {
let name = match[index].substring(2, match[index].length - 1) let name = match[matchIndex].substring(2, match[matchIndex].length - 1)
if (names.indexOf(name) < 0) { if (names.indexOf(name) < 0) {
names.push(name) names.push(name)
// eslint-disable-next-line
let obj = undefined let obj = undefined
for (let i = 0; i < state.variables?.length; i++) { for (let i = 0; i < state.variables?.length; i++) {
if (state.variables[i].variableName === name) { if (state.variables[i].variableName === name) {
@ -523,6 +544,42 @@ const parseVariable = () => {
} }
} }
} }
}
} else {
const reg = new RegExp('\\${(.*?)}', 'gim')
const match = codeCom.value.state.doc.toString().match(reg)
const names = []
if (match !== null) {
for (let index = 0; index < match.length; index++) {
let name = match[index].substring(2, match[index].length - 1)
if (names.indexOf(name) < 0) {
names.push(name)
let obj = undefined
for (let i = 0; i < state.variables?.length; i++) {
if (state.variables[i].variableName === name) {
obj = state.variables[i]
if (!obj.hasOwnProperty('defaultValueScope')) {
obj.defaultValueScope = 'EDIT'
}
}
}
if (obj === undefined) {
obj = {
variableName: name,
alias: '',
type: [],
required: false,
defaultValue: '',
details: '',
defaultValueScope: 'EDIT'
}
obj.type.push('TEXT')
}
state.variablesTmp.push(obj)
}
}
}
}
state.variables = JSON.parse(JSON.stringify(state.variablesTmp)) state.variables = JSON.parse(JSON.stringify(state.variablesTmp))
} }
@ -779,6 +836,7 @@ const mousedownDrag = () => {
@change="changeFlagCode = true" @change="changeFlagCode = true"
:height="`${dragHeight}px`" :height="`${dragHeight}px`"
dom-id="sql-editor" dom-id="sql-editor"
:regexp="/\$f2cde\[(.*?)\]/g"
ref="myCm" ref="myCm"
:quotaMap="fieldFormList.filter(ele => ['num'].includes(ele.type)).map(ele => ele.name)" :quotaMap="fieldFormList.filter(ele => ['num'].includes(ele.type)).map(ele => ele.name)"
:dimensionMap=" :dimensionMap="
@ -906,7 +964,7 @@ const mousedownDrag = () => {
</div> </div>
<div <div
class="variable-item flex-align-center" class="variable-item flex-align-center"
@click="insertFieldToCodeMirror(`[${fieldForm.name}]`)" @click="insertFieldToCodeMirror(`$f2cde[${fieldForm.name}]`)"
v-for="fieldForm in builtInList" v-for="fieldForm in builtInList"
:key="fieldForm.id" :key="fieldForm.id"
> >
@ -919,7 +977,7 @@ const mousedownDrag = () => {
class="variable-item flex-align-center" class="variable-item flex-align-center"
v-for="fieldForm in fieldFormListComputed" v-for="fieldForm in fieldFormListComputed"
:key="fieldForm.id" :key="fieldForm.id"
@click="insertFieldToCodeMirror(`[${fieldForm.name}]`)" @click="insertFieldToCodeMirror(`$f2cde[${fieldForm.name}]`)"
:class="['num'].includes(fieldForm.type) && 'with-type'" :class="['num'].includes(fieldForm.type) && 'with-type'"
> >
<el-icon> <el-icon>
@ -931,7 +989,7 @@ const mousedownDrag = () => {
></component ></component
></Icon> ></Icon>
</el-icon> </el-icon>
<span :title="fieldForm.name">{{ fieldForm.name }}</span> <span :title="fieldForm.name" class="ellipsis">{{ fieldForm.name }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -941,7 +999,7 @@ const mousedownDrag = () => {
<el-drawer <el-drawer
:title="dialogTitle" :title="dialogTitle"
v-model="showVariableMgm" v-model="showVariableMgm"
custom-class="sql-dataset-drawer" modal-class="sql-dataset-drawer"
size="870px" size="870px"
direction="rtl" direction="rtl"
> >
@ -1181,7 +1239,7 @@ const mousedownDrag = () => {
z-index: 10; z-index: 10;
&:hover { &:hover {
.ed-icon { .ed-icon {
color: var(--ed-color-primary, #3370ff); color: var(--ed-color-primary, #3370ff) !important;
} }
} }
} }
@ -1593,6 +1651,7 @@ const mousedownDrag = () => {
} }
.ed-input-group__prepend { .ed-input-group__prepend {
padding: 0 11px; padding: 0 11px;
width: 163px;
} }
.de-group__prepend { .de-group__prepend {
.ed-date-editor { .ed-date-editor {
@ -1607,7 +1666,6 @@ const mousedownDrag = () => {
.ed-date-editor { .ed-date-editor {
width: 100%; width: 100%;
display: inline-block;
} }
.select-type { .select-type {
@ -1619,15 +1677,16 @@ const mousedownDrag = () => {
.select-svg-icon { .select-svg-icon {
position: absolute; position: absolute;
left: 24px; left: 24px;
top: 15px; top: 19px;
} }
.content { .content {
height: 62px; height: 80px;
width: 822px; width: 822px;
border-radius: 4px; border-radius: 4px;
background: #e1eaff; background: #e1eaff;
position: relative; position: relative;
line-height: 22px;
padding: 9px 0 9px 40px; padding: 9px 0 9px 40px;
font-family: var(--de-custom_font, 'PingFang'); font-family: var(--de-custom_font, 'PingFang');
font-size: 14px; font-size: 14px;

View File

@ -136,11 +136,11 @@ const setFieldForm = () => {
const setNameIdTrans = (from, to, originName, name2Auto?: string[]) => { const setNameIdTrans = (from, to, originName, name2Auto?: string[]) => {
let name2Id = originName let name2Id = originName
const nameIdMap = [...state.dimensionData, ...state.quotaData].reduce((pre, next) => { const nameIdMap = [...dimensionDataList, ...quotaDataList].reduce((pre, next) => {
pre[next[from]] = next[to] pre[next[from]] = next[to]
return pre return pre
}, {}) }, {})
const on = originName.match(/\[(.+?)\]/g) const on = originName.match(/\[(.+?)\]/g) || []
if (on) { if (on) {
on.forEach(itm => { on.forEach(itm => {
const ele = itm.slice(1, -1) const ele = itm.slice(1, -1)
@ -235,10 +235,8 @@ watch(
) )
) )
} else { } else {
state.dimensionData = JSON.parse(JSON.stringify(dimensionDataList)).filter( state.dimensionData = JSON.parse(JSON.stringify(dimensionDataList))
ele => ele.extField === 0 state.quotaData = JSON.parse(JSON.stringify(quotaDataList))
)
state.quotaData = JSON.parse(JSON.stringify(quotaDataList)).filter(ele => ele.extField === 0)
} }
} }
) )
@ -652,10 +650,8 @@ initFunction()
.mr0 { .mr0 {
margin-right: 0; margin-right: 0;
:deep(.ed-select__prefix--light) { :deep(.ed-select__prefix::after) {
padding: 0; display: none;
border: none;
margin: 0;
} }
} }

View File

@ -16,9 +16,12 @@ const props = defineProps({
domId: propTypes.string.def('editor'), domId: propTypes.string.def('editor'),
height: propTypes.string.def('250px'), height: propTypes.string.def('250px'),
quotaMap: propTypes.arrayOf(String).def(() => []), quotaMap: propTypes.arrayOf(String).def(() => []),
dimensionMap: propTypes.arrayOf(String).def(() => []) dimensionMap: propTypes.arrayOf(String).def(() => []),
regexp: {
type: RegExp,
default: /\[(.*?)\]/g
}
}) })
const emits = defineEmits(['change']) const emits = defineEmits(['change'])
const codeComInit = (doc: string, sqlMode?: boolean) => { const codeComInit = (doc: string, sqlMode?: boolean) => {
@ -45,7 +48,7 @@ const codeComInit = (doc: string, sqlMode?: boolean) => {
} //!placeholderMatcher } //!placeholderMatcher
const placeholderMatcher = new MatchDecorator({ const placeholderMatcher = new MatchDecorator({
regexp: /\[(.*?)\]/g, regexp: new RegExp(props.regexp),
decoration: match => decoration: match =>
Decoration.replace({ Decoration.replace({
widget: new PlaceholderWidget(match[1]) widget: new PlaceholderWidget(match[1])

View File

@ -18,6 +18,7 @@ import nothingTree from '@/assets/img/nothing-tree.png'
import { BusiTreeRequest } from '@/models/tree/TreeNode' import { BusiTreeRequest } from '@/models/tree/TreeNode'
import { filterFreeFolder } from '@/utils/utils' import { filterFreeFolder } from '@/utils/utils'
export interface Tree { export interface Tree {
isCross: boolean
name: string name: string
value?: string | number value?: string | number
id: string | number id: string | number
@ -47,6 +48,7 @@ const treeRef = ref()
const filterText = ref('') const filterText = ref('')
let union = [] let union = []
let allfields = [] let allfields = []
let isCross = false
const datasetForm = reactive({ const datasetForm = reactive({
pid: '', pid: '',
name: '' name: ''
@ -156,6 +158,7 @@ const createInit = (type, data: Tree, exec, name: string) => {
if (type === 'dataset') { if (type === 'dataset') {
union = data.union union = data.union
allfields = data.allfields allfields = data.allfields
isCross = data.isCross
} }
if (data.id) { if (data.id) {
const request = { leaf: false, weight: 7 } as BusiTreeRequest const request = { leaf: false, weight: 7 } as BusiTreeRequest
@ -264,6 +267,7 @@ const saveDataset = () => {
if (nodeType.value === 'dataset') { if (nodeType.value === 'dataset') {
params.union = union params.union = union
params.allFields = allfields params.allFields = allfields
params.isCross = isCross
} }
if (cmd.value === 'move' && !checkPid(params.pid)) { if (cmd.value === 'move' && !checkPid(params.pid)) {
return return

View File

@ -43,6 +43,7 @@ const props = defineProps({
const primaryColor = computed(() => { const primaryColor = computed(() => {
return appearanceStore.themeColor === 'custom' ? appearanceStore.customColor : '#3370FF' return appearanceStore.themeColor === 'custom' ? appearanceStore.customColor : '#3370FF'
}) })
const isCross = inject<Ref>('isCross')
const iconName = { const iconName = {
left: icon_leftAssociation, left: icon_leftAssociation,
@ -62,7 +63,7 @@ const sqlNode = ref<SqlNode>()
const allfields = inject('allfields') as Ref const allfields = inject('allfields') as Ref
const getNodeField = ({ datasourceId, id, info, tableName, type, currentDsFields }) => { const getNodeField = ({ datasourceId, id, info, tableName, type, currentDsFields }) => {
return getTableField({ datasourceId, id, info, tableName, type }) return getTableField({ datasourceId, id, info, tableName, type, isCross: isCross.value })
.then(res => { .then(res => {
const idOriginNameMap = allfields.value.reduce((pre, next) => { const idOriginNameMap = allfields.value.reduce((pre, next) => {
pre[`${next.datasetTableId}${next.originName}`] = next.id pre[`${next.datasetTableId}${next.originName}`] = next.id
@ -230,7 +231,8 @@ const saveSqlNode = (val: SqlNode, cb) => {
id: id, id: id,
info: state.visualNode.info, info: state.visualNode.info,
tableName, tableName,
type: 'sql' type: 'sql',
isCross: isCross.value
}).then(res => { }).then(res => {
nodeField.value = res as unknown as Field[] nodeField.value = res as unknown as Field[]
nodeField.value.forEach(ele => { nodeField.value.forEach(ele => {
@ -244,7 +246,13 @@ const saveSqlNode = (val: SqlNode, cb) => {
} }
return return
} }
const obj = { info: JSON.stringify({ table: tableName, sql }), id, tableName, sqlVariableDetails } const obj = {
info: JSON.stringify({ table: tableName, sql }),
id,
datasourceId,
tableName,
sqlVariableDetails
}
dfsNodeBack([obj], [id], state.nodeList) dfsNodeBack([obj], [id], state.nodeList)
emits('reGetName') emits('reGetName')
} }
@ -261,13 +269,15 @@ const closeSqlNode = () => {
changeSqlId.value.length === 1 changeSqlId.value.length === 1
) { ) {
currentNode.value = state.nodeList[0] currentNode.value = state.nodeList[0]
const { datasourceId, id, info, tableName } = currentNode.value const { datasourceId, id, info, tableName, sqlVariableDetails } = currentNode.value
getTableField({ getTableField({
datasourceId, datasourceId,
id, id,
info, info,
tableName, tableName,
type: 'sql' type: 'sql',
isCross: isCross.value,
sqlVariableDetails: sqlVariableDetails
}).then(res => { }).then(res => {
const idOriginNameMap = allfields.value.reduce((pre, next) => { const idOriginNameMap = allfields.value.reduce((pre, next) => {
pre[`${next.datasetTableId}${next.originName}`] = next.id pre[`${next.datasetTableId}${next.originName}`] = next.id
@ -364,7 +374,7 @@ const confirmEditUnion = () => {
if (!!ids.length) { if (!!ids.length) {
const idArr = allfields.value.reduce((pre, next) => { const idArr = allfields.value.reduce((pre, next) => {
if (next.extField === 2) { if (next.extField === 2) {
let idMap = next.originName.match(/\[(.+?)\]/g) let idMap = next.originName.match(/\[(.+?)\]/g) || []
idMap = idMap.filter( idMap = idMap.filter(
itx => !next.params?.map(element => element.id).includes(itx.slice(1, -1)) itx => !next.params?.map(element => element.id).includes(itx.slice(1, -1))
) )
@ -452,7 +462,7 @@ const handleCommand = (ele, command) => {
if (!!fakeDelId.length) { if (!!fakeDelId.length) {
const idArr = allfields.value.reduce((pre, next) => { const idArr = allfields.value.reduce((pre, next) => {
if (next.extField === 2) { if (next.extField === 2) {
const idMap = next.originName.match(/\[(.+?)\]/g) const idMap = next.originName.match(/\[(.+?)\]/g) || []
const result = idMap.map(itm => { const result = idMap.map(itm => {
return itm.slice(1, -1) return itm.slice(1, -1)
}) })
@ -908,6 +918,7 @@ const drop_handler = ev => {
id: currentNode.value.id, id: currentNode.value.id,
info: currentNode.value.info, info: currentNode.value.info,
tableName, tableName,
isCross: isCross.value,
type type
}) })
.then(res => { .then(res => {
@ -1190,7 +1201,7 @@ const emits = defineEmits([
<el-drawer <el-drawer
:before-close="closeEditUnion" :before-close="closeEditUnion"
v-model="editUnion" v-model="editUnion"
custom-class="union-item-drawer" modal-class="union-item-drawer"
size="600px" size="600px"
direction="rtl" direction="rtl"
> >

View File

@ -1,11 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue' import { ref, reactive, inject, type Ref } from 'vue'
import UnionFieldList from './UnionFieldList.vue' import UnionFieldList from './UnionFieldList.vue'
import UnionItemEdit from './UnionItemEdit.vue' import UnionItemEdit from './UnionItemEdit.vue'
import type { Field, NodeType, UnionType, Node } from './util' import type { Field, NodeType, UnionType, Node } from './util'
import { getTableField } from '@/api/dataset' import { getTableField } from '@/api/dataset'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
const isCross = inject<Ref>('isCross')
const changeParentFields = val => { const changeParentFields = val => {
parent.currentDsFields = val parent.currentDsFields = val
} }
@ -69,10 +71,15 @@ const initState = () => {
} }
const getParams = (obj: Node) => { const getParams = (obj: Node) => {
return ['datasourceId', 'id', 'info', 'tableName', 'type'].reduce((pre, next) => { return ['datasourceId', 'id', 'info', 'tableName', 'type'].reduce(
(pre, next) => {
pre[next] = obj[next] pre[next] = obj[next]
return pre return pre
}, {}) },
{
isCross: isCross.value
}
)
} }
const getFields = async () => { const getFields = async () => {
const [n, p] = props.editArr as Node[] const [n, p] = props.editArr as Node[]

View File

@ -223,10 +223,11 @@ init()
} }
.union-selector { .union-selector {
width: 180px; width: 180px;
:deep(.ed-select__prefix--light) { :deep(.ed-select__prefix) {
border-right: none;
font-size: 22px; font-size: 22px;
padding: 0; &::after {
display: none;
}
} }
} }
.union-add { .union-add {

View File

@ -16,6 +16,10 @@ import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.s
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg' import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import icon_info_outlined from '@/assets/svg/icon_info_outlined.svg' import icon_info_outlined from '@/assets/svg/icon_info_outlined.svg'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import {
iconFieldCalculatedMap,
iconFieldCalculatedQMap
} from '@/components/icon-group/field-calculated-list'
import { enumValueDs } from '@/api/dataset' import { enumValueDs } from '@/api/dataset'
import { import {
ref, ref,
@ -66,11 +70,6 @@ import { cloneDeep, debounce } from 'lodash-es'
import { XpackComponent } from '@/components/plugin' import { XpackComponent } from '@/components/plugin'
import { iconFieldMap } from '@/components/icon-group/field-list' import { iconFieldMap } from '@/components/icon-group/field-list'
import { iconDatasourceMap } from '@/components/icon-group/datasource-list' import { iconDatasourceMap } from '@/components/icon-group/datasource-list'
const route = useRoute()
const appId:any = ref('')
if (route.query.appId) {
appId.value = route.query.appId
}
interface DragEvent extends MouseEvent { interface DragEvent extends MouseEvent {
dataTransfer: DataTransfer dataTransfer: DataTransfer
} }
@ -86,7 +85,13 @@ const { wsCache } = useCache()
const appStore = useAppStoreWithOut() const appStore = useAppStoreWithOut()
const embeddedStore = useEmbedded() const embeddedStore = useEmbedded()
const { t } = useI18n() const { t } = useI18n()
const { push } = useRouter() const route = useRoute()
const { push } = useRouter() || {
push: val => {
if (embeddedStore.getToken) return
window.location.href = val as string
}
}
const quotaTableHeight = ref(238) const quotaTableHeight = ref(238)
const creatDsFolder = ref() const creatDsFolder = ref()
const editCalcField = ref(false) const editCalcField = ref(false)
@ -116,6 +121,7 @@ const currentField = ref({
name: '', name: '',
idArr: [] idArr: []
}) })
const isCross = ref(false)
let isUpdate = false let isUpdate = false
const fieldTypes = index => { const fieldTypes = index => {
@ -253,14 +259,14 @@ const getDsName = (id: string) => {
} }
const pushDataset = () => { const pushDataset = () => {
wsCache.set(`dataset-info-id`, nodeInfo.id)
if (appStore.isDataEaseBi) { if (appStore.isDataEaseBi) {
embeddedStore.clearState() embeddedStore.clearState()
useEmitt().emitter.emit('changeCurrentComponent', 'Dataset') useEmitt().emitter.emit('changeCurrentComponent', 'Dataset')
return return
} }
const routeName = embeddedStore.getToken && appStore.getIsIframe ? 'dataset-embedded' : 'dataset' const routeName = embeddedStore.getToken && appStore.getIsIframe ? 'dataset-embedded' : 'dataset'
wsCache.set(`${routeName}-info-id`, nodeInfo.id) if (!!history.state.back && !appStore.getIsIframe) {
if (!!history.state.back) {
history.back() history.back()
} else { } else {
push({ push({
@ -362,6 +368,7 @@ const editeSave = () => {
...nodeInfo, ...nodeInfo,
name: datasetName.value, name: datasetName.value,
union, union,
isCross: isCross.value,
allFields: allfields.value, allFields: allfields.value,
nodeType: 'dataset' nodeType: 'dataset'
}) })
@ -486,7 +493,7 @@ const delFieldById = arr => {
const allfieldsId = allfields.value.map(ele => ele.id).concat(paramsId) const allfieldsId = allfields.value.map(ele => ele.id).concat(paramsId)
allfields.value = allfields.value.filter(ele => { allfields.value = allfields.value.filter(ele => {
if (![2, 3].includes(ele.extField)) return true if (![2, 3].includes(ele.extField)) return true
const idMap = ele.originName.match(/\[(.+?)\]/g) const idMap = ele.originName.match(/\[(.+?)\]/g) || []
if (!idMap) return true if (!idMap) return true
const result = idMap.every(itm => { const result = idMap.every(itm => {
const id = itm.slice(1, -1) const id = itm.slice(1, -1)
@ -511,7 +518,7 @@ const delFieldByIdFake = (arr, fakeAllfields) => {
const allfieldsId = fakeAllfields.map(ele => ele.id) const allfieldsId = fakeAllfields.map(ele => ele.id)
fakeAllfields = fakeAllfields.filter(ele => { fakeAllfields = fakeAllfields.filter(ele => {
if (![2, 3].includes(ele.extField)) return true if (![2, 3].includes(ele.extField)) return true
const idMap = ele.originName.match(/\[(.+?)\]/g) const idMap = ele.originName.match(/\[(.+?)\]/g) || []
if ( if (
!idMap || !idMap ||
idMap.every(itx => ele.params?.map(element => element.id).includes(itx.slice(1, -1))) idMap.every(itx => ele.params?.map(element => element.id).includes(itx.slice(1, -1)))
@ -731,6 +738,7 @@ const initEdite = async () => {
} }
datasetName.value = nodeInfo.name datasetName.value = nodeInfo.name
allfields.value = res.allFields || [] allfields.value = res.allFields || []
isCross.value = res.isCross || false
dfsUnion(arr, res.union || []) dfsUnion(arr, res.union || [])
const [fir] = res.union as { currentDs: { datasourceId: string } }[] const [fir] = res.union as { currentDs: { datasourceId: string } }[]
dataSource.value = fir?.currentDs?.datasourceId dataSource.value = fir?.currentDs?.datasourceId
@ -834,6 +842,7 @@ const getIconName = (type: number) => {
const allfields = ref([]) const allfields = ref([])
provide('allfields', allfields) provide('allfields', allfields)
provide('isCross', isCross)
let num = +new Date() let num = +new Date()
@ -962,7 +971,7 @@ const confirmEditUnion = () => {
if (!!idList.length) { if (!!idList.length) {
const idArr = allfields.value.reduce((pre, next) => { const idArr = allfields.value.reduce((pre, next) => {
if (idList.includes(next.id)) { if (idList.includes(next.id)) {
const idMap = next.originName.match(/\[(.+?)\]/g) const idMap = next.originName.match(/\[(.+?)\]/g) || []
const result = idMap.map(itm => { const result = idMap.map(itm => {
return itm.slice(1, -1) return itm.slice(1, -1)
}) })
@ -1093,7 +1102,7 @@ const handleFieldschange = val => {
const arr = [] const arr = []
const allfieldsCopy = cloneDeep(unref(allfields)) const allfieldsCopy = cloneDeep(unref(allfields))
dfsNodeList(arr, datasetDrag.value.getNodeList()) dfsNodeList(arr, datasetDrag.value.getNodeList())
enumValueDs({ dataset: { union: arr, allFields: allfieldsCopy }, field }) enumValueDs({ dataset: { union: arr, allFields: allfieldsCopy, isCross: isCross.value }, field })
.then(res => { .then(res => {
enumValue.value = res || [] enumValue.value = res || []
}) })
@ -1105,6 +1114,14 @@ const closeGroupField = () => {
editGroupField.value = false editGroupField.value = false
} }
const disabledEnumArr = computed(() => {
return currentGroupField.groupList?.map(ele => ele.text).flat()
})
const disabledEnum = (item, arr) => {
return disabledEnumArr.value.includes(item) && !arr.includes(item)
}
const titleForGroup = ref(t('dataset.create_grouping_field')) const titleForGroup = ref(t('dataset.create_grouping_field'))
const initGroupField = val => { const initGroupField = val => {
@ -1122,7 +1139,7 @@ const initGroupField = val => {
maxTerm, maxTerm,
time: [] time: []
} }
if (currentGroupField.deTypeOrigin === 1) { if (startTime && endTime) {
obj.time = [startTime, endTime] obj.time = [startTime, endTime]
} }
groupList.push(obj) groupList.push(obj)
@ -1138,7 +1155,6 @@ const initGroupField = val => {
const confirmGroupField = () => { const confirmGroupField = () => {
ruleGroupFieldRef.value.validate(val => { ruleGroupFieldRef.value.validate(val => {
let count = 0 let count = 0
let flag = false
let time let time
refsForm.value.forEach(ele => { refsForm.value.forEach(ele => {
ele?.validate(val => { ele?.validate(val => {
@ -1149,7 +1165,6 @@ const confirmGroupField = () => {
}) })
time = setTimeout(() => { time = setTimeout(() => {
clearTimeout(time) clearTimeout(time)
flag = true
time = null time = null
if (val && count === currentGroupField.groupList.length) { if (val && count === currentGroupField.groupList.length) {
const groupList = [] const groupList = []
@ -1240,7 +1255,7 @@ const verify = () => {
const arr = [] const arr = []
dfsNodeList(arr, datasetDrag.value.getNodeList()) dfsNodeList(arr, datasetDrag.value.getNodeList())
datasetPreviewLoading.value = true datasetPreviewLoading.value = true
getPreviewData({ union: arr, allFields: allfieldsCopy }) getPreviewData({ union: arr, allFields: allfieldsCopy, isCross: isCross.value })
.then(() => { .then(() => {
ElMessage.success(t('data_set.validation_succeeded')) ElMessage.success(t('data_set.validation_succeeded'))
}) })
@ -1336,7 +1351,7 @@ const getSqlResultHeight = () => {
sqlResultHeight.value = (document.querySelector('.sql-result') as HTMLElement).offsetHeight sqlResultHeight.value = (document.querySelector('.sql-result') as HTMLElement).offsetHeight
} }
const getDatasource = (weight?: number) => { const getDatasource = (weight?: number) => {
getDatasourceList(weight,appId.value).then(res => { getDatasourceList(weight).then(res => {
const _list = (res as unknown as DataSource[]) || [] const _list = (res as unknown as DataSource[]) || []
if (_list && _list.length > 0 && _list[0].id === '0') { if (_list && _list.length > 0 && _list[0].id === '0') {
state.dataSourceList = dfsChild(_list[0].children) state.dataSourceList = dfsChild(_list[0].children)
@ -1408,7 +1423,7 @@ const datasetSave = () => {
creatDsFolder.value.createInit( creatDsFolder.value.createInit(
'dataset', 'dataset',
{ id: pid || '0', union, allfields: allfields.value }, { id: pid || '0', union, allfields: allfields.value, isCross: isCross.value },
'', '',
datasetName.value datasetName.value
) )
@ -1425,7 +1440,7 @@ const datasetPreview = () => {
const arr = [] const arr = []
dfsNodeList(arr, datasetDrag.value.getNodeList()) dfsNodeList(arr, datasetDrag.value.getNodeList())
datasetPreviewLoading.value = true datasetPreviewLoading.value = true
getPreviewData({ union: arr, allFields: allfields.value }) getPreviewData({ union: arr, allFields: allfields.value, isCross: isCross.value })
.then(res => { .then(res => {
columns.value = generateColumns((res.data.fields as Field[]) || []) columns.value = generateColumns((res.data.fields as Field[]) || [])
tableData.value = (res.data.data as Array<{}>) || [] tableData.value = (res.data.data as Array<{}>) || []
@ -1615,6 +1630,24 @@ const handleClick = () => {
}) })
} }
const sourceChange = val => {
if (val) return
if (crossDatasources.value) {
isCross.value = !val
ElMessageBox.confirm(t('common.source_tips'), {
confirmButtonText: t('dataset.confirm'),
cancelButtonText: t('common.cancel'),
showCancelButton: true,
confirmButtonType: 'primary',
type: 'warning',
autofocus: false,
showClose: false
}).then(() => {
isCross.value = val
})
}
}
const finish = res => { const finish = res => {
const { id, pid, name } = res const { id, pid, name } = res
datasetName.value = name datasetName.value = name
@ -1666,6 +1699,14 @@ const getDsIconName = data => {
if (!data.leaf) return dvFolder if (!data.leaf) return dvFolder
return iconDatasourceMap[data.type] return iconDatasourceMap[data.type]
} }
const getIconNameCalc = (deType, extField, dimension = false) => {
if (extField === 2) {
const iconFieldCalculated = dimension ? iconFieldCalculatedMap : iconFieldCalculatedQMap
return iconFieldCalculated[deType]
}
return iconFieldMap[fieldType[deType]]
}
</script> </script>
<template> <template>
@ -1702,7 +1743,7 @@ const getDsIconName = data => {
<div class="container dataset-db" @mouseup="mouseupDrag"> <div class="container dataset-db" @mouseup="mouseupDrag">
<p v-show="!showLeft" class="arrow-right" @click="showLeft = true"> <p v-show="!showLeft" class="arrow-right" @click="showLeft = true">
<el-icon> <el-icon>
<Icon name="icon_right_outlined"><icon_right_outlined class="svg-icon" /></Icon> <Icon><icon_right_outlined class="svg-icon" /></Icon>
</el-icon> </el-icon>
</p> </p>
<div <div
@ -1719,6 +1760,14 @@ const getDsIconName = data => {
:style="{ width: LeftWidth + 'px' }" :style="{ width: LeftWidth + 'px' }"
> >
<div class="table-list-top"> <div class="table-list-top">
<el-switch
style="margin-bottom: 8px"
v-model="isCross"
@change="sourceChange"
:active-text="$t('common.cross_source')"
:inactive-text="$t('common.single_source')"
/>
<p class="select-ds"> <p class="select-ds">
{{ t('data_set.select_data_source') }} {{ t('data_set.select_data_source') }}
<span class="left-outlined"> <span class="left-outlined">
@ -1923,7 +1972,7 @@ const getDsIconName = data => {
><component ><component
class="svg-icon" class="svg-icon"
:class="`field-icon-${fieldType[[2, 3].includes(data.deType) ? 2 : 0]}`" :class="`field-icon-${fieldType[[2, 3].includes(data.deType) ? 2 : 0]}`"
:is="iconFieldMap[fieldType[data.deType]]" :is="getIconNameCalc(data.deType, data.extField)"
></component ></component
></Icon> ></Icon>
</el-icon> </el-icon>
@ -1957,7 +2006,7 @@ const getDsIconName = data => {
><component ><component
class="svg-icon" class="svg-icon"
:class="`field-icon-${fieldType[[2, 3].includes(data.deType) ? 2 : 0]}`" :class="`field-icon-${fieldType[[2, 3].includes(data.deType) ? 2 : 0]}`"
:is="iconFieldMap[fieldType[data.deType]]" :is="getIconNameCalc(data.deType, data.extField, true)"
></component ></component
></Icon> ></Icon>
</el-icon> </el-icon>
@ -2091,6 +2140,7 @@ const getDsIconName = data => {
:class=" :class="
!!scope.row.deTypeArr && !!scope.row.deTypeArr.length && 'select-type' !!scope.row.deTypeArr && !!scope.row.deTypeArr.length && 'select-type'
" "
v-if="scope.row.extField !== 3"
popper-class="cascader-panel" popper-class="cascader-panel"
v-model="scope.row.deTypeArr" v-model="scope.row.deTypeArr"
@change="val => cascaderChange(scope.row, val)" @change="val => cascaderChange(scope.row, val)"
@ -2111,6 +2161,7 @@ const getDsIconName = data => {
<span>{{ data.label }}</span> <span>{{ data.label }}</span>
</template> </template>
</el-cascader> </el-cascader>
<div style="padding-left: 30px" v-else>{{ $t('data_set.text') }}</div>
<span class="select-svg-icon"> <span class="select-svg-icon">
<el-icon> <el-icon>
<Icon <Icon
@ -2134,7 +2185,13 @@ const getDsIconName = data => {
> >
<template #default="scope"> <template #default="scope">
<div class="column-style"> <div class="column-style">
<span class="flex-align-center icon" v-if="scope.row.extField === 0"> <span style="color: #8d9199" v-if="scope.row.extField === 2">{{
t('dataset.calc_field')
}}</span>
<span style="color: #8d9199" v-else-if="scope.row.extField === 3">{{
t('dataset.grouping_field')
}}</span>
<span class="flex-align-center icon" v-else-if="scope.row.extField === 0">
<el-icon> <el-icon>
<Icon className="primary-color" <Icon className="primary-color"
><component ><component
@ -2145,7 +2202,6 @@ const getDsIconName = data => {
</el-icon> </el-icon>
{{ fieldTypes(scope.row.deExtractType) }} {{ fieldTypes(scope.row.deExtractType) }}
</span> </span>
<span v-else style="color: #8d9199">{{ t('dataset.calc_field') }}</span>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -2328,7 +2384,13 @@ const getDsIconName = data => {
> >
<template #default="scope"> <template #default="scope">
<div class="column-style"> <div class="column-style">
<span class="flex-align-center icon" v-if="scope.row.extField === 0"> <span style="color: #8d9199" v-if="scope.row.extField === 2">{{
t('dataset.calc_field')
}}</span>
<span style="color: #8d9199" v-else-if="scope.row.extField === 3">{{
t('dataset.grouping_field')
}}</span>
<span class="flex-align-center icon" v-else-if="scope.row.extField === 0">
<el-icon> <el-icon>
<Icon className="green-color" <Icon className="green-color"
><component ><component
@ -2339,7 +2401,6 @@ const getDsIconName = data => {
</el-icon> </el-icon>
{{ fieldTypes(scope.row.deExtractType) }} {{ fieldTypes(scope.row.deExtractType) }}
</span> </span>
<span v-else style="color: #8d9199">{{ t('dataset.calc_field') }}</span>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -2490,7 +2551,7 @@ const getDsIconName = data => {
<el-drawer <el-drawer
:title="t('dataset.edit_union_relation')" :title="t('dataset.edit_union_relation')"
v-model="editUnion" v-model="editUnion"
custom-class="union-dataset-drawer" modal-class="union-dataset-drawer"
size="840px" size="840px"
:before-close="closeEditUnion" :before-close="closeEditUnion"
direction="rtl" direction="rtl"
@ -2508,7 +2569,7 @@ const getDsIconName = data => {
ref="creatDsFolder" ref="creatDsFolder"
></creat-ds-group> ></creat-ds-group>
<el-dialog <el-dialog
custom-class="calc-field-edit-dialog" modal-class="calc-field-edit-dialog"
v-model="editCalcField" v-model="editCalcField"
width="1000px" width="1000px"
:title="calcTitle" :title="calcTitle"
@ -2651,6 +2712,7 @@ const getDsIconName = data => {
style="width: 100%" style="width: 100%"
multiple multiple
collapse-tags collapse-tags
filterable
collapse-tags-tooltip collapse-tags-tooltip
:max-collapse-tags="2" :max-collapse-tags="2"
v-model="domain.text" v-model="domain.text"
@ -2659,6 +2721,7 @@ const getDsIconName = data => {
v-for="item in enumValue" v-for="item in enumValue"
:key="item" :key="item"
:label="item" :label="item"
:disabled="disabledEnum(item, domain.text)"
:value="item" :value="item"
/> </el-select /> </el-select
></el-form-item> ></el-form-item>
@ -2753,7 +2816,7 @@ const getDsIconName = data => {
{{ t('auth.add_condition') }} {{ t('auth.add_condition') }}
</el-button> </el-button>
<div class="line"></div> <div class="line"></div>
<div class="group-fields_item"> <div class="group-fields_item" style="align-items: center">
<el-input <el-input
:placeholder="t('common.inputText')" :placeholder="t('common.inputText')"
style="width: 278px; margin-right: 24px" style="width: 278px; margin-right: 24px"
@ -2847,7 +2910,7 @@ const getDsIconName = data => {
height: 24px; height: 24px;
:deep(.ed-input__wrapper) { :deep(.ed-input__wrapper) {
background-color: #050e21; background-color: #050e21;
border: 1px solid #0089ff; box-shadow: 0 0 0 1px var(--ed-color-primary);
padding: 0 4px; padding: 0 4px;
} }
:deep(.ed-input__inner) { :deep(.ed-input__inner) {
@ -2963,7 +3026,7 @@ const getDsIconName = data => {
z-index: 10; z-index: 10;
&:hover { &:hover {
.ed-icon { .ed-icon {
color: var(--ed-color-primary, #3370ff); color: var(--ed-color-primary, #3370ff) !important;
} }
} }
} }
@ -3203,8 +3266,8 @@ const getDsIconName = data => {
} }
.ed-button.is-secondary.is-disabled { .ed-button.is-secondary.is-disabled {
color: #5f5f5f !important; color: #bbbfc4 !important;
border-color: #5f5f5f !important; border-color: #bbbfc4 !important;
} }
.father .child { .father .child {

View File

@ -397,6 +397,7 @@ const save = ({ logic, items, errorMessage }) => {
table.value.id = nodeInfo.id table.value.id = nodeInfo.id
table.value.row = 100000 table.value.row = 100000
table.value.filename = exportForm.value.name table.value.filename = exportForm.value.name
table.value.dataEaseBi = isDataEaseBi.value || appStore.getIsIframe
if (errorMessage) { if (errorMessage) {
ElMessage.error(errorMessage) ElMessage.error(errorMessage)
return return
@ -405,10 +406,17 @@ const save = ({ logic, items, errorMessage }) => {
exportDatasetLoading.value = true exportDatasetLoading.value = true
exportDatasetData(table.value) exportDatasetData(table.value)
.then(res => { .then(res => {
if (res.code === 0) { if (isDataEaseBi.value || appStore.getIsIframe) {
openMessageLoading(exportData) const blob = new Blob([res.data], { type: 'application/vnd.ms-excel' })
const link = document.createElement('a')
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
link.download = table.value.filename + '.xlsx' //
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} else { } else {
ElMessage.error(res.msg) openMessageLoading(exportData)
} }
}) })
.finally(() => { .finally(() => {
@ -1040,8 +1048,8 @@ const proxyAllowDrop = debounce((arg1, arg2) => {
key="structPreview" key="structPreview"
:columns="columns" :columns="columns"
v-loading="dataPreviewLoading" v-loading="dataPreviewLoading"
header-class="header-cell"
:data="tableData" :data="tableData"
header-class="excel-header-cell"
:width="width" :width="width"
:height="height" :height="height"
fixed fixed
@ -1057,7 +1065,6 @@ const proxyAllowDrop = debounce((arg1, arg2) => {
<el-table <el-table
v-loading="dataPreviewLoading" v-loading="dataPreviewLoading"
class="dataset-preview_table" class="dataset-preview_table"
header-class="header-cell"
:data="tableData" :data="tableData"
@row-click="rowClick" @row-click="rowClick"
key="dataPreview" key="dataPreview"
@ -1217,6 +1224,10 @@ const proxyAllowDrop = debounce((arg1, arg2) => {
background: #eff0f1; background: #eff0f1;
} }
} }
.custom-tree {
height: calc(100vh - 172px);
padding: 0 8px;
}
.dataset-manage { .dataset-manage {
display: flex; display: flex;
width: 100%; width: 100%;
@ -1226,6 +1237,9 @@ const proxyAllowDrop = debounce((arg1, arg2) => {
&.de-100vh { &.de-100vh {
height: 100vh; height: 100vh;
.custom-tree {
height: calc(100vh - 122px);
}
} }
.resource-area { .resource-area {
@ -1392,11 +1406,6 @@ const proxyAllowDrop = debounce((arg1, arg2) => {
} }
} }
.custom-tree {
height: calc(100vh - 172px);
padding: 0 8px;
}
.custom-tree-node { .custom-tree-node {
width: calc(100% - 30px); width: calc(100% - 30px);
display: flex; display: flex;

View File

@ -2,10 +2,15 @@
import icon_expandRight_filled from '@/assets/svg/icon_expand-right_filled.svg' import icon_expandRight_filled from '@/assets/svg/icon_expand-right_filled.svg'
import { ref } from 'vue' import { ref } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { timestampFormatDate } from '../dataset/form/util'
import { useI18n } from '@/hooks/web/useI18n'
defineProps({ defineProps({
name: propTypes.string.def('') name: propTypes.string.def(''),
time: propTypes.number.def(0),
showTime: propTypes.bool.def(false)
}) })
const active = ref(true) const active = ref(true)
const { t } = useI18n()
defineExpose({ defineExpose({
active active
}) })
@ -18,6 +23,9 @@ defineExpose({
<Icon name="icon_expand-right_filled"><icon_expandRight_filled class="svg-icon" /></Icon> <Icon name="icon_expand-right_filled"><icon_expandRight_filled class="svg-icon" /></Icon>
</el-icon> </el-icon>
<span class="name">{{ name }}</span> <span class="name">{{ name }}</span>
<span v-show="showTime" class="time">
{{ t('data_source.data_time') }}{{ timestampFormatDate(time) }}</span
>
</p> </p>
<slot :active="active"></slot> <slot :active="active"></slot>
</div> </div>
@ -60,7 +68,12 @@ defineExpose({
line-height: 24px; line-height: 24px;
margin-left: 8px; margin-left: 8px;
} }
.time {
color: #8f959e;
font-size: 14px;
line-height: 22px;
padding: 0 0 0 8px;
}
&.active { &.active {
.title { .title {
.ed-icon { .ed-icon {

View File

@ -8,7 +8,7 @@ defineProps({
<template> <template>
<div class="base-info-item"> <div class="base-info-item">
<p class="label">{{ label }}</p> <p class="label">{{ label }}</p>
<p class="value"> <p class="value ellipsis">
<slot></slot> <slot></slot>
</p> </p>
</div> </div>

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import icon_excel from '@/assets/svg/icon_excel.svg' import icon_excel from '@/assets/svg/icon_excel.svg'
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg' import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
const props = withDefaults( withDefaults(
defineProps<{ defineProps<{
name?: string name?: string
size?: number size?: number
@ -41,9 +41,8 @@ const del = () => {
align-items: center; align-items: center;
width: 100%; width: 100%;
height: 58px; height: 58px;
padding: 0 16px 0 12px; padding: 0 0 0 0;
border-radius: 4px; border-radius: 4px;
border: 1px solid #dee0e3;
.excel { .excel {
font-size: 32px; font-size: 32px;
margin-right: 14.67px; margin-right: 14.67px;

View File

@ -0,0 +1,54 @@
<script lang="ts" setup>
import icon_excel from '@/assets/svg/icon_excel.svg'
withDefaults(
defineProps<{
name?: string
size?: number
showDel?: boolean
}>(),
{
name: '',
size: 0
}
)
</script>
<template>
<div class="excel-info_base">
<el-icon class="excel">
<Icon name="icon_excel"><icon_excel class="svg-icon" /></Icon>
</el-icon>
<p :title="name" class="name ellipsis">{{ name || '-' }}</p>
<p class="size">{{ size || '-' }}</p>
</div>
</template>
<style lang="less" scoped>
.excel-info_base {
display: flex;
align-items: center;
width: 100%;
height: 24px;
padding: 0 0 0 0;
border-radius: 4px;
font-weight: 400;
.excel {
font-size: 24px;
margin-right: 4px;
}
.name {
color: #1f2329;
font-size: 14px;
max-width: calc(100% - 112px);
margin-right: 12px;
line-height: 22px;
}
.size {
color: #8f959e;
font-size: 14px;
line-height: 22px;
}
}
</style>

View File

@ -66,7 +66,7 @@ const handleChange = (val: boolean) => {
</el-icon> </el-icon>
<div class="info"> <div class="info">
<p class="name">{{ $t('auth.dataset') }}</p> <p class="name">{{ $t('auth.dataset') }}</p>
<p class="size">{{ t('data_source.or_large_screen') }}1</p> <p class="size">{{ t('data_source.or_large_screen') }}</p>
</div> </div>
<el-button class="create" secondary :disabled="disabled" @click="createDataset"> <el-button class="create" secondary :disabled="disabled" @click="createDataset">
{{ t('data_source.go_to_create') }} {{ t('data_source.go_to_create') }}

View File

@ -190,7 +190,17 @@ const initApiItem = (
) => { ) => {
pluginDs.value = pluginDsList pluginDs.value = pluginDsList
pluginIndex.value = indexPlugin pluginIndex.value = indexPlugin
if (!isPluginDs) {
const arr = pluginDs.value.filter(ele => {
return ele.type === from.type
})
if (arr && arr.length > 0) {
isPlugin.value = true
}
} else {
isPlugin.value = isPluginDs isPlugin.value = isPluginDs
}
copyItem.value = val.copy copyItem.value = val.copy
copyDs.value = from.copy copyDs.value = from.copy
dsType.value = from.type dsType.value = from.type
@ -354,6 +364,12 @@ const saveItem = () => {
} }
} }
returnAPIItem('returnItem', cloneDeep(apiItem)) returnAPIItem('returnItem', cloneDeep(apiItem))
if (isPlugin.value) {
xpackApiItemBasicInfo?.value?.invokeMethod({
methodName: 'resetForm',
args: []
})
}
edit_api_item.value = false edit_api_item.value = false
} }
const before = () => { const before = () => {
@ -450,12 +466,14 @@ const validateItem = () => {
} }
cancelMap['/datasource/checkApiDatasource']?.() cancelMap['/datasource/checkApiDatasource']?.()
const params = Base64.encode(JSON.stringify(paramsList)) const params = Base64.encode(JSON.stringify(paramsList))
formLoading.value = true
checkApiItem({ checkApiItem({
dsType: dsType.value, dsType: dsType.value,
data: Base64.encode(JSON.stringify(apiItem)), data: Base64.encode(JSON.stringify(apiItem)),
paramsList: params paramsList: params
}) })
.then(response => { .then(response => {
formLoading.value = false
apiItem.jsonFields = response.data.jsonFields apiItem.jsonFields = response.data.jsonFields
apiItem.fields = [] apiItem.fields = []
apiItem.name = response.data.name apiItem.name = response.data.name
@ -464,6 +482,7 @@ const validateItem = () => {
ElMessage.success(t('datasource.validate_success')) ElMessage.success(t('datasource.validate_success'))
}) })
.catch(() => { .catch(() => {
formLoading.value = false
ElMessage.error(t('data_source.verification_failed')) ElMessage.error(t('data_source.verification_failed'))
}) })
} }
@ -483,6 +502,12 @@ const handleSubmit = param => {
const closeEditItem = () => { const closeEditItem = () => {
cancelMap['/datasource/checkApiDatasource']?.() cancelMap['/datasource/checkApiDatasource']?.()
if (isPlugin.value) {
xpackApiItemBasicInfo?.value?.invokeMethod({
methodName: 'resetForm',
args: []
})
}
edit_api_item.value = false edit_api_item.value = false
} }
@ -645,35 +670,25 @@ defineExpose({
activeName === 'table' ? t('datasource.data_table') : t('data_source.interface_parameters') activeName === 'table' ? t('datasource.data_table') : t('data_source.interface_parameters')
" "
v-model="edit_api_item" v-model="edit_api_item"
custom-class="api-datasource-drawer" modal-class="api-datasource-drawer"
size="1000px" size="1000px"
:before-close="closeEditItem" :before-close="closeEditItem"
direction="rtl" direction="rtl"
> >
<div class="flex-center"> <div style="display: flex; width: 100%; justify-content: center">
<el-steps :active="active" align-center> <el-steps custom style="max-width: 400px; flex: 1" :active="active" align-center>
<el-step> <el-step>
<template #icon> <template #title>
<div class="step-icon"> {{ t('datasource.api_step_1') }}
<span class="icon">
{{ active <= 0 ? '1' : '' }}
</span>
<span class="title">{{ t('datasource.api_step_1') }}</span>
</div>
</template> </template>
</el-step> </el-step>
<el-step> <el-step>
<template #icon> <template #title>
<div class="step-icon"> {{
<span class="icon">
{{ active <= 1 ? '2' : '' }}
</span>
<span class="title">{{
activeName === 'table' activeName === 'table'
? t('datasource.api_step_2') ? t('datasource.api_step_2')
: t('data_source.extract_parameters') : t('data_source.extract_parameters')
}}</span> }}
</div>
</template> </template>
</el-step> </el-step>
</el-steps> </el-steps>
@ -979,7 +994,7 @@ defineExpose({
</el-row> </el-row>
<template #footer> <template #footer>
<el-button secondary @click="closeEditItem">{{ t('common.cancel') }}</el-button> <el-button secondary @click="closeEditItem">{{ t('common.cancel') }}</el-button>
<el-button v-show="active === 0" secondary @click="validate" <el-button v-show="active === 0" :disabled="formLoading" secondary @click="validate"
>{{ t('commons.validate') }} >{{ t('commons.validate') }}
</el-button> </el-button>
<el-button type="primary" v-show="active === 0" :disabled="disabledNext" @click="next" <el-button type="primary" v-show="active === 0" :disabled="disabledNext" @click="next"
@ -996,84 +1011,16 @@ defineExpose({
<style lang="less"> <style lang="less">
.api-datasource-drawer { .api-datasource-drawer {
.select-type { .select-type {
.ed-select__prefix--light { .ed-select__prefix {
border-right: none;
padding: 0;
font-size: 16px; font-size: 16px;
&::after {
display: none;
}
} }
} }
.ed-drawer__body { .ed-drawer__body {
padding: 24px 24px 80px 24px !important; padding: 24px 24px 80px 24px !important;
} }
.flex-center {
.ed-steps {
width: 630px;
}
.ed-step.is-center .ed-step__line {
width: 208px;
right: 104px;
z-index: 5;
left: calc(100% - 104px);
}
.ed-step__icon.is-icon {
width: auto;
position: relative;
z-index: 0;
}
.ed-step__head.is-finish::after {
right: calc(100% - 133px);
top: 44%;
}
.ed-step__head.is-process .ed-step__icon {
background-color: transparent;
.step-icon {
.icon {
background: var(--ed-color-primary);
}
}
}
.ed-step__head.is-finish .ed-step__icon {
background-color: transparent;
.step-icon {
.icon {
border: 1px solid var(--ed-color-primary);
}
}
}
.ed-step__head.is-wait .ed-step__icon {
background-color: transparent;
.step-icon {
.icon {
color: #8f959e;
border: 1px solid #8f959e;
}
}
}
.step-icon {
display: flex;
padding: 0 48px;
align-items: center;
.icon {
width: 28px;
height: 28px;
line-height: 27px;
border-radius: 50%;
}
.title {
margin-left: 8px;
color: #1f2329;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
.ed-form { .ed-form {
width: 100%; width: 100%;

View File

@ -3,7 +3,7 @@ import icon_drag_outlined from '@/assets/svg/icon_drag_outlined.svg'
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg' import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg' import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { computed, onBeforeMount, PropType, toRefs, inject, ref } from 'vue' import { computed, onBeforeMount, PropType, toRefs, inject } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { KeyValue } from './ApiTestModel.js' import { KeyValue } from './ApiTestModel.js'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'

View File

@ -291,7 +291,7 @@ const saveDataset = () => {
request.apiConfiguration = '' request.apiConfiguration = ''
checkRepeat(request).then(res => { checkRepeat(request).then(res => {
let method = request.id === '' ? save : update let method = request.id === '' ? save : update
if (!request.type.startsWith('API')) { if (!request.type.startsWith('API') && request.type !== 'ExcelRemote') {
request.syncSetting = null request.syncSetting = null
} }
if (res) { if (res) {

View File

@ -9,7 +9,7 @@ import deDelete from '@/assets/svg/de-delete.svg'
import icon_warning_filled from '@/assets/svg/icon_warning_filled.svg' import icon_warning_filled from '@/assets/svg/icon_warning_filled.svg'
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg' import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg' import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import { ref, reactive, h, computed, toRefs, nextTick, watch } from 'vue' import { ref, reactive, computed, toRefs, nextTick, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import type { FormInstance, FormRules } from 'element-plus-secondary' import type { FormInstance, FormRules } from 'element-plus-secondary'
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue' import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
@ -24,7 +24,7 @@ import { CustomPassword } from '@/components/custom-password'
import { ElForm, ElMessage, ElMessageBox } from 'element-plus-secondary' import { ElForm, ElMessage, ElMessageBox } from 'element-plus-secondary'
import Cron from '@/components/cron/src/Cron.vue' import Cron from '@/components/cron/src/Cron.vue'
import { ComponentPublicInstance } from 'vue' import { ComponentPublicInstance } from 'vue'
import { PluginComponent, XpackComponent } from '@/components/plugin' import { XpackComponent } from '@/components/plugin'
import { iconFieldMap } from '@/components/icon-group/field-list' import { iconFieldMap } from '@/components/icon-group/field-list'
import { boolean } from 'mathjs' import { boolean } from 'mathjs'
const { t } = useI18n() const { t } = useI18n()
@ -82,6 +82,9 @@ const state = reactive({
}) })
const schemas = ref([]) const schemas = ref([])
const targetCharset = ref(['GBK', 'UTF-8'])
const charset = ref(['GBK', 'BIG5', 'ISO-8859-1', 'UTF-8', 'UTF-16', 'CP850', 'EUC_JP', 'EUC_KR'])
const loading = ref(false) const loading = ref(false)
const dsForm = ref<FormInstance>() const dsForm = ref<FormInstance>()
@ -106,8 +109,6 @@ const defaultRule = {
const rule = ref<FormRules>(cloneDeep(defaultRule)) const rule = ref<FormRules>(cloneDeep(defaultRule))
const api_table_title = ref('') const api_table_title = ref('')
const editApiItem = ref() const editApiItem = ref()
const xpack = ref()
const visible = ref(false)
const defaultApiItem = { const defaultApiItem = {
name: '', name: '',
deTableName: '', deTableName: '',
@ -446,35 +447,6 @@ const addApiItem = item => {
}) })
} }
const addLarkItem = item => {
let apiItem = null
let editItem = false
api_table_title.value = t('datasource.data_table')
if (item) {
apiItem = cloneDeep(item)
editItem = true
} else {
apiItem = cloneDeep(defaultApiItem)
apiItem.type = activeName.value
let serialNumber1 =
form.value.apiConfiguration.length > 0
? form.value.apiConfiguration[form.value.apiConfiguration.length - 1].serialNumber + 1
: 0
let serialNumber2 =
form.value.paramsConfiguration && form.value.paramsConfiguration.length > 0
? form.value.paramsConfiguration[form.value.paramsConfiguration.length - 1].serialNumber + 1
: 0
apiItem.serialNumber = serialNumber1 + serialNumber2
}
visible.value = true
nextTick(() => {
xpack?.value?.invokeMethod({
methodName: 'initApiItem',
args: [apiItem, form.value, activeName.value, editItem, isSupportSetKey.value]
})
})
}
const activeName = ref('table') const activeName = ref('table')
const showPriority = ref(false) const showPriority = ref(false)
const showSSH = ref(false) const showSSH = ref(false)
@ -656,7 +628,6 @@ const apiRule = {
} }
const dialogEditParams = ref(false) const dialogEditParams = ref(false)
const dialogRenameApi = ref(false) const dialogRenameApi = ref(false)
const dialogAddLarkItem = ref(false)
const activeParamsName = ref('') const activeParamsName = ref('')
const activeParamsID = ref(0) const activeParamsID = ref(0)
const paramsObj = ref({ const paramsObj = ref({
@ -773,21 +744,6 @@ const handleApiParams = (cmd: string, data) => {
} }
} }
const editParams = data => {
dialogEditParams.value = true
}
const getPluginStatic = () => {
const arr = pluginDs.value.filter(ele => {
return ele.type === form.value.type
})
return pluginIndex.value
? pluginIndex.value
: arr && arr.length > 0
? arr[0].staticMap?.index
: null
}
const delParams = data => { const delParams = data => {
ElMessageBox.confirm(t('data_source.sure_to_delete'), { ElMessageBox.confirm(t('data_source.sure_to_delete'), {
confirmButtonType: 'danger', confirmButtonType: 'danger',
@ -848,7 +804,7 @@ defineExpose({
v-loading="loading" v-loading="loading"
> >
<el-form-item <el-form-item
:label="t('auth.datasource') + ' ' + t('chart.name')" :label="t('data_source.data_source_name')"
prop="name" prop="name"
v-show="activeStep !== 2" v-show="activeStep !== 2"
> >
@ -881,7 +837,9 @@ defineExpose({
</el-tabs> </el-tabs>
<el-button type="primary" style="margin-left: auto" @click="() => addApiItem(null)"> <el-button type="primary" style="margin-left: auto" @click="() => addApiItem(null)">
<template #icon> <template #icon>
<Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon> <Icon name="icon_add_outlined">
<icon_add_outlined class="svg-icon" />
</Icon>
</template> </template>
{{ t('common.add') }} {{ t('common.add') }}
</el-button> </el-button>
@ -914,7 +872,9 @@ defineExpose({
</el-col> </el-col>
<el-col style="text-align: right" :span="5"> <el-col style="text-align: right" :span="5">
<el-icon class="de-copy-icon hover-icon" @click.stop="copyItem(api)"> <el-icon class="de-copy-icon hover-icon" @click.stop="copyItem(api)">
<Icon name="de-copy"><deCopy class="svg-icon" /></Icon> <Icon name="de-copy">
<deCopy class="svg-icon" />
</Icon>
</el-icon> </el-icon>
<span @click.stop> <span @click.stop>
@ -1012,13 +972,13 @@ defineExpose({
<template #default="scope"> <template #default="scope">
<div class="flex-align-center icon"> <div class="flex-align-center icon">
<el-icon> <el-icon>
<Icon <Icon>
><component <component
class="svg-icon" class="svg-icon"
:class="`field-icon-${fieldType[scope.row.deType]}`" :class="`field-icon-${fieldType[scope.row.deType]}`"
:is="iconFieldMap[fieldType[scope.row.deType]]" :is="iconFieldMap[fieldType[scope.row.deType]]"
></component ></component>
></Icon> </Icon>
</el-icon> </el-icon>
{{ fieldTypeText[scope.row.deType] }} {{ fieldTypeText[scope.row.deType] }}
</div> </div>
@ -1029,9 +989,9 @@ defineExpose({
<template #default="scope"> <template #default="scope">
<el-button text @click.stop="delParams(scope.row)"> <el-button text @click.stop="delParams(scope.row)">
<template #icon> <template #icon>
<Icon name="icon_delete-trash_outlined" <Icon name="icon_delete-trash_outlined">
><icon_deleteTrash_outlined class="svg-icon" <icon_deleteTrash_outlined class="svg-icon" />
/></Icon> </Icon>
</template> </template>
</el-button> </el-button>
</template> </template>
@ -1193,7 +1153,9 @@ defineExpose({
<span class="name">{{ t('datasource.schema') }}<i class="required" /></span> <span class="name">{{ t('datasource.schema') }}<i class="required" /></span>
<el-button text size="small" @click="getDsSchema()"> <el-button text size="small" @click="getDsSchema()">
<template #icon> <template #icon>
<Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon> <Icon name="icon_add_outlined">
<icon_add_outlined class="svg-icon" />
</Icon>
</template> </template>
{{ t('datasource.get_schema') }} {{ t('datasource.get_schema') }}
</el-button> </el-button>
@ -1208,6 +1170,26 @@ defineExpose({
@blur="validatorSchema" @blur="validatorSchema"
/> />
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 'oracle'" :label="$t('datasource.charset')">
<el-select
v-model="form.configuration.charset"
filterable
:placeholder="$t('datasource.please_choose_charset')"
class="de-select"
>
<el-option v-for="item in charset" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type == 'oracle'" :label="$t('datasource.targetCharset')">
<el-select
v-model="form.configuration.targetCharset"
filterable
:placeholder="$t('datasource.please_choose_targetCharset')"
class="de-select"
>
<el-option v-for="item in targetCharset" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item <el-form-item
:label="t('datasource.extra_params')" :label="t('datasource.extra_params')"
v-if="form.configuration.urlType !== 'jdbcUrl' && form.type !== 'es'" v-if="form.configuration.urlType !== 'jdbcUrl' && form.type !== 'es'"
@ -1236,9 +1218,9 @@ defineExpose({
</el-form-item> </el-form-item>
<template v-if="showSSH"> <template v-if="showSSH">
<el-form-item> <el-form-item>
<el-checkbox v-model="form.configuration.useSSH">{{ <el-checkbox v-model="form.configuration.useSSH"
t('data_source.enable_ssh') >{{ t('data_source.enable_ssh') }}
}}</el-checkbox> </el-checkbox>
</el-form-item> </el-form-item>
<el-form-item :label="t('data_source.host')" prop="configuration.sshHost"> <el-form-item :label="t('data_source.host')" prop="configuration.sshHost">
<el-input <el-input
@ -1468,7 +1450,7 @@ defineExpose({
<el-form-item v-if="form.syncSetting.syncRate === 'CRON'" prop="syncSetting.cron"> <el-form-item v-if="form.syncSetting.syncRate === 'CRON'" prop="syncSetting.cron">
<el-popover :width="834" v-model="cronEdit" trigger="click"> <el-popover :width="834" v-model="cronEdit" trigger="click">
<template #default> <template #default>
<div style="width: 814px; height: 400px; overflow-y: auto"> <div style="width: 814px; height: 450px; overflow-y: auto">
<cron <cron
v-if="showCron" v-if="showCron"
v-model="form.syncSetting.cron" v-model="form.syncSetting.cron"
@ -1580,6 +1562,7 @@ defineExpose({
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
.ed-radio { .ed-radio {
height: 22px; height: 22px;
} }
@ -1598,6 +1581,7 @@ defineExpose({
.de-select { .de-select {
width: 100%; width: 100%;
} }
.ed-input-number { .ed-input-number {
width: 100%; width: 100%;
} }
@ -1625,16 +1609,20 @@ defineExpose({
.ed-input__wrapper { .ed-input__wrapper {
width: 100%; width: 100%;
} }
width: 100%; width: 100%;
} }
.simple-cron { .simple-cron {
height: 32px; height: 32px;
.ed-select, .ed-select,
.ed-input-number { .ed-input-number {
width: 140px; width: 140px;
margin: 0 8px; margin: 0 8px;
} }
} }
.detail-inner { .detail-inner {
width: 800px; width: 800px;
padding-top: 8px; padding-top: 8px;
@ -1655,6 +1643,7 @@ defineExpose({
border: 1px solid #bbbfc4; border: 1px solid #bbbfc4;
width: 300px; width: 300px;
padding: 16px; padding: 16px;
.name-copy { .name-copy {
display: none; display: none;
line-height: 24px; line-height: 24px;
@ -1682,6 +1671,7 @@ defineExpose({
.table-info-mr { .table-info-mr {
margin: 28px 0 12px 0; margin: 28px 0 12px 0;
.api-tabs { .api-tabs {
:deep(.ed-tabs__nav-wrap::after) { :deep(.ed-tabs__nav-wrap::after) {
display: none; display: none;
@ -1718,6 +1708,7 @@ defineExpose({
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
line-height: 22px; line-height: 22px;
&::before { &::before {
width: 8px; width: 8px;
height: 8px; height: 8px;
@ -1758,6 +1749,7 @@ defineExpose({
flex-wrap: wrap; flex-wrap: wrap;
margin-left: -16px; margin-left: -16px;
} }
.api-card { .api-card {
height: 120px; height: 120px;
width: 392px; width: 392px;
@ -1772,20 +1764,24 @@ defineExpose({
&:hover { &:hover {
border-color: var(--ed-color-primary); border-color: var(--ed-color-primary);
} }
.name { .name {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
margin-right: 8px; margin-right: 8px;
max-width: 70%; max-width: 70%;
} }
.req-title, .req-title,
.req-value { .req-value {
display: flex; display: flex;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
:nth-child(1) { :nth-child(1) {
width: 120px; width: 120px;
} }
:nth-child(2) { :nth-child(2) {
margin-left: 24px; margin-left: 24px;
max-width: 230px; max-width: 230px;
@ -1794,20 +1790,25 @@ defineExpose({
white-space: nowrap; white-space: nowrap;
} }
} }
.req-title { .req-title {
color: var(--deTextSecondary, #646a73); color: var(--deTextSecondary, #646a73);
margin: 16px 0 4px 0; margin: 16px 0 4px 0;
} }
.req-value { .req-value {
color: var(--deTextPrimary, #1f2329); color: var(--deTextPrimary, #1f2329);
} }
.de-copy-icon { .de-copy-icon {
margin-right: 16px; margin-right: 16px;
color: var(--deTextSecondary, #646a73); color: var(--deTextSecondary, #646a73);
} }
.de-delete-icon { .de-delete-icon {
cursor: pointer; cursor: pointer;
} }
.de-tag { .de-tag {
display: inline-flex; display: inline-flex;
justify-content: center; justify-content: center;
@ -1835,6 +1836,7 @@ defineExpose({
padding: 20px 24px !important; padding: 20px 24px !important;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
.small { .small {
height: 28px; height: 28px;
min-width: 48px !important; min-width: 48px !important;
@ -1852,11 +1854,13 @@ defineExpose({
margin-left: 8.67px; margin-left: 8.67px;
color: var(--deTextPrimary, #1f2329); color: var(--deTextPrimary, #1f2329);
} }
i { i {
font-size: 14.666666030883789px; font-size: 14.666666030883789px;
color: var(--deWarning, #ff8800); color: var(--deWarning, #ff8800);
line-height: 22px; line-height: 22px;
} }
.foot { .foot {
text-align: right; text-align: right;
width: 100%; width: 100%;
@ -1869,9 +1873,11 @@ defineExpose({
display: flex !important; display: flex !important;
justify-content: space-between; justify-content: space-between;
padding-right: 0; padding-right: 0;
&::after { &::after {
display: none; display: none;
} }
.name { .name {
.required::after { .required::after {
content: '*'; content: '*';

View File

@ -1,6 +1,5 @@
<script lang="tsx" setup> <script lang="tsx" setup>
import icon_upload_outlined from '@/assets/svg/icon_upload_outlined.svg' import icon_upload_outlined from '@/assets/svg/icon_upload_outlined.svg'
import icon_refresh_outlined from '@/assets/svg/icon_refresh_outlined.svg'
import { Icon } from '@/components/icon-custom' import { Icon } from '@/components/icon-custom'
import { ElIcon } from 'element-plus-secondary' import { ElIcon } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -15,6 +14,7 @@ import {
onBeforeUnmount, onBeforeUnmount,
nextTick nextTick
} from 'vue' } from 'vue'
import { fieldType as fieldTypeLowercase } from '@/utils/attr'
import { ElMessage, ElMessageBox } from 'element-plus-secondary' import { ElMessage, ElMessageBox } from 'element-plus-secondary'
import { save, update } from '@/api/datasource' import { save, update } from '@/api/datasource'
import type { Action } from 'element-plus-secondary' import type { Action } from 'element-plus-secondary'
@ -36,6 +36,7 @@ export interface Param {
creator?: string creator?: string
isPlugin?: boolean isPlugin?: boolean
staticMap?: any staticMap?: any
configuration?: {}
} }
export interface Field { export interface Field {
@ -44,6 +45,10 @@ export interface Field {
fieldSize: number fieldSize: number
fieldType: string fieldType: string
name: string name: string
deExtractType: number
checked: boolean
primaryKey: boolean
length: number
} }
const props = defineProps({ const props = defineProps({
param: { param: {
@ -116,10 +121,17 @@ const fieldType = {
DOUBLE: 'value' DOUBLE: 'value'
} }
const fieldTypeToStr = {
0: 'TEXT',
2: 'LONG',
3: 'DOUBLE'
}
const generateColumns = (arr: Field[]) => const generateColumns = (arr: Field[]) =>
arr.map(ele => ({ arr.map(ele => ({
key: ele.originName, key: ele.originName,
fieldType: ele.fieldType, fieldType: ele.fieldType,
deExtractType: ele.deExtractType,
dataKey: ele.originName, dataKey: ele.originName,
title: ele.name, title: ele.name,
checked: ele.checked, checked: ele.checked,
@ -325,52 +337,6 @@ const saveExcelData = (sheetFileMd5, table, params, successCb, finallyCb) => {
} else { } else {
method = update method = update
} }
if (new Set(sheetFileMd5).size !== sheetFileMd5.length && !props.param.id) {
ElMessageBox.confirm(t('dataset.merge_title'), {
confirmButtonText: t('dataset.merge'),
tip: t('dataset.task.excel_replace_msg'),
cancelButtonText: t('dataset.no_merge'),
confirmButtonType: 'primary',
type: 'warning',
autofocus: false,
callback: (action: Action) => {
if (action === 'close') return
loading.value = true
table.mergeSheet = action === 'confirm'
if (action === 'confirm') {
method(table)
.then(res => {
emitter.emit('showFinishPage', res)
successCb?.()
ElMessage({
message: t('commons.save_success'),
type: 'success'
})
})
.finally(() => {
finallyCb?.()
loading.value = false
})
}
if (action === 'cancel') {
method(table)
.then(res => {
emitter.emit('showFinishPage', res)
successCb?.()
ElMessage({
message: t('commons.save_success'),
type: 'success'
})
})
.finally(() => {
finallyCb?.()
loading.value = false
})
}
}
})
} else {
if (loading.value) return if (loading.value) return
loading.value = true loading.value = true
method(table) method(table)
@ -387,7 +353,6 @@ const saveExcelData = (sheetFileMd5, table, params, successCb, finallyCb) => {
loading.value = false loading.value = false
}) })
} }
}
const onChange = file => { const onChange = file => {
state.fileList = file state.fileList = file
@ -455,8 +420,17 @@ const appendReplaceExcel = response => {
const status = ref(false) const status = ref(false)
const initMultipleTable = ref(false) const initMultipleTable = ref(false)
const currentMode = ref('preview') const currentMode = ref('preview')
const refreshData = () => {
currentMode.value = 'preview' const deExtractTypeChange = item => {
item.deType = item.deExtractType
const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
sheet.fields.forEach(row => {
if (row.originName === item.dataKey) {
row.deExtractType = item.deExtractType
row.deType = item.deExtractType
row.fieldType = fieldTypeToStr[item.deExtractType]
}
})
} }
const lengthChange = val => { const lengthChange = val => {
@ -476,6 +450,15 @@ const primaryKeyChange = val => {
}) })
} }
const fieldOptions = [
{ label: t('dataset.text'), value: 0 },
{ label: t('dataset.value'), value: 2 },
{
label: t('dataset.value') + '(' + t('dataset.float') + ')',
value: 3
}
]
const handleSelectionChange = val => { const handleSelectionChange = val => {
if (!initMultipleTable.value) { if (!initMultipleTable.value) {
multipleSelection.value = val multipleSelection.value = val
@ -517,7 +500,7 @@ const disabledFieldLength = item => {
if (!item.checked) { if (!item.checked) {
return true return true
} }
if (item.fieldType !== 'TEXT') { if (item.deExtractType !== 0) {
return true return true
} }
} }
@ -534,6 +517,9 @@ const changeCurrentMode = val => {
} }
initMultipleTable.value = false initMultipleTable.value = false
}) })
} else {
const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
handleNodeClick(sheet)
} }
} }
@ -638,14 +624,31 @@ defineExpose({
:rules="[ :rules="[
{ {
required: true, required: true,
message: t('common.please_input') + t('datasource.datasource') + t('common.name') message:
t('common.please_input') +
t('common.empty') +
t('datasource.datasource') +
t('common.empty') +
t('common.name')
} }
]" ]"
:label="t('visualization.custom') + t('datasource.datasource') + t('common.name')" :label="
t('visualization.custom') +
t('common.empty') +
t('datasource.datasource') +
t('common.empty') +
t('common.name')
"
> >
<el-input <el-input
v-model="param.name" v-model="param.name"
:placeholder="t('common.please_input') + t('datasource.datasource') + t('common.name')" :placeholder="
t('common.please_input') +
t('common.empty') +
t('datasource.datasource') +
t('common.empty') +
t('common.name')
"
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -706,20 +709,48 @@ defineExpose({
<el-table-column :label="t('data_set.field_name')"> <el-table-column :label="t('data_set.field_name')">
<template #default="scope">{{ scope.row.title }}</template> <template #default="scope">{{ scope.row.title }}</template>
</el-table-column> </el-table-column>
<el-table-column :label="t('data_set.field_type')">
<template #default="scope">
<div class="flex-align-center">
<el-icon>
<Icon>
<component
:class="`svg-icon field-icon-${fieldType[scope.row.fieldType]}`"
:is="iconFieldMap[fieldType[scope.row.fieldType]]"
></component>
</Icon>
</el-icon>
{{ t(`dataset.${fieldType[scope.row.fieldType]}`) }} <el-table-column prop="deExtractType" :label="t('datasource.field_type')">
</div> <template #default="scope">
<el-select
v-model="scope.row.deExtractType"
class="select-type"
style="display: inline-block; width: 120px"
@change="deExtractTypeChange(scope.row)"
>
<template #prefix>
<el-icon>
<Icon :className="`field-icon-${fieldTypeLowercase[scope.row.deExtractType]}`"
><component
class="svg-icon"
:class="`field-icon-${fieldTypeLowercase[scope.row.deExtractType]}`"
:is="iconFieldMap[fieldTypeLowercase[scope.row.deExtractType]]"
></component
></Icon>
</el-icon>
</template>
<el-option
v-for="item in fieldOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left">
<el-icon>
<Icon :className="`field-icon-${fieldTypeLowercase[item.value]}`"
><component
class="svg-icon"
:class="`field-icon-${fieldTypeLowercase[item.value]}`"
:is="iconFieldMap[fieldTypeLowercase[item.value]]"
></component
></Icon>
</el-icon>
</span>
<span style="float: left; font-size: 12px; color: #8492a6">{{
item.label
}}</span>
</el-option>
</el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -784,7 +815,8 @@ defineExpose({
background: #f5f6f7; background: #f5f6f7;
padding: 16px; padding: 16px;
.btn-select { .btn-select {
width: 164px; min-width: 164px;
padding: 0 6px;
height: 32px; height: 32px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -802,7 +834,7 @@ defineExpose({
} }
.ed-button.is-text { .ed-button.is-text {
height: 24px; height: 24px;
width: 74px; min-width: 74px;
line-height: 24px; line-height: 24px;
} }
.ed-button + .ed-button { .ed-button + .ed-button {

View File

@ -9,7 +9,15 @@ import DsTypeList from './DsTypeList.vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import EditorDetail from './EditorDetail.vue' import EditorDetail from './EditorDetail.vue'
import ExcelDetail from './ExcelDetail.vue' import ExcelDetail from './ExcelDetail.vue'
import { save, update, validate, latestUse, isShowFinishPage, checkRepeat } from '@/api/datasource' import {
save,
update,
validate,
latestUse,
isShowFinishPage,
checkRepeat,
loadRemoteFile
} from '@/api/datasource'
import { Base64 } from 'js-base64' import { Base64 } from 'js-base64'
import type { Param } from './ExcelDetail.vue' import type { Param } from './ExcelDetail.vue'
import type { Configuration, ApiConfiguration, SyncSetting } from './option' import type { Configuration, ApiConfiguration, SyncSetting } from './option'
@ -23,6 +31,7 @@ import { useCache } from '@/hooks/web/useCache'
import Icon from '@/components/icon-custom/src/Icon.vue' import Icon from '@/components/icon-custom/src/Icon.vue'
import { XpackComponent, PluginComponent } from '@/components/plugin' import { XpackComponent, PluginComponent } from '@/components/plugin'
import { iconDatasourceMap } from '@/components/icon-group/datasource-list' import { iconDatasourceMap } from '@/components/icon-group/datasource-list'
import ExcelRemoteDetail from '@/views/visualized/data/datasource/form/ExcelRemoteDetail.vue'
interface Node { interface Node {
name: string name: string
@ -73,6 +82,7 @@ const activeStep = ref(0)
const detail = ref() const detail = ref()
const xpack = ref() const xpack = ref()
const excel = ref() const excel = ref()
const excelRemote = ref()
const latestUseTypes = ref([]) const latestUseTypes = ref([])
const currentType = ref<DsType>('OLTP') const currentType = ref<DsType>('OLTP')
const filterText = ref('') const filterText = ref('')
@ -85,20 +95,13 @@ const selectDsType = (type: string) => {
currentDsType.value = type currentDsType.value = type
activeStep.value = 1 activeStep.value = 1
activeApiStep.value = 1 activeApiStep.value = 1
currentTypeList.value
.map(ele => ele.dbList)
.flat()
.some(ele => {
if (ele.type === currentDsType.value) {
isPlugin.value = ele['isPlugin']
}
})
nextTick(() => { nextTick(() => {
detail?.value?.initForm(type, pluginDs.value, pluginIndex.value, isPlugin.value) || detail?.value?.initForm(type, pluginDs.value, pluginIndex.value, isPlugin.value) ||
xpack?.value?.invokeMethod({ xpack?.value?.invokeMethod({
methodName: 'initForm', methodName: 'initForm',
args: type args: type
}) })
excelRemote?.value?.initForm(type)
if (!dsTree.value) return if (!dsTree.value) return
currentTypeList.value currentTypeList.value
.map(ele => ele.dbList) .map(ele => ele.dbList)
@ -204,7 +207,11 @@ const activeApiStep = ref(0)
const setNextStep = () => { const setNextStep = () => {
activeApiStep.value = activeStep.value + 1 activeApiStep.value = activeStep.value + 1
if (currentDsType.value.includes('API') && activeStep.value === 1) return if (
(currentDsType.value.includes('API') || currentDsType.value === 'ExcelRemote') &&
activeStep.value === 1
)
return
activeStep.value = activeStep.value + 1 activeStep.value = activeStep.value + 1
} }
@ -223,6 +230,14 @@ const next = () => {
return return
} }
if (currentDsType.value.includes('ExcelRemote') && activeStep.value !== 2) {
const validate = excelRemote.value.validateExcel()
if (validate) {
setNextStep()
}
return
}
if (currentDsType.value.includes('API') && activeStep.value !== 2) { if (currentDsType.value.includes('API') && activeStep.value !== 2) {
const validateFrom = detail.value.submitForm() const validateFrom = detail.value.submitForm()
validateFrom(val => { validateFrom(val => {
@ -237,7 +252,15 @@ const next = () => {
} }
const complete = (params, successCb, finallyCb) => { const complete = (params, successCb, finallyCb) => {
excel.value.saveExcelDs( excel?.value?.saveExcelDs(
params,
() => {
pid.value = params.pid
successCb()
},
finallyCb
)
excelRemote?.value?.saveExcelDs(
params, params,
() => { () => {
pid.value = params.pid pid.value = params.pid
@ -292,25 +315,14 @@ const handleShowFinishPage = ({ id, name, pid }) => {
emitter.on('showFinishPage', handleShowFinishPage) emitter.on('showFinishPage', handleShowFinishPage)
const prev = () => { const prev = () => {
if (
(currentDsType.value.includes('API') && activeApiStep.value === 1) ||
activeStep.value === 1
) {
ElMessageBox.confirm(t('data_source.the_previous_step'), {
confirmButtonType: 'primary',
type: 'warning',
autofocus: false,
showClose: false
}).then(() => {
prevConfirm() prevConfirm()
})
} else {
prevConfirm()
}
} }
const prevConfirm = () => { const prevConfirm = () => {
if (currentDsType.value.includes('API') && activeApiStep.value === 2) { if (
(currentDsType.value.includes('API') || currentDsType.value === 'ExcelRemote') &&
activeApiStep.value === 2
) {
activeApiStep.value = 1 activeApiStep.value = 1
activeStep.value = 1 activeStep.value = 1
return return
@ -369,7 +381,10 @@ const validateDS = () => {
args: [{ eventName: 'validateDs', args: request }] args: [{ eventName: 'validateDs', args: request }]
}) })
} else { } else {
const validateFrom = detail?.value?.submitForm() let validateFrom = detail?.value?.submitForm()
if (excelRemote?.value?.submitForm()) {
validateFrom = excelRemote?.value?.submitForm()
}
validateFrom(val => { validateFrom(val => {
if (val) { if (val) {
doValidateDs(request) doValidateDs(request)
@ -380,9 +395,34 @@ const validateDS = () => {
const doValidateDs = request => { const doValidateDs = request => {
dsLoading.value = true dsLoading.value = true
if (currentDsType.value === 'ExcelRemote') {
let excelRequest = JSON.parse(JSON.stringify(form2.configuration))
excelRequest.datasourceId = form2.id || 0
excelRequest.editType = form2.editType
excelRequest.userName = Base64.encode(excelRequest.userName)
excelRequest.passwd = Base64.encode(excelRequest.passwd)
return loadRemoteFile(excelRequest)
.then(res => {
dsLoading.value = false
if (!res) {
ElMessage.warning(res.msg)
return
}
if (res?.code !== 0) {
ElMessage.warning(res.msg)
return
}
ElMessage.success(t('datasource.validate_success'))
dsLoading.value = false
})
.catch(() => {
ElMessage.error(t('data_source.verification_failed'))
dsLoading.value = false
})
}
validate(request) validate(request)
.then(res => { .then(res => {
if (res.data.type === 'API') { if (res.data.type.includes('API')) {
let error = 0 let error = 0
const status = JSON.parse(res.data.status) as Array<{ status: string; name: string }> const status = JSON.parse(res.data.status) as Array<{ status: string; name: string }>
for (let i = 0; i < status.length; i++) { for (let i = 0; i < status.length; i++) {
@ -435,13 +475,13 @@ const saveDS = () => {
configuration: string configuration: string
apiConfiguration: string apiConfiguration: string
} }
if (currentDsType.value === 'Excel') { if (currentDsType.value === 'Excel') {
excel.value.uploadStatus(false) excel.value.uploadStatus(false)
if (!excel.value.sheetFile?.name) { if (!excel.value.sheetFile?.name) {
excel.value.uploadStatus(true) excel.value.uploadStatus(true)
return return
} }
const validate = excel.value.submitForm() const validate = excel.value.submitForm()
validate(val => { validate(val => {
if (val) { if (val) {
@ -452,7 +492,25 @@ const saveDS = () => {
} }
} }
}) })
return
}
if (currentDsType.value === 'ExcelRemote') {
const validate = excelRemote.value.submitForm()
validate(val => {
if (val) {
const validateApi = excelRemote?.value?.submitSyncSettingForm()
validateApi(v => {
if (v) {
if (editDs.value && form2.id) {
complete(null, null, null)
} else {
creatDsFolder.value.createInit('datasource', { id: pid.value }, '', form2.name)
}
}
})
}
})
return return
} else if (currentDsType.value.includes('API')) { } else if (currentDsType.value.includes('API')) {
for (let i = 0; i < request.apiConfiguration.length; i++) { for (let i = 0; i < request.apiConfiguration.length; i++) {
@ -569,16 +627,18 @@ const defaultForm = {
paramsConfiguration: [], paramsConfiguration: [],
enableDataFill: false enableDataFill: false
} }
const form = reactive<Form>(cloneDeep(defaultForm))
const origin = reactive<Form>(cloneDeep(defaultForm))
const defaultForm2 = { const defaultForm2 = {
type: '', type: '',
id: '0', id: '0',
editType: 0, editType: 0,
name: '', name: '',
creator: '' creator: '',
configuration: {}
} }
const origin = reactive<Form>(cloneDeep(defaultForm))
const form = reactive<Form>(cloneDeep(defaultForm))
const form2 = reactive<Param>(cloneDeep(defaultForm2)) const form2 = reactive<Param>(cloneDeep(defaultForm2))
const visible = ref(false) const visible = ref(false)
const editDs = ref(false) const editDs = ref(false)
const pid = ref('0') const pid = ref('0')
@ -611,7 +671,7 @@ const init = (nodeInfo: Form | Param, id?: string, res?: object, supportSetKey:
showFinishPage.value = false showFinishPage.value = false
if (!!nodeInfo) { if (!!nodeInfo) {
if (nodeInfo.type == 'Excel') { if (nodeInfo.type.startsWith('Excel')) {
Object.assign(form2, cloneDeep(nodeInfo)) Object.assign(form2, cloneDeep(nodeInfo))
} else { } else {
Object.assign(form, cloneDeep(nodeInfo)) Object.assign(form, cloneDeep(nodeInfo))
@ -649,6 +709,7 @@ const init = (nodeInfo: Form | Param, id?: string, res?: object, supportSetKey:
} }
nextTick(() => { nextTick(() => {
detail?.value?.clearForm() detail?.value?.clearForm()
excelRemote?.value?.clearForm()
xpack?.value?.invokeMethod({ xpack?.value?.invokeMethod({
methodName: 'clearForm', methodName: 'clearForm',
args: [] args: []
@ -663,6 +724,13 @@ const drawTitle = computed(() => {
if (creator && id && currentDsType.value == 'Excel') { if (creator && id && currentDsType.value == 'Excel') {
return editType === 1 ? t('data_source.append_data') : t('data_source.replace_data') return editType === 1 ? t('data_source.append_data') : t('data_source.replace_data')
} }
if (currentDsType.value == 'ExcelRemote') {
return editDs.value
? !form2.id
? t('data_source.copy_data_source')
: t('datasource.modify')
: t('data_source.create_data_source')
}
return editDs.value return editDs.value
? !form.id ? !form.id
? t('data_source.copy_data_source') ? t('data_source.copy_data_source')
@ -713,26 +781,16 @@ defineExpose({
> >
<template #header="{ close }"> <template #header="{ close }">
<span>{{ drawTitle }}</span> <span>{{ drawTitle }}</span>
<div v-if="!editDs" class="editor-step flex-center"> <div v-if="!editDs" class="flex-center" style="width: 100%">
<el-steps space="150px" :active="activeStep" align-center> <el-steps custom style="max-width: 500px; flex: 1" :active="activeStep" align-center>
<el-step> <el-step>
<template #icon> <template #title>
<div class="step-icon"> {{ t('deDataset.select_data_source') }}
<span class="icon">
{{ activeStep <= 0 ? '1' : '' }}
</span>
<span class="title">{{ t('deDataset.select_data_source') }}</span>
</div>
</template> </template>
</el-step> </el-step>
<el-step> <el-step>
<template #icon> <template #title>
<div class="step-icon"> {{ t('data_source.configuration_information') }}
<span class="icon">
{{ activeStep <= 1 ? '2' : '' }}
</span>
<span class="title">{{ t('data_source.configuration_information') }}</span>
</div>
</template> </template>
</el-step> </el-step>
</el-steps> </el-steps>
@ -833,7 +891,7 @@ defineExpose({
v-if=" v-if="
activeStep !== 0 && activeStep !== 0 &&
currentDsType && currentDsType &&
currentDsType !== 'Excel' && !currentDsType.startsWith('Excel') &&
visible && visible &&
(!isPlugin || currentDsType.startsWith('API')) (!isPlugin || currentDsType.startsWith('API'))
" "
@ -863,6 +921,15 @@ defineExpose({
:param="form2" :param="form2"
></excel-detail> ></excel-detail>
</template> </template>
<template v-if="activeStep !== 0 && currentDsType == 'ExcelRemote'">
<excel-remote-detail
:editDs="editDs"
:is-supportSetKey="isSupportSetKey"
ref="excelRemote"
:active-step="activeApiStep"
:form="form2"
></excel-remote-detail>
</template>
</div> </div>
</div> </div>
<div class="editor-footer"> <div class="editor-footer">
@ -883,8 +950,11 @@ defineExpose({
> >
<el-button <el-button
v-show=" v-show="
(activeStep === 0 && !currentDsType.startsWith('API')) || (activeStep === 0 &&
(activeApiStep !== 2 && currentDsType.startsWith('API')) !currentDsType.startsWith('API') &&
currentDsType !== 'ExcelRemote') ||
(activeApiStep !== 2 &&
(currentDsType.startsWith('API') || currentDsType === 'ExcelRemote'))
" "
type="primary" type="primary"
@click="next" @click="next"
@ -893,8 +963,11 @@ defineExpose({
> >
<el-button <el-button
v-show=" v-show="
(activeStep === 1 && !currentDsType.startsWith('API')) || (activeStep === 1 &&
(activeApiStep === 2 && currentDsType.startsWith('API')) !currentDsType.startsWith('API') &&
currentDsType !== 'ExcelRemote') ||
(activeApiStep === 2 &&
(currentDsType.startsWith('API') || currentDsType === 'ExcelRemote'))
" "
type="primary" type="primary"
@click="saveDS" @click="saveDS"
@ -1122,6 +1195,9 @@ defineExpose({
padding-right: 24px; padding-right: 24px;
float: left; float: left;
border-top: 1px solid rgba(31, 35, 41, 0.15); border-top: 1px solid rgba(31, 35, 41, 0.15);
position: relative;
z-index: 10;
background: #fff;
} }
} }
} }

View File

@ -111,7 +111,13 @@ export const dsTypes = [
}, },
{ {
type: 'Excel', type: 'Excel',
name: 'Excel', name: t('common.local_excel'),
catalog: 'LOCAL',
extraParams: ''
},
{
type: 'ExcelRemote',
name: t('common.remote_excel'),
catalog: 'LOCAL', catalog: 'LOCAL',
extraParams: '' extraParams: ''
} }

View File

@ -49,7 +49,8 @@ import {
uploadFile, uploadFile,
perDeleteDatasource, perDeleteDatasource,
getSimpleDs, getSimpleDs,
supportSetKey supportSetKey,
getTableStatus
} from '@/api/datasource' } from '@/api/datasource'
import CreatDsGroup from './form/CreatDsGroup.vue' import CreatDsGroup from './form/CreatDsGroup.vue'
import type { Tree } from '../dataset/form/CreatDsGroup.vue' import type { Tree } from '../dataset/form/CreatDsGroup.vue'
@ -74,7 +75,7 @@ import {
} from '@/api/datasource' } from '@/api/datasource'
import type { SyncSetting, Node } from './form/option' import type { SyncSetting, Node } from './form/option'
import EditorDatasource from './form/index.vue' import EditorDatasource from './form/index.vue'
import ExcelInfo from './ExcelInfo.vue' import ExcelInfoBase from './ExcelInfoBase.vue'
import SheetTabs from './SheetTabs.vue' import SheetTabs from './SheetTabs.vue'
import BaseInfoItem from './BaseInfoItem.vue' import BaseInfoItem from './BaseInfoItem.vue'
import BaseInfoContent from './BaseInfoContent.vue' import BaseInfoContent from './BaseInfoContent.vue'
@ -204,7 +205,7 @@ const selectDataset = row => {
Object.assign(dsTableDetail, row) Object.assign(dsTableDetail, row)
userDrawer.value = true userDrawer.value = true
dsTableDataLoading.value = true dsTableDataLoading.value = true
getTableField({ tableName: row.tableName, datasourceId: nodeInfo.id }) getTableField({ tableName: row.tableName, datasourceId: nodeInfo.id, isCross: false })
.then(res => { .then(res => {
state.dsTableData = res.data state.dsTableData = res.data
}) })
@ -707,7 +708,7 @@ const filterNode = (value: string, data: BusiTreeNode) => {
} }
const editDatasource = (editType?: number) => { const editDatasource = (editType?: number) => {
if (nodeInfo.type === 'Excel') { if (nodeInfo.type.startsWith('Excel')) {
nodeInfo.editType = editType nodeInfo.editType = editType
} }
return getById(nodeInfo.id).then(res => { return getById(nodeInfo.id).then(res => {
@ -940,7 +941,7 @@ const handleClick = (tabName: TabPaneName) => {
switch (tabName) { switch (tabName) {
case 'config': case 'config':
tableData.value = [] tableData.value = []
if (nodeInfo.type === 'Excel') { if (nodeInfo.type.startsWith('Excel')) {
listDatasourceTables({ datasourceId: nodeInfo.id }).then(res => { listDatasourceTables({ datasourceId: nodeInfo.id }).then(res => {
tabList.value = res.data.map(ele => { tabList.value = res.data.map(ele => {
const { name, tableName } = ele const { name, tableName } = ele
@ -962,6 +963,26 @@ const handleClick = (tabName: TabPaneName) => {
listDatasourceTables({ datasourceId: nodeInfo.id }).then(res => { listDatasourceTables({ datasourceId: nodeInfo.id }).then(res => {
tableData.value = res.data tableData.value = res.data
initSearch() initSearch()
if (nodeInfo.type.startsWith('API') || nodeInfo.type === 'ExcelRemote') {
getTableStatus({ datasourceId: nodeInfo.id }).then(res => {
for (let i = 0; i < state.filterTable.length; i++) {
for (let j = 0; j < res.data.length; j++) {
if (state.filterTable[i].tableName === res.data[j].tableName) {
state.filterTable[i].lastUpdateTime = res.data[j].lastUpdateTime
state.filterTable[i].status = res.data[j].status
}
}
}
for (let i = 0; i < tableData.value.length; i++) {
for (let j = 0; j < res.data.length; j++) {
if (tableData.value[i].tableName === res.data[j].tableName) {
tableData.value[i].lastUpdateTime = res.data[j].lastUpdateTime
tableData.value[i].status = res.data[j].status
}
}
}
})
}
}) })
break break
default: default:
@ -1030,7 +1051,7 @@ onMounted(() => {
loadInit() loadInit()
listDs() listDs()
setSupportSetKey() setSupportSetKey()
const { opt } = router.currentRoute.value.query const { opt } = router?.currentRoute?.value?.query || {}
if (opt && opt === 'create') { if (opt && opt === 'create') {
datasourceEditor.value.init(null, null, null, isSupportSetKey.value) datasourceEditor.value.init(null, null, null, isSupportSetKey.value)
} }
@ -1469,7 +1490,7 @@ const getMenuList = (val: boolean) => {
<template v-if="slotProps.active"> <template v-if="slotProps.active">
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="12"> <el-col :span="12">
<BaseInfoItem :label="t('auth.datasource') + ' ' + t('common.name')">{{ <BaseInfoItem :label="t('data_source.data_source_name')">{{
nodeInfo.name nodeInfo.name
}}</BaseInfoItem> }}</BaseInfoItem>
</el-col> </el-col>
@ -1482,10 +1503,20 @@ const getMenuList = (val: boolean) => {
<el-row :gutter="24"> <el-row :gutter="24">
<el-col v-if="nodeInfo.type === 'Excel'" :span="12"> <el-col v-if="nodeInfo.type === 'Excel'" :span="12">
<BaseInfoItem :label="t('data_source.document')"> <BaseInfoItem :label="t('data_source.document')">
<ExcelInfo :name="nodeInfo.fileName" :size="nodeInfo.size"></ExcelInfo> <ExcelInfoBase :name="nodeInfo.fileName" :size="nodeInfo.size"></ExcelInfoBase>
</BaseInfoItem> </BaseInfoItem>
</el-col> </el-col>
<el-col v-else :span="24"> <el-col v-if="nodeInfo.type === 'ExcelRemote'" :span="12">
<BaseInfoItem :label="t('datasource.remote_excel_url')">
{{ nodeInfo.configuration.url }}
</BaseInfoItem>
</el-col>
<el-col v-if="nodeInfo.type === 'ExcelRemote'" :span="12">
<BaseInfoItem :label="t('data_source.document')">
<ExcelInfoBase :name="nodeInfo.fileName" :size="nodeInfo.size"></ExcelInfoBase>
</BaseInfoItem>
</el-col>
<el-col v-if="!nodeInfo.type.startsWith('Excel')" :span="24">
<BaseInfoItem :label="t('common.description')">{{ <BaseInfoItem :label="t('common.description')">{{
nodeInfo.description nodeInfo.description
}}</BaseInfoItem> }}</BaseInfoItem>
@ -1495,6 +1526,7 @@ const getMenuList = (val: boolean) => {
v-if=" v-if="
!['Excel', 'es'].includes(nodeInfo.type) && !['Excel', 'es'].includes(nodeInfo.type) &&
!nodeInfo.type.startsWith('API') && !nodeInfo.type.startsWith('API') &&
!nodeInfo.type.startsWith('Excel') &&
nodeInfo.weight >= 7 nodeInfo.weight >= 7
" "
> >
@ -1698,7 +1730,47 @@ const getMenuList = (val: boolean) => {
</el-button> </el-button>
</BaseInfoContent> </BaseInfoContent>
<BaseInfoContent <BaseInfoContent
v-if="nodeInfo.type.startsWith('API') && nodeInfo.weight >= 7" v-if="nodeInfo.type.startsWith('Excel')"
v-slot="slotProps"
:name="t('dataset.data_preview')"
:time="nodeInfo.lastSyncTime"
:showTime="nodeInfo.type === 'ExcelRemote'"
>
<template v-if="slotProps.active">
<div class="excel-table">
<SheetTabs
:active-tab="activeTab"
@tab-click="handleTabClick"
:tab-list="tabList"
></SheetTabs>
<div class="sheet-table-content">
<el-auto-resizer>
<template #default="{ height, width }">
<el-table-v2
:columns="columns"
v-loading="dataPreviewLoading"
header-class="excel-header-cell"
:data="tabData"
:width="width"
:height="height"
fixed
><template #empty>
<empty-background
:description="t('data_set.no_data')"
img-type="noneWhite"
/> </template
></el-table-v2>
</template>
</el-auto-resizer>
</div>
</div>
</template>
</BaseInfoContent>
<BaseInfoContent
v-if="
(nodeInfo.type.startsWith('API') || nodeInfo.type === 'ExcelRemote') &&
nodeInfo.weight >= 7
"
v-slot="slotProps" v-slot="slotProps"
:name="t('dataset.update_setting')" :name="t('dataset.update_setting')"
:time="(nodeInfo.lastSyncTime as string)" :time="(nodeInfo.lastSyncTime as string)"
@ -1732,41 +1804,6 @@ const getMenuList = (val: boolean) => {
{{ t('dataset.update_records') }} {{ t('dataset.update_records') }}
</el-button> </el-button>
</BaseInfoContent> </BaseInfoContent>
<BaseInfoContent
v-if="nodeInfo.type === 'Excel'"
v-slot="slotProps"
:name="t('dataset.data_preview')"
>
<template v-if="slotProps.active">
<div class="excel-table">
<SheetTabs
:active-tab="activeTab"
@tab-click="handleTabClick"
:tab-list="tabList"
></SheetTabs>
<div class="sheet-table-content">
<el-auto-resizer>
<template #default="{ height, width }">
<el-table-v2
:columns="columns"
v-loading="dataPreviewLoading"
header-class="excel-header-cell"
:data="tabData"
:width="width"
:height="height"
fixed
><template #empty>
<empty-background
:description="t('data_set.no_data')"
img-type="noneWhite"
/> </template
></el-table-v2>
</template>
</el-auto-resizer>
</div>
</div>
</template>
</BaseInfoContent>
</template> </template>
</template> </template>
<template v-else-if="mounted"> <template v-else-if="mounted">
@ -2352,5 +2389,4 @@ const getMenuList = (val: boolean) => {
color: var(--deTextPrimary, #1f2329); color: var(--deTextPrimary, #1f2329);
} }
} }
</style> </style>