[Core] [Metadata] Supports custom column display (#472)

This commit is contained in:
qianmoQ 2023-11-10 13:08:23 +08:00 committed by GitHub
commit 08b41a839f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 208 additions and 221 deletions

View File

@ -1,5 +1,5 @@
name: Release notes name: 🆕 Release new version
title: Release notes for xxx title: 🆕 [Release] Release notes for xxx
description: New version released description: New version released
labels: labels:
- release - release

View File

@ -9,7 +9,7 @@ check_java_version() {
local java_version=$("$JAVA_HOME"/bin/java -version 2>&1 | awk -F '"' '/version/ {print $2}') local java_version=$("$JAVA_HOME"/bin/java -version 2>&1 | awk -F '"' '/version/ {print $2}')
local major_version=$(echo "$java_version" | awk -F. '{print $1}') local major_version=$(echo "$java_version" | awk -F. '{print $1}')
if [ "$major_version" != "1" ] && [ "$major_version" != "11" ]; then if [ "$major_version" != "1" ] && [ "$major_version" != "11" ]; then
printf "Error: Java version %s is not supported. Please use Java 1.8 or 11.\n" "$java_version" printf "Error: Java version [ %s ] is not supported. Please use Java 1.8 or 11.\n" "$java_version"
exit 1 exit 1
fi fi
} }

View File

@ -9,7 +9,7 @@ check_java_version() {
local java_version=$("$JAVA_HOME"/bin/java -version 2>&1 | awk -F '"' '/version/ {print $2}') local java_version=$("$JAVA_HOME"/bin/java -version 2>&1 | awk -F '"' '/version/ {print $2}')
local major_version=$(echo "$java_version" | awk -F. '{print $1}') local major_version=$(echo "$java_version" | awk -F. '{print $1}')
if [ "$major_version" != "1" ] && [ "$major_version" != "11" ]; then if [ "$major_version" != "1" ] && [ "$major_version" != "11" ]; then
printf "Error: Java version %s is not supported. Please use Java 1.8 or 11.\n" "$java_version" printf "Error: Java version [ %s ] is not supported. Please use Java 1.8 or 11.\n" "$java_version"
exit 1 exit 1
fi fi
} }

View File

@ -122,12 +122,21 @@ public class TableServiceImpl
} }
} }
table.getColumns() // If the columns of the query are not passed, they are obtained through metadata
.stream() if (configure.getColumns() == null || configure.getColumns().size() == 0) {
.sorted(Comparator.comparing(item -> Integer.parseInt(item.getPosition()))) table.getColumns()
.forEach(column -> columns.add(SqlColumn.builder() .stream()
.column(String.format("`%s`", column.getName())) .sorted(Comparator.comparing(item -> Integer.parseInt(item.getPosition())))
.build())); .forEach(column -> columns.add(SqlColumn.builder()
.column(String.format("`%s`", column.getName()))
.build()));
}
else {
configure.getColumns()
.forEach(column -> columns.add(SqlColumn.builder()
.column(String.format("`%s`", column.getColumn()))
.build()));
}
int offset = configure.getPagination().getPageSize() * (configure.getPagination().getCurrentPage() - 1); int offset = configure.getPagination().getPageSize() * (configure.getPagination().getCurrentPage() - 1);
SqlBody body = SqlBody.builder() SqlBody body = SqlBody.builder()
.type(SqlType.SELECT) .type(SqlType.SELECT)

View File

@ -4,4 +4,5 @@ export default {
updateSuccess: 'Update Success', updateSuccess: 'Update Success',
deleteRows: 'Delete Rows', deleteRows: 'Delete Rows',
deleteSuccess: 'Delete Success', deleteSuccess: 'Delete Success',
visibleColumn: 'Visible Column',
} }

View File

@ -4,4 +4,5 @@ export default {
updateSuccess: '更新成功', updateSuccess: '更新成功',
deleteRows: '删除行', deleteRows: '删除行',
deleteSuccess: '删除成功', deleteSuccess: '删除成功',
visibleColumn: '可见列',
} }

