feat: user input

This commit is contained in:
wangdan-fit2cloud 2025-07-02 17:46:39 +08:00
parent 464890abc5
commit 32dd4b41b6
12 changed files with 241 additions and 146 deletions

View File

@ -29,10 +29,10 @@
"cropperjs": "^2.0.0-rc.2", "cropperjs": "^2.0.0-rc.2",
"dingtalk-jsapi": "^3.1.0", "dingtalk-jsapi": "^3.1.0",
"echarts": "^5.6.0", "echarts": "^5.6.0",
"element-plus": "^2.9.10", "element-plus": "^2.10.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"katex": "^0.16.10",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"katex": "^0.16.10",
"md-editor-v3": "^5.6.1", "md-editor-v3": "^5.6.1",
"mermaid": "^11.6.0", "mermaid": "^11.6.0",
"moment": "^2.30.1", "moment": "^2.30.1",

View File

@ -169,6 +169,8 @@
v-else v-else
ref="quickInputRef" ref="quickInputRef"
v-model="inputValue" v-model="inputValue"
:autosize="{ minRows: 1, maxRows: isMobile ? 4 : 10 }"
type="textarea"
:placeholder=" :placeholder="
recorderStatus === 'START' recorderStatus === 'START'
? `${$t('chat.inputPlaceholder.speaking')}...` ? `${$t('chat.inputPlaceholder.speaking')}...`
@ -176,27 +178,17 @@
? `${$t('chat.inputPlaceholder.recorderLoading')}...` ? `${$t('chat.inputPlaceholder.recorderLoading')}...`
: $t('chat.inputPlaceholder.default') : $t('chat.inputPlaceholder.default')
" "
:autosize="{ minRows: 1, maxRows: isMobile ? 4 : 10 }"
type="textarea"
:maxlength="100000" :maxlength="100000"
@keydown.enter="sendChatHandle($event)" @keydown.enter="sendChatHandle($event)"
@paste="handlePaste" @paste="handlePaste"
@drop="handleDrop" @drop="handleDrop"
class="chat-operate-textarea"
/> />
<div class="operate flex-between"> <div class="operate flex-between">
<div> <div>
<!-- <el-button <slot name="userInput" />
v-if="isUserInput || isAPIInput"
class="user-input-button mb-8"
type="primary"
text
@click="toggleUserInput"
>
<AppIcon iconName="app-user-input"></AppIcon>
</el-button> -->
</div> </div>
<div> <div>
<template v-if="props.applicationDetails.stt_model_enable"> <template v-if="props.applicationDetails.stt_model_enable">
<span v-if="mode === 'mobile'"> <span v-if="mode === 'mobile'">
@ -305,6 +297,7 @@ import { ref, computed, onMounted, nextTick, watch, type Ref } from 'vue'
import Recorder from 'recorder-core' import Recorder from 'recorder-core'
import TouchChat from './TouchChat.vue' import TouchChat from './TouchChat.vue'
import applicationApi from '@/api/application/application' import applicationApi from '@/api/application/application'
import UserForm from '@/components/ai-chat/component/user-form/index.vue'
import { MsgAlert } from '@/utils/message' import { MsgAlert } from '@/utils/message'
import { type chatType } from '@/api/type/application' import { type chatType } from '@/api/type/application'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -329,7 +322,6 @@ const props = withDefaults(
isMobile: boolean isMobile: boolean
appId?: string appId?: string
chatId: string chatId: string
showUserInput?: boolean
sendMessage: (question: string, other_params_data?: any, chat?: chatType) => void sendMessage: (question: string, other_params_data?: any, chat?: chatType) => void
openChatId: () => Promise<string> openChatId: () => Promise<string>
validate: () => Promise<any> validate: () => Promise<any>
@ -362,6 +354,31 @@ const localLoading = computed({
}, },
}) })
const showUserInput = ref(true)
const form_data = ref<any>({})
const api_form_data = ref<any>({})
const toggleUserInput = () => {
showUserInput.value = !showUserInput.value
if (showUserInput.value) {
//
initialFormData.value = JSON.parse(JSON.stringify(form_data.value))
initialApiFormData.value = JSON.parse(JSON.stringify(api_form_data.value))
}
}
function UserFormConfirm() {
showUserInput.value = false
}
function UserFormCancel() {
//
form_data.value = JSON.parse(JSON.stringify(initialFormData.value))
api_form_data.value = JSON.parse(JSON.stringify(initialApiFormData.value))
userFormRef.value?.render(form_data.value)
showUserInput.value = false
}
const upload = ref() const upload = ref()
const imageExtensions = ['JPG', 'JPEG', 'PNG', 'GIF', 'BMP'] const imageExtensions = ['JPG', 'JPEG', 'PNG', 'GIF', 'BMP']
@ -807,7 +824,7 @@ function autoSendMessage() {
uploadVideoList.value = [] uploadVideoList.value = []
uploadOtherList.value = [] uploadOtherList.value = []
if (quickInputRef.value) { if (quickInputRef.value) {
quickInputRef.value.textareaStyle.height = '45px' quickInputRef.value.textarea.style.height = '45px'
} }
}) })
.catch(() => { .catch(() => {
@ -901,10 +918,10 @@ onMounted(() => {
}, 100) }, 100)
} }
setTimeout(() => { setTimeout(() => {
if (quickInputRef.value && mode === 'embed') { nextTick(() => {
quickInputRef.value.textarea.style.height = '0' quickInputRef.value.textarea.style.height = '0'
} })
}, 1800) }, 800)
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -914,6 +931,7 @@ onMounted(() => {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
z-index: 10; z-index: 10;
:deep(.operate-textarea) { :deep(.operate-textarea) {
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08); box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
background-color: #ffffff; background-color: #ffffff;
@ -931,6 +949,8 @@ onMounted(() => {
resize: none; resize: none;
padding: 13px 16px; padding: 13px 16px;
box-sizing: border-box; box-sizing: border-box;
min-height: 47px !important;
height: 0;
} }
.operate { .operate {
@ -986,4 +1006,12 @@ onMounted(() => {
} }
} }
} }
.popperUserInput {
position: absolute;
z-index: 999;
left: 0;
bottom: 50px;
width: calc(100% - 50px);
max-width: 400px;
}
</style> </style>

View File

@ -8,21 +8,13 @@
> >
<el-card shadow="always" class="border-r-8" style="--el-card-padding: 16px 8px"> <el-card shadow="always" class="border-r-8" style="--el-card-padding: 16px 8px">
<div class="flex align-center cursor w-full" style="padding: 0 8px"> <div class="flex align-center cursor w-full" style="padding: 0 8px">
<!-- <el-icon class="mr-8 arrow-icon" :class="showUserInput ? 'rotate-90' : ''"
><CaretRight
/></el-icon> -->
<span class="break-all ellipsis-1 mr-16" :title="inputFieldConfig.title"> <span class="break-all ellipsis-1 mr-16" :title="inputFieldConfig.title">
{{ inputFieldConfig.title }} {{ inputFieldConfig.title }}
</span> </span>
</div> </div>
<el-scrollbar :max-height="first ? 0 : 450"> <el-scrollbar :max-height="first ? 0 : 450">
<el-collapse-transition> <div class="mt-16" style="padding: 0 8px; height: calc(100% - 100px)">
<div
v-show="showUserInput"
class="mt-16"
style="padding: 0 8px; height: calc(100% - 100px)"
>
<DynamicsForm <DynamicsForm
:key="dynamicsFormRefresh" :key="dynamicsFormRefresh"
v-model="form_data_context" v-model="form_data_context"
@ -42,17 +34,17 @@
ref="dynamicsFormRef2" ref="dynamicsFormRef2"
/> />
</div> </div>
</el-collapse-transition>
</el-scrollbar> </el-scrollbar>
<div class="text-right mr-8"> <div class="text-left ml-8">
<el-button type="primary" v-if="first" @click="confirmHandle">{{ <el-button type="primary" class="w-full" v-if="first" @click="confirmHandle">
$t('chat.operation.startChat') <AppIcon iconName="app-chat" class="mr-4"></AppIcon>
}}</el-button> {{ $t('chat.operation.startChat') }}</el-button
<el-button v-if="!first" @click="cancelHandle">{{ $t('common.cancel') }}</el-button> >
<el-button type="primary" v-if="!first" @click="confirmHandle">{{ <el-button type="primary" v-if="!first" @click="confirmHandle">{{
$t('common.confirm') $t('common.confirm')
}}</el-button> }}</el-button>
<el-button v-if="!first" @click="cancelHandle">{{ $t('common.cancel') }}</el-button>
</div> </div>
</el-card> </el-card>
</div> </div>
@ -66,21 +58,20 @@ import { MsgWarning } from '@/utils/message'
import { t } from '@/locales' import { t } from '@/locales'
const route = useRoute() const route = useRoute()
const { const {
params: { accessToken } params: { accessToken },
} = route } = route
const props = defineProps<{ const props = defineProps<{
application: any application: any
type: 'log' | 'ai-chat' | 'debug-ai-chat' type: 'log' | 'ai-chat' | 'debug-ai-chat'
api_form_data: any api_form_data: any
form_data: any form_data: any
first: boolean first?: boolean
}>() }>()
// //
const dynamicsFormRefresh = ref(0) const dynamicsFormRefresh = ref(0)
const inputFieldList = ref<FormField[]>([]) const inputFieldList = ref<FormField[]>([])
const apiInputFieldList = ref<FormField[]>([]) const apiInputFieldList = ref<FormField[]>([])
const inputFieldConfig = ref({ title: t('chat.userInput') }) const inputFieldConfig = ref({ title: t('chat.userInput') })
const showUserInput = ref(true)
const firstMounted = ref(false) const firstMounted = ref(false)
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>() const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
@ -94,7 +85,7 @@ const api_form_data_context = computed({
}, },
set: (data) => { set: (data) => {
emit('update:api_form_data', data) emit('update:api_form_data', data)
} },
}) })
const form_data_context = computed({ const form_data_context = computed({
@ -103,14 +94,14 @@ const form_data_context = computed({
}, },
set: (data) => { set: (data) => {
emit('update:form_data', data) emit('update:form_data', data)
} },
}) })
watch( watch(
() => props.application, () => props.application,
(data) => { (data) => {
handleInputFieldList() handleInputFieldList()
} },
) )
function handleInputFieldList() { function handleInputFieldList() {
@ -128,7 +119,7 @@ function handleInputFieldList() {
input_type: 'TextInput', input_type: 'TextInput',
label: v.name, label: v.name,
default_value: default_value[v.variable], default_value: default_value[v.variable],
required: v.is_required required: v.is_required,
} }
case 'select': case 'select':
return { return {
@ -139,7 +130,7 @@ function handleInputFieldList() {
required: v.is_required, required: v.is_required,
option_list: v.optionList.map((o: any) => { option_list: v.optionList.map((o: any) => {
return { key: o, value: o } return { key: o, value: o }
}) }),
} }
case 'date': case 'date':
return { return {
@ -151,8 +142,8 @@ function handleInputFieldList() {
attrs: { attrs: {
format: 'YYYY-MM-DD HH:mm:ss', format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss', 'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime' type: 'datetime',
} },
} }
default: default:
return v return v
@ -169,7 +160,7 @@ function handleInputFieldList() {
input_type: 'TextInput', input_type: 'TextInput',
label: v.name, label: v.name,
default_value: default_value[v.variable], default_value: default_value[v.variable],
required: v.is_required required: v.is_required,
} }
case 'select': case 'select':
return { return {
@ -180,7 +171,7 @@ function handleInputFieldList() {
required: v.is_required, required: v.is_required,
option_list: v.optionList.map((o: any) => { option_list: v.optionList.map((o: any) => {
return { key: o, value: o } return { key: o, value: o }
}) }),
} }
case 'date': case 'date':
return { return {
@ -192,8 +183,8 @@ function handleInputFieldList() {
attrs: { attrs: {
format: 'YYYY-MM-DD HH:mm:ss', format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss', 'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime' type: 'datetime',
} },
} }
default: default:
break break
@ -210,7 +201,7 @@ function handleInputFieldList() {
input_type: 'TextInput', input_type: 'TextInput',
label: v.variable, label: v.variable,
default_value: v.default_value || default_value[v.variable], default_value: v.default_value || default_value[v.variable],
required: v.is_required required: v.is_required,
} }
case 'select': case 'select':
return { return {
@ -221,7 +212,7 @@ function handleInputFieldList() {
required: v.is_required, required: v.is_required,
option_list: v.optionList.map((o: any) => { option_list: v.optionList.map((o: any) => {
return { key: o, value: o } return { key: o, value: o }
}) }),
} }
case 'date': case 'date':
return { return {
@ -233,8 +224,8 @@ function handleInputFieldList() {
attrs: { attrs: {
format: 'YYYY-MM-DD HH:mm:ss', format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss', 'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime' type: 'datetime',
} },
} }
default: default:
break break
@ -251,7 +242,7 @@ function handleInputFieldList() {
input_type: 'TextInput', input_type: 'TextInput',
label: v.name, label: v.name,
default_value: default_value[v.variable], default_value: default_value[v.variable],
required: v.is_required required: v.is_required,
} }
case 'select': case 'select':
return { return {
@ -262,7 +253,7 @@ function handleInputFieldList() {
required: v.is_required, required: v.is_required,
option_list: v.optionList.map((o: any) => { option_list: v.optionList.map((o: any) => {
return { key: o, value: o } return { key: o, value: o }
}) }),
} }
case 'date': case 'date':
return { return {
@ -274,8 +265,8 @@ function handleInputFieldList() {
attrs: { attrs: {
format: 'YYYY-MM-DD HH:mm:ss', format: 'YYYY-MM-DD HH:mm:ss',
'value-format': 'YYYY-MM-DD HH:mm:ss', 'value-format': 'YYYY-MM-DD HH:mm:ss',
type: 'datetime' type: 'datetime',
} },
} }
default: default:
break break
@ -329,7 +320,7 @@ const validate_query = () => {
} }
if (msg.length > 0) { if (msg.length > 0) {
MsgWarning( MsgWarning(
`${t('chat.tip.inputParamMessage1')} ${msg.join('、')}${t('chat.tip.inputParamMessage2')}` `${t('chat.tip.inputParamMessage1')} ${msg.join('、')}${t('chat.tip.inputParamMessage2')}`,
) )
return Promise.reject(false) return Promise.reject(false)
} }

View File

@ -77,9 +77,17 @@
v-if="type !== 'log'" v-if="type !== 'log'"
> >
<template #operateBefore> <template #operateBefore>
<slot name="operateBefore"> <slot name="operateBefore"> </slot>
<span></span> </template>
</slot> <template #userInput>
<el-button
v-if="isUserInput || isAPIInput"
class="user-input-button mb-8"
@click="toggleUserInput"
>
<el-icon :size="16" class="mr-4"><EditPen /></el-icon>
{{ $t('chat.userInput') }}
</el-button>
</template> </template>
</ChatInputOperate> </ChatInputOperate>
@ -647,8 +655,8 @@ defineExpose({
.popperUserInput { .popperUserInput {
position: absolute; position: absolute;
z-index: 999; z-index: 999;
right: 50px; left: 0;
bottom: 0; bottom: 50px;
width: calc(100% - 50px); width: calc(100% - 50px);
max-width: 400px; max-width: 400px;
} }

View File

@ -550,4 +550,25 @@ export default {
]) ])
}, },
}, },
'app-chat': {
iconReader: () => {
return h('i', [
h(
'svg',
{
style: { height: '100%', width: '100%' },
viewBox: '0 0 1024 1024',
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg',
},
[
h('path', {
d: 'M512 64c247.424 0 448 200.576 448 448S759.424 960 512 960H106.666667a42.666667 42.666667 0 0 1-42.666667-42.666667V512C64 264.576 264.576 64 512 64z m-362.666667 810.666667H512A362.666667 362.666667 0 1 0 149.333333 512v362.666667z m170.666667-298.666667h213.333333a21.333333 21.333333 0 0 1 21.333334 21.333333v42.666667a21.333333 21.333333 0 0 1-21.333334 21.333333h-213.333333A21.333333 21.333333 0 0 1 298.666667 640v-42.666667a21.333333 21.333333 0 0 1 21.333333-21.333333z m0-170.666667h384a21.333333 21.333333 0 0 1 21.333333 21.333334v42.666666a21.333333 21.333333 0 0 1-21.333333 21.333334h-384A21.333333 21.333333 0 0 1 298.666667 469.333333v-42.666666a21.333333 21.333333 0 0 1 21.333333-21.333334z',
fill: 'currentColor',
}),
],
),
])
},
},
} }

