fix: user language

This commit is contained in:
wangdan-fit2cloud 2025-07-10 17:09:19 +08:00
parent 12feaac958
commit f926de4f91
27 changed files with 178 additions and 204 deletions

View File

@ -301,7 +301,7 @@ const getMcpTools: (
* *
* @param file:file * @param file:file
*/ */
const uploadFile: ( const postUploadFile: (
file: any, file: any,
sourceId: string, sourceId: string,
resourceType: resourceType:
@ -313,12 +313,13 @@ const uploadFile: (
| 'TEMPORARY_30_MINUTE' | 'TEMPORARY_30_MINUTE'
| 'TEMPORARY_120_MINUTE' | 'TEMPORARY_120_MINUTE'
| 'TEMPORARY_1_DAY', | 'TEMPORARY_1_DAY',
) => Promise<Result<any>> = (file, sourceId, resourceType) => { loading?: Ref<boolean>,
) => Promise<Result<any>> = (file, sourceId, resourceType, loading) => {
const fd = new FormData() const fd = new FormData()
fd.append('file', file) fd.append('file', file)
fd.append('source_id', sourceId) fd.append('source_id', sourceId)
fd.append('source_type', resourceType) fd.append('source_type', resourceType)
return post(`/oss/file`, fd) return post(`/oss/file`, fd, undefined, loading)
} }
export default { export default {
@ -346,5 +347,5 @@ export default {
postTextToSpeech, postTextToSpeech,
speechToText, speechToText,
getMcpTools, getMcpTools,
uploadFile, postUploadFile,
} }

View File

@ -297,7 +297,7 @@ const modifyChat: (chat_id: string, data: any, loading?: Ref<boolean>) => Promis
* @param resourceType * @param resourceType
* @returns * @returns
*/ */
const uploadFile: ( const postUploadFile: (
file: any, file: any,
sourceId: string, sourceId: string,
resourceType: resourceType:
@ -309,12 +309,13 @@ const uploadFile: (
| 'TEMPORARY_30_MINUTE' | 'TEMPORARY_30_MINUTE'
| 'TEMPORARY_120_MINUTE' | 'TEMPORARY_120_MINUTE'
| 'TEMPORARY_1_DAY', | 'TEMPORARY_1_DAY',
) => Promise<Result<any>> = (file, sourceId, sourceType) => { loading?: Ref<boolean>,
) => Promise<Result<any>> = (file, sourceId, sourceType, loading) => {
const fd = new FormData() const fd = new FormData()
fd.append('file', file) fd.append('file', file)
fd.append('source_id', sourceId) fd.append('source_id', sourceId)
fd.append('source_type', sourceType) fd.append('source_type', sourceType)
return post(`/oss/file`, fd) return post(`/oss/file`, fd, undefined, loading)
} }
export default { export default {
open, open,
@ -344,5 +345,5 @@ export default {
speechToText, speechToText,
deleteChat, deleteChat,
modifyChat, modifyChat,
uploadFile, postUploadFile,
} }

View File

@ -73,11 +73,11 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted } from 'vue'
import KnowledgeSourceComponent from '@/components/ai-chat/component/knowledge-source-component/index.vue' import KnowledgeSourceComponent from '@/components/ai-chat/component/knowledge-source-component/index.vue'
import MdRenderer from '@/components/markdown/MdRenderer.vue' import MdRenderer from '@/components/markdown/MdRenderer.vue'
import OperationButton from '@/components/ai-chat/component/operation-button/index.vue' import OperationButton from '@/components/ai-chat/component/operation-button/index.vue'
import { type chatType } from '@/api/type/application' import { type chatType } from '@/api/type/application'
import { computed } from 'vue'
import bus from '@/bus' import bus from '@/bus'
import useStore from '@/stores' import useStore from '@/stores'
const props = defineProps<{ const props = defineProps<{
@ -100,7 +100,7 @@ const emit = defineEmits([
]) ])
const showAvatar = computed(() => { const showAvatar = computed(() => {
return (user.isEE() || user.isPE())? props.application.show_avatar : true return user.isEE() || user.isPE() ? props.application.show_avatar : true
}) })
const showUserAvatar = computed(() => { const showUserAvatar = computed(() => {
return user.isEE() || user.isPE() ? props.application.show_user_avatar : true return user.isEE() || user.isPE() ? props.application.show_user_avatar : true
@ -165,5 +165,11 @@ const stopChat = (chat: chatType) => {
const startChat = (chat: chatType) => { const startChat = (chat: chatType) => {
props.chatManagement.write(chat.id) props.chatManagement.write(chat.id)
} }
onMounted(() => {
bus.on('chat:stop', () => {
stopChat(props.chatRecord)
})
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -14,7 +14,11 @@
</el-button> </el-button>
<!-- 使用 custom-class 自定义样式 --> <!-- 使用 custom-class 自定义样式 -->
<transition name="el-fade-in-linear"> <transition name="el-fade-in-linear">
<el-card class="custom-speech-card" :class="isTouching ? '' : 'active'" v-if="dialogVisible"> <el-card
class="custom-speech-card white-bg"
:class="isTouching ? '' : 'active'"
v-if="dialogVisible"
>
<p> <p>
<el-text type="info" v-if="isTouching" <el-text type="info" v-if="isTouching"
>00:{{ props.time < 10 ? `0${props.time}` : props.time }}</el-text >00:{{ props.time < 10 ? `0${props.time}` : props.time }}</el-text
@ -43,16 +47,16 @@ import { ref, watch } from 'vue'
const props = defineProps({ const props = defineProps({
time: { time: {
type: Number, type: Number,
default: 0 default: 0,
}, },
start: { start: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}) })
const emit = defineEmits(['TouchStart', 'TouchEnd']) const emit = defineEmits(['TouchStart', 'TouchEnd'])
// //
@ -77,7 +81,7 @@ watch(
dialogVisible.value = false dialogVisible.value = false
isTouching.value = false isTouching.value = false
} }
} },
) )
watch( watch(
() => props.start, () => props.start,
@ -90,7 +94,7 @@ watch(
dialogVisible.value = false dialogVisible.value = false
isTouching.value = false isTouching.value = false
} }
} },
) )
function onTouchStart(event: any) { function onTouchStart(event: any) {
@ -127,7 +131,6 @@ function onTouchEnd() {
left: 50%; /* 水平居中 */ left: 50%; /* 水平居中 */
transform: translateX(-50%); transform: translateX(-50%);
width: 92%; width: 92%;
background: #ffffff;
border: 1px solid #ffffff; border: 1px solid #ffffff;
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08); box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
z-index: 999; z-index: 999;

View File

@ -1,10 +1,16 @@
<template> <template>
<div class="ai-chat__operate p-16"> <div class="ai-chat__operate p-16">
<div class="text-center mb-8" v-if="loading">
<el-button class="border-primary video-stop-button" @click="stopChat">
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
{{ $t('chat.operation.stopChat') }}</el-button
>
</div>
<div class="operate-textarea"> <div class="operate-textarea">
<el-scrollbar max-height="136"> <el-scrollbar max-height="136">
<div <div
class="p-8-12" class="p-8-12"
v-loading="localLoading" v-loading="uploadLoading"
v-if=" v-if="
uploadDocumentList.length || uploadDocumentList.length ||
uploadImageList.length || uploadImageList.length ||
@ -143,6 +149,7 @@
</el-icon> </el-icon>
</div> </div>
<el-image <el-image
v-if="item.url"
:src="item.url" :src="item.url"
alt="" alt=""
fit="cover" fit="cover"
@ -299,7 +306,6 @@ import bus from '@/bus'
import 'recorder-core/src/engine/mp3' import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine' import 'recorder-core/src/engine/mp3-engine'
import { MsgWarning } from '@/utils/message' import { MsgWarning } from '@/utils/message'
import { debounce } from 'lodash'
import chatAPI from '@/api/chat/chat' import chatAPI from '@/api/chat/chat'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@ -347,6 +353,8 @@ const localLoading = computed({
}, },
}) })
const uploadLoading = ref(false)
const inputPlaceholder = computed(() => { const inputPlaceholder = computed(() => {
return recorderStatus.value === 'START' return recorderStatus.value === 'START'
? `${t('chat.inputPlaceholder.speaking')}...` ? `${t('chat.inputPlaceholder.speaking')}...`
@ -430,8 +438,13 @@ const uploadFile = async (file: any, fileList: any) => {
} }
const api = const api =
props.type === 'debug-ai-chat' props.type === 'debug-ai-chat'
? applicationApi.uploadFile(file.raw, 'TEMPORARY_120_MINUTE', 'TEMPORARY_120_MINUTE') ? applicationApi.postUploadFile(
: chatAPI.uploadFile(file.raw, chatId_context.value, 'CHAT') file.raw,
'TEMPORARY_120_MINUTE',
'TEMPORARY_120_MINUTE',
uploadLoading,
)
: chatAPI.postUploadFile(file.raw, chatId_context.value, 'CHAT', uploadLoading)
api.then((ok) => { api.then((ok) => {
file.url = ok.data file.url = ok.data
const split_path = ok.data.split('/') const split_path = ok.data.split('/')
@ -639,7 +652,7 @@ class RecorderManage {
} }
const getSpeechToTextAPI = () => { const getSpeechToTextAPI = () => {
if (props.type === 'ai-chat') { if (props.type === 'ai-chat') {
return (application_id?: string, data?: any, loading?: Ref<boolean>) => { return (data?: any, loading?: Ref<boolean>) => {
return chatAPI.speechToText(data, loading) return chatAPI.speechToText(data, loading)
} }
} else { } else {
@ -802,6 +815,10 @@ function mouseenter(row: any) {
function mouseleave() { function mouseleave() {
showDelete.value = '' showDelete.value = ''
} }
function stopChat() {
bus.emit('chat:stop')
}
onMounted(() => { onMounted(() => {
bus.on('chat-input', (message: string) => { bus.on('chat-input', (message: string) => {
inputValue.value = message inputValue.value = message

View File

@ -20,9 +20,9 @@
link link
>{{ $t('chat.operation.continue') }} >{{ $t('chat.operation.continue') }}
</el-button> </el-button>
<el-button type="primary" v-else-if="!chatRecord.write_ed" @click="stopChat(chatRecord)" link <!-- <el-button type="primary" v-else-if="!chatRecord.write_ed" @click="stopChat(chatRecord)" link
>{{ $t('chat.operation.stopChat') }} >{{ $t('chat.operation.stopChat') }}
</el-button> </el-button> -->
</div> </div>
<ChatOperationButton <ChatOperationButton

View File

@ -51,7 +51,9 @@
:executionIsRightPanel="props.executionIsRightPanel" :executionIsRightPanel="props.executionIsRightPanel"
@open-execution-detail="emit('openExecutionDetail', chatList[index])" @open-execution-detail="emit('openExecutionDetail', chatList[index])"
@openParagraph="emit('openParagraph', chatList[index])" @openParagraph="emit('openParagraph', chatList[index])"
@openParagraphDocument="(val: any)=>emit('openParagraphDocument', chatList[index], val)" @openParagraphDocument="
(val: any) => emit('openParagraphDocument', chatList[index], val)
"
></AnswerContent> ></AnswerContent>
</template> </template>
<TransitionContent <TransitionContent
@ -77,9 +79,6 @@
v-model:show-user-input="showUserInput" v-model:show-user-input="showUserInput"
v-if="type !== 'log'" v-if="type !== 'log'"
> >
<template #operateBefore>
<slot name="operateBefore"> </slot>
</template>
<template #userInput> <template #userInput>
<el-button <el-button
v-if="isUserInput || isAPIInput" v-if="isUserInput || isAPIInput"
@ -105,7 +104,6 @@ import chatLogApi from '@/api/application/chat-log'
import { ChatManagement, type chatType } from '@/api/type/application' import { ChatManagement, type chatType } from '@/api/type/application'
import { randomId } from '@/utils/common' import { randomId } from '@/utils/common'
import useStore from '@/stores' import useStore from '@/stores'
import { isWorkFlow } from '@/utils/application'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import AnswerContent from '@/components/ai-chat/component/answer-content/index.vue' import AnswerContent from '@/components/ai-chat/component/answer-content/index.vue'
import QuestionContent from '@/components/ai-chat/component/question-content/index.vue' import QuestionContent from '@/components/ai-chat/component/question-content/index.vue'
@ -139,7 +137,13 @@ const props = withDefaults(
type: 'ai-chat', type: 'ai-chat',
}, },
) )
const emit = defineEmits(['refresh', 'scroll', 'openExecutionDetail', 'openParagraph','openParagraphDocument']) const emit = defineEmits([
'refresh',
'scroll',
'openExecutionDetail',
'openParagraph',
'openParagraphDocument',
])
const { application, common } = useStore() const { application, common } = useStore()
const isMobile = computed(() => { const isMobile = computed(() => {
return common.isMobile() || mode === 'embed' || mode === 'mobile' return common.isMobile() || mode === 'embed' || mode === 'mobile'
@ -246,14 +250,18 @@ function sendMessage(val: string, other_params_data?: any, chat?: chatType): Pro
return userFormRef.value return userFormRef.value
?.validate() ?.validate()
.then((ok) => { .then((ok) => {
let userFormData = JSON.parse(localStorage.getItem(`${accessToken}userForm`) || '{}') let userFormData = accessToken
? JSON.parse(localStorage.getItem(`${accessToken}userForm`) || '{}')
: {}
const newData = Object.keys(form_data.value).reduce((result: any, key: string) => { const newData = Object.keys(form_data.value).reduce((result: any, key: string) => {
result[key] = Object.prototype.hasOwnProperty.call(userFormData, key) result[key] = Object.prototype.hasOwnProperty.call(userFormData, key)
? userFormData[key] ? userFormData[key]
: form_data.value[key] : form_data.value[key]
return result return result
}, {}) }, {})
localStorage.setItem(`${accessToken}userForm`, JSON.stringify(newData)) if (accessToken) {
localStorage.setItem(`${accessToken}userForm`, JSON.stringify(newData))
}
showUserInput.value = false showUserInput.value = false
@ -300,11 +308,11 @@ const openChatId: () => Promise<string> = () => {
return res.data return res.data
}) })
.catch((res) => { .catch((res) => {
if (res.response.status === 403) { // if (res.response.status === 403) {
return application.asyncAppAuthentication(accessToken).then(() => { // return application.asyncAppAuthentication(accessToken).then(() => {
return openChatId() // return openChatId()
}) // })
} // }
return Promise.reject(res) return Promise.reject(res)
}) })
} }
@ -512,16 +520,7 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para
// //
getChatMessageAPI()(chartOpenId.value, obj) getChatMessageAPI()(chartOpenId.value, obj)
.then((response) => { .then((response) => {
if (response.status === 401) { if (response.status === 460) {
application
.asyncAppAuthentication(accessToken)
.then(() => {
chatMessage(chat, problem)
})
.catch(() => {
errorWrite(chat)
})
} else if (response.status === 460) {
return Promise.reject(t('chat.tip.errorIdentifyMessage')) return Promise.reject(t('chat.tip.errorIdentifyMessage'))
} else if (response.status === 461) { } else if (response.status === 461) {
return Promise.reject(t('chat.tip.errorLimitMessage')) return Promise.reject(t('chat.tip.errorLimitMessage'))
@ -661,6 +660,13 @@ defineExpose({
width: calc(100% - 50px); width: calc(100% - 50px);
max-width: 400px; max-width: 400px;
} }
.video-stop-button {
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
&:hover {
background: #ffffff;
}
}
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
.firstUserInput { .firstUserInput {
.user-form-container { .user-form-container {

View File

@ -4,7 +4,7 @@
:is=" :is="
Object.keys(iconMap).includes(iconName) Object.keys(iconMap).includes(iconName)
? iconMap[iconName].iconReader() ? iconMap[iconName].iconReader()
: iconMap['404'].iconReader() : iconMap['app-404'].iconReader()
" "
class="el-icon app-icon" class="el-icon app-icon"
> >
@ -22,7 +22,7 @@ const props = withDefaults(
iconName?: string iconName?: string
}>(), }>(),
{ {
iconName: '404', iconName: 'app-404',
}, },
) )

View File

@ -292,7 +292,7 @@ export default {
]) ])
}, },
}, },
'app-play-outlined': { 'app-debug-outlined': {
iconReader: () => { iconReader: () => {
return h('i', [ return h('i', [
h( h(

View File

@ -230,6 +230,39 @@ export const iconMap: any = {
]) ])
}, },
}, },
'app-404': {
iconReader: () => {
return h('i', [
h(
'svg',
{
viewBox: '0 0 1024 1024',
version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg',
},
[
h('path', {
d: 'M260.266667 789.333333c-21.333333 0-38.4-17.066667-38.4-38.4v-59.733333H38.4c-12.8 0-29.866667-8.533333-34.133333-21.333333-4.266667-17.066667-4.266667-29.866667 4.266666-42.666667l221.866667-294.4c8.533333-12.8 25.6-17.066667 42.666667-12.8 17.066667 4.266667 25.6 21.333333 25.6 38.4v256h34.133333c21.333333 0 38.4 17.066667 38.4 38.4s-17.066667 38.4-38.4 38.4H298.666667v59.733333c0 21.333333-17.066667 38.4-38.4 38.4z m-145.066667-179.2h106.666667V469.333333l-106.666667 140.8zM913.066667 742.4c-21.333333 0-38.4-17.066667-38.4-38.4v-59.733333h-183.466667c-12.8 0-29.866667-8.533333-34.133333-21.333334-8.533333-12.8-4.266667-29.866667 4.266666-38.4l221.866667-294.4c8.533333-12.8 25.6-17.066667 42.666667-12.8 17.066667 4.266667 25.6 21.333333 25.6 38.4v256h34.133333c21.333333 0 38.4 17.066667 38.4 38.4s-17.066667 38.4-38.4 38.4h-34.133333v59.733334c0 17.066667-17.066667 34.133333-38.4 34.133333zM768 567.466667h106.666667V426.666667L768 567.466667zM533.333333 597.333333c-46.933333 0-85.333333-25.6-119.466666-68.266666-29.866667-38.4-42.666667-93.866667-42.666667-145.066667 0-55.466667 17.066667-106.666667 42.666667-145.066667 29.866667-42.666667 72.533333-68.266667 119.466666-68.266666 46.933333 0 85.333333 25.6 119.466667 68.266666 29.866667 38.4 42.666667 93.866667 42.666667 145.066667 0 55.466667-17.066667 106.666667-42.666667 145.066667-34.133333 46.933333-76.8 68.266667-119.466667 68.266666z m0-362.666666c-55.466667 0-98.133333 68.266667-98.133333 149.333333s46.933333 149.333333 98.133333 149.333333c55.466667 0 98.133333-68.266667 98.133334-149.333333s-46.933333-149.333333-98.133334-149.333333z',
fill: '#978CFF',
}),
h('path', {
d: 'M354.133333 691.2a162.133333 21.333333 0 1 0 324.266667 0 162.133333 21.333333 0 1 0-324.266667 0Z',
fill: '#E3E5FC',
}),
h('path', {
d: 'M8.533333 832a162.133333 21.333333 0 1 0 324.266667 0 162.133333 21.333333 0 1 0-324.266667 0Z',
fill: '#E3E5FC',
}),
h('path', {
d: 'M661.333333 797.866667a162.133333 21.333333 0 1 0 324.266667 0 162.133333 21.333333 0 1 0-324.266667 0Z',
fill: '#E3E5FC',
}),
],
),
])
},
},
// 动态加载的图标 // 动态加载的图标
...dynamicIcons, ...dynamicIcons,
} }

View File

@ -2,7 +2,7 @@
<el-dropdown placement="bottom-start" class="workspace-dropdown" popper-class="workspace-dropdown-popper"> <el-dropdown placement="bottom-start" class="workspace-dropdown" popper-class="workspace-dropdown-popper">
<el-button text style="font-size: 14px" class="workspace-dropdown__button"> <el-button text style="font-size: 14px" class="workspace-dropdown__button">
<AppIcon iconName="app-workspace" style="font-size: 18px"></AppIcon> <AppIcon iconName="app-workspace" style="font-size: 18px"></AppIcon>
<span class="ellipsis" style="max-width: 155px"> <span class="ellipsis" style="max-width: 155px" :title="currentWorkspace?.name">
{{ currentWorkspace?.name }} {{ currentWorkspace?.name }}
</span> </span>
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
@ -19,7 +19,7 @@
:class="`${item.id === currentWorkspace?.id ? 'active' : ''} flex-between`" @click="changeWorkspace(item)"> :class="`${item.id === currentWorkspace?.id ? 'active' : ''} flex-between`" @click="changeWorkspace(item)">
<div class="flex align-center"> <div class="flex align-center">
<AppIcon class="mr-8" iconName="app-workspace" style="font-size: 16px"></AppIcon> <AppIcon class="mr-8" iconName="app-workspace" style="font-size: 16px"></AppIcon>
<span class="ellipsis"> <span class="ellipsis" :title="item.name">
{{ item.name }} {{ item.name }}
</span> </span>
</div> </div>

View File

@ -271,7 +271,7 @@ export default {
label: '文本转语音', label: '文本转语音',
text: '将文本通过语音合成模型转换为音频', text: '将文本通过语音合成模型转换为音频',
tts_model: { tts_model: {
label: '语音识别模型', label: '语音合成模型',
}, },
content: { content: {
label: '选择文本内容', label: '选择文本内容',

View File

@ -1,28 +1,28 @@
import { useLocalStorage } from '@vueuse/core'; import { useLocalStorage } from '@vueuse/core'
import { computed } from 'vue'; import { computed } from 'vue'
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n'
import { i18n, langCode, localeConfigKey } from '@/locales/index'; import { i18n, langCode, localeConfigKey } from '@/locales/index'
export function useLocale() { export function useLocale() {
const { locale } = useI18n({ useScope: 'global' }); const { locale } = useI18n({ useScope: 'global' })
function changeLocale(lang: string) { function changeLocale(lang: string) {
// 如果切换的语言不在对应语言文件里则默认为简体中文 // 如果切换的语言不在对应语言文件里则默认为简体中文
if (!langCode.includes(lang)) { if (!langCode.includes(lang)) {
lang = 'en-US'; lang = 'en-US'
}
locale.value = lang;
useLocalStorage(localeConfigKey, 'en-US').value = lang;
} }
const getComponentsLocale = computed(() => { locale.value = lang
const localeMessage = i18n.global.getLocaleMessage(locale.value) as Record<string, any>; useLocalStorage(localeConfigKey, 'en-US').value = lang
return localeMessage.componentsLocale; }
});
return { const getComponentsLocale = computed(() => {
changeLocale, const localeMessage = i18n.global.getLocaleMessage(locale.value) as Record<string, any>
getComponentsLocale, return localeMessage.componentsLocale
locale, })
};
return {
changeLocale,
getComponentsLocale,
locale,
}
} }

View File

@ -24,8 +24,8 @@ instance.interceptors.request.use(
} }
const { chatUser } = useStore() const { chatUser } = useStore()
const token = chatUser.getToken() const token = chatUser.getToken()
// const language = chatUser.getLanguage() const language = chatUser.getLanguage()
// config.headers['Accept-Language'] = `${language}` config.headers['Accept-Language'] = `${language}`
if (token) { if (token) {
config.headers['AUTHORIZATION'] = `Bearer ${token}` config.headers['AUTHORIZATION'] = `Bearer ${token}`
} }
@ -177,12 +177,12 @@ export const postStream: (url: string, data?: unknown) => Promise<Result<any> |
) => { ) => {
const { chatUser } = useStore() const { chatUser } = useStore()
const token = chatUser.getToken() const token = chatUser.getToken()
// const language = user.getLanguage() const language = chatUser.getLanguage()
const headers: HeadersInit = { 'Content-Type': 'application/json' } const headers: HeadersInit = { 'Content-Type': 'application/json' }
if (token) { if (token) {
headers['AUTHORIZATION'] = `Bearer ${token}` headers['AUTHORIZATION'] = `Bearer ${token}`
} }
// headers['Accept-Language'] = `${language}` headers['Accept-Language'] = `${language}`
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',
body: data ? JSON.stringify(data) : undefined, body: data ? JSON.stringify(data) : undefined,

View File

@ -47,27 +47,6 @@ const useApplicationStore = defineStore('application', {
}) })
}, },
async asyncAppAuthentication(
token: string,
loading?: Ref<boolean>,
authentication_value?: any,
) {
return new Promise((resolve, reject) => {
applicationApi
.postAppAuthentication(token, loading, authentication_value)
.then((res: any) => {
localStorage.setItem(`${token}-accessToken`, res.data)
sessionStorage.setItem(`${token}-accessToken`, res.data)
resolve(res)
})
.catch((error: any) => {
reject(error)
})
})
},
async refreshAccessToken(token: string) {
this.asyncAppAuthentication(token)
},
// 修改应用 // 修改应用
async asyncPutApplication(id: string, data: any, loading?: Ref<boolean>) { async asyncPutApplication(id: string, data: any, loading?: Ref<boolean>) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -3,9 +3,8 @@ import ChatAPI from '@/api/chat/chat'
import type { ChatProfile, ChatUserProfile } from '@/api/type/chat' import type { ChatProfile, ChatUserProfile } from '@/api/type/chat'
import type { LoginRequest } from '@/api/type/user' import type { LoginRequest } from '@/api/type/user'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { useLocalStorage } from '@vueuse/core' import { getBrowserLang } from '@/locales/index'
import { localeConfigKey } from '@/locales/index'
import useUserStore from './user'
interface ChatUser { interface ChatUser {
// 用户id // 用户id
id: string id: string
@ -24,6 +23,9 @@ const useChatUserStore = defineStore('chat-user', {
accessToken: undefined, accessToken: undefined,
}), }),
actions: { actions: {
getLanguage() {
return localStorage.getItem(`${this.accessToken}-locale`) || getBrowserLang()
},
setAccessToken(accessToken: string) { setAccessToken(accessToken: string) {
this.accessToken = accessToken this.accessToken = accessToken
}, },
@ -41,9 +43,8 @@ const useChatUserStore = defineStore('chat-user', {
applicationProfile() { applicationProfile() {
return ChatAPI.applicationProfile().then((ok) => { return ChatAPI.applicationProfile().then((ok) => {
this.application = ok.data this.application = ok.data
const user = useUserStore() localStorage.setItem(`${this.accessToken}-locale`, ok.data?.language || this.getLanguage())
useLocalStorage<string>(localeConfigKey, 'en-US').value =
ok?.data?.language || user.getLanguage()
if (this.application.custom_theme) { if (this.application.custom_theme) {
this.application['custom_theme']['theme_color'] = this.application['custom_theme']['theme_color'] =
ok.data?.custom_theme?.theme_color || '#3370FF' ok.data?.custom_theme?.theme_color || '#3370FF'

View File

@ -1,32 +1,19 @@
import {defineStore} from 'pinia' import { defineStore } from 'pinia'
import {type Ref} from 'vue' import { type Ref } from 'vue'
import LoginApi from '@/api/user/login' import LoginApi from '@/api/user/login'
import type {LoginRequest} from '@/api/type/login' import type { LoginRequest } from '@/api/type/login'
import useUserStore from './user' import useUserStore from './user'
const useLoginStore = defineStore('login', { const useLoginStore = defineStore('login', {
state: () => ({ state: () => ({
token: '', token: '',
userAccessToken: '',
}), }),
actions: { actions: {
getToken(): string | null { getToken(): string | null {
if (this.token) { if (this.token) {
return this.token return this.token
} }
const user = useUserStore() return localStorage.getItem('token')
return user.userType === 1 ? localStorage.getItem('token') : this.getAccessToken()
},
getAccessToken() {
const token = sessionStorage.getItem(`${this.userAccessToken}-accessToken`)
if (token) {
return token
}
const local_token = localStorage.getItem(`${token}-accessToken`)
if (local_token) {
return local_token
}
return localStorage.getItem(`accessToken`)
}, },
async asyncLogin(data: LoginRequest, loading?: Ref<boolean>) { async asyncLogin(data: LoginRequest, loading?: Ref<boolean>) {
@ -99,7 +86,6 @@ const useLoginStore = defineStore('login', {
return ok.data return ok.data
}) })
}, },
}, },
}) })

View File

@ -11,7 +11,6 @@ import { defaultPlatformSetting } from '@/utils/theme'
import useLoginStore from './login' import useLoginStore from './login'
export interface userStateTypes { export interface userStateTypes {
userType: number // 1 系统操作者 2 对话用户
userInfo: User | null userInfo: User | null
version?: string version?: string
license_is_valid: boolean license_is_valid: boolean
@ -22,7 +21,6 @@ export interface userStateTypes {
const useUserStore = defineStore('user', { const useUserStore = defineStore('user', {
state: (): userStateTypes => ({ state: (): userStateTypes => ({
userType: 1, // 1 系统操作者 2 对话用户
userInfo: null, userInfo: null,
version: '', version: '',
license_is_valid: false, license_is_valid: false,
@ -32,11 +30,8 @@ const useUserStore = defineStore('user', {
}), }),
actions: { actions: {
getLanguage() { getLanguage() {
return this.userType === 1 return localStorage.getItem('MaxKB-locale') || getBrowserLang()
? localStorage.getItem('MaxKB-locale') || getBrowserLang()
: sessionStorage.getItem('language') || getBrowserLang()
}, },
setWorkspaceId(workspace_id: string) { setWorkspaceId(workspace_id: string) {
this.workspace_id = workspace_id this.workspace_id = workspace_id
localStorage.setItem('workspace_id', workspace_id) localStorage.setItem('workspace_id', workspace_id)

View File

@ -433,7 +433,6 @@ h5 {
.layout-bg { .layout-bg {
background: var(--app-layout-bg-color); background: var(--app-layout-bg-color);
} }
.white-bg { .white-bg {
background: #ffffff; background: #ffffff;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div v-show="show" class="workflow-dropdown-menu border border-r-6"> <div v-show="show" class="workflow-dropdown-menu border border-r-6 white-bg">
<el-tabs v-model="activeName" class="workflow-dropdown-tabs"> <el-tabs v-model="activeName" class="workflow-dropdown-tabs">
<div style="display: flex; width: 100%; justify-content: center" class="mb-12"> <div style="display: flex; width: 100%; justify-content: center" class="mb-12">
<el-input <el-input
@ -321,7 +321,6 @@ onMounted(() => {
z-index: 99; z-index: 99;
width: 400px; width: 400px;
box-shadow: 0px 4px 8px 0px var(--app-text-color-light-1); box-shadow: 0px 4px 8px 0px var(--app-text-color-light-1);
background: #ffffff;
padding-bottom: 8px; padding-bottom: 8px;
.title { .title {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="workflow-publish-history border-l"> <div class="workflow-publish-history border-l white-bg">
<h4 class="border-b p-16-24">{{ $t('views.applicationWorkflow.setting.releaseHistory') }}</h4> <h4 class="border-b p-16-24">{{ $t('views.applicationWorkflow.setting.releaseHistory') }}</h4>
<div class="list-height pt-0"> <div class="list-height pt-0">
<el-scrollbar> <el-scrollbar>
@ -137,7 +137,6 @@ onMounted(() => {
position: absolute; position: absolute;
right: 0; right: 0;
top: 57px; top: 57px;
background: #ffffff;
height: calc(100vh - 57px); height: calc(100vh - 57px);
z-index: 9; z-index: 9;
.list-height { .list-height {

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="application-workflow" v-loading="loading"> <div class="application-workflow" v-loading="loading">
<div class="header border-b flex-between p-12-24"> <div class="header border-b flex-between p-12-24 white-bg">
<div class="flex align-center"> <div class="flex align-center">
<back-button @click="back"></back-button> <back-button @click="back"></back-button>
<h4>{{ detail?.name }}</h4> <h4>{{ detail?.name }}</h4>
@ -29,7 +29,7 @@
{{ $t('views.applicationWorkflow.setting.addComponent') }} {{ $t('views.applicationWorkflow.setting.addComponent') }}
</el-button> </el-button>
<el-button @click="clickShowDebug" :disabled="showDebug"> <el-button @click="clickShowDebug" :disabled="showDebug">
<AppIcon iconName="app-play-outlined" class="mr-4"></AppIcon> <AppIcon iconName="app-debug-outlined" class="mr-4"></AppIcon>
{{ $t('views.applicationWorkflow.setting.debug') }}</el-button {{ $t('views.applicationWorkflow.setting.debug') }}</el-button
> >
<el-button @click="saveApplication(true)"> <el-button @click="saveApplication(true)">
@ -142,7 +142,7 @@ import Workflow from '@/workflow/index.vue'
import DropdownMenu from '@/views/application-workflow/component/DropdownMenu.vue' import DropdownMenu from '@/views/application-workflow/component/DropdownMenu.vue'
import PublishHistory from '@/views/application-workflow/component/PublishHistory.vue' import PublishHistory from '@/views/application-workflow/component/PublishHistory.vue'
import ApplicationAPI from '@/api/application/application' import ApplicationAPI from '@/api/application/application'
import { isAppIcon,resetUrl } from '@/utils/common' import { isAppIcon, resetUrl } from '@/utils/common'
import { MsgSuccess, MsgError, MsgConfirm } from '@/utils/message' import { MsgSuccess, MsgError, MsgConfirm } from '@/utils/message'
import { datetimeFormat } from '@/utils/time' import { datetimeFormat } from '@/utils/time'
import { mapToUrlParams } from '@/utils/application' import { mapToUrlParams } from '@/utils/application'
@ -575,9 +575,6 @@ onBeforeUnmount(() => {
.application-workflow { .application-workflow {
background: var(--app-layout-bg-color); background: var(--app-layout-bg-color);
height: 100%; height: 100%;
.header {
background: #ffffff;
}
.workflow-main { .workflow-main {
height: calc(100vh - 62px); height: calc(100vh - 62px);
box-sizing: border-box; box-sizing: border-box;

View File

@ -7,10 +7,11 @@
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed, onBeforeMount } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import useStore from '@/stores' import useStore from '@/stores'
import { useI18n } from 'vue-i18n'
const { locale } = useI18n({ useScope: 'global' })
const route = useRoute() const route = useRoute()
const { chatUser, common } = useStore() const { chatUser, common } = useStore()
@ -23,7 +24,6 @@ const {
} = route as any } = route as any
const currentTemplate = computed(() => { const currentTemplate = computed(() => {
console.log(common.isMobile())
let modeName = '' let modeName = ''
if (!mode || mode === 'pc') { if (!mode || mode === 'pc') {
modeName = common.isMobile() ? 'mobile' : 'pc' modeName = common.isMobile() ? 'mobile' : 'pc'
@ -34,6 +34,8 @@ const currentTemplate = computed(() => {
return components[name].default return components[name].default
}) })
const applicationAvailable = ref<boolean>(true) const applicationAvailable = ref<boolean>(true)
onBeforeMount(() => {
locale.value = chatUser.getLanguage()
})
</script> </script>

View File

@ -1,50 +0,0 @@
<template>
<div>
<el-form ref="FormRef" :model="form" @submit.prevent="validator">
<el-form-item prop="value" :rules="rules">
<el-input show-password v-model="form.value" />
</el-form-item>
<el-button class="w-full mt-8" type="primary" @click="validator" :loading="loading">
{{ $t('common.confirm') }}</el-button
>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
import { t } from '@/locales'
const route = useRoute()
const FormRef = ref()
const {
params: { accessToken },
} = route as any
const { application } = useStore()
const loading = ref<boolean>(false)
const auth = () => {
return application.asyncAppAuthentication(accessToken, loading, form.value).then(() => {})
}
const validator_auth = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error(t('chat.passwordValidator.errorMessage1')))
} else {
auth().catch(() => {
callback(new Error(t('chat.passwordValidator.errorMessage2')))
})
}
}
const validator = () => {
FormRef.value.validate()
}
const rules = reactive({
value: [{ required: true, validator: validator_auth, trigger: 'manual' }],
})
const form = ref({
type: 'password',
value: '',
})
</script>
<style lang="scss" scoped></style>

View File

@ -16,13 +16,13 @@
@click="changeLang(lang.value)" @click="changeLang(lang.value)"
class="flex-between" class="flex-between"
> >
<span :class="lang.value === user.getLanguage() ? 'primary' : ''">{{ <span :class="lang.value === chatUser.getLanguage() ? 'primary' : ''">{{
lang.label lang.label
}}</span> }}</span>
<el-icon <el-icon
:class="lang.value === user.getLanguage() ? 'primary' : ''" :class="lang.value === chatUser.getLanguage() ? 'primary' : ''"
v-if="lang.value === user.getLanguage()" v-if="lang.value === chatUser.getLanguage()"
> >
<Check /> <Check />
</el-icon> </el-icon>
@ -51,7 +51,7 @@ defineProps({
default: true, default: true,
}, },
}) })
const { user, theme } = useStore() const { chatUser, theme } = useStore()
const changeLang = (lang: string) => { const changeLang = (lang: string) => {
useLocalStorage(localeConfigKey, getBrowserLang()).value = lang useLocalStorage(localeConfigKey, getBrowserLang()).value = lang
@ -59,7 +59,7 @@ const changeLang = (lang: string) => {
} }
const currentLanguage = computed(() => { const currentLanguage = computed(() => {
return langList.value?.filter((v: any) => v.value === user.getLanguage())?.[0]?.label return langList.value?.filter((v: any) => v.value === chatUser.getLanguage())?.[0]?.label
}) })
const fileURL = computed(() => { const fileURL = computed(() => {

View File

@ -166,7 +166,7 @@ import PasswordAuth from '@/views/chat/auth/component/password.vue'
import { isAppIcon } from '@/utils/common' import { isAppIcon } from '@/utils/common'
const router = useRouter() const router = useRouter()
const { login, user, theme, chatUser } = useStore() const { theme, chatUser } = useStore()
const { locale } = useI18n({ useScope: 'global' }) const { locale } = useI18n({ useScope: 'global' })
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const route = useRoute() const route = useRoute()
@ -226,6 +226,7 @@ function makeCode() {
} }
onBeforeMount(() => { onBeforeMount(() => {
locale.value = chatUser.getLanguage()
makeCode() makeCode()
}) })

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="login-preview mr-16"> <div class="login-preview mr-16 white-bg">
<div class="header"> <div class="header">
<div class="tag flex-between"> <div class="tag flex-between">
<div class="flex align-center"> <div class="flex align-center">
@ -78,7 +78,6 @@ const fileURL = computed(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.login-preview { .login-preview {
background: #ffffff;
border-radius: 4px; border-radius: 4px;
transform-origin: center; transform-origin: center;
.login-container { .login-container {