View File

@ -281,7 +281,9 @@ export default defineComponent({
} }
const currentNode = node[0]; const currentNode = node[0];
if (currentNode.level === DataStructureEnum.COLUMN) { if (currentNode.level === DataStructureEnum.COLUMN) {
this.applyValue.node.selected = true; if (this.applyValue.node) {
this.applyValue.node.selected = true;
}
currentNode.selected = false; currentNode.selected = false;
return; return;
} }

View File

@ -0,0 +1,81 @@
<template>
<div>
<Drawer v-model="visible"
:title="$t('source.manager.visibleColumn')"
:mask-closable="false"
@close="handlerCancel">
<CheckboxGroup v-model="applyColumns">
<List header=""
footer="">
<ListItem v-for="column in columns"
:key="column">
<Checkbox :label="column.field"></Checkbox>
</ListItem>
</List>
</CheckboxGroup>
<template #close>
<Tooltip :content="$t('common.apply')"
transfer>
<Button size="small"
type="primary"
:style="{marginTop: '3px'}"
@click="handlerCancel">
{{ $t('common.apply') }}
</Button>
</Tooltip>
</template>
</Drawer>
</div>
</template>
<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
name: "TableColumn",
props: {
isVisible: {
type: Boolean
},
columns: {
type: Array,
default: () => []
}
},
data()
{
return {
applyColumns: []
}
},
created()
{
this.handlerInitialize();
},
methods: {
handlerInitialize()
{
this.columns.filter((item: { checked: any; }) => item.checked)
.forEach((item: { field: any; }) => {
this.applyColumns.push(item.field)
})
},
handlerCancel()
{
this.$emit('onClose', this.applyColumns)
this.visible = false
}
},
computed: {
visible: {
get(): boolean
{
return this.isVisible;
},
set(value: boolean)
{
this.$emit('close', value);
}
}
}
});
</script>

View File