View File

@ -7,10 +7,9 @@ interface ChatUser {
// 用户id // 用户id
id: string id: string
} }
interface Application {}
interface Chat { interface Chat {
chat_profile?: ChatProfile chat_profile?: ChatProfile
application?: Application application?: any
chatUserProfile?: ChatUserProfile chatUserProfile?: ChatUserProfile
token?: string token?: string
accessToken?: string accessToken?: string
@ -28,6 +27,7 @@ const useChatUserStore = defineStore('chat-user', {
getChatProfile() { getChatProfile() {
return ChatAPI.chatProfile(this.accessToken as string).then((ok) => { return ChatAPI.chatProfile(this.accessToken as string).then((ok) => {
this.chat_profile = ok.data this.chat_profile = ok.data
return this.chat_profile return this.chat_profile
}) })
}, },
@ -38,6 +38,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
this.application['custom_theme']['theme_color'] =
ok.data?.custom_theme.theme_color || '#3370FF'
}) })
}, },
isAuthentication() { isAuthentication() {

View File

@ -19,8 +19,7 @@ const useThemeStore = defineStore('theme', {
setTheme(data?: any) { setTheme(data?: any) {
const { changeTheme } = useElementPlusTheme(this.themeInfo?.theme || defalueColor) const { changeTheme } = useElementPlusTheme(this.themeInfo?.theme || defalueColor)
changeTheme(defalueColor) changeTheme(data?.['theme'] || defalueColor)
changeTheme(data?.['theme'])
this.themeInfo = cloneDeep(data) this.themeInfo = cloneDeep(data)
}, },

View File

@ -4,7 +4,6 @@ import type { User } from '@/api/type/user'
import UserApi from '@/api/user/user' import UserApi from '@/api/user/user'
import LoginApi from '@/api/user/login' import LoginApi from '@/api/user/login'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import ThemeApi from '@/api/system-settings/theme'
import { useLocalStorage } from '@vueuse/core' import { useLocalStorage } from '@vueuse/core'
// import { defaultPlatformSetting } from '@/utils/theme' // import { defaultPlatformSetting } from '@/utils/theme'
@ -141,6 +140,7 @@ const useUserStore = defineStore('user', {
theme.themeInfo = { theme.themeInfo = {
...defaultPlatformSetting, ...defaultPlatformSetting,
} }
theme.setTheme()
} }
resolve(ok) resolve(ok)
}) })

