Files
dyf-vue-ui/src/views/controlCenter/6170/index.vue
2025-09-09 14:16:19 +08:00

949 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="device-page p-2">
<!-- 头部信息栏 -->
<div class="header-bar">
<div>设备名称{{ deviceDetail.deviceName }}</div>
<div>设备型号{{ deviceDetail.deviceImei }}</div>
<div class="device-status">设备状态
<span :class="{ online: deviceDetail.onlineStatus === 1, offline: deviceDetail?.onlineStatus === 0 }">
{{ deviceDetail.onlineStatus === 1 ? "在线" : "离线" }}
</span>
</div>
<div>电量{{ deviceDetail.batteryPercentage || 0 }}%</div>
<div>续航{{ deviceDetail.batteryRemainingTime || "0" }} 分钟</div>
</div>
<!-- 主体内容区域 -->
<div class="content-wrapper">
<!-- 第一行灯光模式 + 灯光亮度强制报警位置信息 -->
<el-row :gutter="20" class="content-row">
<el-col :lg="16" :xs="24">
<div class="content-card">
<h4 class="section-title">灯光模式</h4>
<div class="light-mode">
<!-- 使用v-for循环渲染灯光模式卡片 -->
<div class="mode-card" :class="{ 'active': mode.active }"
@click.stop="handleModeClick(mode.id)" v-for="mode in lightModes" :key="mode.id">
<img :src="mode.active ? mode.activeIcon : mode.icon" :alt="mode.name"
class="mode-icon" />
<div class="mode-name">{{ mode.name }}</div>
<el-switch v-model="mode.switchStatus" />
</div>
<!-- 激光模式单独处理 -->
<div class="mode-card" :class="{ 'active': laserMode.active }" @click="handleLaserClick">
<img :src="laserMode.active ? laserMode.activeIcon : laserMode.icon"
:alt="laserMode.name" class="mode-icon" />
<div class="mode-name">{{ laserMode.name }}</div>
<el-switch v-model="laserMode.switchStatus" />
</div>
</div>
</div>
</el-col>
<el-col :lg="8" :xs="24">
<div class="brightness-alarm">
<div class="brightness-control">
<span class="brightness-label">灯光亮度</span>
<el-input class="inputTFT" v-model="deviceDetail.lightBrightness" :min="0" :max="100"
:step="1" size="small" />
<span class="brightness-value">%</span>
<el-button type="primary" class="save-btn" @click="saveBtn" :loading="lightModesLoading"
:loading-text="lightModesLoading ? '保存中...' : '保存'"> {{
lightModesLoading ? '保存中' : '保存' }}</el-button>
</div>
<el-button type="danger" class="alarm-btn" @click="forceAlarm" :loading="forceAlarmLoading"
:loading-text="forceAlarmLoading ? '报警中...' : '强制报警'"> {{
forceAlarmLoading ? '报警中' : '强制报警' }}</el-button>
</div>
<div class="content-card_gps">
<h4 class="section-title">位置信息</h4>
<div class="location-info">
<div class="location-item">
<span class="location-icon"></span>
<span>经纬度 {{ deviceDetail && deviceDetail.longitude ?
Number(deviceDetail.longitude).toFixed(4) : '无' }}
{{ deviceDetail && deviceDetail.latitude ? Number(deviceDetail.latitude).toFixed(4)
: '无' }} </span>
</div>
<div class="location-item">
<div>地址 <span class="lacatin_gps">{{ deviceDetail.address || "未获取到地址" }}</span></div>
<el-button link type="primary" class="view-btn"
@click="lookMap(deviceDetail)">查看</el-button>
</div>
</div>
</div>
</el-col>
</el-row>
<!-- 第二行人员信息登记 + 发送信息 -->
<el-row :gutter="20" class="content-row">
<el-col :lg="16" :xs="24">
<div class="content-card">
<h4 class="section-title">人员信息登记</h4>
<div class="form-grid">
<div class="form-item">
<span class="form-label">单位:</span>
<el-input v-if="deviceDetail" placeholder="请输入单位名称"
v-model="deviceDetail.personnelInfo.unitName" />
</div>
<div class="form-item">
<span class="form-label">职位:</span>
<el-input v-if="deviceDetail" placeholder="请输入职位名称"
v-model="deviceDetail.personnelInfo.position" />
</div>
<div class="form-item">
<span class="form-label">姓名</span>
<el-input v-if="deviceDetail" placeholder="请输入职位姓名"
v-model="deviceDetail.personnelInfo.name" />
</div>
<div class="form-item">
<span class="form-label">ID:</span>
<el-input v-if="deviceDetail" placeholder="请输入ID"
v-model="deviceDetail.personnelInfo.code" />
</div>
<el-button type="primary" class="register-btn" @click="registerPostInit"
:loading="fullscreenLoading" :loading-text="fullscreenLoading ? '登记中...' : '登记'"> {{
fullscreenLoading ? '登记中' : '登记' }}</el-button>
</div>
</div>
</el-col>
<el-col :lg="8" :xs="24">
<div class="content-card">
<h4 class="section-title">发送信息</h4>
<div class="message-content">
<el-input type="textarea" class="textareaTFT" :rows="4" placeholder="现场危险,停止救援!紧急撤离至安全区域!"
v-model="deviceDetail.sendMsg" resize="none" />
<div style="text-align: end;clear: both;">
<el-button type="primary" class="send-btn" @click="sendTextMessage"
:loading="sendTextLoading" :loading-text="sendTextLoading ? '发送中...' : '发送'"> {{
sendTextLoading ? '发送中' : '发送' }}</el-button>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
<!-- ===========充电提示框====== -->
<el-dialog title="充电提示" v-model="centerDialogVisible" width="15%">
<div style="display: flex; align-items: center;">
<h3 style="color: rgba(224, 52, 52, 1)">设备电量低于20%</h3>
</div>
<div>请及时充电</div>
<span slot="footer" class="dialog-footer" style="text-align: right;display: block;">
<el-button type="primary" @click="centerDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script setup name="DeviceControl" lang="ts">
const route = useRoute();
import { useMqtt } from '@/utils/mqtt';
import api from '@/api/controlCenter/controlPanel/index'
import { DeviceDetail, LightMode } from '@/api/controlCenter/controlPanel/types';
import { generateShortId, getDeviceStatus } from '@/utils/function';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
// 导入图片资源(确保路径正确)
import strongLightDefault from '@/assets/images/strong-light.png';
import strongLightActive from '@/assets/images/strong-light_HL.png';
import weakLightDefault from '@/assets/images/weak-light.png';
import weakLightActive from '@/assets/images/weak-light_HL.png';
import strobeLightDefault from '@/assets/images/strobe-light.png';
import strobeLightActive from '@/assets/images/strobe-light_HL.png';
import floodLightDefault from '@/assets/images/flood-light.png';
import floodLightActive from '@/assets/images/flood-light_HL.png';
import laserLightDefault from '@/assets/images/laser-light.png';
import laserLightActive from '@/assets/images/laser-light_HL.png';
import closeDefault from '@/assets/images/close.png';
import closeActive from '@/assets/images/close_HL.png';
const fullscreenLoading = ref(false)
const forceAlarmLoading = ref(false) //强制报警
const sendTextLoading = ref(false)
const lightModesLoading = ref(false)
const centerDialogVisible = ref(false)
const {
connected,
connect,
subscribe,
onConnect,
onError,
onMessage,
disconnect
} = useMqtt();
// 灯光模式数据(引用导入的图片)
const lightModes = ref<LightMode[]>([
{
id: 'strong',
name: '强光',
icon: strongLightDefault, // 直接使用导入的变量
activeIcon: strongLightActive,
switchStatus: true,
instructValue: '1',
active: true,
},
{
id: 'weak',
name: '弱光',
icon: weakLightDefault,
activeIcon: weakLightActive,
switchStatus: false,
instructValue: '2',
active: false,
},
{
id: 'strobe',
name: '爆闪',
icon: strobeLightDefault,
activeIcon: strobeLightActive,
switchStatus: false,
instructValue: '3',
active: false
},
{
id: 'flood',
name: '泛光',
icon: floodLightDefault,
activeIcon: floodLightActive,
switchStatus: false,
instructValue: '4',
active: false
},
{
id: 'close',
name: '关闭',
icon: closeDefault,
activeIcon: closeActive,
switchStatus: false,
instructValue: '0',
active: false
},
]);
const laserMode = ref<LightMode>({
id: 'laser',
name: '激光',
icon: laserLightDefault,
activeIcon: laserLightActive,
switchStatus: false,
instructValue: '1',
active: false
});
const deviceDetail = ref<DeviceDetail & { typeName: string }>({
// 重点personnelInfo 初始化为空对象,避免 undefined
personnelInfo: {
unitName: '',
position: '',
name: '',
code: ''
},
lightBrightness: '',
deviceName: '',
deviceImei: '',
onlineStatus: 0,
batteryPercentage: 0,
batteryRemainingTime: '',
longitude: '',
latitude: '',
address: '',
sendMsg: '',
chargeState: '0',
typeName: ''
});
// 保留原有的操作中标志位
const isUpdatingStatus = ref(false);
const handleModeClick = async (modeId: string) => {
if (isUpdatingStatus.value || isSyncingStatus.value) return;
try {
const deviceId = route.params.deviceId as string;
if (!deviceId) return;
const targetMode = lightModes.value.find(m => m.id === modeId);
if (!targetMode || !targetMode.instructValue) return;
// 标记为用户操作中的更新
isUpdatingStatus.value = true;
// 调用接口(仅用户点击时触发一次)
const res = await api.lightModeSettings({
deviceId,
instructValue: targetMode.instructValue,
deviceImei: deviceDetail.value.deviceImei,
typeName: deviceDetail.value.typeName,
});
if (res.code === 200) {
ElMessage.closeAll();
proxy?.$modal.msgSuccess(res.msg);
setActiveLightMode(modeId);
} else {
proxy?.$modal.msgError(res.msg);
const prevActiveMode = lightModes.value.find(m => m.active);
if (prevActiveMode) {
setActiveLightMode(prevActiveMode.id);
}
}
} catch (error) {
proxy?.$modal.msgError("操作失败,请稍后重试");
// 异常时恢复状态
const prevActiveMode = lightModes.value.find(m => m.active);
if (prevActiveMode) {
setActiveLightMode(prevActiveMode.id);
}
} finally {
// 无论成功失败,最终重置用户操作标志位
isUpdatingStatus.value = false;
}
};
const isSyncingStatus = ref(false);
const setActiveLightMode = (targetModeId: string) => {
isSyncingStatus.value = true; // 开启阻断更新switchStatus时watch不触发接口
lightModes.value.forEach(mode => {
const isActive = mode.id === targetModeId;
mode.active = isActive;
mode.switchStatus = isActive; // 这里更新会触发watch但被isSyncingStatus阻断
});
isSyncingStatus.value = false; // 同步完成,重置标志位
};
const getList = async () => {
try {
const deviceId = route.params.deviceId;
if (!deviceId) return;
const res = await api.deviceDeatil(deviceId as string);
deviceDetail.value = res.data;
// 处理人员信息空值(原有逻辑保留)
if (!deviceDetail.value.personnelInfo) {
deviceDetail.value.personnelInfo = {
unitName: '',
position: '',
name: '',
code: ''
};
}
// 1. 匹配接口返回的灯光模式
let targetModeId = "strong";
const mainLightMode = String(res.data.mainLightMode || "1"); // 接口值转字符串,“强光”
const matchedMode = lightModes.value.find(
mode => mode.instructValue === mainLightMode
);
if (matchedMode) {
targetModeId = matchedMode.id;
}
setActiveLightMode(targetModeId);
const laserStatus = Number(res.data.laserLightMode);
laserMode.value.active = laserStatus === 1;
laserMode.value.switchStatus = laserStatus === 1;
setTimeout(() => {
console.log('延迟检查激光状态:', laserMode.value.active, laserMode.value.switchStatus);
}, 1000);
} catch (error) {
console.error("获取设备详情失败:", error);
setActiveLightMode("strong"); // 异常时默认强光
}
};
// 激光接口调用
const handleLaserClick = async () => {
try {
const deviceId = route.params.deviceId as string;
if (!deviceId) return;
const targetStatus = !laserMode.value.active;
const res = await api.laserModeSettings({
deviceId,
deviceImei: deviceDetail.value.deviceImei,
typeName: deviceDetail.value.typeName,
instructValue: targetStatus ? 1 : 0
});
if (res.code === 200) {
ElMessage.closeAll();
proxy?.$modal.msgSuccess(res.msg);
laserMode.value.active = targetStatus;
laserMode.value.switchStatus = targetStatus;
} else {
proxy?.$modal.msgError(res.msg);
// 恢复之前的状态
laserMode.value.switchStatus = !targetStatus;
}
} catch (error: any) {
proxy?.$modal.msgError(error.msg);
// 恢复之前的状态
laserMode.value.switchStatus = !laserMode.value.switchStatus;
} finally { }
}
// 人员信息发送
const registerPostInit = () => {
if (!deviceDetail.value.personnelInfo.unitName) {
ElMessage.closeAll();
proxy?.$modal.msgWarning('单位名称不能为空');
return
}
if (!deviceDetail.value.personnelInfo.name) {
ElMessage.closeAll();
proxy?.$modal.msgWarning('姓名不能为空');
return
}
if (!deviceDetail.value.personnelInfo.position) {
ElMessage.closeAll();
proxy?.$modal.msgWarning('职位不能为空');
return
}
if (!deviceDetail.value.personnelInfo.code) {
ElMessage.closeAll();
proxy?.$modal.msgWarning('ID不能为空');
return
}
let data = {
code: deviceDetail.value.personnelInfo.code,
name: deviceDetail.value.personnelInfo.name,
position: deviceDetail.value.personnelInfo.position,
unitName: deviceDetail.value.personnelInfo.unitName,
deviceId: route.params.deviceId,
deviceImei: deviceDetail.value.deviceImei
}
fullscreenLoading.value = true
api.registerPersonInfo(data).then((res) => {
console.log(res, 'res');
if (res.code === 200) {
fullscreenLoading.value = false
proxy?.$modal.msgSuccess(res.msg);
} else {
fullscreenLoading.value = false
proxy?.$modal.msgError(res.msg);
}
})
}
// 灯光亮度
const saveBtn = () => {
lightModesLoading.value = true
let data = {
deviceId: route.params.deviceId,
instructValue: deviceDetail.value.lightBrightness + '.00',
deviceImei: deviceDetail.value.deviceImei,
}
api.lightBrightnessSettings(data).then((res) => {
if (res.code === 200) {
lightModesLoading.value = false
proxy?.$modal.msgSuccess(res.msg);
} else {
lightModesLoading.value = false
proxy?.$modal.msgError(res.msg);
}
})
}
// 强制报警
const forceAlarm = async () => {
try {
await proxy?.$modal.confirm('确定要对该设备开启强制报警?', '提示');
forceAlarmLoading.value = true
// 2. 准备请求数据
const batchId = generateShortId();
let data = {
deviceIds: [route.params.deviceId],
typeName: deviceDetail.value.typeName,
deviceImeiList: [deviceDetail.value.deviceImei],
batchId: batchId,
instructValue: '1', //强制报警1解除报警0
}
const registerRes = await api.sendAlarmMessage(data);
if (registerRes.code !== 200) {
proxy?.$modal.msgWarning(registerRes.msg)
return
}
// 4. 获取设备状态
let deviceImei = deviceDetail.value.deviceImei
const statusRes = await getDeviceStatus({
functionMode: 2,
batchId,
typeName: 'FunctionAccessBatchStatusRule',
deviceImei,
interval: 500
},
api.deviceRealTimeStatus
);
// 只有当状态为'OK'时才显示成功弹窗
if (statusRes.data.functionAccess === 'OK') {
proxy?.$modal.msgSuccess(statusRes.msg);
}
} catch (error: any) {
proxy?.$modal.msgWarning(error.msg)
} finally {
forceAlarmLoading.value = false;
}
}
// 发送文本消息
const sendTextMessage = async () => {
// 防重复提交
if (!deviceDetail.value.sendMsg) {
ElMessage.closeAll();
proxy?.$modal.msgWarning('发送信息不能为空');
return;
}
try {
sendTextLoading.value = true;
// 2. 准备请求数据
const batchId = generateShortId();
const data = {
sendMsg: deviceDetail.value.sendMsg,
deviceIds: [route.params.deviceId],
typeName: deviceDetail.value.typeName,
batchId: batchId,
deviceImeiList: [deviceDetail.value.deviceImei],
};
// 3.人员信息
const registerRes = await api.deviceSendMessage(data);
if (registerRes.code !== 200) {
proxy?.$modal.msgWarning(registerRes.msg)
return
}
// 4. 获取设备状态
let deviceImei = deviceDetail.value.deviceImei
const statusRes = await getDeviceStatus({
functionMode: 2,
batchId,
typeName: 'FunctionAccessBatchStatusRule',
deviceImei,
interval: 500
},
api.deviceRealTimeStatus
);
// 只有当状态为'OK'时才显示成功弹窗
if (statusRes.data.functionAccess === 'OK') {
proxy?.$modal.msgSuccess(statusRes.msg);
}
} catch (error: any) {
proxy?.$modal.msgWarning(error.msg)
} finally {
sendTextLoading.value = false;
}
}
const lookMap = (row: any) => {
console.log(row, 'row');
router.push({
path: '/controlCenter/controlPanel', // 目标页面的正确路由路径(需与项目路由配置一致)
query: {
view: 'map',
deviceId: row.deviceId // 可选传递当前设备ID用于地图定位/筛选
}
});
}
const getMainLightModeLabel = (mode: any) => {
const modeMap = {
0: 'close', // 0 → 关闭
1: 'strong', // 1 → 强光
2: 'weak', // 2 → 弱光
3: 'strobe', // 3 → 爆闪
4: 'flood' // 4 → 泛光
}
return modeMap[mode] || (console.log('未知的灯光模式:', mode), '未知');
}
// 处理设备消息
const handleDeviceMessage = (msg: any) => {
try {
// 解析设备消息(假设格式为 { state: [类型, 模式值, 亮度, 续航...] }
const payloadObj = JSON.parse(msg.payload.toString());
const deviceState = payloadObj.state; // 设备状态数组
if (!Array.isArray(deviceState)) {
return;
}
// 用switch处理不同的消息类型deviceState[0]
switch (deviceState[0]) {
case 1:
// 类型1灯光主键
const lightModeId = getMainLightModeLabel(deviceState[1]); // 获取模式ID如'strong'
const brightness = deviceState[2]; // 亮度值
const batteryTime = deviceState[3]; // 续航时间
console.log('灯光模式消息:', { 模式ID: lightModeId, 亮度: brightness, 续航: batteryTime });
// 1. 同步灯光模式状态
if (lightModeId !== 'unknown') {
lightModes.value.forEach(mode => {
const isActive = mode.id === lightModeId;
mode.active = isActive;
mode.switchStatus = isActive;
});
}
// 2.亮度
if (brightness !== undefined) {
deviceDetail.value.lightBrightness = brightness.toString();
}
// 3.续航时间
if (batteryTime !== undefined) {
deviceDetail.value.batteryRemainingTime = batteryTime.toString();
}
break;
case 12:
// 灯光主键
const lightModeIdA = getMainLightModeLabel(deviceState[1]); // 获取模式ID如'strong'
if (lightModeIdA !== 'unknown') {
lightModes.value.forEach(mode => {
const isActive = mode.id === lightModeIdA;
mode.active = isActive;
mode.switchStatus = isActive;
});
}
// 激光
const laserValue = deviceState[2];
// 同步激光模式状态1=开启true0=关闭false
laserMode.value.active = laserValue === 1;
laserMode.value.switchStatus = laserValue === 1;
deviceDetail.value.batteryPercentage = deviceState[3]; //电量
deviceDetail.value.batteryRemainingTime = deviceState[5]; //续航时间
// getList(); // 重新获取设备详情
if (deviceDetail.value.batteryPercentage < 20 && Number(deviceDetail.value.chargeState) == 0) {
centerDialogVisible.value=true
}
break;
default:
// 其他类型消息(不处理,仅打印)
console.log('未处理的消息类型:', deviceState[0]);
break;
}
} catch (e) {
}
};
onMounted(async () => {
await getList(); // 先获取设备信息
// 连接mqtt
onConnect(async () => {
const deviceImei = deviceDetail.value.deviceImei;
if (!deviceImei) {
return;
}
try {
await subscribe(`A/${deviceImei}`, { qos: 1 });
console.log('订阅成功');
} catch (err) {
console.error('订阅失败onConnect内:', err);
}
});
// 2. 注册消息接收回调(核心:处理设备发送的消息)
onMessage((msg) => {
console.log('收到新消息:', {
主题: msg.topic,
内容: msg.payload,
时间: msg.time,
QoS: msg.qos
});
// 在这里处理消息(根据实际业务逻辑)
handleDeviceMessage(msg);
});
onError((err) => {
console.error('MQTT连接失败原因:', err.message); // 关键:打印连接失败的具体原因
});
connect();
});
onUnmounted(() => {
// 只有当连接已建立时,才执行断开操作(避免无效调用)
if (connected.value) {
console.log('页面离开断开MQTT连接');
disconnect(); // 调用断开连接方法
}
});
</script>
<style lang="scss" scoped>
.device-page {
.header-bar {
border-radius: 8px;
background: linear-gradient(135deg, #3400e7, #009bff);
color: white;
padding: 20px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
.device-status {
.online {
color: #00ff00;
}
.offline {
color: rgb(224, 52, 52);
}
}
}
.content-wrapper {
.content-row {
margin-bottom: 10px;
}
.content-card {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 34, 96, 0.1);
background: white;
padding: 0px 20px 50px;
border: 1px solid #ebeef5;
height: 289px;
position: relative;
}
.content-card_gps {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 34, 96, 0.1);
background: white;
padding: 0px 20px 50px;
border: 1px solid #ebeef5;
height: 78%;
}
.section-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 20px;
color: #303133;
}
.light-mode {
display: flex;
justify-content: space-between;
}
.lacatin_gps {
height: 70px;
border-radius: 4px;
background: rgba(247, 248, 252, 1);
display: inline-block;
width: 400px;
padding: 10px;
}
.mode-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 15px;
border: 1px solid #dcdfe6;
border-radius: 8px;
width: 125px;
cursor: pointer;
transition: all 0.3s ease;
&.active {
border-color: #409eff;
background-color: rgba(64, 158, 255, 0.05);
}
.mode-icon {
width: 48px;
margin-bottom: 10px;
transition: all 0.3s ease;
object-fit: scale-down;
height: 50px;
}
.mode-name {
font-size: 14px;
margin-bottom: 12px;
color: #606266;
}
.el-switch {
--el-switch-on-color: #409eff;
--el-switch-off-color: #dcdfe6;
}
}
.brightness-alarm {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
.brightness-control {
display: flex;
align-items: center;
gap: 10px;
border-radius: 39px;
box-shadow: 0px 0px 6px 0px rgba(0, 34, 96, 0.1);
background: rgba(255, 255, 255, 1);
width: 70%;
height: 54px;
padding: 10px 20px;
.brightness-label {
font-size: 14px;
color: #606266;
min-width: 70px;
}
.brightness-value {
font-size: 14px;
color: #409eff;
font-weight: 500;
}
.save-btn {
padding: 6px 20px;
border-radius: 29px;
background: rgba(2, 124, 251, 1);
border: none
}
.inputTFT {
width: 130px;
height: 40px;
}
}
.alarm-btn {
background-color: rgba(224, 52, 52, 1);
border-color: rgba(224, 52, 52, 1);
padding: 8px 20px;
border-radius: 27px;
}
}
.location-info {
.location-item {
display: flex;
align-items: center;
margin-bottom: 15px;
font-size: 14px;
color: #606266;
.location-icon {
margin-right: 8px;
font-weight: bold;
color: #409eff;
}
.view-btn {
margin: 0 8px;
padding: 0;
font-size: 13px;
}
}
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 15px;
text-align: right;
.form-item {
display: flex;
align-items: center;
.form-label {
min-width: 35px;
font-size: 14px;
color: #606266;
margin-right: 10px;
}
.el-input {
flex: 1;
}
}
.register-btn {
grid-column: 1 / 3;
justify-self: start;
padding: 6px 20px;
position: absolute;
right: 19px;
bottom: 30px;
border-radius: 29px;
background: rgba(2, 124, 251, 1);
border: none
}
}
.message-content {
// display: flex;
// flex-direction: column;
// gap: 10px;
.el-textarea {
border: 1px solid rgba(29, 111, 255, 1);
border-radius: 4px;
background: rgba(247, 248, 252, 1);
border-radius: 6px;
padding: 10px;
}
.el-textarea__inner {
background: rgba(247, 248, 252, 1);
}
.send-btn {
align-self: flex-end;
padding: 10px 20px;
border-radius: 29px;
background: rgba(2, 124, 251, 1);
border: none;
margin: 20px 0px 30px 0;
}
}
.upload-area {
display: flex;
justify-content: center;
align-items: center;
height: 100px;
border: 1px dashed #dcdfe6;
border-radius: 6px;
cursor: pointer;
}
.video-input {
display: flex;
gap: 10px;
align-items: center;
.el-input {
flex: 1;
}
.save-video-btn {
padding: 6px 12px;
}
}
}
// 响应式调整
@media (max-width: 768px) {
.header-bar {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.form-grid {
grid-template-columns: 1fr;
}
.form-grid .register-btn {
grid-column: 1;
}
.light-mode .mode-group {
gap: 10px;
}
.light-mode .mode-item {
width: 60px;
}
.light-mode .mode-icon {
width: 30px;
height: 30px;
}
}
}
.path-img {
width: 52px;
height: 28px;
}
</style>