You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by zh...@apache.org on 2022/01/18 12:34:15 UTC

[dolphinscheduler] branch dev updated: [Feature][UI Next] Add token manage. (#8105)

This is an automated email from the ASF dual-hosted git repository.

zhongjiajie pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 759fcb8  [Feature][UI Next] Add token manage. (#8105)
759fcb8 is described below

commit 759fcb8e11b4cf5900363b99af552ddf49867fe4
Author: songjianet <17...@qq.com>
AuthorDate: Tue Jan 18 20:34:07 2022 +0800

    [Feature][UI Next] Add token manage. (#8105)
---
 .../src/locales/modules/en_US.ts                   |  17 ++
 .../src/locales/modules/zh_CN.ts                   |  17 ++
 .../src/router/modules/security.ts                 |   8 +
 .../src/service/modules/token/index.ts             |  18 +-
 .../src/service/modules/token/types.ts             |  21 ++-
 .../src/service/modules/users/index.ts             |   2 +-
 .../src/service/modules/users/types.ts             |  28 ++-
 ...alert-group-modal.tsx => alarm-group-modal.tsx} |   6 +-
 .../views/security/alarm-group-manage/index.tsx    |   4 +-
 .../components/environment-modal.tsx               |   2 +-
 .../token-manage/components/token-modal.tsx        | 207 +++++++++++++++++++++
 .../security/token-manage/components/use-modal.ts  | 164 ++++++++++++++++
 .../security/token-manage/index.module.scss}       |  50 +++--
 .../{alarm-group-manage => token-manage}/index.tsx |  14 +-
 .../src/views/security/token-manage/use-table.ts   | 192 +++++++++++++++++++
 15 files changed, 700 insertions(+), 50 deletions(-)

diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index e0f0d34..b26fd0d 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -372,6 +372,23 @@ const security = {
     environment_config_tips: 'Please enter your environment config',
     environment_description_tips: 'Please enter your environment description',
     worker_group_tips: 'Please select worker group'
+  },
+  token: {
+    create_token: 'Create Token',
+    edit_token: 'Edit Token',
+    search_tips: 'Please enter keywords',
+    user: 'User',
+    user_tips: 'Please select user',
+    token: 'Token',
+    token_tips: 'Please enter your token',
+    expiration_time: 'Expiration Time',
+    expiration_time_tips: 'Please select expiration time',
+    create_time: 'Create Time',
+    update_time: 'Update Time',
+    operation: 'Operation',
+    edit: 'Edit',
+    delete: 'Delete',
+    delete_confirm: 'Delete?'
   }
 }
 
diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
index d456a81..c5f4264 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -372,6 +372,23 @@ const security = {
     environment_config_tips: '请输入环境配置',
     environment_description_tips: '请输入环境描述',
     worker_group_tips: '请选择Worker分组'
+  },
+  token: {
+    create_token: '创建令牌',
+    edit_token: '编辑令牌',
+    search_tips: '请输入关键词',
+    user: '用户',
+    user_tips: '请选择用户',
+    token: '令牌',
+    token_tips: '请输入令牌',
+    expiration_time: '失效时间',
+    expiration_time_tips: '请选择失效时间',
+    create_time: '创建时间',
+    update_time: '更新时间',
+    operation: '操作',
+    edit: '编辑',
+    delete: '删除',
+    delete_confirm: '确定删除吗?'
   }
 }
 
diff --git a/dolphinscheduler-ui-next/src/router/modules/security.ts b/dolphinscheduler-ui-next/src/router/modules/security.ts
index 521d9a3..359e932 100644
--- a/dolphinscheduler-ui-next/src/router/modules/security.ts
+++ b/dolphinscheduler-ui-next/src/router/modules/security.ts
@@ -76,6 +76,14 @@ export default {
       meta: {
         title: '环境管理'
       }
+    },
+    {
+      path: '/security/token-manage',
+      name: 'token-manage',
+      component: components['token-manage'],
+      meta: {
+        title: '令牌管理管理'
+      }
     }
   ]
 }
