This is an automated email from the ASF dual-hosted git repository.
benjobs pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampark.git
The following commit(s) were added to refs/heads/dev by this push:
new 41f1dc9a5 [Improve] login page style, fix change teamId bug (#1912)
41f1dc9a5 is described below
commit 41f1dc9a5448b6aacf9ddf40e46ff3c57fce8f43
Author: Sizhu Wang <[email protected]>
AuthorDate: Wed Oct 26 23:34:11 2022 +0800
[Improve] login page style, fix change teamId bug (#1912)
---
.../streampark-console-newui/package.json | 2 +-
.../src/assets/images/logo-bg.jpg | Bin 0 -> 758479 bytes
.../src/components/Button/src/props.ts | 5 +-
.../streampark-console-newui/src/design/color.less | 2 +
.../src/design/public.less | 22 +-
.../src/design/var/index.less | 16 -
.../header/components/user-dropdown/index.vue | 4 +-
.../src/store/modules/user.ts | 20 +-
.../src/utils/cache/persistent.ts | 17 +-
.../src/utils/cache/storageCache.ts | 2 +-
.../src/views/base/login/Login.vue | 70 +----
.../src/views/base/login/LoginForm.vue | 25 +-
.../src/views/base/login/LoginFormTitle.vue | 2 +-
.../src/views/flink/app/EditFlink.vue | 4 +-
.../src/views/flink/app/View.vue | 344 ++-------------------
.../flink/app/components/AppView/AppDashboard.vue | 87 ++++++
.../src/views/flink/app/components/Dependency.vue | 7 -
.../app/components/PodTemplate/PomTemplateTab.vue | 2 +-
.../src/views/flink/app/hooks/useAppTable.ts | 128 ++++++++
.../src/views/flink/app/hooks/useAppTableAction.ts | 293 ++++++++++++++++++
.../src/views/flink/app/styles/Add.less | 6 +
.../src/views/flink/notebook/Submit.less | 10 +
.../views/flink/setting/components/AlertModal.vue | 2 +-
23 files changed, 622 insertions(+), 448 deletions(-)
diff --git a/streampark-console/streampark-console-newui/package.json
b/streampark-console/streampark-console-newui/package.json
index c7f8f7899..4a5e16569 100644
--- a/streampark-console/streampark-console-newui/package.json
+++ b/streampark-console/streampark-console-newui/package.json
@@ -1,6 +1,6 @@
{
"name": "streampark-webapp",
- "version": "2.0.0",
+ "version": "1.2.4",
"author": {
"name": "streampark",
"url": "https://streampark.apache.org"
diff --git
a/streampark-console/streampark-console-newui/src/assets/images/logo-bg.jpg
b/streampark-console/streampark-console-newui/src/assets/images/logo-bg.jpg
new file mode 100644
index 000000000..8771c8fb8
Binary files /dev/null and
b/streampark-console/streampark-console-newui/src/assets/images/logo-bg.jpg
differ
diff --git
a/streampark-console/streampark-console-newui/src/components/Button/src/props.ts
b/streampark-console/streampark-console-newui/src/components/Button/src/props.ts
index f6547e0e4..a8fca6a3d 100644
---
a/streampark-console/streampark-console-newui/src/components/Button/src/props.ts
+++
b/streampark-console/streampark-console-newui/src/components/Button/src/props.ts
@@ -15,7 +15,10 @@
* limitations under the License.
*/
export const buttonProps = {
- color: { type: String, validator: (v) => ['error', 'warning', 'success',
''].includes(v) },
+ color: {
+ type: String,
+ validator: (v) => ['error', 'warning', 'success', 'primary',
''].includes(v),
+ },
loading: { type: Boolean },
disabled: { type: Boolean },
/**
diff --git a/streampark-console/streampark-console-newui/src/design/color.less
b/streampark-console/streampark-console-newui/src/design/color.less
index 2410e1561..0fc2a3bf4 100644
--- a/streampark-console/streampark-console-newui/src/design/color.less
+++ b/streampark-console/streampark-console-newui/src/design/color.less
@@ -155,3 +155,5 @@ html {
// Modal
@modal-mask-bg: fade(@black, 45%);
+
+@background-color-base: #e6f7ff;
diff --git a/streampark-console/streampark-console-newui/src/design/public.less
b/streampark-console/streampark-console-newui/src/design/public.less
index 6268fac5b..bbaa20016 100644
--- a/streampark-console/streampark-console-newui/src/design/public.less
+++ b/streampark-console/streampark-console-newui/src/design/public.less
@@ -24,8 +24,8 @@
// =================================
::-webkit-scrollbar {
- width: 7px;
- height: 8px;
+ width: 14px;
+ height: 12px;
}
// ::-webkit-scrollbar-track {
@@ -33,21 +33,21 @@
// }
::-webkit-scrollbar-track {
- background-color: rgb(0 0 0 / 5%);
+ // background-color: rgb(0 0 0 / 5%);
+ background-color: unset;
}
+::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb {
- // background: rgba(0, 0, 0, 0.6);
- background-color: rgb(144 147 153 / 30%);
- // background-color: rgba(144, 147, 153, 0.3);
- border-radius: 2px;
- box-shadow: inset 0 0 6px rgb(0 0 0 / 20%);
+ background-color: #e6f7ff;
}
-::-webkit-scrollbar-thumb:hover {
- background-color: @border-color-dark;
+[data-theme="dark"] {
+ ::-webkit-scrollbar-thumb:hover,
+ ::-webkit-scrollbar-thumb {
+ background-color: #2a2a2a;
+ }
}
-
// =================================
// ==============nprogress==========
// =================================
diff --git
a/streampark-console/streampark-console-newui/src/design/var/index.less
b/streampark-console/streampark-console-newui/src/design/var/index.less
index e4c2a9203..d14f45ca2 100644
--- a/streampark-console/streampark-console-newui/src/design/var/index.less
+++ b/streampark-console/streampark-console-newui/src/design/var/index.less
@@ -53,19 +53,3 @@
@content();
}
}
-
-@background-color-base: #e6f7ff;
-
-@gray-1: #ffffff;
-@gray-2: #fafafa;
-@gray-3: #f5f5f5;
-@gray-4: #f0f0f0;
-@gray-5: #d9d9d9;
-@gray-6: #bfbfbf;
-@gray-7: #8c8c8c;
-@gray-8: #595959;
-@gray-9: #434343;
-@gray-10: #262626;
-@gray-11: #1f1f1f;
-@gray-12: #141414;
-@gray-13: #000000;
diff --git
a/streampark-console/streampark-console-newui/src/layouts/default/header/components/user-dropdown/index.vue
b/streampark-console/streampark-console-newui/src/layouts/default/header/components/user-dropdown/index.vue
index 2bd30ab97..db6fd4d9f 100644
---
a/streampark-console/streampark-console-newui/src/layouts/default/header/components/user-dropdown/index.vue
+++
b/streampark-console/streampark-console-newui/src/layouts/default/header/components/user-dropdown/index.vue
@@ -17,8 +17,8 @@
<template>
<Dropdown placement="bottomLeft"
:overlayClassName="`${prefixCls}-dropdown-overlay`">
<span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
- <img :class="`${prefixCls}__header`" :src="getUserInfo.avatar" />
- <span :class="`${prefixCls}__info hidden md:block`">
+ <!-- <img :class="`${prefixCls}__header`" :src="getUserInfo.avatar" />
-->
+ <span :class="`${prefixCls}__info hidden md:block px-10px`">
<span :class="`${prefixCls}__name `" class="truncate">
{{ getUserInfo.username }}
</span>
diff --git
a/streampark-console/streampark-console-newui/src/store/modules/user.ts
b/streampark-console/streampark-console-newui/src/store/modules/user.ts
index 48999bba4..a99b307ac 100644
--- a/streampark-console/streampark-console-newui/src/store/modules/user.ts
+++ b/streampark-console/streampark-console-newui/src/store/modules/user.ts
@@ -38,6 +38,8 @@ import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
import { h } from 'vue';
import { getUserTeamId } from '/@/utils';
import { usePermission } from '/@/hooks/web/usePermission';
+import { AesEncryption } from '/@/utils/cipher';
+import { cacheCipher } from '/@/settings/encryptionSetting';
interface TeamListType {
label: string;
@@ -86,7 +88,7 @@ export const useUserStore = defineStore({
return this.expire || getAuthCache<string>(EXPIRE_KEY);
},
getRoleList(): RoleEnum[] {
- return this.roleList.length > 0 ? this.roleList :
getAuthCache<RoleEnum[]>(ROLES_KEY);
+ return this.roleList?.length > 0 ? this.roleList :
getAuthCache<RoleEnum[]>(ROLES_KEY);
},
getSessionTimeout(): boolean {
return !!this.sessionTimeout;
@@ -110,13 +112,20 @@ export const useUserStore = defineStore({
setToken(info: string | undefined) {
this.token = info ? info : ''; // for null or undefined value
setAuthCache(TOKEN_KEY, info);
+ let cacheToken = this.token;
+ // production encrypted
+ if (import.meta.env.PROD && cacheToken) {
+ const encryption = new AesEncryption({ key: cacheCipher.key, iv:
cacheCipher.iv });
+ cacheToken = encryption.encryptByAES(cacheToken);
+ }
+ localStorage.setItem(TOKEN_KEY, cacheToken);
},
setExpire(info: string | undefined) {
this.expire = info || '';
setAuthCache(EXPIRE_KEY, info);
},
setRoleList(roleList: RoleEnum[]) {
- this.roleList = roleList;
+ this.roleList = roleList || [];
setAuthCache(ROLES_KEY, roleList);
},
setUserInfo(info: UserInfo | null) {
@@ -140,11 +149,12 @@ export const useUserStore = defineStore({
setData(data: Recordable) {
const { token, expire, user, permissions, roles = [] } = data;
- this.setToken(token);
this.setExpire(expire);
this.setUserInfo(user);
this.setRoleList(roles);
this.setPermissions(permissions);
+ // set token must be placed at the end, need to listen to this value
+ this.setToken(token);
},
// set team
async setTeamId(data: { teamId: string; userId?: string }):
Promise<boolean> {
@@ -157,7 +167,7 @@ export const useUserStore = defineStore({
} else {
const resp = await fetchSetUserTeam(data);
- const { permissions, roles, user } = resp;
+ const { permissions, roles = [], user } = resp;
this.setUserInfo(user);
this.setRoleList(roles as RoleEnum[]);
this.setPermissions(permissions);
@@ -214,11 +224,11 @@ export const useUserStore = defineStore({
console.log('Token cancellation failed');
}
}
- this.setToken(undefined);
this.setSessionTimeout(false);
this.setUserInfo(null);
sessionStorage.removeItem(APP_TEAMID_KEY_);
localStorage.removeItem(APP_TEAMID_KEY_);
+ this.setToken(undefined);
goLogin && router.push(PageEnum.BASE_LOGIN);
},
diff --git
a/streampark-console/streampark-console-newui/src/utils/cache/persistent.ts
b/streampark-console/streampark-console-newui/src/utils/cache/persistent.ts
index 8feb24cdb..4488d17e3 100644
--- a/streampark-console/streampark-console-newui/src/utils/cache/persistent.ts
+++ b/streampark-console/streampark-console-newui/src/utils/cache/persistent.ts
@@ -34,7 +34,7 @@ import {
} from '/@/enums/cacheEnum';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
import { toRaw } from 'vue';
-import { pick, omit, debounce } from 'lodash-es';
+import { pick, omit } from 'lodash-es';
interface BasicStore {
[TOKEN_KEY]: string | number | null | undefined;
@@ -131,12 +131,21 @@ window.addEventListener('beforeunload', function () {
function storageChange(e: any) {
const { key, newValue, oldValue } = e;
-
if (!key) {
Persistent.clearAll();
return;
}
-
+ // token change reload
+ if (key === TOKEN_KEY && oldValue !== newValue) {
+ const { [TOKEN_KEY]: tokenCache } = pick(ls.get(APP_LOCAL_CACHE_KEY),
TOKEN_KEY);
+ // no token or token value is invalid
+ if (tokenCache?.value !== newValue) {
+ Persistent.clearLocal();
+ return;
+ }
+ window.location.reload();
+ return;
+ }
if (!!newValue && !!oldValue) {
if (APP_LOCAL_CACHE_KEY === key) {
Persistent.clearLocal();
@@ -147,6 +156,6 @@ function storageChange(e: any) {
}
}
-window.addEventListener('storage', debounce(storageChange, 500));
+window.addEventListener('storage', storageChange);
initPersistentMemory();
diff --git
a/streampark-console/streampark-console-newui/src/utils/cache/storageCache.ts
b/streampark-console/streampark-console-newui/src/utils/cache/storageCache.ts
index afb9bc9d6..e307d1df5 100644
---
a/streampark-console/streampark-console-newui/src/utils/cache/storageCache.ts
+++
b/streampark-console/streampark-console-newui/src/utils/cache/storageCache.ts
@@ -61,7 +61,7 @@ export const createStorage = ({
this.hasEncrypt = hasEncrypt;
}
- private getKey(key: string) {
+ getKey(key: string) {
return `${this.prefixKey}${key}`.toUpperCase();
}
diff --git
a/streampark-console/streampark-console-newui/src/views/base/login/Login.vue
b/streampark-console/streampark-console-newui/src/views/base/login/Login.vue
index 5fcbfa4a8..4297352d8 100644
--- a/streampark-console/streampark-console-newui/src/views/base/login/Login.vue
+++ b/streampark-console/streampark-console-newui/src/views/base/login/Login.vue
@@ -16,34 +16,12 @@
-->
<template>
<div :class="prefixCls" class="relative w-full h-full px-4">
- <AppLocalePicker
- class="absolute text-white top-4 right-4 enter-x xl:text-gray-600"
- :showText="false"
- v-if="!sessionTimeout && showLocale"
- />
- <AppDarkModeToggle class="absolute top-3 right-7 enter-x"
v-if="!sessionTimeout" />
-
- <span class="-enter-x xl:hidden">
- <AppLogo :alwaysShowTitle="true" />
- </span>
-
<div class="container relative h-full py-2">
<div class="flex h-full">
- <div class="hidden min-h-full pl-4 mr-4 xl:flex xl:flex-col xl:w-4/12">
- <AppLogo class="!w-auto -enter-x" />
- <div class="my-auto">
- <div class="mt-10 font-medium text-white -enter-x">
- <span class="inline-block mt-4 text-3xl"> {{
t('sys.login.signInTitle') }}</span>
- </div>
- <div class="mt-5 font-normal text-gray-300 text-md -enter-x">
- {{ t('sys.login.signInDesc') }}
- </div>
- </div>
- </div>
- <div class="flex w-full h-full py-5 xl:h-auto xl:py-0 xl:my-0
xl:w-8/12">
+ <div class="flex w-full h-full py-5 xl:h-auto xl:py-0 xl:my-0">
<div
:class="`${prefixCls}-form`"
- class="relative w-full px-5 py-8 mx-auto my-auto rounded-md
shadow-md xl:bg-transparent sm:px-8 xl:p-4 xl:shadow-none sm:w-3/4 lg:w-2/4
xl:w-auto enter-x"
+ class="relative w-auto px-8 bg-[rgba(0,0,0,0.5)] py-10 mx-auto
my-auto rounded-md shadow-md enter-y"
>
<LoginForm />
<ForgetPasswordForm />
@@ -54,15 +32,9 @@
</div>
</template>
<script lang="ts" setup>
- // import { computed } from 'vue';
- import { AppLogo } from '/@/components/Application';
- import { AppLocalePicker, AppDarkModeToggle } from
'/@/components/Application';
import LoginForm from './LoginForm.vue';
import ForgetPasswordForm from './ForgetPasswordForm.vue';
- // import { useGlobSetting } from '/@/hooks/setting';
- import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
- import { useLocaleStore } from '/@/store/modules/locale';
defineProps({
sessionTimeout: {
@@ -72,9 +44,6 @@
// const globSetting = useGlobSetting();
const { prefixCls } = useDesign('login');
- const { t } = useI18n();
- const localeStore = useLocaleStore();
- const showLocale = localeStore.getShowPicker;
// const title = computed(() => globSetting?.title ?? '');
</script>
<style lang="less">
@@ -99,10 +68,6 @@
border: 1px solid #4a5569;
}
- &-form {
- background: transparent !important;
- }
-
.app-iconify {
color: #fff;
}
@@ -117,31 +82,18 @@
.@{prefix-cls} {
min-height: 100%;
overflow: hidden;
- @media (max-width: @screen-xl) {
- background-color: #293146;
-
- .@{prefix-cls}-form {
- background-color: #fff;
- }
- }
-
- &::before {
+ background: url('/@/assets/images/logo-bg.jpg') no-repeat 50%;
+ background-size: cover;
+ &::after {
content: '';
+ width: 100%;
position: absolute;
- top: 0;
left: 0;
- width: 100%;
- height: 100%;
- margin-left: -66%;
- background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
- // background-image: url(/@/assets/svg/login-bg.svg);
- // background-position: 100%;
- // background-repeat: no-repeat;
- // background-size: auto 100%;
-
- @media (max-width: @screen-xl) {
- display: none;
- }
+ top: 0;
+ bottom: -20px;
+ background: inherit;
+ filter: blur(4px);
+ z-index: 2;
}
.@{logo-prefix-cls} {
diff --git
a/streampark-console/streampark-console-newui/src/views/base/login/LoginForm.vue
b/streampark-console/streampark-console-newui/src/views/base/login/LoginForm.vue
index 9610dcc8f..881eda7c5 100644
---
a/streampark-console/streampark-console-newui/src/views/base/login/LoginForm.vue
+++
b/streampark-console/streampark-console-newui/src/views/base/login/LoginForm.vue
@@ -15,7 +15,7 @@
limitations under the License.
-->
<template>
- <LoginFormTitle v-show="getShow" class="enter-x mb-40px" />
+ <LoginFormTitle v-show="getShow" class="enter-x mb-40px text-light-50" />
<Form
class="p-4 enter-x"
:model="formData"
@@ -51,28 +51,27 @@
<ACol :span="12">
<FormItem>
<!-- No logic, you need to deal with it yourself -->
- <Checkbox v-model:checked="rememberMe" size="small">
+ <Checkbox v-model:checked="rememberMe" size="small"
class="!text-light-500">
{{ t('sys.login.rememberMe') }}
</Checkbox>
</FormItem>
</ACol>
- <ACol :span="12">
+ <!-- No logic, you need to deal with it yourself -->
+ <!-- <ACol :span="12">
<FormItem :style="{ 'text-align': 'right' }">
- <!-- No logic, you need to deal with it yourself -->
<Button type="link" size="small"
@click="setLoginState(LoginStateEnum.RESET_PASSWORD)">
{{ t('sys.login.forgetPassword') }}
</Button>
</FormItem>
- </ACol>
+ </ACol> -->
</ARow>
-
<FormItem class="enter-x">
<Button type="primary" block @click="handleLogin" :loading="loading">
- 通过 {{ loginText.buttonText }} 登录
+ {{ loginText.buttonText }}
</Button>
</FormItem>
- <FormItem class="enter-x text-center">
- <Button type="link" @click="changeLoginType"> 使用 {{ loginText.linkText
}} </Button>
+ <FormItem class="enter-x text-left">
+ <Button type="link" @click="changeLoginType"> {{ loginText.linkText }}
</Button>
</FormItem>
</Form>
<TeamModal v-model:visible="modelVisible" :userId="userId"
@success="handleTeamSuccess" />
@@ -112,7 +111,7 @@
const { createErrorModal, createMessage } = useMessage();
const { prefixCls } = useDesign('login');
const userStore = useUserStore();
- const { setLoginState, getLoginState } = useLoginState();
+ const { getLoginState } = useLoginState();
const { getFormRules } = useFormRules();
interface LoginForm {
account: string;
@@ -130,12 +129,12 @@
});
const loginText = computed(() => {
- const localText = '本地账户';
+ const localText = 'Login';
const ldapText = 'openLDAP';
if (loginType.value === LoginTypeEnum.LOCAL) {
- return { buttonText: localText, linkText: ldapText };
+ return { buttonText: localText, linkText: 'Login by openLDAP' };
}
- return { buttonText: ldapText, linkText: localText };
+ return { buttonText: ldapText, linkText: 'Login by Password' };
});
const { validForm } = useFormValid(formRef);
diff --git
a/streampark-console/streampark-console-newui/src/views/base/login/LoginFormTitle.vue
b/streampark-console/streampark-console-newui/src/views/base/login/LoginFormTitle.vue
index 954a7ef78..05284e4b8 100644
---
a/streampark-console/streampark-console-newui/src/views/base/login/LoginFormTitle.vue
+++
b/streampark-console/streampark-console-newui/src/views/base/login/LoginFormTitle.vue
@@ -15,7 +15,7 @@
limitations under the License.
-->
<template>
- <h2 class="mb-3 text-2xl font-bold text-center xl:text-3xl enter-x
xl:text-left">
+ <h2 class="mb-6 text-2xl font-bold text-center xl:text-3xl enter-x">
{{ getFormTitle }}
</h2>
</template>
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/EditFlink.vue
b/streampark-console/streampark-console-newui/src/views/flink/app/EditFlink.vue
index 0091a21fb..3b724b191 100644
---
a/streampark-console/streampark-console-newui/src/views/flink/app/EditFlink.vue
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/EditFlink.vue
@@ -225,4 +225,6 @@
</BasicForm>
</PageWrapper>
</template>
-<style scoped></style>
+<style lang="less">
+ @import url('./styles/Add.less');
+</style>
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/View.vue
b/streampark-console/streampark-console-newui/src/views/flink/app/View.vue
index 2e5809072..858dfbbb2 100644
--- a/streampark-console/streampark-console-newui/src/views/flink/app/View.vue
+++ b/streampark-console/streampark-console-newui/src/views/flink/app/View.vue
@@ -15,29 +15,23 @@
limitations under the License.
-->
<script lang="ts">
- import { defineComponent, onMounted, ref, reactive, unref, onUnmounted,
watch } from 'vue';
- import { useI18n } from '/@/hooks/web/useI18n';
+ import { defineComponent, ref, unref, onUnmounted, watch } from 'vue';
import { useUserStore } from '/@/store/modules/user';
+ import { useAppTableAction } from './hooks/useAppTableAction';
export default defineComponent({
name: 'AppView',
});
</script>
<script lang="ts" setup name="AppView">
import { useTimeoutFn } from '@vueuse/core';
- import { Row, Col, Tooltip, Badge, Divider, Select, Input, Tag } from
'ant-design-vue';
- import { fetchAppRecord, fetchDashboard, fetchAppRemove } from
'/@/api/flink/app/app';
- import { fetchFlamegraph } from '/@/api/flink/app/metrics';
- import { ActionItem, useTable } from '/@/components/Table';
- import { Icon } from '/@/components/Icon';
- import { useFlinkApplication } from './hooks/useApp';
- import { useRouter } from 'vue-router';
- import { useFlinkAppStore } from '/@/store/modules/flinkApplication';
+ import { Tooltip, Badge, Divider, Tag } from 'ant-design-vue';
+ import { fetchAppRecord } from '/@/api/flink/app/app';
+ import { useTable } from '/@/components/Table';
import { PageWrapper } from '/@/components/Page';
import { BasicTable, TableAction } from '/@/components/Table';
import { AppListRecord } from '/@/api/flink/app/app.type';
- import { useMessage } from '/@/hooks/web/useMessage';
import { getAppColumns, launchTitleMap } from './data';
- import { handleIsStart, handleView } from './utils';
+ import { handleView } from './utils';
import { useDrawer } from '/@/components/Drawer';
import { useModal } from '/@/components/Modal';
@@ -45,19 +39,10 @@
import StopApplicationModal from
'./components/AppView/StopApplicationModal.vue';
import LogModal from './components/AppView/LogModal.vue';
import BuildDrawer from './components/AppView/BuildDrawer.vue';
+ import AppDashboard from './components/AppView/AppDashboard.vue';
import State from './components/State';
- import StatisticCard from './components/AppView/statisticCard.vue';
-
- const SelectOption = Select.Option;
- const InputGroup = Input.Group;
- const InputSearch = Input.Search;
-
- const { t } = useI18n();
- const router = useRouter();
const userStore = useUserStore();
- const flinkAppStore = useFlinkAppStore();
- const { createMessage } = useMessage();
const optionApps = {
starting: new Map(),
@@ -65,52 +50,13 @@
launch: new Map(),
};
- const tagsOptions = ref<Recordable>([]);
- const dashboardLoading = ref(true);
- const dashBigScreenMap = reactive<Recordable>({});
+ const appDashboardRef = ref<any>();
const searchText = ref('');
const tags = ref(undefined);
const jobType = ref(undefined);
const userId = ref(undefined);
const yarn = ref<Nullable<string>>(null);
- // Get Dashboard Metrics Data
- async function handleDashboard(showLoading: boolean) {
- try {
- dashboardLoading.value = showLoading;
- const res = await fetchDashboard();
- if (res) {
- Object.assign(dashBigScreenMap, {
- availiableTask: {
- staticstics: { title: 'Available Task Slots', value:
res.availableSlot },
- footer: [
- { title: 'Task Slots', value: res.totalSlot },
- { title: 'Task Managers', value: res.totalTM },
- ],
- },
- runningJob: {
- staticstics: { title: 'Running Jobs', value: res.runningJob },
- footer: [
- { title: 'Total Task', value: res.task.total },
- { title: 'Running Task', value: res.task.running },
- ],
- },
- jobManager: {
- staticstics: { title: 'JobManager Memory', value: res.jmMemory },
- footer: [{ title: 'Total JobManager Mem', value: `${res.jmMemory}
MB` }],
- },
- taskManager: {
- staticstics: { title: 'TaskManager Memory', value: res.tmMemory },
- footer: [{ title: 'Total TaskManager Mem', value: `${res.tmMemory}
MB` }],
- },
- });
- }
- } catch (error) {
- console.error(error);
- } finally {
- dashboardLoading.value = false;
- }
- }
const [registerStartModal, { openModal: openStartModal }] = useModal();
const [registerStopModal, { openModal: openStopModal }] = useModal();
const [registerLogModal, { openModal: openLogModal }] = useModal();
@@ -173,9 +119,18 @@
columns: getAppColumns(),
showIndexColumn: false,
showTableSetting: true,
+ useSearchForm: true,
tableSetting: { fullScreen: true, redo: false },
actionColumn: { dataIndex: 'operation', title: 'Operation', width: 180 },
});
+ const { getTableActions, getActionDropdown, formConfig } = useAppTableAction(
+ openStartModal,
+ openStopModal,
+ openLogModal,
+ openBuildDrawer,
+ reload,
+ optionApps,
+ );
watch(
() => userStore.getTeamId,
@@ -194,26 +149,8 @@
openBuildDrawer(true, { appId: app.id });
}
- /* Click to edit */
- function handleEdit(app: AppListRecord) {
- flinkAppStore.setApplicationId(app.id);
- if (app.appType === 1) {
- // jobType( 1 custom code 2: flinkSQL)
- router.push({ path: '/flink/app/edit_streampark', query: { appId: app.id
} });
- } else if (app.appType === 2) {
- //Apache Flink
- router.push({ path: '/flink/app/edit_flink' });
- }
- }
- /* Click for details */
- function handleDetail(app: AppListRecord) {
- flinkAppStore.setApplicationId(app.id);
- router.push({ path: '/flink/app/detail', query: { appId: app.id } });
- }
-
/* view */
async function handleJobView(app: AppListRecord) {
- console.log('app', app.state, app.optionState);
// Task is running, restarting, in savePoint
if ([4, 5].includes(app.state) || app['optionState'] === 4) {
console.log(app);
@@ -221,172 +158,7 @@
handleView(app, unref(yarn));
}
}
- /* Click to delete */
- async function handleDelete(app: AppListRecord) {
- const hide = createMessage.loading('deleting', 0);
- try {
- await fetchAppRemove(app.id);
- createMessage.success('delete successful');
- reload();
- } catch (error) {
- console.error(error);
- } finally {
- hide();
- }
- }
- async function handleFlameGraph(app: AppListRecord) {
- const hide = createMessage.loading('flameGraph generating...', 0);
- try {
- const { data } = await fetchFlamegraph({
- appId: app.id,
- width: document.documentElement.offsetWidth ||
document.body.offsetWidth,
- });
- if (data != null) {
- const blob = new Blob([data], { type: 'image/svg+xml' });
- const imageUrl = (window.URL ||
window.webkitURL).createObjectURL(blob);
- window.open(imageUrl);
- }
- } catch (error) {
- console.error(error);
- createMessage.error('flameGraph generate failed');
- } finally {
- hide();
- }
- }
-
- function handleCancel(app: AppListRecord) {
- if (!optionApps.stopping.get(app.id) || app['optionState'] === 0) {
- openStopModal(true, { application: app });
- }
- }
- /* log view */
- function handleSeeLog(app: AppListRecord) {
- openLogModal(true, { app: unref(app) });
- }
- const {
- handleCheckLaunchApp,
- handleAppCheckStart,
- handleCanStop,
- handleForcedStop,
- handleCopy,
- handleMapping,
- users,
- } = useFlinkApplication(openStartModal);
-
- /* tag */
- function handleInitTagsOptions() {
- const params = Object.assign({}, { pageSize: 999999999, pageNum: 1 });
- fetchAppRecord(params).then((res) => {
- const dataSource = res?.records || [];
- dataSource.forEach((record) => {
- if (record.tags !== null && record.tags !== undefined && record.tags
!== '') {
- const tagsArray = record.tags.split(',') as string[];
- tagsArray.forEach((x: string) => {
- if (x.length > 0 && tagsOptions.value.indexOf(x) == -1) {
- tagsOptions.value.push(x);
- }
- });
- }
- });
- });
- }
-
- /* Operation button */
- function getTableActions(record: AppListRecord): ActionItem[] {
- return [
- {
- tooltip: { title: 'Edit Application' },
- auth: 'app:update',
- icon: 'clarity:note-edit-line',
- onClick: handleEdit.bind(null, record),
- },
- {
- tooltip: { title: 'Launch Application' },
- ifShow: [-1, 1, 4].includes(record.launch) && record['optionState']
=== 0,
- icon: 'ant-design:cloud-upload-outlined',
- onClick: handleCheckLaunchApp.bind(null, record),
- },
- {
- tooltip: { title: 'Launching Progress Detail' },
- ifShow: [-1, 2].includes(record.launch) || record['optionState'] === 1,
- auth: 'app:update',
- icon: 'ant-design:container-outlined',
- onClick: openBuildProgressDetailDrawer.bind(null, record),
- },
- {
- tooltip: { title: 'Start Application' },
- ifShow: handleIsStart(record, optionApps),
- auth: 'app:start',
- icon: 'ant-design:play-circle-outlined',
- onClick: handleAppCheckStart.bind(null, record),
- },
- {
- tooltip: { title: 'Cancel Application' },
- ifShow: record.state === 5 && record['optionState'] === 0,
- auth: 'app:cancel',
- icon: 'ant-design:pause-circle-outlined',
- onClick: handleCancel.bind(null, record),
- },
- {
- tooltip: { title: 'View Application Detail' },
- auth: 'app:detail',
- icon: 'ant-design:eye-outlined',
- onClick: handleDetail.bind(null, record),
- },
- {
- tooltip: { title: 'See Flink Start log' },
- ifShow: [5, 6].includes(record.executionMode),
- auth: 'app:detail',
- icon: 'ant-design:sync-outlined',
- onClick: handleSeeLog.bind(null, record),
- },
- {
- tooltip: { title: 'Forced Stop Application' },
- ifShow: handleCanStop(record),
- auth: 'app:cancel',
- icon: 'ant-design:pause-circle-outlined',
- onClick: handleForcedStop.bind(null, record),
- },
- ];
- }
-
- /* pull down button */
- function getActionDropdown(record: AppListRecord): ActionItem[] {
- return [
- {
- label: 'Copy Application',
- auth: 'app:copy',
- icon: 'ant-design:copy-outlined',
- onClick: handleCopy.bind(null, record),
- },
- {
- label: 'Remapping Application',
- ifShow: [0, 7, 10, 11, 13].includes(record.state),
- auth: 'app:mapping',
- icon: 'ant-design:deployment-unit-outlined',
- onClick: handleMapping.bind(null, record),
- },
- {
- label: 'View FlameGraph',
- ifShow: record.flameGraph,
- auth: 'app:flameGraph',
- icon: 'ant-design:fire-outlined',
- onClick: handleFlameGraph.bind(null, record),
- },
- {
- popConfirm: {
- title: 'Are you sure delete this job ?',
- confirm: handleDelete.bind(null, record),
- },
- label: 'Delete',
- ifShow: [0, 7, 9, 10, 13, 18, 19].includes(record.state),
- auth: 'app:delete',
- icon: 'ant-design:delete-outlined',
- color: 'error',
- },
- ];
- }
/* Update options data */
function handleOptionApp(data: {
type: 'starting' | 'stopping' | 'launch';
@@ -398,7 +170,7 @@
const { start, stop } = useTimeoutFn(() => {
if (!getLoading()) {
- handleDashboard(false);
+ appDashboardRef.value?.handleDashboard(false);
reload({ polling: true });
}
start();
@@ -408,10 +180,6 @@
stop();
});
- onMounted(() => {
- handleDashboard(true);
- handleInitTagsOptions();
- });
watch(
() => [tags.value, userId.value, jobType.value],
() => {
@@ -421,80 +189,8 @@
</script>
<template>
<PageWrapper contentFullHeight>
- <Row :gutter="24" class="dashboard">
- <Col
- class="gutter-row mt-10px"
- :md="6"
- :xs="24"
- v-for="(value, key) in dashBigScreenMap"
- :key="key"
- >
- <StatisticCard
- :statisticProps="value.staticstics"
- :footerList="value.footer"
- :loading="dashboardLoading"
- />
- </Col>
- </Row>
- <BasicTable @register="registerTable" class="app_list !px-0 mt-20px">
- <template #headerTop>
- <div class="text-right my-15px">
- <InputGroup compact>
- <div class="pr-16px">
- <Select
- placeholder="Tags"
- show-search
- allowClear
- v-model:value="tags"
- class="!w-150px text-left"
- >
- <SelectOption v-for="tag in tagsOptions" :key="tag">{{ tag
}}</SelectOption>
- </Select>
- </div>
-
- <div class="pr-16px">
- <Select
- placeholder="Owner"
- allowClear
- v-model:value="userId"
- class="!w-120px text-left"
- >
- <SelectOption v-for="u in users" :key="u.userId">
- <span v-if="u.nickName"> {{ u.nickName }} </span>
- <span v-else> {{ u.username }} </span>
- </SelectOption>
- </Select>
- </div>
- <div class="pr-16px">
- <Select
- placeholder="Type"
- allowClear
- v-model:value="jobType"
- class="w-100px text-left"
- >
- <SelectOption value="1">JAR</SelectOption>
- <SelectOption value="2">SQL</SelectOption>
- </Select>
- </div>
- <div class="pr-16px">
- <InputSearch
- placeholder="Search..."
- v-model:value="searchText"
- @search="reload({ polling: true })"
- class="!w-250px text-left"
- />
- </div>
- <a-button
- type="primary"
- style="margin-left: 20px"
- @click="router.push({ path: '/flink/app/add' })"
- >
- <Icon icon="ant-design:plus-outlined" />
- {{ t('common.add') }}
- </a-button>
- </InputGroup>
- </div>
- </template>
+ <AppDashboard ref="appDashboardRef" />
+ <BasicTable @register="registerTable" class="app_list !px-0 mt-20px"
:formConfig="formConfig">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'jobName'">
<span class="app_type app_jar" v-if="record['jobType'] === 1"> JAR
</span>
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/components/AppView/AppDashboard.vue
b/streampark-console/streampark-console-newui/src/views/flink/app/components/AppView/AppDashboard.vue
new file mode 100644
index 000000000..73b825da2
--- /dev/null
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/components/AppView/AppDashboard.vue
@@ -0,0 +1,87 @@
+<!--
+ 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 lang="ts" setup>
+ import { onMounted, reactive, ref } from 'vue';
+ import { fetchDashboard } from '/@/api/flink/app/app';
+ import StatisticCard from './statisticCard.vue';
+ import { Row, Col } from 'ant-design-vue';
+ const dashBigScreenMap = reactive<Recordable>({});
+ const dashboardLoading = ref(true);
+
+ // Get Dashboard Metrics Data
+ async function handleDashboard(showLoading: boolean) {
+ try {
+ dashboardLoading.value = showLoading;
+ const res = await fetchDashboard();
+ if (res) {
+ Object.assign(dashBigScreenMap, {
+ availiableTask: {
+ staticstics: { title: 'Available Task Slots', value:
res.availableSlot },
+ footer: [
+ { title: 'Task Slots', value: res.totalSlot },
+ { title: 'Task Managers', value: res.totalTM },
+ ],
+ },
+ runningJob: {
+ staticstics: { title: 'Running Jobs', value: res.runningJob },
+ footer: [
+ { title: 'Total Task', value: res.task.total },
+ { title: 'Running Task', value: res.task.running },
+ ],
+ },
+ jobManager: {
+ staticstics: { title: 'JobManager Memory', value: res.jmMemory },
+ footer: [{ title: 'Total JobManager Mem', value: `${res.jmMemory}
MB` }],
+ },
+ taskManager: {
+ staticstics: { title: 'TaskManager Memory', value: res.tmMemory },
+ footer: [{ title: 'Total TaskManager Mem', value: `${res.tmMemory}
MB` }],
+ },
+ });
+ }
+ } catch (error) {
+ console.error(error);
+ } finally {
+ dashboardLoading.value = false;
+ }
+ }
+
+ onMounted(() => {
+ handleDashboard(true);
+ });
+
+ defineExpose({ handleDashboard });
+</script>
+<template>
+ <Row :gutter="24" class="dashboard">
+ <Col
+ class="gutter-row mt-10px"
+ :md="6"
+ :xs="24"
+ v-for="(value, key) in dashBigScreenMap"
+ :key="key"
+ >
+ <StatisticCard
+ :statisticProps="value.staticstics"
+ :footerList="value.footer"
+ :loading="dashboardLoading"
+ />
+ </Col>
+ </Row>
+</template>
+<style lang="less"></style>
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/components/Dependency.vue
b/streampark-console/streampark-console-newui/src/views/flink/app/components/Dependency.vue
index 734fdfc2a..926f80877 100644
---
a/streampark-console/streampark-console-newui/src/views/flink/app/components/Dependency.vue
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/components/Dependency.vue
@@ -232,10 +232,3 @@
</TabPane>
</Tabs>
</template>
-<style lang="less">
- .pom-card {
- .ant-tabs-nav {
- margin-bottom: 0 !important;
- }
- }
-</style>
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/components/PodTemplate/PomTemplateTab.vue
b/streampark-console/streampark-console-newui/src/views/flink/app/components/PodTemplate/PomTemplateTab.vue
index 3296d16e3..866149e04 100644
---
a/streampark-console/streampark-console-newui/src/views/flink/app/components/PodTemplate/PomTemplateTab.vue
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/components/PodTemplate/PomTemplateTab.vue
@@ -268,7 +268,7 @@
}
</script>
<template>
- <Tabs type="card" v-model:activeKey="podTemplateTab">
+ <Tabs type="card" v-model:activeKey="podTemplateTab" class="pom-card">
<TabPane key="pod-template" tab="Pod Template" forceRender>
<TemplateButtonGroup
visualType="ptVisual"
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/hooks/useAppTable.ts
b/streampark-console/streampark-console-newui/src/views/flink/app/hooks/useAppTable.ts
new file mode 100644
index 000000000..7cebb7e2a
--- /dev/null
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/hooks/useAppTable.ts
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+import { AppListRecord } from '/@/api/flink/app/app.type';
+import { ActionItem } from '/@/components/Table';
+
+export const useAppTable = () => {
+ /* Operation button */
+ function getTableActions(record: AppListRecord): ActionItem[] {
+ return [
+ {
+ tooltip: { title: 'Edit Application' },
+ auth: 'app:update',
+ icon: 'clarity:note-edit-line',
+ onClick: handleEdit.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Launch Application' },
+ ifShow: [-1, 1, 4].includes(record.launch) && record['optionState']
=== 0,
+ icon: 'ant-design:cloud-upload-outlined',
+ onClick: handleCheckLaunchApp.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Launching Progress Detail' },
+ ifShow: [-1, 2].includes(record.launch) || record['optionState'] === 1,
+ auth: 'app:update',
+ icon: 'ant-design:container-outlined',
+ onClick: openBuildProgressDetailDrawer.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Start Application' },
+ ifShow: handleIsStart(record, optionApps),
+ auth: 'app:start',
+ icon: 'ant-design:play-circle-outlined',
+ onClick: handleAppCheckStart.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Cancel Application' },
+ ifShow: record.state === 5 && record['optionState'] === 0,
+ auth: 'app:cancel',
+ icon: 'ant-design:pause-circle-outlined',
+ onClick: handleCancel.bind(null, record),
+ },
+ {
+ tooltip: { title: 'View Application Detail' },
+ auth: 'app:detail',
+ icon: 'ant-design:eye-outlined',
+ onClick: handleDetail.bind(null, record),
+ },
+ {
+ tooltip: { title: 'See Flink Start log' },
+ ifShow: [5, 6].includes(record.executionMode),
+ auth: 'app:detail',
+ icon: 'ant-design:sync-outlined',
+ onClick: handleSeeLog.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Forced Stop Application' },
+ ifShow: handleCanStop(record),
+ auth: 'app:cancel',
+ icon: 'ant-design:pause-circle-outlined',
+ onClick: handleForcedStop.bind(null, record),
+ },
+ ];
+ }
+ /* Click to edit */
+ function handleEdit(app: AppListRecord) {
+ flinkAppStore.setApplicationId(app.id);
+ if (app.appType === 1) {
+ // jobType( 1 custom code 2: flinkSQL)
+ router.push({ path: '/flink/app/edit_streampark', query: { appId: app.id
} });
+ } else if (app.appType === 2) {
+ //Apache Flink
+ router.push({ path: '/flink/app/edit_flink' });
+ }
+ }
+ /* pull down button */
+ function getActionDropdown(record: AppListRecord): ActionItem[] {
+ return [
+ {
+ label: 'Copy Application',
+ auth: 'app:copy',
+ icon: 'ant-design:copy-outlined',
+ onClick: handleCopy.bind(null, record),
+ },
+ {
+ label: 'Remapping Application',
+ ifShow: [0, 7, 10, 11, 13].includes(record.state),
+ auth: 'app:mapping',
+ icon: 'ant-design:deployment-unit-outlined',
+ onClick: handleMapping.bind(null, record),
+ },
+ {
+ label: 'View FlameGraph',
+ ifShow: record.flameGraph,
+ auth: 'app:flameGraph',
+ icon: 'ant-design:fire-outlined',
+ onClick: handleFlameGraph.bind(null, record),
+ },
+ {
+ popConfirm: {
+ title: 'Are you sure delete this job ?',
+ confirm: handleDelete.bind(null, record),
+ },
+ label: 'Delete',
+ ifShow: [0, 7, 9, 10, 13, 18, 19].includes(record.state),
+ auth: 'app:delete',
+ icon: 'ant-design:delete-outlined',
+ color: 'error',
+ },
+ ];
+ }
+ return {};
+};
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/hooks/useAppTableAction.ts
b/streampark-console/streampark-console-newui/src/views/flink/app/hooks/useAppTableAction.ts
new file mode 100644
index 000000000..38ab6ba05
--- /dev/null
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/hooks/useAppTableAction.ts
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+import { computed, onMounted, ref } from 'vue';
+import { useRouter } from 'vue-router';
+import { handleIsStart } from '../utils';
+import { useFlinkAppStore } from '/@/store/modules/flinkApplication';
+import { useFlinkApplication } from './useApp';
+import { fetchAppRecord, fetchAppRemove } from '/@/api/flink/app/app';
+import { AppListRecord } from '/@/api/flink/app/app.type';
+import { fetchFlamegraph } from '/@/api/flink/app/metrics';
+import { ActionItem, FormProps } from '/@/components/Table';
+import { useMessage } from '/@/hooks/web/useMessage';
+export enum JobTypeEnum {
+ JAR = 1,
+ SQL = 2,
+}
+
+// Create form configurations and operation functions in the application table
+export const useAppTableAction = (
+ openStartModal: Fn,
+ openStopModal: Fn,
+ openLogModal: Fn,
+ openBuildDrawer: Fn,
+ reload: Fn,
+ optionApps: Recordable,
+) => {
+ const tagsOptions = ref<Recordable>([]);
+
+ const flinkAppStore = useFlinkAppStore();
+ const router = useRouter();
+ const { createMessage } = useMessage();
+
+ const {
+ handleCheckLaunchApp,
+ handleAppCheckStart,
+ handleCanStop,
+ handleForcedStop,
+ handleCopy,
+ handleMapping,
+ users,
+ } = useFlinkApplication(openStartModal);
+
+ /* Operation button */
+ function getTableActions(record: AppListRecord): ActionItem[] {
+ return [
+ {
+ tooltip: { title: 'Edit Application' },
+ auth: 'app:update',
+ icon: 'clarity:note-edit-line',
+ onClick: handleEdit.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Launch Application' },
+ ifShow: [-1, 1, 4].includes(record.launch) && record['optionState']
=== 0,
+ icon: 'ant-design:cloud-upload-outlined',
+ onClick: handleCheckLaunchApp.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Launching Progress Detail' },
+ ifShow: [-1, 2].includes(record.launch) || record['optionState'] === 1,
+ auth: 'app:update',
+ icon: 'ant-design:container-outlined',
+ onClick: () => openBuildDrawer(true, { appId: record.id }),
+ },
+ {
+ tooltip: { title: 'Start Application' },
+ ifShow: handleIsStart(record, optionApps),
+ auth: 'app:start',
+ icon: 'ant-design:play-circle-outlined',
+ onClick: handleAppCheckStart.bind(null, record),
+ },
+ {
+ tooltip: { title: 'Cancel Application' },
+ ifShow: record.state === 5 && record['optionState'] === 0,
+ auth: 'app:cancel',
+ icon: 'ant-design:pause-circle-outlined',
+ onClick: handleCancel.bind(null, record),
+ },
+ {
+ tooltip: { title: 'View Application Detail' },
+ auth: 'app:detail',
+ icon: 'ant-design:eye-outlined',
+ onClick: handleDetail.bind(null, record),
+ },
+ {
+ tooltip: { title: 'See Flink Start log' },
+ ifShow: [5, 6].includes(record.executionMode),
+ auth: 'app:detail',
+ icon: 'ant-design:sync-outlined',
+ onClick: () => openLogModal(true, { app: record }),
+ },
+ {
+ tooltip: { title: 'Forced Stop Application' },
+ ifShow: handleCanStop(record),
+ auth: 'app:cancel',
+ icon: 'ant-design:pause-circle-outlined',
+ onClick: handleForcedStop.bind(null, record),
+ },
+ ];
+ }
+ /* Click to edit */
+ function handleEdit(app: AppListRecord) {
+ flinkAppStore.setApplicationId(app.id);
+ if (app.appType === 1) {
+ // jobType( 1 custom code 2: flinkSQL)
+ router.push({ path: '/flink/app/edit_streampark', query: { appId: app.id
} });
+ } else if (app.appType === 2) {
+ //Apache Flink
+ router.push({ path: '/flink/app/edit_flink' });
+ }
+ }
+
+ /* Click for details */
+ function handleDetail(app: AppListRecord) {
+ flinkAppStore.setApplicationId(app.id);
+ router.push({ path: '/flink/app/detail', query: { appId: app.id } });
+ }
+ // click stop application
+ function handleCancel(app: AppListRecord) {
+ if (!optionApps.stopping.get(app.id) || app['optionState'] === 0) {
+ openStopModal(true, { application: app });
+ }
+ }
+ /* pull down button */
+ function getActionDropdown(record: AppListRecord): ActionItem[] {
+ return [
+ {
+ label: 'Copy Application',
+ auth: 'app:copy',
+ icon: 'ant-design:copy-outlined',
+ onClick: handleCopy.bind(null, record),
+ },
+ {
+ label: 'Remapping Application',
+ ifShow: [0, 7, 10, 11, 13].includes(record.state),
+ auth: 'app:mapping',
+ icon: 'ant-design:deployment-unit-outlined',
+ onClick: handleMapping.bind(null, record),
+ },
+ {
+ label: 'View FlameGraph',
+ ifShow: record.flameGraph,
+ auth: 'app:flameGraph',
+ icon: 'ant-design:fire-outlined',
+ onClick: handleFlameGraph.bind(null, record),
+ },
+ {
+ popConfirm: {
+ title: 'Are you sure delete this job ?',
+ confirm: handleDelete.bind(null, record),
+ },
+ label: 'Delete',
+ ifShow: [0, 7, 9, 10, 13, 18, 19].includes(record.state),
+ auth: 'app:delete',
+ icon: 'ant-design:delete-outlined',
+ color: 'error',
+ },
+ ];
+ }
+
+ async function handleFlameGraph(app: AppListRecord) {
+ const hide = createMessage.loading('flameGraph generating...', 0);
+ try {
+ const { data } = await fetchFlamegraph({
+ appId: app.id,
+ width: document.documentElement.offsetWidth ||
document.body.offsetWidth,
+ });
+ if (data != null) {
+ const blob = new Blob([data], { type: 'image/svg+xml' });
+ const imageUrl = (window.URL ||
window.webkitURL).createObjectURL(blob);
+ window.open(imageUrl);
+ }
+ } catch (error) {
+ console.error(error);
+ createMessage.error('flameGraph generate failed');
+ } finally {
+ hide();
+ }
+ }
+
+ /* Click to delete */
+ async function handleDelete(app: AppListRecord) {
+ const hide = createMessage.loading('deleting', 0);
+ try {
+ await fetchAppRemove(app.id);
+ createMessage.success('delete successful');
+ reload();
+ } catch (error) {
+ console.error(error);
+ } finally {
+ hide();
+ }
+ }
+
+ const formConfig = computed((): Partial<FormProps> => {
+ return {
+ baseColProps: { span: 5, style: { paddingRight: '20px' } },
+ actionColOptions: { span: 4 },
+ showSubmitButton: false,
+ colon: true,
+ resetButtonOptions: {
+ text: 'Add New',
+ color: 'primary',
+ preIcon: 'ant-design:plus-outlined',
+ },
+ async resetFunc() {
+ router.push({ path: '/flink/app/add' });
+ },
+ schemas: [
+ {
+ label: 'Tags',
+ field: 'tag',
+ component: 'Select',
+ componentProps: {
+ placeholder: 'Tags',
+ showSearch: true,
+ options: tagsOptions.value.map((t: Recordable) => ({ label: t,
value: t })),
+ },
+ },
+ {
+ label: 'Owner',
+ field: 'userId',
+ component: 'Select',
+ componentProps: {
+ placeholder: 'Owner',
+ showSearch: true,
+ options: users.value.map((u: Recordable) => {
+ return { label: u.nickName || u.username, value: u.userId };
+ }),
+ },
+ },
+ {
+ label: 'Type',
+ field: 'jobType',
+ component: 'Select',
+ componentProps: {
+ placeholder: 'Type',
+ showSearch: true,
+ options: [
+ { label: 'JAR', value: JobTypeEnum.JAR },
+ { label: 'SQL', value: JobTypeEnum.SQL },
+ ],
+ },
+ },
+ {
+ label: 'keyWords',
+ field: 'searchText',
+ component: 'InputSearch',
+ componentProps: {
+ placeholder: 'Search',
+ onSearch: () => reload({ polling: true }),
+ },
+ },
+ ],
+ };
+ });
+
+ /* tag */
+ function handleInitTagsOptions() {
+ const params = Object.assign({}, { pageSize: 999999999, pageNum: 1 });
+ fetchAppRecord(params).then((res) => {
+ const dataSource = res?.records || [];
+ dataSource.forEach((record) => {
+ if (record.tags !== null && record.tags !== undefined && record.tags
!== '') {
+ const tagsArray = record.tags.split(',') as string[];
+ tagsArray.forEach((x: string) => {
+ if (x.length > 0 && tagsOptions.value.indexOf(x) == -1) {
+ tagsOptions.value.push(x);
+ }
+ });
+ }
+ });
+ });
+ }
+ onMounted(() => {
+ handleInitTagsOptions();
+ });
+ return { getTableActions, getActionDropdown, formConfig };
+};
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/app/styles/Add.less
b/streampark-console/streampark-console-newui/src/views/flink/app/styles/Add.less
index 985589e60..df09985fc 100644
---
a/streampark-console/streampark-console-newui/src/views/flink/app/styles/Add.less
+++
b/streampark-console/streampark-console-newui/src/views/flink/app/styles/Add.less
@@ -296,3 +296,9 @@
transform: translateX(10px);
opacity: 0;
}
+
+.pom-card {
+ .ant-tabs-nav {
+ margin-bottom: 0 !important;
+ }
+}
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/notebook/Submit.less
b/streampark-console/streampark-console-newui/src/views/flink/notebook/Submit.less
index 304e614ac..7f8584053 100644
---
a/streampark-console/streampark-console-newui/src/views/flink/notebook/Submit.less
+++
b/streampark-console/streampark-console-newui/src/views/flink/notebook/Submit.less
@@ -14,6 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+[data-theme="dark"] {
+ .nodebook-submit {
+ .current-line,
+ .current-line-both {
+ border: 1px solid hsl(205deg 100% 74% / 20%) !important;
+ background-color: hsl(205deg 100% 74% / 20%);
+ }
+ }
+}
+
.nodebook-submit {
.code-box {
height: 600px;
diff --git
a/streampark-console/streampark-console-newui/src/views/flink/setting/components/AlertModal.vue
b/streampark-console/streampark-console-newui/src/views/flink/setting/components/AlertModal.vue
index caece4d27..fa405f5bd 100644
---
a/streampark-console/streampark-console-newui/src/views/flink/setting/components/AlertModal.vue
+++
b/streampark-console/streampark-console-newui/src/views/flink/setting/components/AlertModal.vue
@@ -205,7 +205,7 @@
<SvgIcon name="alarm" size="25" />
Alert Setting
</template>
- <BasicForm @register="registerForm">
+ <BasicForm @register="registerForm" class="!mt-15px">
<template #type="{ model, field }">
<Select
v-model:value="model[field]"