View File

@ -2,8 +2,8 @@
<el-row class="not-found-container"> <el-row class="not-found-container">
<el-col class="img" :xs="0" :sm="0" :md="12" :lg="12" :xl="12"> </el-col> <el-col class="img" :xs="0" :sm="0" :md="12" :lg="12" :xl="12"> </el-col>
<el-col class="message-container" :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> <el-col class="message-container" :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div class="title">{{ $t('views.common.notFound.title') }}</div> <div class="title">{{ $t('common.notFound.title') }}</div>
<div class="message">{{ $t('views.common.notFound.message') }}</div> <div class="message">{{ $t('common.notFound.message') }}</div>
<!-- TODO 暂时不处理 --> <!-- TODO 暂时不处理 -->
<!-- <div class="operate"><el-button type="primary" @click="router.push('/')">{{ $t('views.notFound.operate') }}</el-button></div> --> <!-- <div class="operate"><el-button type="primary" @click="router.push('/')">{{ $t('views.notFound.operate') }}</el-button></div> -->
</el-col> </el-col>

View File

@ -30,14 +30,5 @@ const is_auth = computed({
emit('update:modelValue', v) emit('update:modelValue', v)
}, },
}) })
const customStyle = computed(() => {
return {
background: props.application_profile?.custom_theme?.theme_color,
color: props.application_profile?.custom_theme?.header_font_color,
border: 'none',
...props.style,
}
})
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>