diff --git a/dolphinscheduler-ui-next/src/service/modules/token/index.ts b/dolphinscheduler-ui-next/src/service/modules/token/index.ts
index e7b9437..69a898d 100644
--- a/dolphinscheduler-ui-next/src/service/modules/token/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/token/index.ts
@@ -44,8 +44,24 @@ export function queryAccessTokenByUser(params: UserReq): any {
 
 export function updateToken(data: UpdateTokenReq): any {
   return axios({
-    url: '/access-tokens',
+    url: `/access-tokens/${data.id}`,
     method: 'put',
     data
   })
 }
+
+export function deleteToken(id: number): any {
+  return axios({
+    url: `/access-tokens/${id}`,
+    method: 'delete',
+    params: { id }
+  })
+}
+
+export function generateToken(data: TokenReq): any {
+  return axios({
+    url: `/access-tokens/generate`,
+    method: 'post',
+    data
+  })
+}
diff --git a/dolphinscheduler-ui-next/src/service/modules/token/types.ts b/dolphinscheduler-ui-next/src/service/modules/token/types.ts
index 98bc1f9..b4456bc 100644
--- a/dolphinscheduler-ui-next/src/service/modules/token/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/token/types.ts
@@ -48,4 +48,23 @@ interface UpdateTokenReq extends TokenReq {
   userType?: string
 }
 
-export { ListReq, TokenReq, UserReq, UpdateTokenReq }
+interface TokenItem {
+  id: number
+  userId: number
+  token: string
+  expireTime: string
+  createTime: string
+  updateTime: string
+  userName: string
+}
+
+interface TokenRes {
+  totalList: TokenItem[]
+  total: number
+  totalPage: number
+  pageSize: number
+  currentPage: number
+  start: number
+}
+
+export { ListReq, TokenReq, UserReq, UpdateTokenReq, TokenItem, TokenRes }
diff --git a/dolphinscheduler-ui-next/src/service/modules/users/index.ts b/dolphinscheduler-ui-next/src/service/modules/users/index.ts
index 2b52b19..1e91571 100644
--- a/dolphinscheduler-ui-next/src/service/modules/users/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/users/index.ts
@@ -127,7 +127,7 @@ export function listUser(): any {
   })
 }
 
