[Improve] App view table resize icon (#3526)
[Improve] App view table resize icon
diff --git a/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue b/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
index 093e606..1d4722c 100644
--- a/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
+++ b/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
@@ -16,20 +16,27 @@
</template>
</BasicForm>
- <Table
- ref="tableElRef"
- v-bind="getBindValues"
- :rowClassName="getRowClassName"
- v-show="getEmptyDataIsShowTable"
- @change="handleTableChange"
- >
- <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
- <slot :name="item" v-bind="data || {}"></slot>
- </template>
- <template #headerCell="{ column }">
- <HeaderCell :column="column" />
- </template>
- </Table>
+ <div ref="tableContainerRef" class="relative">
+ <Table
+ ref="tableElRef"
+ v-bind="getBindValues"
+ :rowClassName="getRowClassName"
+ v-show="getEmptyDataIsShowTable"
+ @change="handleTableChange"
+ >
+ <template
+ #[item]="data"
+ v-for="item in omit(Object.keys($slots), 'insertTable')"
+ :key="item"
+ >
+ <slot :name="item" v-bind="data || {}"></slot>
+ </template>
+ <template #headerCell="{ column }">
+ <HeaderCell :column="column" />
+ </template>
+ </Table>
+ <slot name="insertTable" :tableContainer="tableContainerRef"></slot>
+ </div>
</div>
</template>
<script lang="ts">
@@ -100,6 +107,7 @@
const wrapRef = ref(null);
const formRef = ref(null);
+ const tableContainerRef = ref(null);
const innerPropsRef = ref<Partial<BasicTableProps>>();
const { prefixCls } = useDesign('basic-table');
@@ -319,13 +327,19 @@
return unref(getBindValues).size as SizeType;
},
};
- createTableContext({ ...tableAction, wrapRef, tableFullScreen, getBindValues });
+ createTableContext({
+ ...tableAction,
+ wrapRef,
+ tableFullScreen,
+ getBindValues,
+ });
expose(tableAction);
emit('register', tableAction, formActions);
return {
+ omit,
formRef,
tableElRef,
getBindValues,
@@ -336,6 +350,7 @@
handleTableChange,
getRowClassName,
wrapRef,
+ tableContainerRef,
tableAction,
redoHeight,
getFormProps: getFormProps as any,
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue b/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
index 641a2dc..9b4a4e8 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
+++ b/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
@@ -49,7 +49,7 @@
} from './components/State';
import { useSavepoint } from './hooks/useSavepoint';
import { useAppTableColumns } from './hooks/useAppTableColumns';
-
+ import AppTableResize from './components/AppResize.vue';
const { t } = useI18n();
const optionApps = {
starting: new Map(),
@@ -62,7 +62,7 @@
const yarn = ref<Nullable<string>>(null);
const currentTablePage = ref(1);
- const { onTableColumnResize, getAppColumns } = useAppTableColumns();
+ const { onTableColumnResize, tableColumnWidth, getAppColumns } = useAppTableColumns();
const { openSavepoint } = useSavepoint(handleOptionApp);
const [registerStartModal, { openModal: openStartModal }] = useModal();
const [registerStopModal, { openModal: openStopModal }] = useModal();
@@ -348,6 +348,13 @@
<TableAction v-bind="getTableActions(record, currentTablePage)" />
</template>
</template>
+ <template #insertTable="{ tableContainer }">
+ <AppTableResize
+ :table-container="tableContainer"
+ :resize-min="100"
+ v-model:left="tableColumnWidth.jobName"
+ />
+ </template>
</BasicTable>
<StartApplicationModal @register="registerStartModal" @update-option="handleOptionApp" />
<StopApplicationModal @register="registerStopModal" @update-option="handleOptionApp" />
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppResize.vue b/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppResize.vue
new file mode 100644
index 0000000..2aa8d43
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppResize.vue
@@ -0,0 +1,156 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<script setup lang="ts">
+ import { EllipsisOutlined } from '@ant-design/icons-vue';
+ import { isNil } from 'lodash-es';
+ import { ref, computed, onMounted, onUnmounted } from 'vue';
+ defineOptions({ name: 'AppResize' });
+ const props = withDefaults(
+ defineProps<{
+ resizeMin?: number;
+ left: number;
+ tableContainer: HTMLElement | null;
+ }>(),
+ {
+ resizeMin: 100,
+ },
+ );
+ const emit = defineEmits(['resizeEnd', 'update:left']);
+ const domRef = ref<HTMLElement | null>(null);
+ const getStyle = computed(() => {
+ return {
+ left: `${props.left + 10}px`,
+ top: '50px',
+ };
+ });
+ const isMove = ref(false);
+
+ function getResizeLeft(e: MouseEvent): number {
+ const getOffsetLeft = (elem: HTMLElement | null): number => {
+ let innerOffsetLeft = 0;
+ let currentElem = elem;
+ do {
+ if (!isNil(currentElem!.offsetLeft)) innerOffsetLeft += currentElem!.offsetLeft;
+ } while ((currentElem = currentElem!.offsetParent as HTMLElement));
+ return Math.floor(innerOffsetLeft);
+ };
+
+ const offsetLeft = getOffsetLeft(props.tableContainer);
+ const maxResize = props.tableContainer!.getBoundingClientRect().width - 510;
+ let newLeft = e.pageX - offsetLeft;
+ if (newLeft > maxResize) newLeft = maxResize;
+
+ if (newLeft < props.resizeMin) newLeft = props.resizeMin;
+
+ return newLeft;
+ }
+
+ function startMove() {
+ isMove.value = true;
+ }
+
+ function move(e: MouseEvent) {
+ if (!isMove.value) return;
+ e.preventDefault();
+ const left = getResizeLeft(e);
+ emit('update:left', left);
+ }
+
+ function mouseup(e: MouseEvent) {
+ isMove.value = false;
+ if (e.target !== domRef.value) {
+ e.stopPropagation();
+ emit('resizeEnd', { left: props.left });
+ }
+ }
+
+ onMounted(() => {
+ window.addEventListener('mouseup', mouseup);
+ window.addEventListener('mousemove', move);
+ });
+ onUnmounted(() => {
+ window.removeEventListener('mouseup', mouseup);
+ window.removeEventListener('mousemove', move);
+ });
+</script>
+
+<template>
+ <div ref="domRef" class="resize app-vertical" :style="getStyle" @mousedown="startMove">
+ <div class="resize-handle app-handle-vertical">
+ <EllipsisOutlined />
+ </div>
+ </div>
+</template>
+
+<style lang="less">
+ :root {
+ --resize-border-color: #f0f0f0;
+ --resize-handle-border: #e8e8e8;
+ --resize-background-color: #fbfbfb;
+ }
+
+ [data-theme='dark']:root {
+ --resize-border-color: #303030;
+ --resize-handle-border: #303030;
+ --resize-background-color: #1d1d1d;
+ }
+
+ .resize {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 10;
+ background: transparent;
+
+ &.app-vertical {
+ width: 7px;
+ height: calc(100% - 95px);
+ border-left: 1px solid var(--resize-border-color);
+ cursor: col-resize;
+ }
+ }
+
+ .resize-handle {
+ position: relative;
+ top: -2px;
+ left: 50%;
+ width: 30px;
+ height: 10px;
+ margin-right: 15px;
+ border: 1px solid var(--resize-handle-border);
+ border-radius: 2px;
+ background: var(--resize-background-color);
+ line-height: 10px;
+ text-align: center;
+ pointer-events: none;
+
+ &.app-handle-vertical {
+ top: 50%;
+ left: auto;
+ margin-top: -15px;
+ margin-right: auto;
+ margin-left: 5px;
+ transform: rotate(90deg);
+ transform-origin: left top;
+ }
+
+ span {
+ position: relative;
+ top: -2px;
+ }
+ }
+</style>
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts b/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
index 95058e9..82f1e39 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
+++ b/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
@@ -40,7 +40,7 @@
const dataIndexStr = columns?.dataIndex.toString() ?? '';
if (Reflect.has(tableColumnWidth.value, dataIndexStr)) {
// when table column width changed, save it to table column width ref
- tableColumnWidth.value[dataIndexStr] = width;
+ tableColumnWidth.value[dataIndexStr] = width < 100 ? 100 : width;
}
}
@@ -53,7 +53,7 @@
resizable: true,
width: unref(tableColumnWidth).jobName,
},
- { title: t('flink.app.flinkVersion'), dataIndex: 'flinkVersion', width: 110 },
+ { title: t('flink.app.flinkVersion'), dataIndex: 'flinkVersion' },
{ title: t('flink.app.tags'), ellipsis: true, dataIndex: 'tags', width: 150 },
{
title: t('flink.app.runStatus'),
@@ -97,5 +97,5 @@
},
{ title: t('flink.app.owner'), dataIndex: 'nickName', width: unref(tableColumnWidth).nickName },
]);
- return { getAppColumns, onTableColumnResize };
+ return { getAppColumns, onTableColumnResize, tableColumnWidth };
};