Browse Source

[Feature][UI Next] Add profile function. (#7858)

* [Feature][UI Next] Add profile function.
* Update .env
songjianet 3 years ago
parent
commit
30cf030ad9
23 changed files with 382 additions and 107 deletions
  1. 4 1
      dolphinscheduler-ui-next/src/components/card/index.tsx
  2. 25 16
      dolphinscheduler-ui-next/src/components/modal/index.tsx
  3. 0 0
      dolphinscheduler-ui-next/src/layouts/content/components/locales/index.module.scss
  4. 13 13
      dolphinscheduler-ui-next/src/layouts/content/components/language/index.tsx
  5. 5 4
      dolphinscheduler-ui-next/src/layouts/content/components/language/use-dropdown.ts
  6. 3 3
      dolphinscheduler-ui-next/src/layouts/content/components/navbar/index.tsx
  7. 2 2
      dolphinscheduler-ui-next/src/layouts/content/components/sidebar/index.tsx
  8. 8 11
      dolphinscheduler-ui-next/src/layouts/content/index.tsx
  9. 4 4
      dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts
  10. 18 1
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  11. 17 0
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  12. 9 11
      dolphinscheduler-ui-next/src/store/language/language.ts
  13. 4 4
      dolphinscheduler-ui-next/src/store/language/types.ts
  14. 2 0
      dolphinscheduler-ui-next/src/utils/index.ts
  15. 22 0
      dolphinscheduler-ui-next/src/utils/regex.ts
  16. 5 15
      dolphinscheduler-ui-next/src/views/login/index.tsx
  17. 5 4
      dolphinscheduler-ui-next/src/views/login/use-translate.ts
  18. 96 16
      dolphinscheduler-ui-next/src/views/profile/index.tsx
  19. 0 1
      dolphinscheduler-ui-next/src/views/profile/info.tsx
  20. 75 0
      dolphinscheduler-ui-next/src/views/profile/use-form.ts
  21. 4 1
      dolphinscheduler-ui-next/src/views/profile/use-profile.ts
  22. 30 0
      dolphinscheduler-ui-next/src/views/profile/use-update.ts
  23. 31 0
      dolphinscheduler-ui-next/src/views/profile/use-userinfo.ts

+ 4 - 1
dolphinscheduler-ui-next/src/components/card/index.tsx

@@ -27,7 +27,10 @@ const contentStyle = {
 }
 
 const props = {
-  title: String as PropType<string>,
+  title: {
+    type: String as PropType<string>,
+    required: true,
+  },
 }
 
 const Card = defineComponent({

+ 25 - 16
dolphinscheduler-ui-next/src/components/modal/index.tsx

@@ -35,6 +35,10 @@ const props = {
   confirmText: {
     type: String as PropType<string>,
   },
+  confirmDisabled: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
 }
 
 const Modal = defineComponent({
@@ -55,7 +59,7 @@ const Modal = defineComponent({
     return { t, onCancel, onConfirm }
   },
   render() {
-    const { $slots, t, onCancel, onConfirm } = this
+    const { $slots, t, onCancel, onConfirm, confirmDisabled } = this
 
     return (
       <NModal
@@ -63,21 +67,26 @@ const Modal = defineComponent({
         class={styles.container}
         mask-closable={false}
       >
-	      <NCard title={this.title}>
-		      {{
-						default: () => renderSlot($slots, 'default'),
-			      footer: () => (
-				      <div class={styles['btn-box']}>
-					      <NButton quaternary size='small' onClick={onCancel}>
-						      {this.cancelText || t('modal.cancel')}
-					      </NButton>
-					      <NButton type='info' size='small' onClick={onConfirm}>
-						      {this.confirmText || t('modal.confirm')}
-					      </NButton>
-				      </div>
-			      ),
-		      }}
-	      </NCard>
+        <NCard title={this.title}>
+          {{
+            default: () => renderSlot($slots, 'default'),
+            footer: () => (
+              <div class={styles['btn-box']}>
+                <NButton quaternary size='small' onClick={onCancel}>
+                  {this.cancelText || t('modal.cancel')}
+                </NButton>
+                <NButton
+                  type='info'
+                  size='small'
+                  onClick={onConfirm}
+                  disabled={confirmDisabled}
+                >
+                  {this.confirmText || t('modal.confirm')}
+                </NButton>
+              </div>
+            ),
+          }}
+        </NCard>
       </NModal>
     )
   },

dolphinscheduler-ui-next/src/layouts/content/components/language/index.module.scss → dolphinscheduler-ui-next/src/layouts/content/components/locales/index.module.scss


+ 13 - 13
dolphinscheduler-ui-next/src/layouts/content/components/language/index.tsx

@@ -15,28 +15,28 @@
  * limitations under the License.
  */
 
-import { defineComponent, ref, PropType, onMounted } from 'vue'
+import { defineComponent, ref, PropType } from 'vue'
 import { NDropdown, NIcon, NButton } from 'naive-ui'
 import styles from './index.module.scss'
 import { DownOutlined } from '@vicons/antd'
 import { useDropDown } from './use-dropdown'
-import { useLanguageStore } from '@/store/language/language'
+import { useLocalesStore } from '@/store/locales/locales'
 
-const Language = defineComponent({
-  name: 'Language',
+const Locales = defineComponent({
+  name: 'Locales',
   props: {
-    languageOptions: {
+    localesOptions: {
       type: Array as PropType<any>,
       default: [],
     },
   },
   setup(props) {
-    const languageStore = useLanguageStore()
-    const lang = ref()
-    lang.value = languageStore.getLang
-    
-    const chooseVal = ref(props.languageOptions.filter((item: { key: string }) => item.key === lang.value)[0].label)
-    
+    const localesStore = useLocalesStore()
+    const chooseVal = ref(
+      props.localesOptions.filter(
+        (item: { key: string }) => item.key === localesStore.getLocales
+      )[0].label
+    )
     const { handleSelect } = useDropDown(chooseVal)
 
     return { handleSelect, chooseVal }
@@ -46,7 +46,7 @@ const Language = defineComponent({
       <NDropdown
         trigger='hover'
         show-arrow
-        options={this.languageOptions}
+        options={this.localesOptions}
         on-select={this.handleSelect}
       >
         <NButton text>
@@ -60,4 +60,4 @@ const Language = defineComponent({
   },
 })
 
-export default Language
+export default Locales

+ 5 - 4
dolphinscheduler-ui-next/src/layouts/content/components/language/use-dropdown.ts

@@ -17,17 +17,18 @@
 
 import { DropdownOption } from 'naive-ui'
 import { useI18n } from 'vue-i18n'
-import { useLanguageStore } from '@/store/language/language'
+import { useLocalesStore } from '@/store/locales/locales'
+import type { Locales } from '@/store/locales/types'
 
 export function useDropDown(chooseVal: any) {
   const { locale } = useI18n()
-  const languageStore = useLanguageStore()
+  const localesStore = useLocalesStore()
 
   const handleSelect = (key: string | number, option: DropdownOption) => {
     // console.log(key, option)
     chooseVal.value = option.label
-    locale.value = key as string
-    languageStore.setLang(locale.value)
+    locale.value = key as Locales
+    localesStore.setLocales(locale.value as Locales)
   }
   return {
     handleSelect,

+ 3 - 3
dolphinscheduler-ui-next/src/layouts/content/components/navbar/index.tsx

@@ -19,7 +19,7 @@ import { defineComponent, PropType } from 'vue'
 import styles from './index.module.scss'
 import { NMenu } from 'naive-ui'
 import Logo from '../logo'
-import Language from '../language'
+import Locales from '../locales'
 import User from '../user'
 import Theme from '../theme'
 import { useMenuClick } from './use-menuClick'
@@ -32,7 +32,7 @@ const Navbar = defineComponent({
       type: Array as PropType<any>,
       default: [],
     },
-    languageOptions: {
+    localesOptions: {
       type: Array as PropType<any>,
       default: [],
     },
@@ -59,7 +59,7 @@ const Navbar = defineComponent({
         </div>
         <div class={styles.settings}>
           <Theme />
-          <Language languageOptions={this.languageOptions} />
+          <Locales localesOptions={this.localesOptions} />
           <User profileOptions={this.profileOptions} />
         </div>
       </div>

+ 2 - 2
dolphinscheduler-ui-next/src/layouts/content/components/sidebar/index.tsx

@@ -39,7 +39,7 @@ const Sidebar = defineComponent({
 
     const { handleMenuClick } = useMenuClick()
 
-    return { collapsedRef, defaultExpandedKeys, handleMenuClick } 
+    return { collapsedRef, defaultExpandedKeys, handleMenuClick }
   },
   render() {
     return (
@@ -59,7 +59,7 @@ const Sidebar = defineComponent({
         />
       </NLayoutSider>
     )
-  }
+  },
 })
 
 export default Sidebar

+ 8 - 11
dolphinscheduler-ui-next/src/layouts/content/index.tsx

@@ -21,23 +21,19 @@ import NavBar from './components/navbar'
 import SideBar from './components/sidebar'
 import { useDataList } from './use-dataList'
 import { useMenuStore } from '@/store/menu/menu'
-import { useLanguageStore } from '@/store/language/language'
+import { useLocalesStore } from '@/store/locales/locales'
 import { useI18n } from 'vue-i18n'
 
 const Content = defineComponent({
   name: 'Content',
   setup() {
     const menuStore = useMenuStore()
-
     const { locale } = useI18n()
-    const languageStore = useLanguageStore()
-    const lang = ref()
-    lang.value = languageStore.getLang
-
+    const localesStore = useLocalesStore()
     const { state, changeMenuOption, changeHeaderMenuOptions } = useDataList()
 
-    locale.value = lang.value
-    
+    locale.value = localesStore.getLocales
+
     onMounted(() => {
       menuStore.setMenuKey('home')
       changeMenuOption(state)
@@ -54,7 +50,8 @@ const Content = defineComponent({
     const genSideMenu = (state: any) => {
       const key = menuStore.getMenuKey
       state.sideMenuOptions =
-        state.menuOptions.filter((menu: { key: string }) => menu.key === key)[0].children || []
+        state.menuOptions.filter((menu: { key: string }) => menu.key === key)[0]
+          .children || []
       state.isShowSide = state.sideMenuOptions.length !== 0
     }
 
@@ -67,7 +64,7 @@ const Content = defineComponent({
       ...toRefs(state),
       menuStore,
       changeMenuOption,
-      getSideMenuOptions
+      getSideMenuOptions,
     }
   },
   render() {
@@ -77,7 +74,7 @@ const Content = defineComponent({
           <NavBar
             onHandleMenuClick={this.getSideMenuOptions}
             headerMenuOptions={this.headerMenuOptions}
-            languageOptions={this.languageOptions}
+            localesOptions={this.localesOptions}
             profileOptions={this.userDropdownOptions}
           />
         </NLayoutHeader>

+ 4 - 4
dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts

@@ -51,7 +51,7 @@ export function useDataList() {
     return () => h(NIcon, null, { default: () => h(icon) })
   }
 
-  const languageOptions = [
+  const localesOptions = [
     {
       label: 'English',
       key: 'en_US',
@@ -82,11 +82,11 @@ export function useDataList() {
 
   const state = reactive({
     isShowSide: false,
-    languageOptions,
+    localesOptions,
     userDropdownOptions,
     menuOptions: [],
     headerMenuOptions: [],
-    sideMenuOptions: []
+    sideMenuOptions: [],
   })
 
   const changeMenuOption = (state: any) => {
@@ -270,6 +270,6 @@ export function useDataList() {
   return {
     state,
     changeHeaderMenuOptions,
-    changeMenuOption
+    changeMenuOption,
   }
 }

+ 18 - 1
dolphinscheduler-ui-next/src/locales/modules/en_US.ts

@@ -24,6 +24,11 @@ const login = {
   login: 'Login',
 }
 
+const modal = {
+  cancel: 'Cancel',
+  confirm: 'Confirm',
+}
+
 const theme = {
   light: 'Light',
   dark: 'Dark',
@@ -84,7 +89,7 @@ const password = {
   password_tips: 'Please enter your password',
   confirm_password_tips: 'Please enter your confirm password',
   two_password_entries_are_inconsistent:
-    'Two Password Entries Are Inconsistent',
+    'Two password entries are inconsistent',
   submit: 'Submit',
 }
 
@@ -94,13 +99,25 @@ const profile = {
   username: 'Username',
   email: 'Email',
   phone: 'Phone',
+  state: 'State',
   permission: 'Permission',
   create_time: 'Create Time',
   update_time: 'Update Time',
+  administrator: 'Administrator',
+  ordinary_user: 'Ordinary User',
+  edit_profile: 'Edit Profile',
+  username_tips: 'Please enter your username',
+  email_tips: 'Please enter your email',
+  email_correct_tips: 'Please enter your email in the correct format',
+  phone_tips: 'Please enter your phone',
+  state_tips: 'Please choose your state',
+  enable: 'Enable',
+  disable: 'Disable',
 }
 
 export default {
   login,
+  modal,
   theme,
   userDropdown,
   menu,

+ 17 - 0
dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts

@@ -24,6 +24,11 @@ const login = {
   login: '登录',
 }
 
+const modal = {
+  cancel: '取消',
+  confirm: '确定',
+}
+
 const theme = {
   light: '浅色',
   dark: '深色',
@@ -93,13 +98,25 @@ const profile = {
   username: '用户名',
   email: '邮箱',
   phone: '手机',
+  state: '状态',
   permission: '权限',
   create_time: '创建时间',
   update_time: '更新时间',
+  administrator: '管理员',
+  ordinary_user: '普通用户',
+  edit_profile: '编辑用户',
+  username_tips: '请输入用户名',
+  email_tips: '请输入邮箱',
+  email_correct_tips: '请输入正确格式的邮箱',
+  phone_tips: '请输入手机号',
+  state_tips: '请选择状态',
+  enable: '启用',
+  disable: '禁用',
 }
 
 export default {
   login,
+  modal,
   theme,
   userDropdown,
   menu,

+ 9 - 11
dolphinscheduler-ui-next/src/store/language/language.ts

@@ -16,24 +16,22 @@
  */
 
 import { defineStore } from 'pinia'
-import LanguageStore from './types'
-import { useStorage } from '@vueuse/core'
-import { ref } from 'vue'
+import { LocalesStore, Locales } from './types'
 
-export const useLanguageStore = defineStore({
+export const useLocalesStore = defineStore({
   id: 'language',
-  state: (): LanguageStore => ({
-    storageLang: ref('')
+  state: (): LocalesStore => ({
+    locales: 'zh_CN',
   }),
+  persist: true,
   getters: {
-    getLang(): string | null {
-      return window.localStorage.getItem('lang')
+    getLocales(): Locales {
+      return this.locales
     },
   },
   actions: {
-    setLang(lang: string): void {
-      this.storageLang = useStorage('lang', lang)
-      this.storageLang = lang
+    setLocales(lang: Locales): void {
+      this.locales = lang
     },
   },
 })

+ 4 - 4
dolphinscheduler-ui-next/src/store/language/types.ts

@@ -15,10 +15,10 @@
  * limitations under the License.
  */
 
-import { Ref } from 'vue'
+type Locales = 'zh_CN' | 'en_US'
 
-interface LanguageStore {
-  storageLang: Ref
+interface LocalesStore {
+  locales: Locales
 }
 
-export default LanguageStore
+export { LocalesStore, Locales }

+ 2 - 0
dolphinscheduler-ui-next/src/utils/index.ts

@@ -16,9 +16,11 @@
  */
 
 import mapping from './mapping'
+import regex from './regex'
 
 const utils = {
   mapping,
+  regex,
 }
 
 export default utils

+ 22 - 0
dolphinscheduler-ui-next/src/utils/regex.ts

@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+const regex = {
+  email: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, // support Chinese mailbox
+}
+
+export default regex

+ 5 - 15
dolphinscheduler-ui-next/src/views/login/index.tsx

@@ -15,33 +15,23 @@
  * limitations under the License.
  */
 
-import { defineComponent, ref, toRefs, withKeys, onMounted } from 'vue'
+import { defineComponent, toRefs, withKeys } from 'vue'
 import styles from './index.module.scss'
 import { NInput, NButton, NSwitch, NForm, NFormItem } from 'naive-ui'
 import { useForm } from './use-form'
 import { useTranslate } from './use-translate'
 import { useLogin } from './use-login'
-import { useLanguageStore } from '@/store/language/language'
+import { useLocalesStore } from '@/store/locales/locales'
 
 const login = defineComponent({
   name: 'login',
   setup() {
     const { state, t, locale } = useForm()
-
-    const languageStore = useLanguageStore()
-    const lang = ref()
-    lang.value = languageStore.getLang
-    
     const { handleChange } = useTranslate(locale)
-
     const { handleLogin } = useLogin(state)
+    const localesStore = useLocalesStore()
 
-    onMounted(() => {
-      // console.log('login', lang)
-      handleChange(lang.value)
-    })
-
-    return { t, handleChange, handleLogin, ...toRefs(state), lang }
+    return { t, handleChange, handleLogin, ...toRefs(state), localesStore }
   },
   render() {
     return (
@@ -49,7 +39,7 @@ const login = defineComponent({
         <div class={styles['language-switch']}>
           <NSwitch
             onUpdateValue={this.handleChange}
-            default-value={this.lang}
+            default-value={this.localesStore.getLocales}
             checked-value='en_US'
             unchecked-value='zh_CN'
           >

+ 5 - 4
dolphinscheduler-ui-next/src/views/login/use-translate.ts

@@ -16,14 +16,15 @@
  */
 
 import { WritableComputedRef } from 'vue'
-import { useLanguageStore } from '@/store/language/language'
+import { useLocalesStore } from '@/store/locales/locales'
+import type { Locales } from '@/store/locales/types'
 
 export function useTranslate(locale: WritableComputedRef<string>) {
-  const languageStore = useLanguageStore()
+  const localesStore = useLocalesStore()
 
-  const handleChange = (value: string) => {
+  const handleChange = (value: Locales) => {
     locale.value = value
-    languageStore.setLang(value)
+    localesStore.setLocales(value)
   }
   return {
     handleChange,

+ 96 - 16
dolphinscheduler-ui-next/src/views/profile/index.tsx

@@ -15,33 +15,113 @@
  * limitations under the License.
  */
 
-import { defineComponent } from 'vue'
-import { useI18n } from 'vue-i18n'
-import { NButton } from 'naive-ui'
+import { defineComponent, onMounted, ref, toRefs } from 'vue'
+import { useForm } from './use-form'
+import {
+  NButton,
+  NForm,
+  NFormItem,
+  NInput,
+  NRadioGroup,
+  NRadio,
+} from 'naive-ui'
+import { useUserinfo } from './use-userinfo'
+import { useUpdate } from './use-update'
 import Card from '@/components/card'
+import Modal from '@/components/modal'
 import Info from './info'
+import utils from '@/utils'
 
 const profile = defineComponent({
   name: 'profile',
   setup() {
-    const { t } = useI18n()
+    let showModalRef = ref(false)
+    const { state, t } = useForm()
+    const { handleUpdate } = useUpdate(state)
+    const { getUserInfo } = useUserinfo()
 
-    return { t }
+    onMounted(async () => {
+      await getUserInfo()
+    })
+
+    const onCancel = () => {
+      showModalRef.value = false
+    }
+
+    const onConfirm = async () => {
+      showModalRef.value = false
+      await handleUpdate()
+      await getUserInfo()
+    }
+
+    return { ...toRefs(state), showModalRef, t, onCancel, onConfirm }
   },
   render() {
-    const { t } = this
+    const { t, onCancel, onConfirm } = this
 
     return (
-      <Card title={t('profile.profile')}>
-        {{
-          default: () => <Info />,
-          'header-extra': () => (
-            <NButton type='info' size='small'>
-              {t('profile.edit')}
-            </NButton>
-          ),
-        }}
-      </Card>
+      <div>
+        <Card title={t('profile.profile')}>
+          {{
+            default: () => <Info />,
+            'header-extra': () => (
+              <NButton
+                type='info'
+                size='small'
+                onClick={() => (this.showModalRef = !this.showModalRef)}
+              >
+                {t('profile.edit')}
+              </NButton>
+            ),
+          }}
+        </Card>
+        <Modal
+          title={t('profile.edit_profile')}
+          show={this.showModalRef}
+          onCancel={onCancel}
+          onConfirm={onConfirm}
+          confirmDisabled={
+            !this.profileForm.username ||
+            !this.profileForm.email ||
+            !utils.regex.email.test(this.profileForm.email)
+          }
+        >
+          {{
+            default: () => (
+              <NForm rules={this.rules} ref='profileFormRef'>
+                <NFormItem label={t('profile.username')} path='username'>
+                  <NInput
+                    v-model={[this.profileForm.username, 'value']}
+                    placeholder={t('profile.username_tips')}
+                  />
+                </NFormItem>
+                <NFormItem label={t('profile.email')} path='email'>
+                  <NInput
+                    v-model={[this.profileForm.email, 'value']}
+                    placeholder={t('profile.email_tips')}
+                  />
+                </NFormItem>
+                <NFormItem label={t('profile.phone')} path='phone'>
+                  <NInput
+                    v-model={[this.profileForm.phone, 'value']}
+                    placeholder={t('profile.phone_tips')}
+                  />
+                </NFormItem>
+                <NFormItem label={t('profile.state')} path='state'>
+                  <NRadioGroup v-model={[this.profileForm.state, 'value']}>
+                    {[
+                      { value: 1, label: t('profile.enable') },
+                      { value: 0, label: t('profile.disable') },
+                    ].map((item) => {
+                      return <NRadio value={item.value}>{item.label}</NRadio>
+                    })}
+                  </NRadioGroup>
+                </NFormItem>
+              </NForm>
+            ),
+          }}
+        </Modal>
+      </div>
     )
   },
 })

+ 0 - 1
dolphinscheduler-ui-next/src/views/profile/info.tsx

@@ -21,7 +21,6 @@ import styles from './info.module.scss'
 
 const Info = defineComponent({
   name: 'Info',
-  setup() {},
   render() {
     const { infoOptions } = useProfile()
 

+ 75 - 0
dolphinscheduler-ui-next/src/views/profile/use-form.ts

@@ -0,0 +1,75 @@
+/*
+ * 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, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useUserStore } from '@/store/user/user'
+import utils from '@/utils'
+import type { FormRules } from 'naive-ui'
+import type { UserInfoRes } from '@/service/modules/users/types'
+
+export function useForm() {
+  const { t, locale } = useI18n()
+  const userInfo = useUserStore().userInfo as UserInfoRes
+
+  const state = reactive({
+    profileFormRef: ref(),
+    profileForm: {
+      username: userInfo.userName,
+      email: userInfo.email,
+      phone: userInfo.phone,
+      state: userInfo.state,
+    },
+    rules: {
+      username: {
+        trigger: ['input', 'blur'],
+        required: true,
+        validator() {
+          if (state.profileForm.username === '') {
+            return new Error(t('profile.username_tips'))
+          }
+        },
+      },
+      email: {
+        trigger: ['input', 'blur'],
+        required: true,
+        validator() {
+          if (state.profileForm.email === '') {
+            return new Error(t('profile.email_tips'))
+          } else if (!utils.regex.email.test(state.profileForm.email)) {
+            return new Error(t('profile.email_correct_tips'))
+          }
+        },
+      },
+    } as FormRules,
+  })
+
+  watch(userInfo, () => {
+    state.profileForm = {
+      username: userInfo.userName,
+      email: userInfo.email,
+      phone: userInfo.phone,
+      state: userInfo.state,
+    }
+  })
+
+  return {
+    state,
+    t,
+    locale,
+  }
+}

+ 4 - 1
dolphinscheduler-ui-next/src/views/profile/use-profile.ts

@@ -36,7 +36,10 @@ export function useProfile() {
   infoOptions.value.push({ key: t('profile.phone'), value: userInfo.phone })
   infoOptions.value.push({
     key: t('profile.permission'),
-    value: userInfo.userName,
+    value:
+      userInfo.userType === 'ADMIN_USER'
+        ? t('profile.administrator')
+        : t('profile.ordinary_user'),
   })
   infoOptions.value.push({
     key: t('profile.create_time'),

+ 30 - 0
dolphinscheduler-ui-next/src/views/profile/use-update.ts

@@ -14,3 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+import { updateUser } from '@/service/modules/users'
+import { useUserStore } from '@/store/user/user'
+import type { UserInfoRes } from '@/service/modules/users/types'
+
+export function useUpdate(state: any) {
+  const userStore = useUserStore()
+  const userInfo = userStore.userInfo as UserInfoRes
+
+  const handleUpdate = () => {
+    state.profileFormRef.validate(async (valid: any) => {
+      if (!valid) {
+        await updateUser({
+          userPassword: '',
+          id: userInfo.id,
+          userName: state.profileForm.username,
+          tenantId: userInfo.tenantId,
+          email: state.profileForm.email,
+          phone: state.profileForm.phone,
+          state: state.profileForm.state,
+          queue: userInfo.queue,
+        })
+      }
+    })
+  }
+
+  return {
+    handleUpdate,
+  }
+}

+ 31 - 0
dolphinscheduler-ui-next/src/views/profile/use-userinfo.ts

@@ -0,0 +1,31 @@
+/*
+ * 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 { useUserStore } from '@/store/user/user'
+import { getUserInfo as getUserInfoApi } from '@/service/modules/users'
+import type { UserInfoRes } from '@/service/modules/users/types'
+
+export function useUserinfo() {
+  const userStore = useUserStore()
+
+  const getUserInfo = async () => {
+    const userInfoRes: UserInfoRes = await getUserInfoApi()
+    await userStore.setUserInfo(userInfoRes)
+  }
+
+  return { getUserInfo }
+}