-export function listAll(params: ListAllReq): any {
+export function listAll(params?: ListAllReq): any {
   return axios({
     url: '/users/list-all',
     method: 'get',
diff --git a/dolphinscheduler-ui-next/src/service/modules/users/types.ts b/dolphinscheduler-ui-next/src/service/modules/users/types.ts
index eef7b59..95b4a9f 100644
--- a/dolphinscheduler-ui-next/src/service/modules/users/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/users/types.ts
@@ -28,10 +28,10 @@ interface AlertGroupIdReq {
 }
 
 interface UserReq {
-  email: string
-  tenantId: number
-  userName: string
-  userPassword: string
+  email?: string
+  tenantId?: number
+  userName?: string
+  userPassword?: string
   phone?: string
   queue?: string
   state?: number
@@ -97,6 +97,23 @@ interface UserInfoRes extends UserReq, IdReq {
   updateTime: string
 }
 
+interface UserListRes {
+  id: number
+  userName: string
+  userPassword: string
+  email: string
+  phone: string
+  userType: string
+  tenantId: number
+  state: number
+  tenantCode?: any
+  queueName?: any
+  alertGroup?: any
+  queue: string
+  createTime: string
+  updateTime: string
+}
+
 export {
   UserNameReq,
   UserNamesReq,
@@ -112,5 +129,6 @@ export {
   ListAllReq,
   ListReq,
   RegisterUserReq,
-  UserInfoRes
+  UserInfoRes,
+  UserListRes
 }
diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/components/alert-group-modal.tsx b/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/components/alarm-group-modal.tsx
similarity index 98%
rename from dolphinscheduler-ui-next/src/views/security/alarm-group-manage/components/alert-group-modal.tsx
rename to dolphinscheduler-ui-next/src/views/security/alarm-group-manage/components/alarm-group-modal.tsx
index ffe457a..dcfcb4c 100644
--- a/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/components/alert-group-modal.tsx
+++ b/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/components/alarm-group-modal.tsx
@@ -21,8 +21,8 @@ import { NForm, NFormItem, NInput, NSelect } from 'naive-ui'
 import { useModal } from './use-modal'
 import { useI18n } from 'vue-i18n'
 
-const AlertGroupModal = defineComponent({
-  name: 'YarnQueueModal',
+const AlarmGroupModal = defineComponent({
+  name: 'AlarmGroupModal',
   props: {
     showModalRef: {
       type: Boolean as PropType<boolean>,
@@ -163,4 +163,4 @@ const AlertGroupModal = defineComponent({
   }
 })
 
-export default AlertGroupModal
+export default AlarmGroupModal
diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx
index 4ee81d6..c57d10b 100644
--- a/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx
@@ -28,7 +28,7 @@ import { SearchOutlined } from '@vicons/antd'
 import { useI18n } from 'vue-i18n'
 import { useTable } from './use-table'
 import Card from '@/components/card'
-import AlertGroupModal from './components/alert-group-modal'
+import AlarmGroupModal from './components/alarm-group-modal'
 import styles from './index.module.scss'
 
 const alarmGroupManage = defineComponent({
@@ -143,7 +143,7 @@ const alarmGroupManage = defineComponent({
             />
           </div>
         </Card>
-        <AlertGroupModal
+        <AlarmGroupModal
           showModalRef={this.showModalRef}
           statusRef={this.statusRef}
           row={this.row}
diff --git a/dolphinscheduler-ui-next/src/views/security/environment-manage/components/environment-modal.tsx b/dolphinscheduler-ui-next/src/views/security/environment-manage/components/environment-modal.tsx
index 4c04a5d..4ab94d2 100644
--- a/dolphinscheduler-ui-next/src/views/security/environment-manage/components/environment-modal.tsx
+++ b/dolphinscheduler-ui-next/src/views/security/environment-manage/components/environment-modal.tsx
@@ -35,7 +35,7 @@ const envConfigPlaceholder =
   'export HADOOP_CLASSPATH=`hadoop classpath`\n'
 
 const EnvironmentModal = defineComponent({
-  name: 'YarnQueueModal',
+  name: 'EnvironmentModal',
   props: {
     showModalRef: {
       type: Boolean as PropType<boolean>,
diff --git a/dolphinscheduler-ui-next/src/views/security/token-manage/components/token-modal.tsx b/dolphinscheduler-ui-next/src/views/security/token-manage/components/token-modal.tsx
new file mode 100644
index 0000000..e0abcf9
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/token-manage/components/token-modal.tsx
@@ -0,0 +1,207 @@
+/*
+ * 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
+ *
+ *    http://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 { defineComponent, PropType, toRefs, watch } from 'vue'
+import Modal from '@/components/modal'
+import {
+  NForm,
+  NFormItem,
+  NInput,
+  NSelect,
+  NDatePicker,
+  NButton,
+  NIcon,
+  NSpace
+} from 'naive-ui'
+import { ReloadOutlined } from '@vicons/antd'
+import { useModal } from './use-modal'
+import { useI18n } from 'vue-i18n'
+import { useUserStore } from '@/store/user/user'
+import { subDays } from 'date-fns'
+import type { UserInfoRes } from '@/service/modules/users/types'
+
+const TokenModal = defineComponent({
+  name: 'TokenModal',
+  props: {
+    showModalRef: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    statusRef: {
+      type: Number as PropType<number>,
+      default: 0
+    },
+    row: {
+      type: Object as PropType<any>,
+      default: {}
+    }
+  },
+  emits: ['cancelModal', 'confirmModal'],
+  setup(props, ctx) {
+    const { variables, handleValidate, getListData, getToken } = useModal(
+      props,
+      ctx
+    )
+    const { t } = useI18n()
+    const userStore = useUserStore()
+
+    const cancelModal = () => {
+      if (props.statusRef === 0) {
+        variables.model.userId =
+          (userStore.getUserInfo as UserInfoRes).userType === 'GENERAL_USER'
+            ? (userStore.getUserInfo as UserInfoRes).id
+            : ''
+        variables.model.expireTime = Date.now()
+        variables.model.token = ''
+      }
+      ctx.emit('cancelModal', props.showModalRef)
+    }
+
+    const confirmModal = () => {
+      handleValidate(props.statusRef)
+    }
+
+    watch(
+      () => props.showModalRef,
+      () => {
+        props.showModalRef &&
+          (userStore.getUserInfo as UserInfoRes).userType !== 'GENERAL_USER' &&
+          getListData()
+      }
+    )
+
+    watch(
+      () => props.statusRef,
+      () => {
+        if (props.statusRef === 0) {
+          variables.model.userId =
+            (userStore.getUserInfo as UserInfoRes).userType === 'GENERAL_USER'
+              ? (userStore.getUserInfo as UserInfoRes).id
+              : ''
+          variables.model.expireTime = Date.now()
+          variables.model.token = ''
+        } else {
+          variables.model.id = props.row.id
+          variables.model.userId = props.row.userId
+          variables.model.expireTime = new Date(props.row.expireTime).getTime()
+          variables.model.token = props.row.token
+        }
+      }
+    )
+
+    watch(
+      () => props.row,
+      () => {
+        variables.model.id = props.row.id
+        variables.model.userId = props.row.userId
+        variables.model.expireTime = new Date(props.row.expireTime).getTime()
+        variables.model.token = props.row.token
+      }
+    )
+
+    return {
+      t,
+      ...toRefs(variables),
+      cancelModal,
+      confirmModal,
+      getToken,
+      userStore
+    }
+  },
+  render() {
+    const { t, getToken, userStore } = this
+
+    return (
+      <div>
+        <Modal
+          title={
+            this.statusRef === 0
+              ? t('security.token.create_token')
+              : t('security.token.edit_token')
+          }
+          show={this.showModalRef}
+          onCancel={this.cancelModal}
+          onConfirm={this.confirmModal}
+          confirmDisabled={
+            !this.model.userId || !this.model.expireTime || !this.model.token
+          }
+        >
+          {{
+            default: () => (
+              <NForm
+                model={this.model}
+                rules={this.rules}
+                ref='alertGroupFormRef'
+              >
+                <NFormItem
+                  label={t('security.token.expiration_time')}
+                  path='expireTime'
+                >
+                  <NDatePicker
+                    is-date-disabled={(ts: any) => ts < subDays(new Date(), 1)}
+                    style={{ width: '100%' }}
+                    type='datetime'
+                    clearable
+                    v-model={[this.model.expireTime, 'value']}
+                  />
+                </NFormItem>
+                {(userStore.getUserInfo as UserInfoRes).userType !==
+                  'GENERAL_USER' && (
+                  <NFormItem label={t('security.token.user')} path='userId'>
+                    <NSelect
+                      filterable
+                      placeholder={t('security.token.user_tips')}
+                      options={this.model.generalOptions}
+                      v-model={[this.model.userId, 'value']}
+                    />
+                  </NFormItem>
+                )}
+                <NFormItem label={t('security.token.token')} path='token'>
+                  <NSpace>
+                    <NInput
+                      style={{ width: '504px' }}
+                      disabled
+                      placeholder={t('security.token.token_tips')}
+                      v-model={[this.model.token, 'value']}
+                    />
+                    <NButton
+                      strong
+                      secondary
+                      circle
+                      type='info'
+                      onClick={() => getToken()}
+                    >
+                      {{
+                        icon: () => (
+                          <NIcon>
+                            <ReloadOutlined />
+                          </NIcon>
+                        )
+                      }}
+                    </NButton>
+                  </NSpace>
+                </NFormItem>
+              </NForm>
+            )
+          }}
+        </Modal>
+      </div>
+    )
+  }
+})
+
+export default TokenModal
diff --git a/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts b/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts
new file mode 100644
index 0000000..811ceea
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts
@@ -0,0 +1,164 @@
+/*
+ * 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
+ *
+ *    http://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 { reactive, ref, SetupContext } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useUserStore } from '@/store/user/user'
+import { useAsyncState } from '@vueuse/core'
+import { format } from 'date-fns'
+import { queryAlertPluginInstanceList } from '@/service/modules/alert-plugin'
+import { listAll } from '@/service/modules/users'
+import {
+  generateToken,
+  createToken,
+  updateToken
+} from '@/service/modules/token'
+import type { AlertPluginItem } from '@/service/modules/alert-plugin/types'
+import type { UserListRes } from '@/service/modules/users/types'
+import type { UserInfoRes } from '@/service/modules/users/types'
+
+export function useModal(
+  props: any,
+  ctx: SetupContext<('cancelModal' | 'confirmModal')[]>
+) {
+  const { t } = useI18n()
+  const userStore = useUserStore()
+  const variables = reactive({
+    alertGroupFormRef: ref(),
+    model: {
+      id: ref<number>(-1),
+      userId: ref(
+        (userStore.getUserInfo as UserInfoRes).userType === 'GENERAL_USER'
+          ? (userStore.getUserInfo as UserInfoRes).id
+          : ''
+      ),
+      expireTime: ref(Date.now()),
+      token: ref(''),
+      generalOptions: []
+    },
+    rules: {
+      userId: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (variables.model.userId === '') {
+            return new Error(t('security.token.user_tips'))
+          }
+        }
+      },
+      expireTime: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          console.log(variables.model.expireTime)
+          if (!variables.model.expireTime) {
+            return new Error(t('security.token.expiration_time_tips'))
+          }
+        }
+      },
+      token: {
+        required: true,
+        trigger: ['input', 'blur'],
+        validator() {
+          if (variables.model.token === '') {
+            return new Error(t('security.token.token_tips'))
+          }
+        }
+      }
+    }
+  })
+
+  const getListData = () => {
+    const { state } = useAsyncState(
+      listAll().then((res: Array<UserListRes>) => {
+        variables.model.generalOptions = res.map(
+          (item): { label: string; value: number } => {
+            return {
+              label: item.userName,
+              value: item.id
+            }
+          }
+        ) as any
+      }),
+      {}
+    )
+
+    return state
+  }
+
+  const getToken = () => {
+    const data = {
+      userId: (userStore.getUserInfo as UserInfoRes).id,
+      expireTime: format(variables.model.expireTime, 'yyyy-MM-dd HH:mm:ss')
+    }
+
+    useAsyncState(
+      generateToken(data).then((res: string) => {
+        variables.model.token = res
+      }),
+      {}
+    )
+  }
+
+  const handleValidate = (statusRef: number) => {
+    variables.alertGroupFormRef.validate((errors: any) => {
+      if (!errors) {
+        statusRef === 0 ? submitTokenModal() : updateTokenModal()
+      } else {
+        return
+      }
+    })
+  }
+
+  const submitTokenModal = () => {
+    const data = {
+      userId: Number(variables.model.userId),
+      expireTime: format(variables.model.expireTime, 'yyyy-MM-dd HH:mm:ss'),
+      token: variables.model.token
+    }
+
+    createToken(data).then(() => {
+      variables.model.userId =
+        (userStore.getUserInfo as UserInfoRes).userType === 'GENERAL_USER'
+          ? (userStore.getUserInfo as UserInfoRes).id
+          : ''
+      variables.model.expireTime = Date.now()
+      variables.model.token = ''
+      ctx.emit('confirmModal', props.showModalRef)
+    })
+  }
+
+  const updateTokenModal = () => {
+    const data = {
+      id: variables.model.id,
+      userId: Number(variables.model.userId),
+      expireTime: format(variables.model.expireTime, 'yyyy-MM-dd HH:mm:ss'),
+      token: variables.model.token
+    }
+
+    updateToken(data).then(() => {
+      ctx.emit('confirmModal', props.showModalRef)
+    })
+  }
+
+  return {
+    variables,
+    handleValidate,
+    getListData,
+    getToken
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/service/modules/token/types.ts b/dolphinscheduler-ui-next/src/views/security/token-manage/index.module.scss
similarity index 58%
copy from dolphinscheduler-ui-next/src/service/modules/token/types.ts
copy to dolphinscheduler-ui-next/src/views/security/token-manage/index.module.scss
index 98bc1f9..de6cf70 100644
--- a/dolphinscheduler-ui-next/src/service/modules/token/types.ts
+++ b/dolphinscheduler-ui-next/src/views/security/token-manage/index.module.scss
@@ -15,37 +15,29 @@
  * limitations under the License.
  */
 
-interface ListReq {
-  pageNo: number
-  pageSize: number
-  searchVal?: string
-}
+.search-card {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
 
-interface TokenReq {
-  expireTime: string
-  userId: number
-  token?: string
-}
+  .box {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    width: 300px;
 
-interface UserReq {
-  userId?: number
+    button {
+      margin-left: 10px;
+    }
+  }
 }
 
-interface UpdateTokenReq extends TokenReq {
-  id: number
-  alertGroup?: string
-  createTime?: string
-  email?: string
-  phone?: string
-  queue?: string
-  queueName?: string
-  state?: number
-  tenantCode?: string
-  tenantId?: number
-  updateTime?: string
-  userName?: string
-  userPassword?: string
-  userType?: string
-}
+.table-card {
+  margin-top: 8px;
 
-export { ListReq, TokenReq, UserReq, UpdateTokenReq }
+  .pagination {
+    margin-top: 20px;
+    display: flex;
+    justify-content: center;
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/token-manage/index.tsx
similarity index 92%
copy from dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx
copy to dolphinscheduler-ui-next/src/views/security/token-manage/index.tsx
index 4ee81d6..5c723d6 100644
--- a/dolphinscheduler-ui-next/src/views/security/alarm-group-manage/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/security/token-manage/index.tsx
@@ -28,11 +28,11 @@ import { SearchOutlined } from '@vicons/antd'
 import { useI18n } from 'vue-i18n'
 import { useTable } from './use-table'
 import Card from '@/components/card'
-import AlertGroupModal from './components/alert-group-modal'
+import TokenModal from './components/token-modal'
 import styles from './index.module.scss'
 
-const alarmGroupManage = defineComponent({
-  name: 'alarm-group-manage',
+const tokenManage = defineComponent({
+  name: 'token-manage',
   setup() {
     const { t } = useI18n()
     const { variables, getTableData, createColumns } = useTable()
@@ -106,7 +106,7 @@ const alarmGroupManage = defineComponent({
           <div class={styles['search-card']}>
             <div>
               <NButton size='small' type='primary' onClick={handleModalChange}>
-                {t('security.alarm_group.create_alarm_group')}
+                {t('security.token.create_token')}
               </NButton>
             </div>
             <div class={styles.box}>
@@ -114,7 +114,7 @@ const alarmGroupManage = defineComponent({
                 size='small'
                 clearable
                 v-model={[this.searchVal, 'value']}
-                placeholder={t('security.alarm_group.search_tips')}
+                placeholder={t('security.token.search_tips')}
               />
               <NButton size='small' type='primary' onClick={onSearch}>
                 {{
@@ -143,7 +143,7 @@ const alarmGroupManage = defineComponent({
             />
           </div>
         </Card>
-        <AlertGroupModal
+        <TokenModal
           showModalRef={this.showModalRef}
           statusRef={this.statusRef}
           row={this.row}
@@ -155,4 +155,4 @@ const alarmGroupManage = defineComponent({
   }
 })
 
-export default alarmGroupManage
+export default tokenManage
diff --git a/dolphinscheduler-ui-next/src/views/security/token-manage/use-table.ts b/dolphinscheduler-ui-next/src/views/security/token-manage/use-table.ts
new file mode 100644
index 0000000..cf6584e
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/token-manage/use-table.ts
@@ -0,0 +1,192 @@
+/*
+ * 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
+ *
+ *    http://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 { useAsyncState } from '@vueuse/core'
+import { reactive, h, ref } from 'vue'
+import { format } from 'date-fns'
+import { NButton, NPopconfirm, NSpace, NTooltip } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import {
+  queryAlertGroupListPaging,
+  delAlertGroupById
+} from '@/service/modules/alert-group'
+import { DeleteOutlined, EditOutlined } from '@vicons/antd'
+import { queryAccessTokenList, deleteToken } from '@/service/modules/token'
+import type { AlarmGroupRes } from '@/service/modules/alert-group/types'
+import type { TokenRes } from '@/service/modules/token/types'
+
+export function useTable() {
+  const { t } = useI18n()
+
+  const handleEdit = (row: any) => {
+    variables.showModalRef = true
+    variables.statusRef = 1
+    variables.row = row
+  }
+
+  const createColumns = (variables: any) => {
+    variables.columns = [
+      {
+        title: '#',
+        key: 'index'
+      },
+      {
+        title: t('security.token.user'),
+        key: 'userName'
+      },
+      {
+        title: t('security.token.token'),
+        key: 'token'
+      },
+      {
+        title: t('security.token.expiration_time'),
+        key: 'expireTime'
+      },
+      {
+        title: t('security.token.create_time'),
+        key: 'createTime'
+      },
+      {
+        title: t('security.token.update_time'),
+        key: 'updateTime'
+      },
+      {
+        title: t('security.token.operation'),
+        key: 'operation',
+        render(row: any) {
+          return h(NSpace, null, {
+            default: () => [
+              h(
+                NTooltip,
+                {},
+                {
+                  trigger: () =>
+                    h(
+                      NButton,
+                      {
+                        circle: true,
+                        type: 'info',
+                        size: 'small',
+                        onClick: () => {
+                          handleEdit(row)
+                        }
+                      },
+                      {
+                        icon: () => h(EditOutlined)
+                      }
+                    ),
+                  default: () => t('security.token.edit')
+                }
+              ),
+              h(
+                NPopconfirm,
+                {
+                  onPositiveClick: () => {
+                    handleDelete(row)
+                  }
+                },
+                {
+                  trigger: () =>
+                    h(
+                      NTooltip,
+                      {},
+                      {
+                        trigger: () =>
+                          h(
+                            NButton,
+                            {
+                              circle: true,
+                              type: 'error',
+                              size: 'small'
+                            },
+                            {
+                              icon: () => h(DeleteOutlined)
+                            }
+                          ),
+                        default: () => t('security.token.delete')
+                      }
+                    ),
+                  default: () => t('security.token.delete_confirm')
+                }
+              )
+            ]
+          })
+        }
+      }
+    ]
+  }
+
+  const variables = reactive({
+    columns: [],
+    tableData: [],
+    page: ref(1),
+    pageSize: ref(10),
+    searchVal: ref(null),
+    totalPage: ref(1),
+    showModalRef: ref(false),
+    statusRef: ref(0),
+    row: {}
+  })
+
+  const handleDelete = (row: any) => {
+    deleteToken(row.id).then(() => {
+      getTableData({
+        pageSize: variables.pageSize,
+        pageNo:
+          variables.tableData.length === 1 && variables.page > 1
+            ? variables.page - 1
+            : variables.page,
+        searchVal: variables.searchVal
+      })
+    })
+  }
+
+  const getTableData = (params: any) => {
+    const { state } = useAsyncState(
+      queryAccessTokenList({ ...params }).then((res: TokenRes) => {
+        variables.tableData = res.totalList.map((item, index) => {
+          item.expireTime = format(
+            new Date(item.expireTime),
+            'yyyy-MM-dd HH:mm:ss'
+          )
+          item.createTime = format(
+            new Date(item.createTime),
+            'yyyy-MM-dd HH:mm:ss'
+          )
+          item.updateTime = format(
+            new Date(item.updateTime),
+            'yyyy-MM-dd HH:mm:ss'
+          )
+          return {
+            index: index + 1,
+            ...item
+          }
+        }) as any
+        variables.totalPage = res.totalPage
+      }),
+      {}
+    )
+
+    return state
+  }
+
+  return {
+    variables,
+    getTableData,
+    createColumns
+  }
+}