View File

@ -10,10 +10,16 @@
> >
<div class="flex h-full w-full"> <div class="flex h-full w-full">
<div class="chat-pc__left"> <div class="chat-pc__left">
<el-menu class="w-full h-full" :default-active="currentChatId" :collapse="isPcCollapse" collapse-transition popper-class="chat-pc-popper"> <el-menu
<div style="padding: 16px 18px 0 18px;"> class="w-full h-full"
:default-active="currentChatId"
:collapse="isPcCollapse"
collapse-transition
popper-class="chat-pc-popper"
>
<div style="padding: 16px 18px 0 18px">
<div class="flex align-center mb-16"> <div class="flex align-center mb-16">
<div class="flex"> <div class="flex mr-8">
<el-avatar <el-avatar
v-if="isAppIcon(applicationDetail?.icon)" v-if="isAppIcon(applicationDetail?.icon)"
shape="square" shape="square"
@ -26,7 +32,13 @@
</div> </div>
<h4 v-show="!isPcCollapse">{{ applicationDetail?.name }}</h4> <h4 v-show="!isPcCollapse">{{ applicationDetail?.name }}</h4>
</div> </div>
<el-button v-show="!isPcCollapse" class="add-button w-full primary" @click="newChat"> <el-button
type="primary"
plain
v-show="!isPcCollapse"
class="add-button w-full primary"
@click="newChat"
>
<AppIcon iconName="app-create-chat"></AppIcon> <AppIcon iconName="app-create-chat"></AppIcon>
<span class="ml-4">{{ $t('chat.createChat') }}</span> <span class="ml-4">{{ $t('chat.createChat') }}</span>
</el-button> </el-button>
@ -99,8 +111,15 @@
</el-icon> </el-icon>
</template> </template>
<el-menu-item-group v-loading="left_loading"> <el-menu-item-group v-loading="left_loading">
<template #title><span>{{ $t('chat.history') }}</span></template> <template #title
<el-menu-item v-for="row in chatLogData" :index="row.id" :key="row.id" @click="clickListHandle(row)"> ><span>{{ $t('chat.history') }}</span></template
>
<el-menu-item
v-for="row in chatLogData"
:index="row.id"
:key="row.id"
@click="clickListHandle(row)"
>
<div class="flex-between w-full lighter"> <div class="flex-between w-full lighter">
<span :title="row.abstract"> <span :title="row.abstract">
{{ row.abstract }} {{ row.abstract }}
@ -146,12 +165,14 @@
<el-avatar :size="32"> <el-avatar :size="32">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" /> <img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar> </el-avatar>
<span v-show="!isPcCollapse" class="ml-8 color-text-primary">{{ chatUser.chatUserProfile?.nick_name }}</span> <span v-show="!isPcCollapse" class="ml-8 color-text-primary">{{
chatUser.chatUserProfile?.nick_name
}}</span>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="avatar-dropdown"> <el-dropdown-menu class="avatar-dropdown">
<div class="flex align-center" style="padding: 8px 12px;"> <div class="flex align-center" style="padding: 8px 12px">
<div class="mr-8 flex align-center"> <div class="mr-8 flex align-center">
<el-avatar :size="40"> <el-avatar :size="40">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" /> <img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
@ -159,26 +180,41 @@
</div> </div>
<div> <div>
<h4 class="medium mb-4">{{ chatUser.chatUserProfile?.nick_name }}</h4> <h4 class="medium mb-4">{{ chatUser.chatUserProfile?.nick_name }}</h4>
<div class="color-secondary">{{ `${t('common.username')}: ${chatUser.chatUserProfile?.username}` }}</div> <div class="color-secondary">
{{ `${t('common.username')}: ${chatUser.chatUserProfile?.username}` }}
</div> </div>
</div> </div>
<el-dropdown-item v-if="chatUser.chatUserProfile?.source === 'LOCAL'" class="border-t" style="padding-top: 8px; padding-bottom: 8px;" @click="openResetPassword"> </div>
<el-dropdown-item
v-if="chatUser.chatUserProfile?.source === 'LOCAL'"
class="border-t"
style="padding-top: 8px; padding-bottom: 8px"
@click="openResetPassword"
>
<AppIcon iconName="app-export" /> <AppIcon iconName="app-export" />
{{ $t('views.login.resetPassword') }} {{ $t('views.login.resetPassword') }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item v-if="chatUser.chatUserProfile?.source === 'LOCAL'" class="border-t" style="padding-top: 8px; padding-bottom: 8px;" @click="logout"> <el-dropdown-item
v-if="chatUser.chatUserProfile?.source === 'LOCAL'"
class="border-t"
style="padding-top: 8px; padding-bottom: 8px"
@click="logout"
>
<AppIcon iconName="app-export" /> <AppIcon iconName="app-export" />
{{ $t('layout.logout') }} {{ $t('layout.logout') }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
</el-menu> </el-menu>
<el-button v-if="!common.isMobile()" class="pc-collapse" circle size="small" <el-button
@click="isPcCollapse = !isPcCollapse"> v-if="!common.isMobile()"
class="pc-collapse cursor"
circle
@click="isPcCollapse = !isPcCollapse"
>
<el-icon> <el-icon>
<component :is="isPcCollapse ? 'Fold' : 'Expand'" /> <component :is="isPcCollapse ? 'ArrowRightBold' : 'ArrowLeftBold'" />
</el-icon> </el-icon>
</el-button> </el-button>
</div> </div>
@ -237,14 +273,25 @@
</AiChat> </AiChat>
</div> </div>
</el-splitter-panel> </el-splitter-panel>
<el-splitter-panel class="execution-detail-panel" v-model:size="rightPanelSize" :resizable="false" collapsible> <el-splitter-panel
class="execution-detail-panel"
v-model:size="rightPanelSize"
:resizable="false"
collapsible
>
<div class="p-16 flex-between border-b"> <div class="p-16 flex-between border-b">
<h4 class="medium">{{ rightPanelTitle }}</h4> <h4 class="medium">{{ rightPanelTitle }}</h4>
<el-icon size="20" class="cursor" @click="closeExecutionDetail"><Close /></el-icon> <el-icon size="20" class="cursor" @click="closeExecutionDetail"><Close /></el-icon>
</div> </div>
<div class="execution-detail-content" v-loading="rightPanelLoading"> <div class="execution-detail-content" v-loading="rightPanelLoading">
<ParagraphSourceContent v-if="rightPanelType === 'knowledgeSource'" :detail="rightPanelDetail" /> <ParagraphSourceContent
<ExecutionDetailContent v-if="rightPanelType === 'executionDetail'" :detail="executionDetail" /> v-if="rightPanelType === 'knowledgeSource'"
:detail="rightPanelDetail"
/>
<ExecutionDetailContent
v-if="rightPanelType === 'executionDetail'"
:detail="executionDetail"
/>
</div> </div>
</el-splitter-panel> </el-splitter-panel>
</el-splitter> </el-splitter>
@ -257,7 +304,11 @@
</div> </div>
<EditTitleDialog ref="EditTitleDialogRef" @refresh="refreshFieldTitle" /> <EditTitleDialog ref="EditTitleDialogRef" @refresh="refreshFieldTitle" />
<ResetPassword ref="resetPasswordRef" emitConfirm @confirm="handleResetPassword"></ResetPassword> <ResetPassword
ref="resetPasswordRef"
emitConfirm
@confirm="handleResetPassword"
></ResetPassword>
</div> </div>
</template> </template>
@ -288,11 +339,14 @@ const EditTitleDialogRef = ref()
const isCollapse = ref(false) const isCollapse = ref(false)
const isPcCollapse = ref(false) const isPcCollapse = ref(false)
watch(() => common.device, () => { watch(
() => common.device,
() => {
if (common.isMobile()) { if (common.isMobile()) {
isPcCollapse.value = false isPcCollapse.value = false
} }
}) },
)
const logout = () => { const logout = () => {
chatUser.logout().then(() => { chatUser.logout().then(() => {
@ -617,13 +671,12 @@ function closeExecutionDetail() {
margin: 16px; margin: 16px;
box-sizing: border-box; box-sizing: border-box;
&:hover { &:hover {
background-color: #1F23291A; background-color: #1f23291a;
} }
} }
} }
&.el-menu--collapse { &.el-menu--collapse {
.el-menu-item, .el-menu-item,
.el-menu-tooltip__trigger, .el-menu-tooltip__trigger,
.el-sub-menu__title { .el-sub-menu__title {
@ -643,7 +696,7 @@ function closeExecutionDetail() {
.el-menu-item:hover .el-menu-tooltip__trigger, .el-menu-item:hover .el-menu-tooltip__trigger,
.el-sub-menu__title:hover { .el-sub-menu__title:hover {
background-color: #1F23291A; background-color: #1f23291a;
} }
.user-info { .user-info {
@ -663,8 +716,8 @@ function closeExecutionDetail() {
.pc-collapse { .pc-collapse {
position: absolute; position: absolute;
top: 20px; top: 20px;
right: -12px; right: -15px;
box-shadow: 0px 5px 10px 0px #1F23291A; box-shadow: 0px 5px 10px 0px #1f23291a;
} }
} }
@ -678,7 +731,8 @@ function closeExecutionDetail() {
height: calc(100vh - 85px); height: calc(100vh - 85px);
} }
.el-splitter-bar__collapse-icon, .el-splitter-bar__dragger { .el-splitter-bar__collapse-icon,
.el-splitter-bar__dragger {
display: none; display: none;
} }
.execution-detail-panel { .execution-detail-panel {
@ -741,10 +795,10 @@ function closeExecutionDetail() {
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
&:hover { &:hover {
background-color: #1F23291A; background-color: #1f23291a;
} }
&.is-active { &.is-active {
background-color: #3370FF1A; background-color: #3370ff1a;
} }
} }
} }

View File

@ -36,8 +36,8 @@ export default defineConfig((conf: any) => {
const ENV = loadEnv(mode, envDir) const ENV = loadEnv(mode, envDir)
const proxyConf: Record<string, string | ProxyOptions> = {} const proxyConf: Record<string, string | ProxyOptions> = {}
proxyConf['/admin/api'] = { proxyConf['/admin/api'] = {
//target: 'http://47.92.195.88:8080/', target: 'http://47.92.195.88:8080/',
target: 'http://127.0.0.1:8080', // target: 'http://127.0.0.1:8080',
changeOrigin: true, changeOrigin: true,
} }
proxyConf['/oss'] = { proxyConf['/oss'] = {
@ -46,6 +46,7 @@ export default defineConfig((conf: any) => {
rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'), rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),
} }
proxyConf['/chat/api'] = { proxyConf['/chat/api'] = {
// target: 'http://47.92.195.88:8080/',
target: 'http://127.0.0.1:8080', target: 'http://127.0.0.1:8080',
changeOrigin: true, changeOrigin: true,
} }