|
|
@@ -23,33 +23,19 @@
|
|
|
<el-form label-position="top" size="default">
|
|
|
<el-form-item label="语言">
|
|
|
<el-select v-model="config.language" style="width: 100%">
|
|
|
- <el-option label="中文" value="中文" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="TTS">
|
|
|
- <el-select v-model="config.tts" style="width: 100%">
|
|
|
- <el-option label="Minimax" value="Minimax" />
|
|
|
+ <el-option
|
|
|
+ v-for="item in languageOptions"
|
|
|
+ :key="item.dictValue"
|
|
|
+ :label="item.dictLabel"
|
|
|
+ :value="item.dictValue"
|
|
|
+ />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="背景音">
|
|
|
- <el-select v-model="config.bgSound" style="width: 100%">
|
|
|
- <el-option label="客服中心" value="客服中心" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="断句模式">
|
|
|
- <el-radio-group v-model="config.punctuation" style="width: 100%">
|
|
|
- <el-radio-button value="semantic">语义断句</el-radio-button>
|
|
|
- <el-radio-button value="normal">普通断句</el-radio-button>
|
|
|
- </el-radio-group>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="打断模式">
|
|
|
- <el-radio-group v-model="config.interrupt" style="width: 100%">
|
|
|
- <el-radio-button value="smart">智能打断</el-radio-button>
|
|
|
- <el-radio-button value="manual">手动打断</el-radio-button>
|
|
|
+ <el-radio-group v-model="config.bgSoundEnabled">
|
|
|
+ <el-radio-button :value="true">开启</el-radio-button>
|
|
|
+ <el-radio-button :value="false">关闭</el-radio-button>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
@@ -66,31 +52,45 @@
|
|
|
</div>
|
|
|
<div v-if="otherExpanded" class="collapse-content">
|
|
|
<el-form label-position="top" size="default">
|
|
|
- <el-form-item label="打断时长">
|
|
|
- <div class="slider-value">{{ config.interruptTime }}ms</div>
|
|
|
+ <el-form-item label="语速" class="compact-slider">
|
|
|
+ <div class="slider-value">{{ config.speed }}</div>
|
|
|
+ <el-slider
|
|
|
+ v-model="config.speed"
|
|
|
+ :min="0"
|
|
|
+ :max="100"
|
|
|
+ :step="1"
|
|
|
+ />
|
|
|
+ <div class="slider-labels">
|
|
|
+ <span>慢</span>
|
|
|
+ <span>快</span>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="音调" class="compact-slider">
|
|
|
+ <div class="slider-value">{{ config.pitch }}</div>
|
|
|
<el-slider
|
|
|
- v-model="config.interruptTime"
|
|
|
- :min="100"
|
|
|
- :max="500"
|
|
|
- :step="10"
|
|
|
+ v-model="config.pitch"
|
|
|
+ :min="0"
|
|
|
+ :max="100"
|
|
|
+ :step="1"
|
|
|
/>
|
|
|
<div class="slider-labels">
|
|
|
- <span>更灵敏</span>
|
|
|
- <span>更稳定</span>
|
|
|
+ <span>低</span>
|
|
|
+ <span>高</span>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="VAD 时长">
|
|
|
- <div class="slider-value">{{ config.vadTime }}ms</div>
|
|
|
+ <el-form-item label="音量" class="compact-slider">
|
|
|
+ <div class="slider-value">{{ config.volume }}</div>
|
|
|
<el-slider
|
|
|
- v-model="config.vadTime"
|
|
|
- :min="100"
|
|
|
- :max="800"
|
|
|
- :step="10"
|
|
|
+ v-model="config.volume"
|
|
|
+ :min="0"
|
|
|
+ :max="100"
|
|
|
+ :step="1"
|
|
|
/>
|
|
|
<div class="slider-labels">
|
|
|
- <span>更快</span>
|
|
|
- <span>更慢</span>
|
|
|
+ <span>小</span>
|
|
|
+ <span>大</span>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
@@ -108,7 +108,8 @@
|
|
|
:class="{ selected: selectedAgent === agent.id }"
|
|
|
@click="selectedAgent = agent.id"
|
|
|
>
|
|
|
- <div class="avatar" :class="agent.gender"></div>
|
|
|
+ <img v-if="agent.avatarUrl" :src="getAvatarUrl(agent.avatarUrl)" class="avatar-img" />
|
|
|
+ <div v-else class="avatar" :class="agent.gender"></div>
|
|
|
<div class="gender-icon" :class="agent.gender">{{ agent.gender === 'female' ? '♀' : '♂' }}</div>
|
|
|
<div class="agent-name">{{ agent.name }}</div>
|
|
|
</div>
|
|
|
@@ -124,7 +125,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="agents-footer">
|
|
|
- <el-button type="primary" round size="large" class="start-btn" @click="showChat = true">
|
|
|
+ <el-button type="primary" round size="large" class="start-btn" @click="startChat">
|
|
|
开始对话
|
|
|
</el-button>
|
|
|
</div>
|
|
|
@@ -132,7 +133,7 @@
|
|
|
|
|
|
<main class="right-panel chat-panel" v-else>
|
|
|
<!-- 对话历史 -->
|
|
|
- <div class="chat-content">
|
|
|
+ <div class="chat-content" ref="chatContent">
|
|
|
<div
|
|
|
v-for="(msg, index) in chatHistory"
|
|
|
:key="index"
|
|
|
@@ -192,7 +193,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed, watch } from 'vue'
|
|
|
+import { ref, computed, watch, nextTick, onMounted } from 'vue'
|
|
|
import { ArrowUp, ArrowDown, Microphone, PhoneFilled, ChatDotRound, Mute } from '@element-plus/icons-vue'
|
|
|
import { useVoiceRecognition } from './composables/useVoiceRecognition.js'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
@@ -204,6 +205,8 @@ const sidebarVisible = ref(true)
|
|
|
const basicExpanded = ref(true)
|
|
|
const otherExpanded = ref(false)
|
|
|
const currentPage = ref(0)
|
|
|
+const chatContent = ref(null)
|
|
|
+const languageOptions = ref([])
|
|
|
|
|
|
// 语音识别
|
|
|
const { isRecording, currentTranscription, tempTranscription, startRecording, stopRecording } = useVoiceRecognition()
|
|
|
@@ -216,16 +219,6 @@ const playAudio = (base64Audio) => {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- console.log('音频数据长度:', base64Audio.length)
|
|
|
- console.log('音频数据前100字符:', base64Audio.substring(0, 100))
|
|
|
- console.log('音频数据后100字符:', base64Audio.substring(base64Audio.length - 100))
|
|
|
-
|
|
|
- // 检查是否有中间的=号
|
|
|
- const middlePart = base64Audio.substring(100, base64Audio.length - 100)
|
|
|
- if (middlePart.includes('=')) {
|
|
|
- console.log('发现中间有=号,位置:', base64Audio.indexOf('=', 100))
|
|
|
- }
|
|
|
-
|
|
|
const audioData = atob(base64Audio)
|
|
|
const arrayBuffer = new ArrayBuffer(audioData.length)
|
|
|
const view = new Uint8Array(arrayBuffer)
|
|
|
@@ -259,6 +252,15 @@ const playAudio = (base64Audio) => {
|
|
|
// 对话历史
|
|
|
const chatHistory = ref([])
|
|
|
|
|
|
+// 滚动到底部
|
|
|
+const scrollToBottom = () => {
|
|
|
+ nextTick(() => {
|
|
|
+ if (chatContent.value) {
|
|
|
+ chatContent.value.scrollTop = chatContent.value.scrollHeight
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// 波形数据
|
|
|
const waveformData = ref(Array(60).fill(20))
|
|
|
let waveformInterval = null
|
|
|
@@ -272,6 +274,11 @@ const updateWaveform = () => {
|
|
|
|
|
|
// 监听转写结果变化,当有确定性结果时添加到对话历史
|
|
|
watch(currentTranscription, async (newVal, oldVal) => {
|
|
|
+ // 只有在录音状态下才处理语音识别结果
|
|
|
+ if (!isRecording.value || isMicMuted.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
if (newVal && newVal !== oldVal && newVal.length > oldVal.length) {
|
|
|
const newContent = newVal.slice(oldVal.length).trim()
|
|
|
if (newContent) {
|
|
|
@@ -280,6 +287,7 @@ watch(currentTranscription, async (newVal, oldVal) => {
|
|
|
type: 'user',
|
|
|
content: newContent
|
|
|
})
|
|
|
+ scrollToBottom()
|
|
|
|
|
|
// 发送到后端处理
|
|
|
try {
|
|
|
@@ -294,19 +302,14 @@ watch(currentTranscription, async (newVal, oldVal) => {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- // 检查原始响应
|
|
|
- const responseText = await response.text()
|
|
|
- console.log('原始响应文本长度:', responseText.length)
|
|
|
- console.log('原始响应前200字符:', responseText.substring(0, 200))
|
|
|
-
|
|
|
- const data = JSON.parse(responseText)
|
|
|
- console.log('解析后的audio字段长度:', data.audio ? data.audio.length : 0)
|
|
|
+ const data = await response.json()
|
|
|
|
|
|
// 添加客服回复
|
|
|
chatHistory.value.push({
|
|
|
type: 'agent',
|
|
|
content: data.reply
|
|
|
})
|
|
|
+ scrollToBottom()
|
|
|
|
|
|
// 播放语音
|
|
|
if (data.audio) {
|
|
|
@@ -329,12 +332,6 @@ watch(showChat, async (newVal) => {
|
|
|
|
|
|
// 启动波形动画
|
|
|
waveformInterval = setInterval(updateWaveform, 100)
|
|
|
-
|
|
|
- // 添加欢迎消息
|
|
|
- chatHistory.value = [{
|
|
|
- type: 'agent',
|
|
|
- content: '你好,我是你的智能客服助手!请开始说话...'
|
|
|
- }]
|
|
|
} catch (error) {
|
|
|
ElMessage.error('启动语音识别失败: ' + error.message)
|
|
|
}
|
|
|
@@ -347,12 +344,34 @@ watch(showChat, async (newVal) => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
+// 监听输入方式切换
|
|
|
+watch(isTextInput, async (newVal) => {
|
|
|
+ if (newVal && isRecording.value) {
|
|
|
+ // 切换到文本输入时停止语音识别
|
|
|
+ stopRecording()
|
|
|
+ isMicMuted.value = true
|
|
|
+ if (waveformInterval) {
|
|
|
+ clearInterval(waveformInterval)
|
|
|
+ waveformInterval = null
|
|
|
+ }
|
|
|
+ } else if (!newVal && !isRecording.value && showChat.value) {
|
|
|
+ // 切换回语音模式时重新启动语音识别
|
|
|
+ try {
|
|
|
+ await startRecording()
|
|
|
+ isMicMuted.value = false
|
|
|
+ waveformInterval = setInterval(updateWaveform, 100)
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('启动语音识别失败: ' + error.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
// 切换麦克风静音状态
|
|
|
const toggleMic = () => {
|
|
|
if (isRecording.value) {
|
|
|
// 当前正在录音,点击后停止并静音
|
|
|
- stopRecording()
|
|
|
isMicMuted.value = true
|
|
|
+ stopRecording()
|
|
|
if (waveformInterval) {
|
|
|
clearInterval(waveformInterval)
|
|
|
waveformInterval = null
|
|
|
@@ -384,6 +403,7 @@ const sendTextMessage = async () => {
|
|
|
type: 'user',
|
|
|
content: content
|
|
|
})
|
|
|
+ scrollToBottom()
|
|
|
|
|
|
// 清空输入框
|
|
|
textMessage.value = ''
|
|
|
@@ -401,12 +421,7 @@ const sendTextMessage = async () => {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- // 检查原始响应
|
|
|
- const responseText = await response.text()
|
|
|
- console.log('原始响应文本长度:', responseText.length)
|
|
|
- console.log('原始响应前200字符:', responseText.substring(0, 200))
|
|
|
-
|
|
|
- const data = JSON.parse(responseText)
|
|
|
+ const data = await response.json()
|
|
|
console.log('解析后的audio字段长度:', data.audio ? data.audio.length : 0)
|
|
|
|
|
|
// 添加客服回复
|
|
|
@@ -414,6 +429,7 @@ const sendTextMessage = async () => {
|
|
|
type: 'agent',
|
|
|
content: data.reply
|
|
|
})
|
|
|
+ scrollToBottom()
|
|
|
|
|
|
// 播放语音
|
|
|
if (data.audio) {
|
|
|
@@ -425,40 +441,142 @@ const sendTextMessage = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const agents = ref([
|
|
|
- { id: 1, name: '客服 001 号', gender: 'female' },
|
|
|
- { id: 2, name: '客服 002 号', gender: 'male' },
|
|
|
- { id: 3, name: '客服 003 号', gender: 'female' },
|
|
|
- { id: 4, name: '客服 004 号', gender: 'male' },
|
|
|
- { id: 5, name: '客服 005 号', gender: 'female' },
|
|
|
- { id: 6, name: '客服 006 号', gender: 'male' },
|
|
|
- { id: 7, name: '客服 007 号', gender: 'female' },
|
|
|
- { id: 8, name: '客服 008 号', gender: 'male' },
|
|
|
- { id: 9, name: '客服 009 号', gender: 'female' },
|
|
|
- { id: 10, name: '客服 010 号', gender: 'male' },
|
|
|
- { id: 11, name: '客服 011 号', gender: 'female' },
|
|
|
- { id: 12, name: '客服 012 号', gender: 'male' },
|
|
|
- { id: 13, name: '客服 013 号', gender: 'female' },
|
|
|
- { id: 14, name: '客服 014 号', gender: 'male' },
|
|
|
- { id: 15, name: '客服 015 号', gender: 'female' },
|
|
|
- { id: 16, name: '客服 016 号', gender: 'male' },
|
|
|
- { id: 17, name: '客服 017 号', gender: 'female' },
|
|
|
- { id: 18, name: '客服 018 号', gender: 'male' },
|
|
|
- { id: 19, name: '客服 019 号', gender: 'female' },
|
|
|
- { id: 20, name: '客服 020 号', gender: 'male' }
|
|
|
-])
|
|
|
+const agents = ref([])
|
|
|
|
|
|
const config = ref({
|
|
|
- language: '中文',
|
|
|
- tts: 'Minimax',
|
|
|
- bgSound: '客服中心',
|
|
|
- punctuation: 'semantic',
|
|
|
- interrupt: 'smart',
|
|
|
- interruptTime: 200,
|
|
|
- vadTime: 400
|
|
|
+ language: 'zh',
|
|
|
+ bgSoundEnabled: false,
|
|
|
+ speed: 50,
|
|
|
+ pitch: 50,
|
|
|
+ volume: 50
|
|
|
+})
|
|
|
+
|
|
|
+const selectedAgent = ref(null)
|
|
|
+
|
|
|
+// 获取客服列表
|
|
|
+const fetchAgents = async () => {
|
|
|
+ try {
|
|
|
+ const response = await fetch('http://localhost:8080/talk/agent/list?status=0')
|
|
|
+ const result = await response.json()
|
|
|
+ if (result.code === 200 && result.rows) {
|
|
|
+ agents.value = result.rows.map(agent => ({
|
|
|
+ id: agent.id,
|
|
|
+ name: agent.name,
|
|
|
+ gender: agent.gender === '0' ? 'male' : 'female',
|
|
|
+ avatarUrl: agent.avatarUrl,
|
|
|
+ greetingMessage: agent.greetingMessage,
|
|
|
+ ttsVcn: agent.ttsVcn,
|
|
|
+ ttsSpeed: agent.ttsSpeed,
|
|
|
+ ttsPitch: agent.ttsPitch,
|
|
|
+ ttsVolume: agent.ttsVolume,
|
|
|
+ ttsBgs: agent.ttsBgs,
|
|
|
+ language: agent.language
|
|
|
+ }))
|
|
|
+ if (agents.value.length > 0) {
|
|
|
+ selectedAgent.value = agents.value[0].id
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取客服列表失败:', error)
|
|
|
+ ElMessage.error('获取客服列表失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取语言字典
|
|
|
+const fetchLanguageOptions = async () => {
|
|
|
+ try {
|
|
|
+ const response = await fetch('http://localhost:8080/talk/dict/language')
|
|
|
+ const data = await response.json()
|
|
|
+ languageOptions.value = data
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取语言选项失败:', error)
|
|
|
+ ElMessage.error('获取语言选项失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 组件挂载时获取语言选项和客服列表
|
|
|
+onMounted(() => {
|
|
|
+ fetchLanguageOptions()
|
|
|
+ fetchAgents()
|
|
|
})
|
|
|
|
|
|
-const selectedAgent = ref(1)
|
|
|
+// 获取头像完整URL
|
|
|
+const getAvatarUrl = (avatarUrl) => {
|
|
|
+ if (!avatarUrl) return ''
|
|
|
+ if (avatarUrl.startsWith('http://') || avatarUrl.startsWith('https://')) {
|
|
|
+ return avatarUrl
|
|
|
+ }
|
|
|
+ return 'http://localhost:8080' + avatarUrl
|
|
|
+}
|
|
|
+
|
|
|
+// 开始对话
|
|
|
+const startChat = async () => {
|
|
|
+ if (!selectedAgent.value) {
|
|
|
+ ElMessage.warning('请先选择一个客服')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 更新客服配置
|
|
|
+ const selectedAgentData = agents.value.find(a => a.id === selectedAgent.value)
|
|
|
+ if (selectedAgentData) {
|
|
|
+ const response = await fetch('http://localhost:8080/talk/agent/' + selectedAgent.value, {
|
|
|
+ method: 'PUT',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ id: selectedAgent.value,
|
|
|
+ ttsSpeed: config.value.speed,
|
|
|
+ ttsPitch: config.value.pitch,
|
|
|
+ ttsVolume: config.value.volume,
|
|
|
+ ttsBgs: config.value.bgSoundEnabled ? 1 : 0,
|
|
|
+ language: config.value.language
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ if (response.ok) {
|
|
|
+ console.log('客服配置已更新')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示欢迎语并转语音
|
|
|
+ if (selectedAgentData.greetingMessage) {
|
|
|
+ chatHistory.value = [{
|
|
|
+ type: 'agent',
|
|
|
+ content: selectedAgentData.greetingMessage
|
|
|
+ }]
|
|
|
+
|
|
|
+ // 调用后端生成欢迎语语音
|
|
|
+ try {
|
|
|
+ const ttsResponse = await fetch('http://localhost:8080/talk/message', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ message: selectedAgentData.greetingMessage,
|
|
|
+ agentId: selectedAgent.value,
|
|
|
+ isGreeting: true
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const ttsData = await ttsResponse.json()
|
|
|
+ if (ttsData.audio) {
|
|
|
+ playAudio(ttsData.audio)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('生成欢迎语语音失败:', error)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ chatHistory.value = []
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新客服配置失败:', error)
|
|
|
+ }
|
|
|
+
|
|
|
+ showChat.value = true
|
|
|
+}
|
|
|
|
|
|
const pageSize = 6
|
|
|
const totalPages = computed(() => Math.ceil(agents.value.length / pageSize))
|
|
|
@@ -805,10 +923,21 @@ const handleWheel = (e) => {
|
|
|
}
|
|
|
|
|
|
.avatar {
|
|
|
- width: 45px;
|
|
|
- height: 45px;
|
|
|
+ width: 120px;
|
|
|
+ height: 120px;
|
|
|
border-radius: 50%;
|
|
|
- margin: 0 auto 6px;
|
|
|
+ margin: 20px auto 12px;
|
|
|
+ border: 3px solid #e5e7eb;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-img {
|
|
|
+ width: 120px;
|
|
|
+ height: 120px;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin: 20px auto 12px;
|
|
|
+ object-fit: cover;
|
|
|
+ border: 3px solid #e5e7eb;
|
|
|
+ background: #f9fafb;
|
|
|
}
|
|
|
|
|
|
.avatar.female {
|
|
|
@@ -821,17 +950,19 @@ const handleWheel = (e) => {
|
|
|
|
|
|
.gender-icon {
|
|
|
position: absolute;
|
|
|
- top: 42px;
|
|
|
- right: calc(50% - 26px);
|
|
|
- width: 16px;
|
|
|
- height: 16px;
|
|
|
+ top: 110px;
|
|
|
+ right: calc(50% - 70px);
|
|
|
+ width: 28px;
|
|
|
+ height: 28px;
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
color: white;
|
|
|
- font-weight: 600;
|
|
|
- font-size: 9px;
|
|
|
+ font-weight: 700;
|
|
|
+ font-size: 14px;
|
|
|
+ border: 2px solid white;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
.gender-icon.female {
|
|
|
@@ -843,9 +974,10 @@ const handleWheel = (e) => {
|
|
|
}
|
|
|
|
|
|
.agent-name {
|
|
|
- font-size: 12px;
|
|
|
+ font-size: 14px;
|
|
|
color: #1f2937;
|
|
|
- font-weight: 500;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-top: 8px;
|
|
|
}
|
|
|
|
|
|
.start-btn {
|
|
|
@@ -895,7 +1027,26 @@ const handleWheel = (e) => {
|
|
|
justify-content: space-between;
|
|
|
font-size: 12px;
|
|
|
color: #9ca3af;
|
|
|
- margin-top: 4px;
|
|
|
+ margin-top: 2px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 紧凑型滑块样式 */
|
|
|
+.compact-slider {
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.compact-slider :deep(.el-form-item__label) {
|
|
|
+ margin-bottom: 0px;
|
|
|
+ padding-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.compact-slider .slider-value {
|
|
|
+ margin-bottom: 0px;
|
|
|
+}
|
|
|
+
|
|
|
+.compact-slider :deep(.el-form-item__content) {
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
/* 自定义折叠组件样式 */
|