mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: Optimize the mobile voice interaction experience
* fix: Optimize small screen dialogue style * feat: Mobile voice conversation new UI * feat: Optimize the mobile voice interaction experience * feat: Optimize the mobile voice interaction experience
This commit is contained in:
parent
2faabbe392
commit
0eebbb094c
|
|
@ -0,0 +1,20 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no,
|
||||
viewport-fit=cover"
|
||||
/>
|
||||
<title>111</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; height: 100vh">
|
||||
<script
|
||||
async
|
||||
defer
|
||||
src="http://localhost:3000/api/application/embed?protocol=http&host=localhost:3000&token=7ca95a6b12571284">
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<svg width="89" height="22" viewBox="0 0 89 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_11132_142017)">
|
||||
<path d="M9.58875 8.33325H11.1126V14.0475H9.58875V8.33325Z" fill="#3370FF"/>
|
||||
<path d="M36.8547 8.33325H38.3785V14.0475H36.8547V8.33325Z" fill="#3370FF"/>
|
||||
<path d="M59.5767 8.33325H61.1005V14.0475H59.5767V8.33325Z" fill="#3370FF"/>
|
||||
<path d="M86.843 8.33325H88.3668V14.0475H86.843V8.33325Z" fill="#3370FF"/>
|
||||
<path d="M41.399 6.80957H42.9229V14.8096H41.399V6.80957Z" fill="#3370FF"/>
|
||||
<path d="M55.0322 6.04736H56.556V15.5712H55.0322V6.04736Z" fill="#3370FF"/>
|
||||
<path d="M45.9435 8.71411H47.4673V13.2855H45.9435V8.71411Z" fill="#3370FF"/>
|
||||
<path d="M50.4879 7.95239H52.0117V14.4286H50.4879V7.95239Z" fill="#3370FF"/>
|
||||
<path d="M5.04443 6.04736H6.56824V15.5712H5.04443V6.04736Z" fill="#3370FF"/>
|
||||
<path d="M0.5 8.71411H2.02381V13.2855H0.5V8.71411Z" fill="#3370FF"/>
|
||||
<path d="M14.1332 6.80957H15.657V15.1905H14.1332V6.80957Z" fill="#3370FF"/>
|
||||
<path d="M32.3103 6.80957H33.8341V15.1905H32.3103V6.80957Z" fill="#3370FF"/>
|
||||
<path d="M64.1211 6.80957H65.6449V15.5715H64.1211V6.80957Z" fill="#3370FF"/>
|
||||
<path d="M82.2986 6.80957H83.8224V15.1905H82.2986V6.80957Z" fill="#3370FF"/>
|
||||
<path d="M18.6776 6.04736H20.2014V15.9521H18.6776V6.04736Z" fill="#3370FF"/>
|
||||
<path d="M27.7664 5.6665H29.2902V16.7141H27.7664V5.6665Z" fill="#3370FF"/>
|
||||
<path d="M68.6654 5.28564H70.1892V16.7142H68.6654V5.28564Z" fill="#3370FF"/>
|
||||
<path d="M77.7543 5.28564H79.2781V16.7142H77.7543V5.28564Z" fill="#3370FF"/>
|
||||
<path d="M23.2219 2.6189H24.7457V19.3808H23.2219V2.6189Z" fill="#3370FF"/>
|
||||
<path d="M73.2098 3.3811H74.7336V18.6192H73.2098V3.3811Z" fill="#3370FF"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_11132_142017">
|
||||
<rect width="88" height="22" fill="white" transform="translate(0.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,29 @@
|
|||
<svg width="89" height="22" viewBox="0 0 89 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_11133_227282)">
|
||||
<path d="M9.58875 8.33325H11.1126V14.0475H9.58875V8.33325Z" fill="#8F959E"/>
|
||||
<path d="M36.8547 8.33325H38.3785V14.0475H36.8547V8.33325Z" fill="#8F959E"/>
|
||||
<path d="M59.5767 8.33325H61.1005V14.0475H59.5767V8.33325Z" fill="#8F959E"/>
|
||||
<path d="M86.843 8.33325H88.3668V14.0475H86.843V8.33325Z" fill="#8F959E"/>
|
||||
<path d="M41.399 6.80957H42.9229V14.8096H41.399V6.80957Z" fill="#8F959E"/>
|
||||
<path d="M55.0322 6.04736H56.556V15.5712H55.0322V6.04736Z" fill="#8F959E"/>
|
||||
<path d="M45.9435 8.71411H47.4673V13.2855H45.9435V8.71411Z" fill="#8F959E"/>
|
||||
<path d="M50.4879 7.95239H52.0117V14.4286H50.4879V7.95239Z" fill="#8F959E"/>
|
||||
<path d="M5.04443 6.04736H6.56824V15.5712H5.04443V6.04736Z" fill="#8F959E"/>
|
||||
<path d="M0.5 8.71411H2.02381V13.2855H0.5V8.71411Z" fill="#8F959E"/>
|
||||
<path d="M14.1332 6.80957H15.657V15.1905H14.1332V6.80957Z" fill="#8F959E"/>
|
||||
<path d="M32.3103 6.80957H33.8341V15.1905H32.3103V6.80957Z" fill="#8F959E"/>
|
||||
<path d="M64.1211 6.80957H65.6449V15.5715H64.1211V6.80957Z" fill="#8F959E"/>
|
||||
<path d="M82.2986 6.80957H83.8224V15.1905H82.2986V6.80957Z" fill="#8F959E"/>
|
||||
<path d="M18.6776 6.04736H20.2014V15.9521H18.6776V6.04736Z" fill="#8F959E"/>
|
||||
<path d="M27.7664 5.6665H29.2902V16.7141H27.7664V5.6665Z" fill="#8F959E"/>
|
||||
<path d="M68.6654 5.28564H70.1892V16.7142H68.6654V5.28564Z" fill="#8F959E"/>
|
||||
<path d="M77.7543 5.28564H79.2781V16.7142H77.7543V5.28564Z" fill="#8F959E"/>
|
||||
<path d="M23.2219 2.6189H24.7457V19.3808H23.2219V2.6189Z" fill="#8F959E"/>
|
||||
<path d="M73.2098 3.3811H74.7336V18.6192H73.2098V3.3811Z" fill="#8F959E"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_11133_227282">
|
||||
<rect width="88" height="22" fill="white" transform="translate(0.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -112,7 +112,7 @@ const uniqueParagraphList = computed(() => {
|
|||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media only screen and (max-width: 430px) {
|
||||
@media only screen and (max-width: 420px) {
|
||||
.chat-knowledge-source {
|
||||
.execution-details {
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
<div class="item-content mb-16 lighter">
|
||||
<template v-for="(answer_text, index) in answer_text_list" :key="index">
|
||||
<div class="avatar">
|
||||
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
|
||||
<LogoIcon v-else height="32px" width="32px" />
|
||||
<img v-if="application.avatar" :src="application.avatar" height="28px" width="28px" />
|
||||
<LogoIcon v-else height="28px" width="28px" />
|
||||
</div>
|
||||
<div class="content" @mouseup="openControl">
|
||||
<el-card shadow="always" class="mb-8 border-r-8" style="--el-card-padding: 6px 16px">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
<template>
|
||||
<div class="touch-chat w-full mr-8">
|
||||
<el-button
|
||||
text
|
||||
bg
|
||||
class="microphone-button w-full mt-8 ml-8 mb-8"
|
||||
style="font-size: 1rem; padding: 1.2rem 0 !important; background-color: #eff0f1"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
>
|
||||
按住说话
|
||||
</el-button>
|
||||
<!-- 使用 custom-class 自定义样式 -->
|
||||
<transition name="el-fade-in-linear">
|
||||
<el-card class="custom-speech-card" :class="isTouching ? '' : 'active'" v-if="dialogVisible">
|
||||
<p>
|
||||
<el-text type="info" v-if="isTouching"
|
||||
>00:{{ props.time < 10 ? `0${props.time}` : props.time }}</el-text
|
||||
>
|
||||
<span class="lighter" v-else>
|
||||
{{ message }}
|
||||
</span>
|
||||
</p>
|
||||
<div class="close">
|
||||
<el-icon><Close /></el-icon>
|
||||
</div>
|
||||
<p class="lighter" :style="{ visibility: isTouching ? 'visible' : 'hidden' }">
|
||||
{{ message }}
|
||||
</p>
|
||||
<div class="speech-img flex-center border-r-4 mt-16">
|
||||
<img v-if="isTouching" src="@/assets/acoustic-color.svg" alt="" />
|
||||
<img v-else src="@/assets/acoustic.svg" alt="" />
|
||||
</div>
|
||||
</el-card>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { el } from 'element-plus/es/locale'
|
||||
import { ref, watch } from 'vue'
|
||||
const props = defineProps({
|
||||
time: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
start: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['TouchStart', 'TouchEnd'])
|
||||
// 移动端语音
|
||||
const startY = ref(0)
|
||||
const isTouching = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const message = ref('按住说话')
|
||||
|
||||
watch(
|
||||
() => props.time,
|
||||
(val) => {
|
||||
if (val && val === 60) {
|
||||
dialogVisible.value = false
|
||||
emit('TouchEnd', isTouching.value)
|
||||
isTouching.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.start,
|
||||
(val) => {
|
||||
if (val) {
|
||||
isTouching.value = true
|
||||
dialogVisible.value = true
|
||||
message.value = '松开发送,上滑取消'
|
||||
} else {
|
||||
dialogVisible.value = false
|
||||
isTouching.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function onTouchStart(event: any) {
|
||||
emit('TouchStart')
|
||||
startY.value = event.touches[0].clientY
|
||||
// 阻止默认滚动行为
|
||||
event.preventDefault()
|
||||
}
|
||||
function onTouchMove(event: any) {
|
||||
if (!isTouching.value) return
|
||||
// 阻止默认滚动行为
|
||||
event.preventDefault()
|
||||
const currentY = event.touches[0].clientY
|
||||
const deltaY = currentY - startY.value
|
||||
// 判断是否上滑
|
||||
if (deltaY < -50) {
|
||||
// -50 是一个阈值,可以根据需要调整
|
||||
message.value = '松开取消发送'
|
||||
isTouching.value = false
|
||||
}
|
||||
}
|
||||
function onTouchEnd() {
|
||||
emit('TouchEnd', isTouching.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-speech-card {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
left: 50%; /* 水平居中 */
|
||||
transform: translateX(-50%);
|
||||
width: 92%;
|
||||
background: #ffffff;
|
||||
border: 1px solid #ffffff;
|
||||
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
|
||||
z-index: 999;
|
||||
text-align: center;
|
||||
color: var(--app-text-color-secondary);
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
.close {
|
||||
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.1);
|
||||
border: 1px solid rgba(222, 224, 227, 1);
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 100px;
|
||||
display: inline-block;
|
||||
width: 43px;
|
||||
height: 43px;
|
||||
line-height: 50px;
|
||||
font-size: 1.8rem;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.speech-img {
|
||||
text-align: center;
|
||||
background: #ebf1ff;
|
||||
padding: 8px;
|
||||
img {
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
.close {
|
||||
background: #f54a45;
|
||||
color: #ffffff;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
line-height: 57px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
.speech-img {
|
||||
background: #eff0f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="ai-chat__operate p-16-24">
|
||||
<div class="ai-chat__operate p-16">
|
||||
<slot name="operateBefore" />
|
||||
<div class="operate-textarea">
|
||||
<el-scrollbar max-height="136">
|
||||
|
|
@ -114,7 +114,15 @@
|
|||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="flex">
|
||||
<TouchChat
|
||||
v-if="isMicrophone"
|
||||
@TouchStart="startRecording"
|
||||
@TouchEnd="TouchEnd"
|
||||
:time="recorderTime"
|
||||
:start="!mediaRecorderStatus"
|
||||
/>
|
||||
<el-input
|
||||
v-else
|
||||
ref="quickInputRef"
|
||||
v-model="inputValue"
|
||||
:placeholder="
|
||||
|
|
@ -131,61 +139,82 @@
|
|||
/>
|
||||
|
||||
<div class="operate flex align-center">
|
||||
<span v-if="props.applicationDetails.file_upload_enable" class="flex align-center">
|
||||
<el-upload
|
||||
action="#"
|
||||
multiple
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:accept="getAcceptList()"
|
||||
:on-change="(file: any, fileList: any) => uploadFile(file, fileList)"
|
||||
>
|
||||
<el-tooltip effect="dark" placement="top" popper-class="upload-tooltip-width">
|
||||
<template #content>
|
||||
<div class="break-all pre-wrap">
|
||||
{{ $t('chat.uploadFile.label') }}:{{ $t('chat.uploadFile.most')
|
||||
}}{{ props.applicationDetails.file_upload_setting.maxFiles
|
||||
}}{{ $t('chat.uploadFile.limit') }}
|
||||
{{ props.applicationDetails.file_upload_setting.fileLimit }}MB<br />{{
|
||||
$t('chat.uploadFile.fileType')
|
||||
}}:{{ getAcceptList().replace(/\./g, '').replace(/,/g, '、').toUpperCase() }}
|
||||
</div>
|
||||
</template>
|
||||
<el-button text :disabled="checkMaxFilesLimit()" class="mt-4">
|
||||
<el-icon><Paperclip /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</el-upload>
|
||||
<el-divider direction="vertical" />
|
||||
</span>
|
||||
<span v-if="props.applicationDetails.stt_model_enable" class="flex align-center">
|
||||
<el-button text @click="startRecording" v-if="mediaRecorderStatus">
|
||||
<el-icon>
|
||||
<Microphone />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
|
||||
<div v-else class="operate flex align-center">
|
||||
<el-text type="info"
|
||||
>00:{{ recorderTime < 10 ? `0${recorderTime}` : recorderTime }}</el-text
|
||||
>
|
||||
<el-button text type="primary" @click="stopRecording" :loading="recorderLoading">
|
||||
<AppIcon iconName="app-video-stop"></AppIcon>
|
||||
<template v-if="props.applicationDetails.stt_model_enable">
|
||||
<span v-if="mode === 'mobile'">
|
||||
<el-button text @click="isMicrophone = !isMicrophone">
|
||||
<AppIcon v-if="isMicrophone" iconName="app-keyboard"></AppIcon>
|
||||
<el-icon v-else>
|
||||
<Microphone />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
<span class="flex align-center" v-else>
|
||||
<el-button text @click="startRecording" v-if="mediaRecorderStatus">
|
||||
<el-icon>
|
||||
<Microphone />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<el-divider v-if="!startRecorderTime && !recorderLoading" direction="vertical" />
|
||||
</span>
|
||||
|
||||
<el-button
|
||||
v-if="!startRecorderTime && !recorderLoading"
|
||||
text
|
||||
class="sent-button"
|
||||
:disabled="isDisabledChat || loading"
|
||||
@click="sendChatHandle"
|
||||
>
|
||||
<img v-show="isDisabledChat || loading" src="@/assets/icon_send.svg" alt="" />
|
||||
<SendIcon v-show="!isDisabledChat && !loading" />
|
||||
</el-button>
|
||||
<div v-else class="operate flex align-center">
|
||||
<el-text type="info"
|
||||
>00:{{ recorderTime < 10 ? `0${recorderTime}` : recorderTime }}</el-text
|
||||
>
|
||||
<el-button text type="primary" @click="stopRecording" :loading="recorderLoading">
|
||||
<AppIcon iconName="app-video-stop"></AppIcon>
|
||||
</el-button>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-if="!startRecorderTime && !recorderLoading">
|
||||
<span v-if="props.applicationDetails.file_upload_enable" class="flex align-center ml-4">
|
||||
<el-upload
|
||||
action="#"
|
||||
multiple
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:accept="getAcceptList()"
|
||||
:on-change="(file: any, fileList: any) => uploadFile(file, fileList)"
|
||||
>
|
||||
<el-tooltip
|
||||
:disabled="mode === 'mobile'"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
popper-class="upload-tooltip-width"
|
||||
>
|
||||
<template #content>
|
||||
<div class="break-all pre-wrap">
|
||||
{{ $t('chat.uploadFile.label') }}:{{ $t('chat.uploadFile.most')
|
||||
}}{{ props.applicationDetails.file_upload_setting.maxFiles
|
||||
}}{{ $t('chat.uploadFile.limit') }}
|
||||
{{ props.applicationDetails.file_upload_setting.fileLimit }}MB<br />{{
|
||||
$t('chat.uploadFile.fileType')
|
||||
}}:{{ getAcceptList().replace(/\./g, '').replace(/,/g, '、').toUpperCase() }}
|
||||
</div>
|
||||
</template>
|
||||
<el-button text :disabled="checkMaxFilesLimit()" class="mt-4">
|
||||
<el-icon><Paperclip /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</el-upload>
|
||||
</span>
|
||||
<el-divider
|
||||
direction="vertical"
|
||||
v-if="
|
||||
props.applicationDetails.file_upload_enable ||
|
||||
props.applicationDetails.stt_model_enable
|
||||
"
|
||||
/>
|
||||
<el-button
|
||||
text
|
||||
class="sent-button"
|
||||
:disabled="isDisabledChat || loading"
|
||||
@click="sendChatHandle"
|
||||
>
|
||||
<img v-show="isDisabledChat || loading" src="@/assets/icon_send.svg" alt="" />
|
||||
<SendIcon v-show="!isDisabledChat && !loading" />
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -201,6 +230,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue'
|
||||
import Recorder from 'recorder-core'
|
||||
import TouchChat from './TouchChat.vue'
|
||||
import applicationApi from '@/api/application'
|
||||
import { MsgAlert } from '@/utils/message'
|
||||
import { type chatType } from '@/api/type/application'
|
||||
|
|
@ -381,6 +411,8 @@ const uploadFile = async (file: any, fileList: any) => {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
const intervalId = ref<number | null>(null)
|
||||
const recorderTime = ref(0)
|
||||
const startRecorderTime = ref(false)
|
||||
const recorderLoading = ref(false)
|
||||
|
|
@ -397,14 +429,24 @@ const mediaRecorder = ref<any>(null)
|
|||
const isDisabledChat = computed(
|
||||
() => !(inputValue.value.trim() && (props.appId || props.applicationDetails?.name))
|
||||
)
|
||||
// 移动端语音
|
||||
const isMicrophone = ref(false)
|
||||
|
||||
const TouchEnd = (bool: Boolean) => {
|
||||
if (bool) {
|
||||
stopRecording()
|
||||
} else {
|
||||
stopTimer()
|
||||
mediaRecorder.value.close()
|
||||
mediaRecorder.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 开始录音
|
||||
const startRecording = async () => {
|
||||
try {
|
||||
// 取消录音控制台日志
|
||||
Recorder.CLog = function () {}
|
||||
mediaRecorderStatus.value = false
|
||||
handleTimeChange()
|
||||
mediaRecorder.value = new Recorder({
|
||||
type: 'mp3',
|
||||
bitRate: 128,
|
||||
|
|
@ -414,8 +456,12 @@ const startRecording = async () => {
|
|||
mediaRecorder.value.open(
|
||||
() => {
|
||||
mediaRecorder.value.start()
|
||||
mediaRecorderStatus.value = false
|
||||
handleTimeChange()
|
||||
},
|
||||
(err: any) => {
|
||||
stopTimer()
|
||||
mediaRecorder.value.close()
|
||||
MsgAlert(
|
||||
t('common.tip'),
|
||||
`${t('chat.tip.recorderTip')}
|
||||
|
|
@ -439,6 +485,8 @@ const startRecording = async () => {
|
|||
customClass: 'record-tip-confirm'
|
||||
}
|
||||
)
|
||||
mediaRecorder.value.close()
|
||||
stopTimer()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -479,8 +527,10 @@ const uploadRecording = async (audioBlob: Blob) => {
|
|||
// 自动发送
|
||||
if (props.applicationDetails.stt_autosend) {
|
||||
nextTick(() => {
|
||||
autoSendMessage()
|
||||
autoSendMessage()
|
||||
})
|
||||
} else {
|
||||
isMicrophone.value = false
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -492,22 +542,36 @@ const uploadRecording = async (audioBlob: Blob) => {
|
|||
console.error(`${t('chat.uploadFile.errorMessage')}:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTimeChange = () => {
|
||||
startRecorderTime.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
if (recorderTime.value === 60) {
|
||||
recorderTime.value = 0
|
||||
stopRecording()
|
||||
startRecorderTime.value = false
|
||||
}
|
||||
recorderTime.value = 0
|
||||
intervalId.value = setInterval(() => {
|
||||
if (!startRecorderTime.value) {
|
||||
clearInterval(intervalId.value!)
|
||||
intervalId.value = null
|
||||
return
|
||||
}
|
||||
|
||||
recorderTime.value++
|
||||
handleTimeChange()
|
||||
|
||||
if (recorderTime.value === 60) {
|
||||
stopRecording()
|
||||
clearInterval(intervalId.value!)
|
||||
intervalId.value = null
|
||||
startRecorderTime.value = false
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
// 停止计时的函数
|
||||
const stopTimer = () => {
|
||||
if (intervalId.value !== null) {
|
||||
clearInterval(intervalId.value)
|
||||
intervalId.value = null
|
||||
startRecorderTime.value = false
|
||||
mediaRecorderStatus.value = true
|
||||
}
|
||||
}
|
||||
|
||||
function autoSendMessage() {
|
||||
props.sendMessage(inputValue.value, {
|
||||
|
|
@ -604,22 +668,98 @@ onMounted(() => {
|
|||
}, 1800)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
@import '../../index.scss';
|
||||
<style lang="scss" scoped>
|
||||
.ai-chat {
|
||||
&__operate {
|
||||
background: #f3f7f9;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 10;
|
||||
|
||||
.file {
|
||||
position: relative;
|
||||
overflow: inherit;
|
||||
&:before {
|
||||
background: linear-gradient(0deg, #f3f7f9 0%, rgba(243, 247, 249, 0) 100%);
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: -16px;
|
||||
left: 0;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
top: -5px;
|
||||
z-index: 1;
|
||||
:deep(.operate-textarea) {
|
||||
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:has(.el-textarea__inner:focus) {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: 8px !important;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding: 13px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.operate {
|
||||
padding: 6px 10px;
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.sent-button {
|
||||
max-height: none;
|
||||
.el-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-loading-spinner {
|
||||
margin-top: -15px;
|
||||
|
||||
.circular {
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.file {
|
||||
position: relative;
|
||||
overflow: inherit;
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
top: -5px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-tooltip-width {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-tooltip-width {
|
||||
width: 300px;
|
||||
@media only screen and (max-width: 768px) {
|
||||
.ai-chat {
|
||||
height: calc(100% - 100px);
|
||||
&__operate {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
font-size: 1rem;
|
||||
.el-icon {
|
||||
font-size: 1.4rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chat-pc {
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -307,7 +307,9 @@ onMounted(() => {
|
|||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@media only screen and (max-width: 430px) {
|
||||
|
||||
@media only screen and (max-width: 420px) {
|
||||
|
||||
.chat-operation-button {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<template>
|
||||
<div class="chat-operation-button">
|
||||
|
||||
<div class="operation-button-container">
|
||||
|
||||
<LogOperationButton
|
||||
v-if="type === 'log'"
|
||||
v-bind:data="chatRecord"
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
<!-- 开场白组件 -->
|
||||
<div class="item-content mb-16">
|
||||
<div class="avatar" v-if="prologue">
|
||||
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
|
||||
<LogoIcon v-else height="32px" width="32px" />
|
||||
<img v-if="application.avatar" :src="application.avatar" height="28px" width="28px" />
|
||||
<LogoIcon v-else height="28px" width="28px" />
|
||||
</div>
|
||||
<div class="content" v-if="prologue">
|
||||
<el-card shadow="always" class="border-r-8" style="--el-card-padding: 10px 16px 12px">
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
:src="application.user_avatar"
|
||||
alt=""
|
||||
fit="cover"
|
||||
style="width: 32px; height: 32px; display: block"
|
||||
style="width: 28px; height: 28px; display: block"
|
||||
/>
|
||||
<AppAvatar v-else>
|
||||
<img src="@/assets/user-icon.svg" style="width: 50%" alt="" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.ai-chat {
|
||||
--padding-left: 40px;
|
||||
--padding-left: 36px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -24,66 +24,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
&__operate {
|
||||
background: #f3f7f9;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 10;
|
||||
|
||||
&:before {
|
||||
background: linear-gradient(0deg, #f3f7f9 0%, rgba(243, 247, 249, 0) 100%);
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: -16px;
|
||||
left: 0;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.operate-textarea {
|
||||
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:has(.el-textarea__inner:focus) {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: 8px !important;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding: 12px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.operate {
|
||||
padding: 6px 10px;
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.sent-button {
|
||||
max-height: none;
|
||||
.el-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-loading-spinner {
|
||||
margin-top: -15px;
|
||||
|
||||
.circular {
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-width {
|
||||
|
|
@ -96,3 +36,14 @@
|
|||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.ai-chat {
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
}
|
||||
.chat-mobile {
|
||||
.el-button.is-text:not(.is-disabled):hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
<template v-if="!isUserInput || !firsUserInput || type === 'log'">
|
||||
<el-scrollbar ref="scrollDiv" @scroll="handleScrollTop">
|
||||
<div ref="dialogScrollbar" class="ai-chat__content p-24">
|
||||
<div ref="dialogScrollbar" class="ai-chat__content p-16">
|
||||
<PrologueContent
|
||||
:type="type"
|
||||
:application="applicationDetails"
|
||||
|
|
@ -514,7 +514,7 @@ defineExpose({
|
|||
setScrollBottom
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
<style lang="scss">
|
||||
@import './index.scss';
|
||||
.firstUserInput {
|
||||
height: 100%;
|
||||
|
|
@ -526,7 +526,7 @@ defineExpose({
|
|||
position: absolute;
|
||||
z-index: 999;
|
||||
right: 50px;
|
||||
bottom: 80px;
|
||||
bottom: 0;
|
||||
width: calc(100% - 50px);
|
||||
max-width: 400px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -296,4 +296,4 @@ const click = () => {
|
|||
dynamicsFormRef.value?.validate()
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -219,4 +219,4 @@ defineExpose({
|
|||
ruleFormRef
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ defineProps<{
|
|||
tooltip: string
|
||||
}>()
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.aiMode-param-dialog {
|
||||
padding: 8px 8px 24px 8px;
|
||||
|
||||
|
|
|
|||
|
|
@ -187,4 +187,4 @@ function getModel() {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -1415,5 +1415,30 @@ export const iconMap: any = {
|
|||
)
|
||||
])
|
||||
}
|
||||
},
|
||||
'app-keyboard': {
|
||||
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: 'M373.333333 352a53.333333 53.333333 0 1 1-106.666666 0 53.333333 53.333333 0 0 1 106.666666 0zM320 576a53.333333 53.333333 0 1 0 0-106.666667 53.333333 53.333333 0 0 0 0 106.666667zM565.333333 352a53.333333 53.333333 0 1 1-106.666666 0 53.333333 53.333333 0 0 1 106.666666 0zM512 576a53.333333 53.333333 0 1 0 0-106.666667 53.333333 53.333333 0 0 0 0 106.666667zM757.333333 352a53.333333 53.333333 0 1 1-106.666666 0 53.333333 53.333333 0 0 1 106.666666 0zM704 576a53.333333 53.333333 0 1 0 0-106.666667 53.333333 53.333333 0 0 0 0 106.666667zM362.666667 661.333333a42.666667 42.666667 0 1 0 0 85.333334h298.666666a42.666667 42.666667 0 1 0 0-85.333334h-298.666666z',
|
||||
fill: 'currentColor'
|
||||
}),
|
||||
h('path', {
|
||||
d: 'M512 42.666667C252.8 42.666667 42.666667 252.8 42.666667 512s210.133333 469.333333 469.333333 469.333333 469.333333-210.133333 469.333333-469.333333S771.2 42.666667 512 42.666667zM128 512a384 384 0 1 1 768 0 384 384 0 0 1-768 0z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const showBack = computed(() => {
|
|||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.content-container {
|
||||
transition: 0.3s;
|
||||
padding: 0 var(--app-view-padding) var(--app-view-padding);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ defineProps({
|
|||
subTitle: String
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.login-form-container {
|
||||
width: 480px;
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ const loginImage = computed(() => {
|
|||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.login-warp {
|
||||
height: 100vh;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { nextTick, onBeforeMount, onMounted, onBeforeUnmount } from 'vue'
|
|||
import { useRoute } from 'vue-router'
|
||||
import useStore from '@/stores'
|
||||
import { DeviceType } from '@/enums/common'
|
||||
/** 参考 Bootstrap 的响应式设计 WIDTH = 600 */
|
||||
const WIDTH = 600
|
||||
/** 参考 Bootstrap 的响应式设计 WIDTH = 768 */
|
||||
const WIDTH = 768
|
||||
|
||||
/** 根据大小变化重新布局 */
|
||||
export default () => {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
html {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
|
|||
|
|
@ -140,4 +140,4 @@ function refresh() {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -99,4 +99,4 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -139,4 +139,4 @@ function submit() {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ src="${window.location.origin}/api/application/embed?protocol=${window.location.
|
|||
<\/script>
|
||||
`
|
||||
source3.value = `<iframe
|
||||
src="${application.location + val + urlParams1.value}&mode=mobile"
|
||||
src="${application.location + val + urlParams1.value}?mode=mobile"
|
||||
style="width: 100%; height: 100%;"
|
||||
frameborder="0"
|
||||
allow="microphone">
|
||||
|
|
@ -144,7 +144,7 @@ allow="microphone">
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.embed-dialog {
|
||||
.title {
|
||||
color: var(--app-text-color) !important;
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ function firstGeneration() {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.authentication-append-input {
|
||||
.el-input-group__append {
|
||||
padding: 0 !important;
|
||||
|
|
|
|||
|
|
@ -110,4 +110,4 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.setting-preview {
|
||||
background: #f5f6f7;
|
||||
height: 570px;
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@
|
|||
{{ $t('views.application.applicationForm.title.appTest') }}
|
||||
</h4>
|
||||
<div class="dialog-bg">
|
||||
<div class="flex align-center p-24">
|
||||
<div class="flex align-center p-16 mb-8">
|
||||
<div
|
||||
class="edit-avatar mr-12"
|
||||
@mouseenter="showEditIcon = true"
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ const refresh = () => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.addDataset-dialog {
|
||||
padding: 0;
|
||||
.el-dialog__header {
|
||||
|
|
|
|||
|
|
@ -175,4 +175,4 @@ const submitHandle = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ function selectedType(type: string) {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.radio-card {
|
||||
line-height: 22px;
|
||||
&.active {
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ function changeHandle(val: string) {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.param-dialog {
|
||||
padding: 8px 8px 24px 8px;
|
||||
.el-dialog__header {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const submit = () => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.param-dialog {
|
||||
padding: 8px 8px 24px 8px;
|
||||
|
||||
|
|
|
|||
|
|
@ -330,8 +330,8 @@ onMounted(() => {
|
|||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
&__main {
|
||||
padding-top: calc(var(--app-header-height) + 24px);
|
||||
height: calc(100vh - var(--app-header-height) - 24px);
|
||||
padding-top: calc(var(--app-header-height) + 16px);
|
||||
height: calc(100vh - var(--app-header-height) - 16px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.new-chat-button {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div
|
||||
class="chat-embed layout-bg"
|
||||
class="chat-mobile layout-bg"
|
||||
v-loading="loading"
|
||||
:style="{
|
||||
'--el-color-primary': applicationDetail?.custom_theme?.theme_color,
|
||||
|
|
@ -310,7 +310,7 @@ onMounted(() => {
|
|||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.chat-embed {
|
||||
.chat-mobile {
|
||||
overflow: hidden;
|
||||
&__header {
|
||||
background: var(--app-header-bg-color);
|
||||
|
|
@ -325,16 +325,17 @@ onMounted(() => {
|
|||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
&__main {
|
||||
padding-top: calc(var(--app-header-height) + 24px);
|
||||
height: calc(100vh - var(--app-header-height) - 24px);
|
||||
padding-top: calc(var(--app-header-height) + 16px);
|
||||
height: calc(100vh - var(--app-header-height) - 16px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.new-chat-button {
|
||||
z-index: 11;
|
||||
font-size: 1rem;
|
||||
}
|
||||
// 历史对话弹出层
|
||||
.chat-popover {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: var(--app-header-height);
|
||||
background: #ffffff;
|
||||
padding-bottom: 24px;
|
||||
|
|
@ -342,7 +343,7 @@ onMounted(() => {
|
|||
}
|
||||
.chat-popover-button {
|
||||
z-index: 2009;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
font-size: 22px;
|
||||
|
|
@ -386,11 +387,11 @@ onMounted(() => {
|
|||
top: 50%;
|
||||
}
|
||||
}
|
||||
.AiChat-embed {
|
||||
.ai-chat__operate {
|
||||
padding-top: 12px;
|
||||
}
|
||||
}
|
||||
// .AiChat-embed {
|
||||
// .ai-chat__operate {
|
||||
// padding-top: 12px;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -326,4 +326,4 @@ function radioChange() {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -132,4 +132,4 @@ const submitHandle = async () => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ const refresh = () => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.select-dataset-dialog {
|
||||
padding: 0;
|
||||
.el-dialog__header {
|
||||
|
|
|
|||
|
|
@ -128,4 +128,4 @@ function submit() {
|
|||
|
||||
defineExpose({open})
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -295,4 +295,4 @@ const submitForm = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ const submit = async (formEl: FormInstance) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.edit-mark-dialog {
|
||||
.el-dialog__header.show-close {
|
||||
padding-right: 15px;
|
||||
|
|
|
|||
|
|
@ -137,4 +137,4 @@ const handleTimeChange = () => {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ onMounted(() => {
|
|||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.login-gradient-divider {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -223,4 +223,4 @@ const handleTimeChange = () => {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -132,4 +132,4 @@ const resetPassword = () => {
|
|||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -150,4 +150,4 @@ const handleDebounceClick = debounce(() => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -167,4 +167,4 @@ const submitForm = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ const open = (problemId: any) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.paragraph-card {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ onMounted(() => {})
|
|||
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.member-dialog {
|
||||
.el-dialog__header {
|
||||
padding-bottom: 19px;
|
||||
|
|
|
|||
|
|
@ -174,4 +174,4 @@ function checkedOperateChange(Name: string | number, row: any, e: boolean) {
|
|||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -186,4 +186,4 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -130,4 +130,4 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ const submit = () => {
|
|||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
<style lang="scss" scoped>
|
||||
.param-dialog {
|
||||
padding: 8px 8px 24px 8px;
|
||||
.el-dialog__header {
|
||||
|
|
|
|||
Loading…
Reference in New Issue