@ -96,13 +96,22 @@
</Tooltip> </Tooltip>
</Space> </Space>
<div style="float: right;"> <div style="float: right;">
<Tooltip :content="$t('common.preview')" <Space>
transfer> <Tooltip :content="$t('common.preview')"
<Button size="small" transfer>
@click="handlerVisibleContent(true)"> <Button size="small"
<FontAwesomeIcon icon="eye"/> @click="handlerVisibleContent(true)">
</Button> <FontAwesomeIcon icon="eye"/>
</Tooltip> </Button>
</Tooltip>
<Tooltip :content="$t('source.manager.visibleColumn')"
transfer>
<Button size="small"
@click="handlerVisibleColumn(null, true)">
<FontAwesomeIcon icon="columns"/>
</Button>
</Tooltip>
</Space>
</div> </div>
</div> </div>
<AgGridVue class="ag-theme-datacap" <AgGridVue class="ag-theme-datacap"
@ -116,7 +125,9 @@
@grid-ready="handlerGridReady" @grid-ready="handlerGridReady"
@sortChanged="handlerSortChanged" @sortChanged="handlerSortChanged"
@cellValueChanged="handlerCellValueChanged" @cellValueChanged="handlerCellValueChanged"
@selectionChanged="handlerSelectionChanged"> @selectionChanged="handlerSelectionChanged"
@columnVisible="handlerColumnVisible"
@columnMoved="handlerColumnMoved">
</AgGridVue> </AgGridVue>
<CircularLoading v-if="refererLoading" <CircularLoading v-if="refererLoading"
:show="refererLoading"> :show="refererLoading">
@ -141,6 +152,13 @@
:columns="dataSelectedChanged.columns" :columns="dataSelectedChanged.columns"
@close="handlerSelectedChangedPreview(false)"> @close="handlerSelectedChangedPreview(false)">
</TableRowDeletePreview> </TableRowDeletePreview>
<!-- Displays the currently selected and unchecked columns -->
<TableColumn v-if="visibleColumn.show"
:isVisible="visibleColumn.show"
:columns="visibleColumn.columns"
@close="handlerVisibleColumn($event, false)"
@onClose="handlerVisibleColumn($event, false)">
</TableColumn>
</div> </div>
</div> </div>
</template> </template>
@ -162,10 +180,11 @@ import {SqlColumn, TableFilter} from "@/model/TableFilter";
import TableCellEditPreview from "@/views/admin/source/components/TableCellEditPreview.vue"; import TableCellEditPreview from "@/views/admin/source/components/TableCellEditPreview.vue";
import TableRowDeletePreview from "@/views/admin/source/components/TableRowDeletePreview.vue"; import TableRowDeletePreview from "@/views/admin/source/components/TableRowDeletePreview.vue";
import {cloneDeep} from "lodash"; import {cloneDeep} from "lodash";
import TableColumn from "@/views/admin/source/components/TableColumn.vue";
export default defineComponent({ export default defineComponent({
name: "TableData", name: "TableData",
components: {TableRowDeletePreview, TableCellEditPreview, MarkdownPreview, InputNumber, CircularLoading, AgGridVue}, components: {TableColumn, TableRowDeletePreview, TableCellEditPreview, MarkdownPreview, InputNumber, CircularLoading, AgGridVue},
props: { props: {
id: { id: {
type: Number, type: Number,
@ -187,6 +206,7 @@ export default defineComponent({
gridOptions: null, gridOptions: null,
gridApi: null as GridApi, gridApi: null as GridApi,
gridColumnApi: null as ColumnApi, gridColumnApi: null as ColumnApi,
originalColumns: [],
configure: { configure: {
headers: [], headers: [],
columns: [], columns: [],
@ -206,6 +226,10 @@ export default defineComponent({
changed: false, changed: false,
pending: false, pending: false,
columns: [] columns: []
},
visibleColumn: {
show: false,
columns: []
} }
} }
}, },
@ -224,6 +248,7 @@ export default defineComponent({
.then(response => { .then(response => {
if (response.status && response.data) { if (response.status && response.data) {
this.configure.headers = createColumnDefs(response.data.headers, response.data.types); this.configure.headers = createColumnDefs(response.data.headers, response.data.types);
this.originalColumns = this.configure.headers
this.configure.columns = response.data.columns; this.configure.columns = response.data.columns;
this.configure.pagination = response.data.pagination; this.configure.pagination = response.data.pagination;
this.visibleContent.content = '```sql\n' + response.data.content + '\n```'; this.visibleContent.content = '```sql\n' + response.data.content + '\n```';
@ -239,23 +264,15 @@ export default defineComponent({
this.gridApi = params.api; this.gridApi = params.api;
this.gridColumnApi = params.columnApi; this.gridColumnApi = params.columnApi;
}, },
handlerSortChanged() handlerRefererData(configure: TableFilter)
{ {
this.configure.columns = []; this.configure.columns = [];
this.gridOptions.overlayNoRowsTemplate = '<span></span>'; this.gridOptions.overlayNoRowsTemplate = '<span></span>';
this.refererLoading = true; this.refererLoading = true;
const columnState = this.gridColumnApi.getColumnState();
const orders = columnState.map((column: { colId: any; sort: any; }) => ({
column: column.colId,
order: column.sort
}));
const configure: TableFilter = new TableFilter();
configure.pagination = this.configure.pagination;
configure.orders = orders;
TableService.getData(this.id, configure) TableService.getData(this.id, configure)
.then(response => { .then(response => {
if (response.status && response.data) { if (response.status && response.data) {
this.configure.headers = createColumnDefs(response.data.headers, response.data.types);
this.configure.columns = response.data.columns; this.configure.columns = response.data.columns;
if (this.configure.columns.length <= 0) { if (this.configure.columns.length <= 0) {
this.gridOptions.overlayNoRowsTemplate = '<span>No Rows To Show</span>'; this.gridOptions.overlayNoRowsTemplate = '<span>No Rows To Show</span>';
@ -269,6 +286,12 @@ export default defineComponent({
}) })
.finally(() => this.refererLoading = false) .finally(() => this.refererLoading = false)
}, },
handlerSortChanged()
{
const configure: TableFilter = new TableFilter();
this.getSortConfigure(configure)
this.handlerRefererData(configure)
},
handlerCellValueChanged(event: { data: any; colDef: { field: string; }; oldValue: any; newValue: any; }) handlerCellValueChanged(event: { data: any; colDef: { field: string; }; oldValue: any; newValue: any; })
{ {
const oldColumn = event.data; const oldColumn = event.data;
@ -324,6 +347,65 @@ export default defineComponent({
{ {
this.visibleContent.show = show; this.visibleContent.show = show;
}, },
handlerColumnVisible(event: { visible: any; column: { visible: any; colId: any; }; })
{
if (!event.visible) {
this.configure.headers.map((column: { field: any; checked: boolean; }) => {
if (column.field === event.column.colId) {
column.checked = false;
}
})
}
},
handlerVisibleColumn(event, show: boolean)
{
this.visibleColumn.show = show;
if (event) {
const configure: TableFilter = new TableFilter()
this.getSortConfigure(configure)
this.getVisibleColumn(configure)
const columns = event.map((item: string) => ({column: item}))
configure.columns = columns
// Remove the reduced column is not selected
this.originalColumns.filter((item: { field: string; }) => !event.includes(item.field))
.map((item: { checked: boolean; }) => {
item.checked = false
})
// Add new Column is selected
this.originalColumns.filter((item: { field: string; }) => event.includes(item.field))
.map((item: { checked: boolean; }) => {
item.checked = true
})
this.handlerRefererData(configure)
}
this.visibleColumn.columns = this.originalColumns
},
handlerColumnMoved(event: { finished: any; })
{
if (event.finished) {
const configure: TableFilter = new TableFilter()
this.getSortConfigure(configure)
this.getVisibleColumn(configure)
this.handlerRefererData(configure)
}
},
getSortConfigure(configure: TableFilter)
{
const columnState = this.gridColumnApi.getColumnState();
const orders = columnState.map((column: { colId: any; sort: any; }) => ({
column: column.colId,
order: column.sort
}));
configure.pagination = this.configure.pagination;
configure.orders = orders;
},
getVisibleColumn(configure: TableFilter)
{
const columns = this.gridColumnApi.getColumnState()
.filter(item => !item.hide)
.map((item: { colId: any; }) => ({column: item.colId}))
configure.columns = columns
},
watchId() watchId()
{ {
watch( watch(

View File

@ -20,7 +20,8 @@ const createColumnDefs = (headers: any[], types: any[]): any[] => {
}, },
cellEditorPopup: true, cellEditorPopup: true,
cellEditor: 'agLargeTextCellEditor', cellEditor: 'agLargeTextCellEditor',
cellEditorParams: {maxLength: 9999999999999, rows: 10} cellEditorParams: {maxLength: 9999999999999, rows: 10},
checked: true
}; };
columnDefs.push(columnDef); columnDefs.push(columnDef);
}) })

View File

@ -1,111 +0,0 @@
<template>
<div>
<ag-grid-vue class="ag-theme-datacap"
:style="{width: configure.width + 'px', height: configure.height + 'px', 'margin-top': '2px'}"
:columnDefs="columnDefs"
:gridOptions="gridOptions"
:rowData="configure.columns"
:tooltipShowDelay="100"
:multiSortKey="'ctrl'"
:rowSelection="'multiple'"
@sortChanged="handlerSortChanged"
@selectionChanged="handlerSelectionChanged">
</ag-grid-vue>
</div>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import {TableConfigure} from "@/components/table/TableConfigure";
import {AgGridVue} from "ag-grid-vue3";
import "ag-grid-community/styles/ag-grid.css";
import "@/components/table/ag-theme-datacap.css";
import {useI18n} from "vue-i18n";
import TableGridOptions from "@/components/table/TableGridOptions";
import {Column} from "ag-grid-community";
import {isEmpty} from "lodash";
import {Sort} from "@/model/sql/Sort";
export default defineComponent({
name: "TablePreview",
components: {AgGridVue},
props: {
configure: {
type: TableConfigure,
default: () => null
},
sortColumns: {
type: Array,
default: () => []
}
},
created()
{
const i18n = useI18n();
this.gridOptions = TableGridOptions.createDataEditorOptions(i18n);
this.handlerInitialize();
},
data()
{
return {
gridOptions: null,
columnDefs: []
}
},
methods: {
handlerInitialize()
{
this.columnDefs = [];
// Build a select box for multiple selection operations
if (this.configure.headers.length > 0) {
this.columnDefs.push({
width: 38,
headerCheckboxSelection: true,
checkboxSelection: true,
showDisabledCheckboxes: true,
lockPinned: true,
pinned: 'left'
})
}
// Render backend returns data
this.configure.headers.forEach((header, index) => {
const hasSort = this.sortColumns?.filter(sortColumn => sortColumn.column === header)[0];
const columnDef = {
headerName: header,
field: header,
sort: hasSort ? hasSort.sort : null,
headerTooltip: header + ' [' + this.configure.types[index] + ']',
comparator: () => {
if (hasSort && hasSort.sort === 'asc') {
return 0;
}
return hasSort && hasSort.sort === 'desc' ? -1 : 1;
}
};
this.columnDefs.push(columnDef)
});
},
handlerSortChanged(event)
{
this.ready = true;
if (this.ready) {
const columns: Column[] = event.columnApi.getAllGridColumns();
// Get all order columns
const orderColumns: Column[] = columns.filter(column => !isEmpty(column.getSort()));
const sort: Array<Sort> = new Array<Sort>();
orderColumns.forEach(column => {
sort.push({
column: column.getColId(),
sort: column.getSort()
});
});
this.$emit('on-sorted', sort);
}
},
handlerSelectionChanged(event)
{
const selectedRows = event.api.getSelectedRows();
this.$emit('on-selected', selectedRows);
}
}
});
</script>

View File

@ -1,79 +0,0 @@
<template>
<div>
<Alert>
<Form inline>
<div v-for="(item, index) in sortColumnItems" v-bind:key="index">
<FormItem :label="$t('common.column')" prop="column">
<Select v-model="item.column" size="small" style="min-width: 200px;">
<Option v-for="column in columns" :value="column.title" v-bind:key="column.title">{{ column.title }}</Option>
</Select>
</FormItem>
<FormItem :label="$t('common.sort')" prop="sort">
<Select v-model="item.sort" size="small" style="min-width: 150px;">
<Option value="ASC">{{ $t('common.asc') }}</Option>
<Option value="DESC">{{ $t('common.desc') }}</Option>
</Select>
</FormItem>
<FormItem prop="action">
<template #label>
<Button @click="handlePlusItem(index)" type="primary" icon="md-add" size="small"/>&nbsp;
<Button :disabled="sortColumnItems.length===1" @click="handleRemoveItem(index)" type="error" icon="md-remove" size="small"/>
</template>
</FormItem>
</div>
<FormItem>
<Button :disabled="sortColumnItems.length === 0" size="small" type="primary" @click="handleApply('formInline')">{{ $t('common.apply') }}</Button>
</FormItem>
</Form>
</Alert>
</div>
</template>
<script>
export default {
name: 'SortByComponent',
props: {
columns: {
type: Array,
default: () => []
}
},
data() {
return {
index: 0,
sortColumnItems: [
{
column: null,
sort: null
}
]
}
},
methods: {
handlePlusItem() {
this.index++;
this.sortColumnItems.push({column: null, sort: null});
},
handleRemoveItem() {
this.sortColumnItems.pop(this.index);
},
handleApply() {
this.$emit('getValue', this.sortColumnItems);
}
}
}
</script>
<style scoped>
.ivu-form-item {
margin-bottom: 0;
}
.ivu-alert {
margin-bottom: 0;
padding: 0;
}
.ivu-alert-info {
border: 0px solid #abdcff;
background-color: transparent;
}
</style>