feat: add platform authentication methods and improve QR code handling

This commit is contained in:
wxg0103 2025-07-25 17:53:54 +08:00
parent 0eb1cf701e
commit f744e8b109
14 changed files with 231 additions and 100 deletions

View File

@ -31,8 +31,30 @@ const putAuthSetting: (auth_type: string, data: any, loading?: Ref<boolean>) =>
return put(`${prefix}/${auth_type}/info`, data, undefined, loading)
}
const platformPrefix = '/chat_user/auth/platform'
const getPlatformInfo: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get(`${platformPrefix}/source`, undefined, loading)
}
const updateConfig: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
loading
) => {
return post(`${platformPrefix}/source`, data, undefined, loading)
}
const validateConnection: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
loading
) => {
return put(`${platformPrefix}/source`, data, undefined, loading)
}
export default {
getAuthSetting,
postAuthSetting,
putAuthSetting
putAuthSetting,
getPlatformInfo,
updateConfig,
validateConnection
}

View File

@ -1,4 +1,4 @@
import { Result } from '@/request/Result'
import {Result} from '@/request/Result'
import {
get,
post,
@ -9,17 +9,17 @@ import {
download,
exportFile,
} from '@/request/chat/index'
import { type ChatProfile } from '@/api/type/chat'
import { type Ref } from 'vue'
import type { ResetPasswordRequest } from '@/api/type/user.ts'
import {type ChatProfile} from '@/api/type/chat'
import {type Ref} from 'vue'
import type {ResetPasswordRequest} from '@/api/type/user.ts'
import useStore from '@/stores'
import type { LoginRequest } from '@/api/type/user'
import type {LoginRequest} from '@/api/type/user'
const prefix: any = { _value: '/workspace/' }
const prefix: any = {_value: '/workspace/'}
Object.defineProperty(prefix, 'value', {
get: function () {
const { user } = useStore()
const {user} = useStore()
return this._value + user.getWorkspaceId() + '/application'
},
})
@ -51,7 +51,7 @@ const chatProfile: (assessToken: string, loading?: Ref<boolean>) => Promise<Resu
assessToken,
loading,
) => {
return get('/profile', { access_token: assessToken }, loading)
return get('/profile', {access_token: assessToken}, loading)
}
/**
*
@ -63,7 +63,7 @@ const anonymousAuthentication: (
assessToken: string,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (assessToken, loading) => {
return post('/auth/anonymous', { access_token: assessToken }, {}, loading)
return post('/auth/anonymous', {access_token: assessToken}, {}, loading)
}
/**
*
@ -77,7 +77,7 @@ const passwordAuthentication: (
password: string,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (assessToken, password, loading) => {
return post('auth/password', { access_token: assessToken, password: password }, {}, loading)
return post('auth/password', {access_token: assessToken, password: password}, {}, loading)
}
/**
*
@ -122,38 +122,40 @@ const getCaptcha: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) =
*
*/
const getQrType: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get('qr_type', undefined, loading)
return get('auth/qr_type', undefined, loading)
}
const getQrSource: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get('qr_type/source', undefined, loading)
return get('auth/qr_type/source', undefined, loading)
}
const getDingCallback: (code: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
const getDingCallback: (code: string, accessToken: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
code,
accessToken,
loading,
) => {
return get('dingtalk', { code }, loading)
return get('auth/dingtalk', {code, accessToken: accessToken}, loading)
}
const getDingOauth2Callback: (code: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
code,
loading,
) => {
return get('dingtalk/oauth2', { code }, loading)
return get('auth/dingtalk/oauth2', {code}, loading)
}
const getWecomCallback: (code: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
const getWecomCallback: (code: string, accessToken: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
code,
accessToken,
loading,
) => {
return get('wecom', { code }, loading)
return get('auth/wecom', {code, accessToken: accessToken}, loading)
}
const getLarkCallback: (code: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
code,
loading,
) => {
return get('lark/oauth2', { code }, loading)
return get('auth/lark/oauth2', {code}, loading)
}
/**
@ -274,17 +276,17 @@ const deleteChat: (chat_id: string, loading?: Ref<boolean>) => Promise<Result<an
chat_id,
loading,
) => {
return del(`historical_conversation/${chat_id}`,undefined,undefined ,loading)
return del(`historical_conversation/${chat_id}`, undefined, undefined, loading)
}
/**
*
* @param loading
* @returns
*
* @param loading
* @returns
*/
const clearChat: (loading?: Ref<boolean>) => Promise<Result<any>> = (
loading
) => {
return del(`historical_conversation/clear`,undefined,undefined, loading)
return del(`historical_conversation/clear`, undefined, undefined, loading)
}
/**
*

View File

@ -103,6 +103,14 @@ const batchSync: (sync_type: string, loading?: Ref<boolean>) => Promise<Result<a
) => {
return post(`${prefix}/sync/${sync_type}`, undefined, undefined, loading)
}
/**
*
*/
const getSyncType: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get(`${prefix}/sync_types`, undefined, loading)
}
export default {
getUserManage,
putUserManage,
@ -112,5 +120,6 @@ export default {
getChatUserList,
batchAddGroup,
batchDelete,
batchSync
batchSync,
getSyncType
}

View File

@ -1,14 +1,17 @@
import { defineStore } from 'pinia'
import {defineStore} from 'pinia'
import ChatAPI from '@/api/chat/chat'
import type { ChatProfile, ChatUserProfile } from '@/api/type/chat'
import type { LoginRequest } from '@/api/type/user'
import type { Ref } from 'vue'
import { getBrowserLang } from '@/locales/index'
import type {ChatProfile, ChatUserProfile} from '@/api/type/chat'
import type {LoginRequest} from '@/api/type/user'
import type {Ref} from 'vue'
import {getBrowserLang} from '@/locales/index'
import LoginApi from "@/api/user/login.ts";
import useUserStore from "@/stores/modules/user.ts";
interface ChatUser {
// 用户id
id: string
}
interface Chat {
chat_profile?: ChatProfile
application?: any
@ -16,6 +19,7 @@ interface Chat {
token?: string
accessToken?: string
}
const useChatUserStore = defineStore('chat-user', {
state: (): Chat => ({
chat_profile: undefined,
@ -111,6 +115,41 @@ const useChatUserStore = defineStore('chat-user', {
return true
})
},
async dingCallback(code: string, accessToken: string) {
return ChatAPI.getDingCallback(code, accessToken).then((ok) => {
this.setToken(ok.data.token)
return this.token
})
},
async dingOauth2Callback(code: string) {
return ChatAPI.getDingOauth2Callback(code).then((ok) => {
this.setToken(ok.data.token)
return this.token
})
},
async wecomCallback(code: string, accessToken: string) {
return ChatAPI.getWecomCallback(code, accessToken).then((ok) => {
this.setToken(ok.data.token)
return this.token
})
},
async larkCallback(code: string) {
return ChatAPI.getLarkCallback(code).then((ok) => {
this.setToken(ok.data.token)
return this.token
})
},
async getQrType() {
return ChatAPI.getQrType().then((ok) => {
return ok.data
})
},
async getQrSource() {
return ChatAPI.getQrSource().then((ok) => {
return ok.data
})
},
},
})

View File

@ -10,9 +10,9 @@
class="mr-8"
style="background: none"
>
<img :src="chatUser.chat_profile?.icon" alt="" />
<img :src="chatUser.chat_profile?.icon" alt=""/>
</el-avatar>
<LogoIcon v-else height="32px" class="mr-8" />
<LogoIcon v-else height="32px" class="mr-8"/>
<h4>{{ chatUser.chat_profile?.application_name }}</h4>
</div>
</template>
@ -29,9 +29,9 @@
class="mr-8"
style="background: none"
>
<img :src="chatUser.chat_profile?.icon" alt="" />
<img :src="chatUser.chat_profile?.icon" alt=""/>
</el-avatar>
<LogoIcon v-else height="32px" class="mr-8" />
<LogoIcon v-else height="32px" class="mr-8"/>
<h4>{{ chatUser.chat_profile?.application_name }}</h4>
</div>
</template>
@ -104,7 +104,7 @@
</el-button>
</div>
<div v-if="showQrCodeTab">
<QrCodeTab :tabs="orgOptions" />
<QrCodeTab :tabs="orgOptions"/>
</div>
<div class="login-gradient-divider lighter mt-24" v-if="modeList.length > 1">
<span>{{ $t('views.login.moreMethod') }}</span>
@ -123,7 +123,7 @@
'font-size': item === 'OAUTH2' ? '8px' : '10px',
color: theme.themeInfo?.theme,
}"
>{{ item }}</span
>{{ item }}</span
>
</el-button>
<el-button
@ -133,7 +133,7 @@
class="login-button-circle color-secondary"
@click="changeMode('QR_CODE')"
>
<img src="@/assets/icon_qr_outlined.svg" width="25px" />
<img src="@/assets/icon_qr_outlined.svg" width="25px"/>
</el-button>
<el-button
v-if="item === 'LOCAL' && loginMode != 'LOCAL'"
@ -150,29 +150,29 @@
</UserLoginLayout>
</template>
<script setup lang="ts">
import { onMounted, ref, onBeforeMount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import type { FormInstance, FormRules } from 'element-plus'
import type { LoginRequest } from '@/api/type/login'
import {onMounted, ref, onBeforeMount} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import type {FormInstance, FormRules} from 'element-plus'
import type {LoginRequest} from '@/api/type/login'
import LoginContainer from '@/layout/login-layout/LoginContainer.vue'
import UserLoginLayout from '@/layout/login-layout/UserLoginLayout.vue'
import loginApi from '@/api/chat/chat.ts'
import { t, getBrowserLang } from '@/locales'
import {t, getBrowserLang} from '@/locales'
import useStore from '@/stores'
import { useI18n } from 'vue-i18n'
import QrCodeTab from '@/views/login/scanCompinents/QrCodeTab.vue'
import { MsgConfirm, MsgError } from '@/utils/message.ts'
import {useI18n} from 'vue-i18n'
import QrCodeTab from '@/views/chat/user-login/scanCompinents/QrCodeTab.vue'
import {MsgConfirm, MsgError} from '@/utils/message.ts'
import PasswordAuth from '@/views/chat/auth/component/password.vue'
import { isAppIcon } from '@/utils/common'
import {isAppIcon} from '@/utils/common'
const router = useRouter()
const { theme, chatUser } = useStore()
const { locale } = useI18n({ useScope: 'global' })
const {theme, chatUser} = useStore()
const {locale} = useI18n({useScope: 'global'})
const loading = ref<boolean>(false)
const route = useRoute()
const identifyCode = ref<string>('')
const {
params: { accessToken },
params: {accessToken},
} = route as any
const loginFormRef = ref<FormInstance>()
const loginForm = ref<LoginRequest>({
@ -211,7 +211,7 @@ const loginHandle = () => {
chatUser.ldapLogin(loginForm.value).then((ok) => {
router.push({
name: 'chat',
params: { accessToken: chatUser.accessToken },
params: {accessToken: chatUser.accessToken},
query: route.query,
})
})
@ -219,7 +219,7 @@ const loginHandle = () => {
chatUser.login(loginForm.value).then((ok) => {
router.push({
name: 'chat',
params: { accessToken: chatUser.accessToken },
params: {accessToken: chatUser.accessToken},
query: route.query,
})
})
@ -239,7 +239,7 @@ onBeforeMount(() => {
})
const modeList = ref<string[]>([])
//const QrList = ref<any[]>([''])
const QrList = ref<any[]>([''])
const loginMode = ref('')
const showQrCodeTab = ref(false)
@ -300,7 +300,8 @@ function redirectAuth(authType: string, needMessage: boolean = false) {
.then(() => {
window.location.href = url
})
.catch(() => {})
.catch(() => {
})
} else {
console.log('url', url)
window.location.href = url
@ -340,6 +341,25 @@ onBeforeMount(() => {
if (modeList.value.length == 1 && ['CAS', 'OIDC', 'OAuth2'].includes(modeList.value[0])) {
redirectAuth(modeList.value[0])
}
// modeList oauth2 cas ldap oidc lark wecom dingtalk
// modeList'CAS', 'OIDC', 'OAuth2' LOCAL
QrList.value = modeList.value.filter((item) => !['CAS', 'OIDC', 'OAuth2', 'LOCAL', 'LDAP'].includes(item))
// modeListlark wecom dingtalk
modeList.value = modeList.value.filter((item) => !['lark', 'wecom', 'dingtalk'].includes(item))
if (QrList.value.length > 0) {
modeList.value.push('QR_CODE')
QrList.value.forEach((item) => {
orgOptions.value.push({
key: item,
value:
item === 'wecom'
? t('views.system.authentication.scanTheQRCode.wecom')
: item === 'dingtalk'
? t('views.system.authentication.scanTheQRCode.dingtalk')
: t('views.system.authentication.scanTheQRCode.lark'),
})
})
}
}
})
</script>

View File

@ -15,7 +15,8 @@
<script setup lang="ts">
import { onMounted, ref, defineAsyncComponent } from 'vue'
import useStore from '@/stores'
import useStore from "@/stores";
const { chatUser } = useStore()
interface Tab {
key: string
@ -40,10 +41,9 @@ const props = defineProps<{ tabs: Tab[] }>()
const activeKey = ref('')
const allConfigs = ref<PlatformConfig[]>([])
const config = ref<Config>({ app_key: '', app_secret: '' })
const { login } = useStore()
async function getPlatformInfo() {
try {
return await login.getQrSource()
return await chatUser.getQrSource()
} catch (error) {
return []
}

View File

@ -1,6 +1,6 @@
<template>
<div class="flex-center mb-16">
<img src="@/assets/logo/logo_dingtalk.svg" alt="" width="24px" class="mr-4" />
<img src="@/assets/logo/logo_dingtalk.svg" alt="" width="24px" class="mr-4"/>
<h2>{{ $t('views.system.authentication.scanTheQRCode.dingtalkQrCode') }}</h2>
</div>
<div class="ding-talk-qrName">
@ -9,11 +9,11 @@
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { useScriptTag } from '@vueuse/core'
import { ref, watch } from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useScriptTag} from '@vueuse/core'
import {ref, watch} from 'vue'
import useStore from '@/stores'
import { MsgError } from '@/utils/message'
import {MsgError} from '@/utils/message'
// DTFrameLogin QRLogin
declare global {
interface Window {
@ -70,10 +70,15 @@ const props = defineProps<{
}>()
const router = useRouter()
const { login } = useStore()
const { load } = useScriptTag('https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js')
const {chatUser} = useStore()
const {load} = useScriptTag('https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js')
const isConfigReady = ref(false)
const route = useRoute()
const {
params: {accessToken},
} = route as any
const initActive = async () => {
try {
await load(true)
@ -97,19 +102,21 @@ const initActive = async () => {
{
redirect_uri: redirectUri,
client_id: data.appKey,
scope: 'openid corpid',
scope: 'openid',
response_type: 'code',
state: 'fit2cloud-ding-qr',
state: 'fit2cloud-ding-chat-qr',
prompt: 'consent',
corpId: data.corp_id
},
(loginResult) => {
console.log(loginResult)
const authCode = loginResult.authCode
login.dingCallback(authCode).then(() => {
router.push({ name: 'home' })
chatUser.dingCallback(authCode, accessToken).then(() => {
router.push({name: 'home'})
})
},
(errorMsg: string) => {
console.log(errorMsg)
MsgError(errorMsg)
}
)
@ -126,7 +133,7 @@ watch(
initActive()
}
},
{ immediate: true }
{immediate: true}
)
</script>

View File

@ -1,16 +1,22 @@
<template>
<div class="flex-center mb-16">
<img src="@/assets/logo/logo_lark.svg " alt="" width="24px" class="mr-4" />
<img src="@/assets/logo/logo_lark.svg " alt="" width="24px" class="mr-4"/>
<h2>{{ $t('views.system.authentication.scanTheQRCode.larkQrCode') }}</h2>
</div>
<div id="lark-qr" class="lark-qrName"></div>
</template>
<script lang="ts" setup>
import { useScriptTag } from '@vueuse/core'
import { onMounted } from 'vue'
import {useScriptTag} from '@vueuse/core'
import {onMounted} from 'vue'
import {useRoute} from "vue-router";
const { load } = useScriptTag(
const route = useRoute()
const {
params: {accessToken},
} = route as any
const {load} = useScriptTag(
'https://lf-package-cn.feishucdn.com/obj/feishu-static/lark/passport/qrcode/LarkSSOSDKWebQRCode-1.0.3.js'
)
@ -33,7 +39,8 @@ const initActive = async () => {
appSecret: props.config.app_secret
}
const redirectUrl = encodeURIComponent(`${window.location.origin}/api/feishu`)
const redirectUrl = encodeURIComponent(`${window.location.origin}/chat/api/auth/lark?accessToken=${accessToken}`)
//const redirectUrl = encodeURIComponent(`http://127.0.0.1:8080/chat/api/auth/lark?accessToken=${accessToken}`)
const url = `https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=${data.agentId}&redirect_uri=${redirectUrl}&response_type=code&state=fit2cloud-lark-qr`
const QRLoginObj = window.QRLogin({
@ -47,7 +54,7 @@ const initActive = async () => {
window.addEventListener('message', async (event: any) => {
if (QRLoginObj.matchOrigin(event.origin) && QRLoginObj.matchData(event.data)) {
const loginTmpCode = event.data.tmp_code
window.location.href = `${url}&tmp_code=${loginTmpCode}`
window.location.replace(`${url}&tmp_code=${loginTmpCode}`)
}
})
}

View File

@ -3,7 +3,7 @@
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import {useRoute, useRouter} from 'vue-router'
import * as ww from '@wecom/jssdk'
import {
WWLoginLangType,
@ -16,10 +16,13 @@ import { MsgError } from '@/utils/message'
import useStore from '@/stores'
import { getBrowserLang } from '@/locales'
const router = useRouter()
const route = useRoute()
const {
params: {accessToken},
} = route as any
const wwLogin = ref({})
const obj = ref<any>({ isWeComLogin: false })
const { login } = useStore()
const { chatUser } = useStore()
const props = defineProps<{
config: {
@ -53,7 +56,7 @@ const init = async () => {
},
onCheckWeComLogin: obj.value,
async onLoginSuccess({ code }: any) {
login.wecomCallback(code).then(() => {
chatUser.wecomCallback(code, accessToken).then(() => {
setTimeout(() => {
router.push({ name: 'home' })
})

View File

@ -52,7 +52,7 @@ template
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { ElForm } from 'element-plus'
import platformApi from '@/api/system/platform-source'
import platformApi from '@/api/chat-user/auth-setting.ts'
import { MsgError, MsgSuccess } from '@/utils/message'
import { t } from '@/locales'

View File

@ -89,7 +89,7 @@
import { reactive, ref, onMounted } from 'vue'
import { copyClick } from '@/utils/clipboard'
import EditModel from './EditModal.vue'
import platformApi from '@/api/system/platform-source'
import platformApi from '@/api/chat-user/auth-setting.ts'
import { MsgError, MsgSuccess } from '@/utils/message'
import { t } from '@/locales'
@ -148,7 +148,7 @@ function createPlatform(key: string, name: string): Platform {
return {
key,
logoSrc: new URL(`../../../assets/logo/logo_${logo}.svg`, import.meta.url).href,
logoSrc: new URL(`../../../../assets/logo/logo_${logo}.svg`, import.meta.url).href,
name,
isActive: false,
isValid: false,

View File

@ -17,7 +17,7 @@ import { useRouter } from 'vue-router'
import LDAP from './component/LDAP.vue'
import CAS from './component/CAS.vue'
import OIDC from './component/OIDC.vue'
//import SCAN from './component/SCAN.vue'
import SCAN from './component/SCAN.vue'
import OAuth2 from './component/OAuth2.vue'
import { t } from '@/locales'
@ -43,11 +43,11 @@ const tabList = [
name: 'OAuth2',
component: OAuth2,
},
// {
// label: t('views.system.authentication.scanTheQRCode.title'),
// name: 'SCAN',
// component: SCAN,
// },
{
label: t('views.system.authentication.scanTheQRCode.title'),
name: 'SCAN',
component: SCAN,
},
]

View File

@ -9,8 +9,12 @@
require-asterisk-position="right">
<el-form-item :label="$t('views.userManage.source.label')" prop="sync_type">
<el-select v-model="form.sync_type" :placeholder="$t('common.selectPlaceholder')">
<el-option :label="t('views.userManage.source.local')" value="LOCAL">
</el-option>
<el-option
v-for="option in syncTypeOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-form>
@ -31,6 +35,14 @@ import type {FormInstance} from 'element-plus'
import {MsgError, MsgSuccess} from '@/utils/message'
import {t} from '@/locales'
import userManageApi from '@/api/system/chat-user'
import systemChatUserApi from '@/api/system/chat-user'
const syncTypeOptions = ref<Array<{ label: string; value: string }>>([
{label: t('views.userManage.source.local'), value: 'LOCAL'},
{label: t('views.system.authentication.scanTheQRCode.wecom'), value: 'wecom'},
{label: 'LDAP', value: 'LDAP'},
{label: t('views.system.authentication.scanTheQRCode.lark'), value: 'lark'},
])
const emit = defineEmits<{
(e: 'refresh'): void;
@ -48,9 +60,18 @@ const form = ref<{
function open() {
form.value = {...defaultForm}
getSyncType()
dialogVisible.value = true
}
async function getSyncType() {
return systemChatUserApi.getSyncType().then((res) => {
if (res.data && res.data.length > 0) {
syncTypeOptions.value = syncTypeOptions.value.filter(option => res.data.includes(option.value))
}
})
}
const formRef = ref<FormInstance>();
const rules = reactive({
@ -65,9 +86,9 @@ const submit = async (formEl: FormInstance | undefined) => {
userManageApi.batchSync(form.value.sync_type, loading).then((res) => {
if (res.data) {
const count = res.data.success_count
let ErrorMsg = ''
if (res.data.conflict_users && res.data.conflict_users.length > 0) {
// res.data.conflict_users
let ErrorMsg = ''
res.data.conflict_users.forEach((item: any) => {
if (item.type === 'username') {
ErrorMsg += '\n\n' + t('views.chatUser.syncMessage.usernameExist') + " [ " + item.users.join(',') + '\n' + ' ]'
@ -76,10 +97,10 @@ const submit = async (formEl: FormInstance | undefined) => {
ErrorMsg += '\n\n' + t('views.chatUser.syncMessage.nicknameExist') + " [ " + item.users.join(',') + '\n' + ' ]'
}
})
MsgSuccess(t('views.chatUser.syncMessage.title', {count: count}) + ErrorMsg)
emit('refresh')
dialogVisible.value = false
}
MsgSuccess(t('views.chatUser.syncMessage.title', {count: count}) + ErrorMsg)
emit('refresh')
dialogVisible.value = false
}
})

View File

@ -1,14 +1,15 @@
import { RoleTypeEnum } from '@/enums/system'
import { t } from '@/locales'
import {RoleTypeEnum} from '@/enums/system'
import {t} from '@/locales'
import useStore from '@/stores'
import {computed} from "vue";
const { user } = useStore()
export const roleTypeMap: any = {
const {user} = useStore()
export const roleTypeMap = computed(() => ({
...(user.is_admin()
? {
[RoleTypeEnum.ADMIN]: t('views.role.systemAdmin'),
}
[RoleTypeEnum.ADMIN]: t('views.role.systemAdmin'),
}
: {}),
[RoleTypeEnum.USER]: t('views.role.user'),
[RoleTypeEnum.WORKSPACE_MANAGE]: t('views.role.workspaceAdmin'),
}
}))