|
@@ -0,0 +1,644 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <CustomNav title="消息记录" leftType="back" />
|
|
|
|
|
+ <view class="page-container">
|
|
|
|
|
+ <!-- 切换按钮 -->
|
|
|
|
|
+ <view class="tab-container">
|
|
|
|
|
+ <view class="tab-buttons">
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="tab-btn"
|
|
|
|
|
+ :class="{ active: activeTab === 'patient' }"
|
|
|
|
|
+ @click="switchTab('patient')"
|
|
|
|
|
+ >
|
|
|
|
|
+ 病人消息
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="tab-btn"
|
|
|
|
|
+ :class="{ active: activeTab === 'family' }"
|
|
|
|
|
+ @click="switchTab('family')"
|
|
|
|
|
+ >
|
|
|
|
|
+ 家属消息
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 消息列表 -->
|
|
|
|
|
+ <view class="messages-container">
|
|
|
|
|
+ <view class="messages-list" v-if="activeTab === 'patient'">
|
|
|
|
|
+ <view class="message-item" v-for="message in patientMessages" :key="message.id">
|
|
|
|
|
+ <view class="message-header">
|
|
|
|
|
+ <view class="message-info">
|
|
|
|
|
+ <text class="message-time">{{ formatTime(message.createTime) }}</text>
|
|
|
|
|
+ <view class="read-status">
|
|
|
|
|
+ <text class="status-text" :class="{ unread: message.status === 0 }">
|
|
|
|
|
+ {{ message.status === 0 ? '未读' : '已读' }}
|
|
|
|
|
+ </text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="popup-status" v-if="message.notifyPopup">
|
|
|
|
|
+ <text class="popup-text">弹窗通知</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="subscribe-status" v-if="message.notifySubscribe">
|
|
|
|
|
+ <text class="subscribe-text">推送订阅</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="message-content">
|
|
|
|
|
+ <text class="content-text">{{ message.content }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="message-footer">
|
|
|
|
|
+ <text class="message-type">{{ getMessageTypeText(message.type) }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="empty-state" v-if="patientMessages.length === 0">
|
|
|
|
|
+ <image class="empty-icon" src="/static/icons/remixicon/chat-1-line.svg" />
|
|
|
|
|
+ <text class="empty-text">暂无病人消息记录</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="messages-list" v-if="activeTab === 'family'">
|
|
|
|
|
+ <view class="message-item" v-for="message in familyMessages" :key="message.id">
|
|
|
|
|
+ <view class="message-header">
|
|
|
|
|
+ <view class="message-info">
|
|
|
|
|
+ <text class="message-time">{{ formatTime(message.createTime) }}</text>
|
|
|
|
|
+ <view class="read-status">
|
|
|
|
|
+ <text class="status-text" :class="{ unread: message.status === 0 }">
|
|
|
|
|
+ {{ message.status === 0 ? '未读' : '已读' }}
|
|
|
|
|
+ </text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="popup-status" v-if="message.notifyPopup">
|
|
|
|
|
+ <text class="popup-text">弹窗通知</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="subscribe-status" v-if="message.notifySubscribe">
|
|
|
|
|
+ <text class="subscribe-text">推送订阅</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="message-content">
|
|
|
|
|
+ <text class="content-text">{{ message.content }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="message-footer">
|
|
|
|
|
+ <text class="message-type">{{ getMessageTypeText(message.type) }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="empty-state" v-if="familyMessages.length === 0">
|
|
|
|
|
+ <image class="empty-icon" src="/static/icons/remixicon/chat-1-line.svg" />
|
|
|
|
|
+ <text class="empty-text">暂无家属消息记录</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <!-- 悬浮发送消息按钮 -->
|
|
|
|
|
+ <view class="fab" @click="showSendModal" role="button" aria-label="发送消息">
|
|
|
|
|
+ <view class="fab-inner">
|
|
|
|
|
+ <uni-icons type="chat" size="28" color="#fff" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 发送消息模态框 -->
|
|
|
|
|
+ <view class="modal" v-if="showSendModalFlag">
|
|
|
|
|
+ <view class="modal-backdrop" @click="cancelSend"></view>
|
|
|
|
|
+ <view class="modal-panel">
|
|
|
|
|
+ <view class="drag-handle"></view>
|
|
|
|
|
+ <view class="modal-header">
|
|
|
|
|
+ <text class="modal-title">发送消息</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="modal-inner">
|
|
|
|
|
+ <view class="form-row">
|
|
|
|
|
+ <text class="label">消息内容</text>
|
|
|
|
|
+ <textarea
|
|
|
|
|
+ v-model="sendForm.content"
|
|
|
|
|
+ placeholder="请输入消息内容"
|
|
|
|
|
+ class="message-input"
|
|
|
|
|
+ :maxlength="500"
|
|
|
|
|
+ />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="form-row checkbox-row">
|
|
|
|
|
+ <view class="switch-list">
|
|
|
|
|
+ <view class="switch-item">
|
|
|
|
|
+ <text class="switch-label">通知家属</text>
|
|
|
|
|
+ <switch :checked="sendForm.notifyFamily" @change="(e: any) => sendForm.notifyFamily = e.detail.value" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="switch-item">
|
|
|
|
|
+ <text class="switch-label">弹窗通知</text>
|
|
|
|
|
+ <switch :checked="sendForm.notifyPopup" @change="(e: any) => sendForm.notifyPopup = e.detail.value" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="switch-item">
|
|
|
|
|
+ <text class="switch-label">推送订阅</text>
|
|
|
|
|
+ <switch :checked="sendForm.notifySubscribe" @change="(e: any) => sendForm.notifySubscribe = e.detail.value" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="fixed-footer">
|
|
|
|
|
+ <button class="btn-cancel" @click="cancelSend">取消</button>
|
|
|
|
|
+ <button class="btn-primary" @click="confirmSend">发送</button>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { ref } from 'vue'
|
|
|
|
|
+import { onLoad } from '@dcloudio/uni-app'
|
|
|
|
|
+import CustomNav from '@/components/custom-nav.vue'
|
|
|
|
|
+import { getDoctorPatientMessages, type DoctorPatientMessagesResponse, type Message } from '@/api/message'
|
|
|
|
|
+import { sendMessage as sendMessageApi, type SendMessageRequest } from '@/api/message'
|
|
|
|
|
+
|
|
|
|
|
+const activeTab = ref<'patient' | 'family'>('patient')
|
|
|
|
|
+const patientMessages = ref<Message[]>([])
|
|
|
|
|
+const familyMessages = ref<Message[]>([])
|
|
|
|
|
+const patientId = ref<string>('')
|
|
|
|
|
+
|
|
|
|
|
+// 发送消息相关
|
|
|
|
|
+const showSendModalFlag = ref(false)
|
|
|
|
|
+const sendForm = ref({
|
|
|
|
|
+ content: '',
|
|
|
|
|
+ notifyFamily: false,
|
|
|
|
|
+ notifyPopup: false,
|
|
|
|
|
+ notifySubscribe: false
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 切换标签
|
|
|
|
|
+const switchTab = (tab: 'patient' | 'family') => {
|
|
|
|
|
+ activeTab.value = tab
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 格式化时间
|
|
|
|
|
+const formatTime = (timeStr: string) => {
|
|
|
|
|
+ if (!timeStr) return ''
|
|
|
|
|
+ const date = new Date(timeStr)
|
|
|
|
|
+ return date.toLocaleString('zh-CN', {
|
|
|
|
|
+ year: 'numeric',
|
|
|
|
|
+ month: '2-digit',
|
|
|
|
|
+ day: '2-digit',
|
|
|
|
|
+ hour: '2-digit',
|
|
|
|
|
+ minute: '2-digit'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 获取消息类型文本
|
|
|
|
|
+const getMessageTypeText = (type: string) => {
|
|
|
|
|
+ switch (type) {
|
|
|
|
|
+ case 'DOCTOR':
|
|
|
|
|
+ return '医生消息'
|
|
|
|
|
+ case 'SYSTEM_DAILY':
|
|
|
|
|
+ return '系统日常'
|
|
|
|
|
+ case 'SYSTEM_ANOMALY':
|
|
|
|
|
+ return '异常提醒'
|
|
|
|
|
+ default:
|
|
|
|
|
+ return '未知类型'
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 获取消息记录
|
|
|
|
|
+const fetchMessages = async () => {
|
|
|
|
|
+ if (!patientId.value) return
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ uni.showLoading({ title: '加载中...' })
|
|
|
|
|
+
|
|
|
|
|
+ const response = await getDoctorPatientMessages(patientId.value)
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+
|
|
|
|
|
+ const resp = response.data as any
|
|
|
|
|
+ if (resp && resp.code === 200 && resp.data) {
|
|
|
|
|
+ const data = resp.data as DoctorPatientMessagesResponse
|
|
|
|
|
+ patientMessages.value = data.patientMessages || []
|
|
|
|
|
+ familyMessages.value = data.familyMessages || []
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '获取消息记录失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ console.error('获取消息记录失败:', error)
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '获取消息记录失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onLoad((options: any) => {
|
|
|
|
|
+ if (options.patientId) {
|
|
|
|
|
+ patientId.value = options.patientId
|
|
|
|
|
+ fetchMessages()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '缺少患者ID参数',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ uni.navigateBack()
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 发送消息相关方法
|
|
|
|
|
+const showSendModal = () => {
|
|
|
|
|
+ sendForm.value.content = '医生提醒:请按时服药,保持健康生活习惯。'
|
|
|
|
|
+ sendForm.value.notifyFamily = false
|
|
|
|
|
+ sendForm.value.notifyPopup = false
|
|
|
|
|
+ sendForm.value.notifySubscribe = false
|
|
|
|
|
+ showSendModalFlag.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const cancelSend = () => {
|
|
|
|
|
+ showSendModalFlag.value = false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const confirmSend = async () => {
|
|
|
|
|
+ if (!sendForm.value.content.trim()) {
|
|
|
|
|
+ uni.showToast({ title: '请输入消息内容', icon: 'none' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ uni.showLoading({ title: '发送中...' })
|
|
|
|
|
+
|
|
|
|
|
+ const receiverIds = [patientId.value]
|
|
|
|
|
+ // TODO: 如果 notifyFamily,添加家属ID
|
|
|
|
|
+
|
|
|
|
|
+ const request: SendMessageRequest = {
|
|
|
|
|
+ receiverIds,
|
|
|
|
|
+ content: sendForm.value.content,
|
|
|
|
|
+ contentFormat: 'PLAIN',
|
|
|
|
|
+ type: 'DOCTOR',
|
|
|
|
|
+ notifyFamily: sendForm.value.notifyFamily,
|
|
|
|
|
+ notifyPopup: sendForm.value.notifyPopup,
|
|
|
|
|
+ notifySubscribe: sendForm.value.notifySubscribe
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const response = await sendMessageApi(request)
|
|
|
|
|
+
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+
|
|
|
|
|
+ const resp = response.data as any
|
|
|
|
|
+ if (resp && resp.code === 200) {
|
|
|
|
|
+ uni.showToast({ title: '消息发送成功', icon: 'success' })
|
|
|
|
|
+ showSendModalFlag.value = false
|
|
|
|
|
+
|
|
|
|
|
+ // 重新获取消息记录
|
|
|
|
|
+ fetchMessages()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: resp?.message || '发送失败', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ console.error('发送消息失败:', error)
|
|
|
|
|
+ uni.showToast({ title: '发送失败', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.page-container {
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ padding-top: calc(var(--status-bar-height) + 44px);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 切换按钮样式 */
|
|
|
|
|
+.tab-container {
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-buttons {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background-color: #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-btn {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 80rpx;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ background-color: transparent;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-btn.active {
|
|
|
|
|
+ background-color: #3742fa;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-btn::after {
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
|
+ width: 1rpx;
|
|
|
|
|
+ height: 40rpx;
|
|
|
|
|
+ background-color: #ddd;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-btn:last-child::after {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 消息列表样式 */
|
|
|
|
|
+.messages-container {
|
|
|
|
|
+ padding: 0 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.messages-list {
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-item {
|
|
|
|
|
+ padding: 30rpx 40rpx;
|
|
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-item:last-child {
|
|
|
|
|
+ border-bottom: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-time {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.read-status {
|
|
|
|
|
+ padding: 8rpx 16rpx;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ background-color: #e8f5e8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-text {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: #52c41a;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-text.unread {
|
|
|
|
|
+ color: #ff4d4f;
|
|
|
|
|
+ background-color: #ffebe9;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.popup-status {
|
|
|
|
|
+ padding: 8rpx 16rpx;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ background-color: #fff2e8;
|
|
|
|
|
+ border: 1rpx solid #ffbb96;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.popup-text {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: #fa8c16;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.subscribe-status {
|
|
|
|
|
+ padding: 8rpx 16rpx;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ background-color: #e6f7ff;
|
|
|
|
|
+ border: 1rpx solid #91d5ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.subscribe-text {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-content {
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.content-text {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ line-height: 1.6;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-footer {
|
|
|
|
|
+ text-align: right;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-type {
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ padding: 6rpx 12rpx;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 空状态样式 */
|
|
|
|
|
+.empty-state {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 100rpx 40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-icon {
|
|
|
|
|
+ width: 120rpx;
|
|
|
|
|
+ height: 120rpx;
|
|
|
|
|
+ margin-bottom: 30rpx;
|
|
|
|
|
+ opacity: 0.5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-text {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 悬浮按钮 */
|
|
|
|
|
+.fab {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ right: 28rpx;
|
|
|
|
|
+ bottom: 160rpx;
|
|
|
|
|
+ width: 110rpx;
|
|
|
|
|
+ height: 110rpx;
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ background: linear-gradient(180deg, #4a90e2, #2d8cf0);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ box-shadow: 0 6rpx 18rpx rgba(0, 0, 0, 0.2);
|
|
|
|
|
+ z-index: 1200;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+}
|
|
|
|
|
+.fab:active { transform: translateY(2rpx); }
|
|
|
|
|
+.fab:focus { outline: none; }
|
|
|
|
|
+.fab-inner {
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ line-height: 28rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 发送消息模态框样式 */
|
|
|
|
|
+.modal {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ z-index: 1300;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.modal-backdrop {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.4);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.modal-panel {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-top-left-radius: 18rpx;
|
|
|
|
|
+ border-top-right-radius: 18rpx;
|
|
|
|
|
+ padding: 28rpx 24rpx 140rpx 24rpx;
|
|
|
|
|
+ box-shadow: 0 -8rpx 30rpx rgba(0,0,0,0.12);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.drag-handle {
|
|
|
|
|
+ width: 64rpx;
|
|
|
|
|
+ height: 6rpx;
|
|
|
|
|
+ background: rgba(0,0,0,0.08);
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ margin: 10rpx auto 14rpx auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.modal-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ gap: 12rpx;
|
|
|
|
|
+ margin-bottom: 6rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.modal-title {
|
|
|
|
|
+ font-size: 36rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ letter-spacing: 1rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.modal-inner {
|
|
|
|
|
+ max-width: 90%;
|
|
|
|
|
+ margin: 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ margin-bottom: 34rpx;
|
|
|
|
|
+ padding: 14rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkbox-row {
|
|
|
|
|
+ margin-top: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.label {
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ margin-bottom: 16rpx;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.message-input {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ min-height: 200rpx;
|
|
|
|
|
+ border: 1rpx solid #ddd;
|
|
|
|
|
+ border-radius: 14rpx;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.switch-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.switch-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ background-color: #f9f9f9;
|
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.switch-label {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fixed-footer {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 40rpx;
|
|
|
|
|
+ padding: 0 24rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.btn-cancel {
|
|
|
|
|
+ width: 220rpx;
|
|
|
|
|
+ height: 100rpx;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ background: #f5f5f5;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.btn-primary {
|
|
|
|
|
+ width: 220rpx;
|
|
|
|
|
+ height: 100rpx;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ background: #3742fa;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ box-shadow: 0 10rpx 28rpx rgba(55, 66, 250, 0.18);
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|