refactor(customer-service): 优化客服模块交互,点击直接弹出聊天框
- 修改 header.vue 和 play.vue 客服按钮点击事件,跳过中间选择弹窗 - 重写 CustomerServiceWindow.vue 样式,匹配 PC 端深色主题风格 - 移除图片发送功能,仅保留文本消息 - 新增遮罩层、头像区分、金色渐变按钮等 UI 元素
This commit is contained in:
parent
b7061d7a0d
commit
8d9154c668
@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="setup">
|
<div class="setup">
|
||||||
<el-tooltip v-if="webconfig.isServerCode||false" class="item" effect="dark" :content="Language.tip_service" placement="bottom">
|
<el-tooltip v-if="webconfig.isServerCode||false" class="item" effect="dark" :content="Language.tip_service" placement="bottom">
|
||||||
<span @click="isShowPop('updateService')" class="btn service" ></span>
|
<span @click="openCustomerService()" class="btn service" ></span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" effect="dark" :content="Language.tip_cn_tw" placement="bottom">
|
<el-tooltip class="item" effect="dark" :content="Language.tip_cn_tw" placement="bottom">
|
||||||
<span @click="isShowPop('updatelang')" class="btn fontj" ></span>
|
<span @click="isShowPop('updatelang')" class="btn fontj" ></span>
|
||||||
@ -69,7 +69,10 @@ export default {
|
|||||||
}else{
|
}else{
|
||||||
this.$store.dispatch('updatemainPop',{type:type,ishow:true});
|
this.$store.dispatch('updatemainPop',{type:type,ishow:true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
openCustomerService(){
|
||||||
|
this.$root.$emit('openCustomerService');
|
||||||
},
|
},
|
||||||
offPop(){
|
offPop(){
|
||||||
this.$store.dispatch('updatemainPop',{type:'',ishow:false});
|
this.$store.dispatch('updatemainPop',{type:'',ishow:false});
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="setup">
|
<div class="setup">
|
||||||
<el-tooltip v-if="webconfig.isServerCode||false" class="item" effect="dark" :content="Language.tip_service" placement="bottom">
|
<el-tooltip v-if="webconfig.isServerCode||false" class="item" effect="dark" :content="Language.tip_service" placement="bottom">
|
||||||
<span @click="isShowPop('updateService')" class="btn service" ></span>
|
<span @click="openCustomerService()" class="btn service" ></span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" effect="dark" :content="Language.tip_cn_tw" placement="bottom">
|
<el-tooltip class="item" effect="dark" :content="Language.tip_cn_tw" placement="bottom">
|
||||||
<span @click="isShowPop('updatelang')" class="btn fontj" ></span>
|
<span @click="isShowPop('updatelang')" class="btn fontj" ></span>
|
||||||
@ -192,6 +192,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
openCustomerService(){
|
||||||
|
this.$root.$emit('openCustomerService');
|
||||||
|
},
|
||||||
offPop(){
|
offPop(){
|
||||||
this.$store.dispatch('updatemainPop',{type:'',ishow:false});
|
this.$store.dispatch('updatemainPop',{type:'',ishow:false});
|
||||||
},
|
},
|
||||||
|
|||||||
547
src/components/updateService/CustomerServiceWindow.vue
Normal file
547
src/components/updateService/CustomerServiceWindow.vue
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cs-overlay" v-if="visible" @click.self="closeWindow">
|
||||||
|
<div class="customer-service-window animated bounceInUp">
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<div class="cs-header">
|
||||||
|
<span class="cs-title">在线客服</span>
|
||||||
|
<span class="cs-status" :class="{'online': isConnected}">
|
||||||
|
{{ isConnected ? '已连接' : '连接中...' }}
|
||||||
|
</span>
|
||||||
|
<button class="cs-close" @click="closeWindow">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 消息区域 -->
|
||||||
|
<div class="cs-body">
|
||||||
|
<div class="cs-messages" ref="messageList">
|
||||||
|
<!-- 欢迎信息 -->
|
||||||
|
<div v-if="!sessionId" class="cs-welcome">
|
||||||
|
<div class="cs-welcome-icon">💬</div>
|
||||||
|
<p class="cs-welcome-title">欢迎使用在线客服</p>
|
||||||
|
<p v-if="!isConnected" class="cs-welcome-tip">正在连接...</p>
|
||||||
|
<p v-else-if="sessionStatus === 0" class="cs-welcome-tip warning">客服繁忙,请稍候...</p>
|
||||||
|
<p v-else class="cs-welcome-tip">请输入您的问题</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 消息列表 -->
|
||||||
|
<div v-for="msg in messages" :key="msg.msgId || msg.id"
|
||||||
|
class="cs-message"
|
||||||
|
:class="{
|
||||||
|
'cs-message-own': msg.senderType === 1 || msg.sender_type === 1,
|
||||||
|
'cs-message-system': msg.senderType === 0
|
||||||
|
}">
|
||||||
|
<!-- 系统消息 -->
|
||||||
|
<div v-if="msg.senderType === 0" class="cs-system-msg">
|
||||||
|
{{ msg.content }}
|
||||||
|
</div>
|
||||||
|
<!-- 普通消息 -->
|
||||||
|
<div v-else class="cs-message-content">
|
||||||
|
<div class="cs-message-avatar">
|
||||||
|
<span v-if="msg.senderType === 1 || msg.sender_type === 1">我</span>
|
||||||
|
<span v-else>客服</span>
|
||||||
|
</div>
|
||||||
|
<div class="cs-message-bubble">
|
||||||
|
<div class="cs-message-text">{{ msg.content }}</div>
|
||||||
|
<div class="cs-message-time">{{ formatTime(msg.createTime || msg.create_time) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 输入区域 -->
|
||||||
|
<div class="cs-input-area">
|
||||||
|
<textarea
|
||||||
|
v-model="inputMessage"
|
||||||
|
@keydown.enter.exact.prevent="sendMessage"
|
||||||
|
placeholder="输入消息,按Enter发送..."
|
||||||
|
:disabled="!isConnected || !sessionId"
|
||||||
|
></textarea>
|
||||||
|
<div class="cs-input-actions">
|
||||||
|
<button class="cs-btn cs-btn-send" @click="sendMessage" :disabled="!isConnected || !sessionId || !inputMessage.trim()">
|
||||||
|
发送
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustomerServiceWindow',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
isConnected: false,
|
||||||
|
sessionId: null,
|
||||||
|
sessionStatus: null,
|
||||||
|
messages: [],
|
||||||
|
inputMessage: '',
|
||||||
|
agentInfo: null,
|
||||||
|
heartbeatTimer: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['userInfo', 'webconfig']),
|
||||||
|
socket() {
|
||||||
|
return this.$store.state.io
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 在组件挂载时设置全局回调(需要绑定this)
|
||||||
|
window.__chatConnectedCallback = this.onChatConnected.bind(this)
|
||||||
|
window.__chatSessionAssignedCallback = this.onChatSessionAssigned.bind(this)
|
||||||
|
window.__chatMessageNewCallback = this.onChatMessageNew.bind(this)
|
||||||
|
window.__chatSessionEndedCallback = this.onChatSessionEnded.bind(this)
|
||||||
|
window.__chatOfflineNoticeCallback = this.onChatOfflineNotice.bind(this)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.heartbeatTimer) {
|
||||||
|
clearInterval(this.heartbeatTimer)
|
||||||
|
}
|
||||||
|
// 清除全局回调
|
||||||
|
window.__chatConnectedCallback = null
|
||||||
|
window.__chatSessionAssignedCallback = null
|
||||||
|
window.__chatMessageNewCallback = null
|
||||||
|
window.__chatSessionEndedCallback = null
|
||||||
|
window.__chatOfflineNoticeCallback = null
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.visible = true
|
||||||
|
this.connectChat()
|
||||||
|
},
|
||||||
|
closeWindow() {
|
||||||
|
this.visible = false
|
||||||
|
if (this.heartbeatTimer) {
|
||||||
|
clearInterval(this.heartbeatTimer)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connectChat() {
|
||||||
|
console.log('=== 客服系统:复用现有Socket连接 ===')
|
||||||
|
|
||||||
|
if (!this.socket) {
|
||||||
|
console.error('Socket未初始化,请先登录游戏')
|
||||||
|
this.$message.error('请先登录游戏')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isConnected = this.socket.connected
|
||||||
|
console.log('Socket连接状态:', this.isConnected)
|
||||||
|
|
||||||
|
// 发送客服连接请求
|
||||||
|
console.log('发送chat.connect事件,token:', this.userInfo.online_token)
|
||||||
|
this.socket.emit('chat.connect', {
|
||||||
|
token: this.userInfo.online_token,
|
||||||
|
role: 'user',
|
||||||
|
source: 1 // PC端
|
||||||
|
})
|
||||||
|
|
||||||
|
// 启动心跳
|
||||||
|
this.startHeartbeat()
|
||||||
|
},
|
||||||
|
// 连接成功回调
|
||||||
|
onChatConnected(data) {
|
||||||
|
console.log('[CustomerService] onChatConnected被调用:', data)
|
||||||
|
console.log('[CustomerService] this.visible:', this.visible)
|
||||||
|
if (data.success) {
|
||||||
|
this.sessionId = data.sessionId
|
||||||
|
this.sessionStatus = data.status
|
||||||
|
this.agentInfo = data.agentInfo
|
||||||
|
console.log('[CustomerService] 设置sessionId:', this.sessionId)
|
||||||
|
this.loadHistory()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 客服分配回调
|
||||||
|
onChatSessionAssigned(data) {
|
||||||
|
this.agentInfo = data.agentInfo
|
||||||
|
this.sessionStatus = 1
|
||||||
|
this.addSystemMessage('客服 ' + data.agentInfo.nickname + ' 已接入')
|
||||||
|
},
|
||||||
|
// 新消息回调
|
||||||
|
onChatMessageNew(data) {
|
||||||
|
console.log('[CustomerService] onChatMessageNew:', data)
|
||||||
|
// 服务端直接发送的是消息对象,不是嵌套的 {data: {...}}
|
||||||
|
const msg = data.data || data
|
||||||
|
this.messages.push({
|
||||||
|
msgId: msg.msgId,
|
||||||
|
senderType: msg.senderType,
|
||||||
|
msgType: msg.msgType,
|
||||||
|
content: msg.content,
|
||||||
|
createTime: msg.createTime
|
||||||
|
})
|
||||||
|
this.scrollToBottom()
|
||||||
|
// 发送已读回执
|
||||||
|
if (msg.msgId) {
|
||||||
|
this.socket.emit('chat.message.ack', { msgId: msg.msgId })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 会话结束回调
|
||||||
|
onChatSessionEnded(data) {
|
||||||
|
this.addSystemMessage('会话已结束')
|
||||||
|
this.sessionStatus = 2
|
||||||
|
},
|
||||||
|
// 离线通知回调
|
||||||
|
onChatOfflineNotice(data) {
|
||||||
|
this.addSystemMessage(data.message)
|
||||||
|
},
|
||||||
|
setupChatListeners() {
|
||||||
|
console.log('[Debug] setupChatListeners 已废弃,使用全局回调')
|
||||||
|
},
|
||||||
|
startHeartbeat() {
|
||||||
|
if (this.heartbeatTimer) {
|
||||||
|
clearInterval(this.heartbeatTimer)
|
||||||
|
}
|
||||||
|
this.heartbeatTimer = setInterval(() => {
|
||||||
|
if (this.socket && this.socket.connected) {
|
||||||
|
this.socket.emit('chat.ping', {})
|
||||||
|
}
|
||||||
|
}, 25000)
|
||||||
|
},
|
||||||
|
sendMessage() {
|
||||||
|
const content = this.inputMessage.trim()
|
||||||
|
if (!content || !this.sessionId) return
|
||||||
|
|
||||||
|
const clientMsgId = Date.now()
|
||||||
|
this.socket.emit('chat.message.send', {
|
||||||
|
sessionId: this.sessionId,
|
||||||
|
msgType: 1,
|
||||||
|
content: content,
|
||||||
|
clientMsgId: clientMsgId
|
||||||
|
})
|
||||||
|
|
||||||
|
// 乐观更新UI
|
||||||
|
this.messages.push({
|
||||||
|
msgId: clientMsgId,
|
||||||
|
senderType: 1, // 用户发送的消息
|
||||||
|
msgType: 1,
|
||||||
|
content: content,
|
||||||
|
createTime: Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.inputMessage = ''
|
||||||
|
this.scrollToBottom()
|
||||||
|
},
|
||||||
|
loadHistory() {
|
||||||
|
if (!this.sessionId) return
|
||||||
|
// 聊天API在Pro后端,使用相对路径或配置的聊天API地址
|
||||||
|
// 由于跨域问题,暂时跳过历史消息加载,消息会通过WebSocket实时接收
|
||||||
|
console.log('[CustomerService] 跳过历史消息加载(跨域限制)')
|
||||||
|
// TODO: 后续可以通过WebSocket请求历史消息,或配置正确的API地址
|
||||||
|
},
|
||||||
|
addSystemMessage(text) {
|
||||||
|
this.messages.push({
|
||||||
|
msgId: Date.now(),
|
||||||
|
senderType: 0,
|
||||||
|
msgType: 1,
|
||||||
|
content: text,
|
||||||
|
createTime: Date.now()
|
||||||
|
})
|
||||||
|
this.scrollToBottom()
|
||||||
|
},
|
||||||
|
scrollToBottom() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const el = this.$refs.messageList
|
||||||
|
if (el) {
|
||||||
|
el.scrollTop = el.scrollHeight
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
formatTime(timestamp) {
|
||||||
|
if (!timestamp) return ''
|
||||||
|
const date = new Date(timestamp * 1000)
|
||||||
|
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 遮罩层 */
|
||||||
|
.cs-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 9998;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主窗口 - PC端深色主题 */
|
||||||
|
.customer-service-window {
|
||||||
|
width: 420px;
|
||||||
|
height: 560px;
|
||||||
|
background: #553e31;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题栏 */
|
||||||
|
.cs-header {
|
||||||
|
height: 45px;
|
||||||
|
background: #1f120e;
|
||||||
|
color: #fff;
|
||||||
|
padding: 0 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #d9ac6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-status {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-status.online {
|
||||||
|
background: rgba(76, 175, 80, 0.3);
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #d9ac6b;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 28px;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-close:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息区域 */
|
||||||
|
.cs-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 15px;
|
||||||
|
background: #3d2c22;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条样式 */
|
||||||
|
.cs-messages::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-messages::-webkit-scrollbar-track {
|
||||||
|
background: #2a1f18;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-messages::-webkit-scrollbar-thumb {
|
||||||
|
background: #d9ac6b;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 欢迎信息 */
|
||||||
|
.cs-welcome {
|
||||||
|
text-align: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-welcome-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-welcome-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #d9ac6b;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-welcome-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-welcome-tip.warning {
|
||||||
|
color: #ff9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息项 */
|
||||||
|
.cs-message {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-own {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-system {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 系统消息 */
|
||||||
|
.cs-system-msg {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息内容 */
|
||||||
|
.cs-message-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-own .cs-message-content {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 头像 */
|
||||||
|
.cs-message-avatar {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #1f120e;
|
||||||
|
color: #d9ac6b;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-own .cs-message-avatar {
|
||||||
|
background: linear-gradient(135deg, #d9ac6b 0%, #a67c3d 100%);
|
||||||
|
color: #1f120e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息气泡 */
|
||||||
|
.cs-message-bubble {
|
||||||
|
background: #2a1f18;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-own .cs-message-bubble {
|
||||||
|
background: linear-gradient(135deg, #d9ac6b 0%, #a67c3d 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-text {
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-own .cs-message-text {
|
||||||
|
color: #1f120e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-time {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #777;
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-message-own .cs-message-time {
|
||||||
|
color: rgba(31, 18, 14, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入区域 */
|
||||||
|
.cs-input-area {
|
||||||
|
border-top: 1px solid #2a1f18;
|
||||||
|
padding: 12px;
|
||||||
|
background: #1f120e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-input-area textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 65px;
|
||||||
|
background: #3d2c22;
|
||||||
|
border: 1px solid #553e31;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px;
|
||||||
|
resize: none;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-input-area textarea::placeholder {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-input-area textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #d9ac6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-input-area textarea:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-input-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-btn {
|
||||||
|
padding: 8px 18px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-btn-send {
|
||||||
|
background: linear-gradient(180deg, #f7ee99 0%, #78681c 50%, #c0b141 100%);
|
||||||
|
color: #1f120e;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-btn-send:hover:not(:disabled) {
|
||||||
|
background: linear-gradient(180deg, #ffed48 0%, #78681c 50%, #ffed48 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user