|
@@ -1,165 +1,148 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="console">
|
|
<div class="console">
|
|
|
|
|
|
|
|
- <div class="left-panel">
|
|
|
|
|
- <h2>学员列表</h2>
|
|
|
|
|
- <div class="search">
|
|
|
|
|
- <input type="text" placeholder="搜索用户昵称" v-model="searchKeyword">
|
|
|
|
|
- <button @click="searchUsers()">搜索</button>
|
|
|
|
|
|
|
+ <!-- 左侧:学员列表 -->
|
|
|
|
|
+ <aside class="panel left-panel">
|
|
|
|
|
+ <div class="panel-header">
|
|
|
|
|
+ <span class="panel-title">学员列表</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <el-tabs class="live-console-tab-right" v-model="tabLeft.activeName" @tab-click="handleUserClick" :stretch="true">
|
|
|
|
|
|
|
+ <div class="search-bar">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="searchKeyword"
|
|
|
|
|
+ placeholder="搜索用户昵称"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ prefix-icon="el-icon-search"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @keyup.enter.native="searchUsers"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-button type="primary" size="small" @click="searchUsers">搜索</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-tabs class="user-tabs" v-model="tabLeft.activeName" @tab-click="handleUserClick" :stretch="true">
|
|
|
<el-tab-pane :label="alLabel" name="al">
|
|
<el-tab-pane :label="alLabel" name="al">
|
|
|
- <el-scrollbar class="custom-scrollbar" ref="manageLeftRef_al" style="height: 800px; width: 100%;">
|
|
|
|
|
- <el-row class='scrollbar-demo-item' v-for="u in alDisplayList" :key="u.userId">
|
|
|
|
|
- <el-col :span="20">
|
|
|
|
|
- <el-row type="flex" align="middle">
|
|
|
|
|
- <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="4" >
|
|
|
|
|
- <el-popover
|
|
|
|
|
- width="100"
|
|
|
|
|
- trigger="click">
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
- <i class="el-icon-more" slot="reference"></i>
|
|
|
|
|
- </el-popover>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+ <el-scrollbar class="user-scroll" ref="manageLeftRef_al">
|
|
|
|
|
+ <div class="user-card" v-for="u in alDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" />
|
|
|
|
|
+ <div class="user-info">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">ID: {{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-dropdown trigger="click" @command="cmd => cmd === 'mute' ? changeUserState(u) : blockUser(u)">
|
|
|
|
|
+ <i class="el-icon-more action-btn"></i>
|
|
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
|
|
+ <el-dropdown-item command="mute">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</el-dropdown-item>
|
|
|
|
|
+ <el-dropdown-item command="block" divided>拉黑</el-dropdown-item>
|
|
|
|
|
+ </el-dropdown-menu>
|
|
|
|
|
+ </el-dropdown>
|
|
|
|
|
+ </div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="onlineLabel" name="online">
|
|
<el-tab-pane :label="onlineLabel" name="online">
|
|
|
- <el-scrollbar class="custom-scrollbar" ref="manageLeftRef_online" style="height: 800px; width: 100%;">
|
|
|
|
|
- <el-row class='scrollbar-demo-item' v-for="u in onlineDisplayList" :key="u.userId">
|
|
|
|
|
- <el-col :span="20">
|
|
|
|
|
- <el-row type="flex" align="middle">
|
|
|
|
|
- <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="4" >
|
|
|
|
|
- <el-popover
|
|
|
|
|
- width="100"
|
|
|
|
|
- trigger="click">
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
- <i class="el-icon-more" slot="reference"></i>
|
|
|
|
|
- </el-popover>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+ <el-scrollbar class="user-scroll" ref="manageLeftRef_online">
|
|
|
|
|
+ <div class="user-card" v-for="u in onlineDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" />
|
|
|
|
|
+ <div class="user-info">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">ID: {{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-dropdown trigger="click" @command="cmd => cmd === 'mute' ? changeUserState(u) : blockUser(u)">
|
|
|
|
|
+ <i class="el-icon-more action-btn"></i>
|
|
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
|
|
+ <el-dropdown-item command="mute">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</el-dropdown-item>
|
|
|
|
|
+ <el-dropdown-item command="block" divided>拉黑</el-dropdown-item>
|
|
|
|
|
+ </el-dropdown-menu>
|
|
|
|
|
+ </el-dropdown>
|
|
|
|
|
+ </div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="offlineLabel" name="offline">
|
|
<el-tab-pane :label="offlineLabel" name="offline">
|
|
|
- <el-scrollbar class="custom-scrollbar" ref="manageLeftRef_offline" style="height: 800px; width: 100%;">
|
|
|
|
|
- <el-row class='scrollbar-demo-item' v-for="u in offlineDisplayList" :key="u.userId">
|
|
|
|
|
- <el-col :span="20">
|
|
|
|
|
- <el-row type="flex" align="middle">
|
|
|
|
|
- <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="4" >
|
|
|
|
|
- <el-popover
|
|
|
|
|
- width="100"
|
|
|
|
|
- trigger="click">
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
- <i class="el-icon-more" slot="reference"></i>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
- </el-popover>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+ <el-scrollbar class="user-scroll" ref="manageLeftRef_offline">
|
|
|
|
|
+ <div class="user-card" v-for="u in offlineDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" />
|
|
|
|
|
+ <div class="user-info">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">ID: {{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-dropdown trigger="click" @command="cmd => cmd === 'mute' ? changeUserState(u) : blockUser(u)">
|
|
|
|
|
+ <i class="el-icon-more action-btn"></i>
|
|
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
|
|
+ <el-dropdown-item command="mute">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</el-dropdown-item>
|
|
|
|
|
+ <el-dropdown-item command="block" divided>拉黑</el-dropdown-item>
|
|
|
|
|
+ </el-dropdown-menu>
|
|
|
|
|
+ </el-dropdown>
|
|
|
|
|
+ </div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="silencedUserLabel" name="silenced">
|
|
<el-tab-pane :label="silencedUserLabel" name="silenced">
|
|
|
- <el-scrollbar class="custom-scrollbar" ref="manageLeftRef_silenced" style="height: 800px; width: 100%;">
|
|
|
|
|
- <el-row class='scrollbar-demo-item' v-for="u in silencedDisplayList" :key="u.userId">
|
|
|
|
|
- <el-col :span="20">
|
|
|
|
|
- <el-row type="flex" align="middle">
|
|
|
|
|
- <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
|
|
|
|
|
- <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="4" >
|
|
|
|
|
- <el-popover
|
|
|
|
|
- width="100"
|
|
|
|
|
- trigger="click">
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
- <i class="el-icon-more" slot="reference"></i>
|
|
|
|
|
- </el-popover>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+ <el-scrollbar class="user-scroll" ref="manageLeftRef_silenced">
|
|
|
|
|
+ <div class="user-card" v-for="u in silencedDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" />
|
|
|
|
|
+ <div class="user-info">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">ID: {{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-dropdown trigger="click" @command="cmd => cmd === 'mute' ? changeUserState(u) : blockUser(u)">
|
|
|
|
|
+ <i class="el-icon-more action-btn"></i>
|
|
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
|
|
+ <el-dropdown-item command="mute">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</el-dropdown-item>
|
|
|
|
|
+ <el-dropdown-item command="block" divided>拉黑</el-dropdown-item>
|
|
|
|
|
+ </el-dropdown-menu>
|
|
|
|
|
+ </el-dropdown>
|
|
|
|
|
+ </div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
</el-tabs>
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="middle-panel">
|
|
|
|
|
- <h2>消息管理</h2>
|
|
|
|
|
-
|
|
|
|
|
|
|
+ </aside>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 中间:消息管理 -->
|
|
|
|
|
+ <main class="panel middle-panel">
|
|
|
|
|
+ <div class="panel-header">
|
|
|
|
|
+ <span class="panel-title">消息管理</span>
|
|
|
|
|
+ <label class="global-toggle">
|
|
|
|
|
+ <el-switch v-model="globalVisible" @change="globalVisibleChange" active-color="#6366f1" />
|
|
|
|
|
+ <span>全局用户自见</span>
|
|
|
|
|
+ </label>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <div class="discussion-messages">
|
|
|
|
|
- <h3>讨论区消息</h3>
|
|
|
|
|
- <div class="message-settings">
|
|
|
|
|
- <label>
|
|
|
|
|
- <input type="checkbox" v-model="globalVisible" @change="globalVisibleChange">
|
|
|
|
|
- 全局用户自见
|
|
|
|
|
- </label>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="chat-area">
|
|
|
<div class="message-container" @click="handleMessageBoxClick">
|
|
<div class="message-container" @click="handleMessageBoxClick">
|
|
|
- <el-scrollbar class="custom-scrollbar" style="height: 500px; width: 100%;" ref="manageRightRef">
|
|
|
|
|
- <el-row v-for="(m, msgIndex) in msgList" :key="msgRowKey(m, msgIndex)">
|
|
|
|
|
- <el-row v-if="m.userId !== userId" style="margin-top: 5px" type="flex" align="top" >
|
|
|
|
|
- <el-col :span="3" style="margin-left: 10px"><el-avatar :src="m.avatar"/></el-col>
|
|
|
|
|
- <el-col :span="15">
|
|
|
|
|
- <el-row style="margin-left: 10px">
|
|
|
|
|
- <el-col><div style="font-size: 12px; color: #999; margin-bottom: 3px;">{{ m.nickName }}</div></el-col>
|
|
|
|
|
- <el-col :span="24" style="max-width: 200px;">
|
|
|
|
|
- <div v-if="m.replyUserName || m.replyContent" style="font-size: 12px; color: #999; margin-bottom: 4px; word-break: break-all;">
|
|
|
|
|
- 回复 {{ m.replyUserName || '用户' }}:{{ m.replyContent }}
|
|
|
|
|
- </div>
|
|
|
|
|
- <div style="white-space: normal; word-wrap: break-word;background-color: #f0f2f5; padding: 8px; border-radius: 5px;font-size: 14px;width: 100%;">
|
|
|
|
|
- {{ m.msg }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="changeUserState(m)">{{ m.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="blockUser(m)">拉黑</a>
|
|
|
|
|
- <a v-if="isUserSingleVisible(m)" style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="singleVisible(m)">解除用户自见</a>
|
|
|
|
|
- <a v-else style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="singleVisible(m)">用户自见</a>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="replyUser(m)">回复</a>
|
|
|
|
|
- <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="deleteMsg(m)">删除</a>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- <el-row v-if="m.userId === userId" style="padding: 8px 0" type="flex" align="top" justify="end">
|
|
|
|
|
- <div style="display: flex;justify-content: flex-end">
|
|
|
|
|
- <div style="display: flex;justify-content: flex-end;flex-direction: column;max-width: 200px;align-items: flex-end">
|
|
|
|
|
- <div style="font-size: 12px; color: #999; margin-bottom: 3px;">{{ m.nickName }}</div>
|
|
|
|
|
- <div v-if="m.replyUserName || m.replyContent" style="font-size: 12px; color: #999; margin-bottom: 4px; max-width: 200px; word-break: break-all; text-align: right;">
|
|
|
|
|
|
|
+ <el-scrollbar class="chat-scroll" ref="manageRightRef">
|
|
|
|
|
+ <div v-for="(m, msgIndex) in msgList" :key="msgRowKey(m, msgIndex)" class="msg-row">
|
|
|
|
|
+ <!-- 管理员消息(右) -->
|
|
|
|
|
+ <div v-if="m.userId === userId && m.msgId == null" class="msg-bubble msg-right">
|
|
|
|
|
+ <div class="msg-meta">{{ m.nickName }}</div>
|
|
|
|
|
+ <div v-if="m.replyUserName || m.replyContent" class="msg-reply">
|
|
|
|
|
+ 回复 {{ m.replyUserName || '用户' }}:{{ m.replyContent }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="msg-text admin-text">{{ m.msg }}</div>
|
|
|
|
|
+ <el-avatar :size="32" :src="m.avatar" class="msg-avatar" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 用户消息(左) -->
|
|
|
|
|
+ <div v-else class="msg-bubble msg-left">
|
|
|
|
|
+ <el-avatar :size="32" :src="m.avatar" class="msg-avatar" />
|
|
|
|
|
+ <div class="msg-content">
|
|
|
|
|
+ <div class="msg-meta">{{ m.nickName }}</div>
|
|
|
|
|
+ <div v-if="m.replyUserName || m.replyContent" class="msg-reply">
|
|
|
回复 {{ m.replyUserName || '用户' }}:{{ m.replyContent }}
|
|
回复 {{ m.replyUserName || '用户' }}:{{ m.replyContent }}
|
|
|
</div>
|
|
</div>
|
|
|
- <div style="white-space: normal; word-wrap: break-word;width: 100%; background-color: #e6f7ff; padding: 8px; border-radius: 5px;font-size: 14px;">{{ m.msg }}</div>
|
|
|
|
|
|
|
+ <div class="msg-text">{{ m.msg }}</div>
|
|
|
|
|
+ <div class="msg-actions">
|
|
|
|
|
+ <a @click.stop="changeUserState(m)">{{ m.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
+ <a @click.stop="blockUser(m)">拉黑</a>
|
|
|
|
|
+ <a @click.stop="singleVisible(m)">{{ isUserSingleVisible(m) ? '解除自见' : '用户自见' }}</a>
|
|
|
|
|
+ <a @click.stop="replyUser(m)">回复</a>
|
|
|
|
|
+ <a @click.stop="deleteMsg(m)">删除</a>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- <el-avatar :src="m.avatar" style="margin-left: 10px; margin-right: 10px;"/>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
- </el-row>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- <!-- 底部留白 -->
|
|
|
|
|
- <div style="height: 20px;"></div>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="height: 16px;"></div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
- <!-- 暂停后:回到最新并恢复实时滚动 -->
|
|
|
|
|
<el-button
|
|
<el-button
|
|
|
v-if="showLoadLatestBtn"
|
|
v-if="showLoadLatestBtn"
|
|
|
class="load-latest-btn"
|
|
class="load-latest-btn"
|
|
|
type="primary"
|
|
type="primary"
|
|
|
- size="small"
|
|
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ round
|
|
|
@click.stop="loadLatestMessages"
|
|
@click.stop="loadLatestMessages"
|
|
|
:disabled="isLoadingHistory"
|
|
:disabled="isLoadingHistory"
|
|
|
:loading="isLoadingHistory"
|
|
:loading="isLoadingHistory"
|
|
@@ -167,100 +150,79 @@
|
|
|
回到最新
|
|
回到最新
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- <!-- <div class="message-list">-->
|
|
|
|
|
- <!-- <div class="message-item" v-for="msg in msgList" :key="msg.id">-->
|
|
|
|
|
- <!-- <div class="message-avatar">-->
|
|
|
|
|
- <!-- <img :src="msg.avatar" alt="用户头像">-->
|
|
|
|
|
- <!-- </div>-->
|
|
|
|
|
- <!-- <div class="message-content">-->
|
|
|
|
|
- <!-- <div class="message-user">{{ msg.user }}</div>-->
|
|
|
|
|
- <!-- <div class="message-text">{{ msg.text }}</div>-->
|
|
|
|
|
- <!-- </div>-->
|
|
|
|
|
- <!-- <div class="message-actions">-->
|
|
|
|
|
- <!--<!– <button @click="toggleVisible(msg)">–>-->
|
|
|
|
|
- <!--<!– {{ msg.isVisible ? '仅用户自见' : '全局可见' }}–>-->
|
|
|
|
|
- <!--<!– </button>–>-->
|
|
|
|
|
- <!-- <button @click="deleteMessage(msg)">删除</button>-->
|
|
|
|
|
- <!-- </div>-->
|
|
|
|
|
- <!-- </div>-->
|
|
|
|
|
- <!-- </div>-->
|
|
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="system-messages">
|
|
|
|
|
- <h3>系统消息</h3>
|
|
|
|
|
- <textarea placeholder="输入系统消息" v-model="newMsg"></textarea>
|
|
|
|
|
- <div class="message-actions">
|
|
|
|
|
- <button @click="sendMessage">发送消息</button>
|
|
|
|
|
- <button @click="sendPopMessage">弹窗消息</button>
|
|
|
|
|
- <button @click="showTopMsgDialog">官方消息</button>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <div class="send-area">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ v-model="newMsg"
|
|
|
|
|
+ placeholder="输入系统消息..."
|
|
|
|
|
+ :rows="2"
|
|
|
|
|
+ resize="none"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="send-actions">
|
|
|
|
|
+ <el-button size="small" type="primary" @click="sendMessage">发送消息</el-button>
|
|
|
|
|
+ <el-button size="small" @click="sendPopMessage">弹窗消息</el-button>
|
|
|
|
|
+ <el-button size="small" @click="showTopMsgDialog">官方消息</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </main>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 右侧:运营工具 -->
|
|
|
|
|
+ <aside class="panel right-panel">
|
|
|
|
|
+ <div class="panel-header">
|
|
|
|
|
+ <span class="panel-title">运营工具</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <div class="right-panel">
|
|
|
|
|
- <h2>运营工具</h2>
|
|
|
|
|
- <div class="live-player">
|
|
|
|
|
- <h3>直播观看</h3>
|
|
|
|
|
|
|
+ <div class="tool-block">
|
|
|
|
|
+ <div class="block-label">直播观看</div>
|
|
|
<LivePlayer ref="livePlayer" :videoParam="videoParam" />
|
|
<LivePlayer ref="livePlayer" :videoParam="videoParam" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="automation">
|
|
|
|
|
- <h3>运营自动化</h3>
|
|
|
|
|
- <div class="timeline">
|
|
|
|
|
- <h4>时间轴设置</h4>
|
|
|
|
|
- <div class="timeline-items">
|
|
|
|
|
- <div class="timeline-item" v-for="item in timelineItems.slice(0,2)" :key="item.time">
|
|
|
|
|
- <div class="time">{{ formatDate(item.absValue) }}</div>
|
|
|
|
|
- <div class="action">{{ item.taskName }}</div>
|
|
|
|
|
- <button class="delete" @click="removeTimelineItem(item)">删除</button>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="tool-block" v-if="timelineItems.length">
|
|
|
|
|
+ <div class="block-label">时间轴任务</div>
|
|
|
|
|
+ <div class="timeline-list">
|
|
|
|
|
+ <div class="timeline-row" v-for="item in timelineItems.slice(0, 2)" :key="item.id || item.time">
|
|
|
|
|
+ <span class="timeline-time">{{ formatDate(item.absValue) }}</span>
|
|
|
|
|
+ <span class="timeline-name">{{ item.taskName }}</span>
|
|
|
|
|
+ <el-button type="text" size="mini" class="timeline-del" @click="removeTimelineItem(item)">删除</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- <!-- <button class="add" @click="addTimelineItem">添加时间节点</button>-->
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="watermark">
|
|
|
|
|
- <h3>直播氛围自动化</h3>
|
|
|
|
|
- <div class="watermark-settings">
|
|
|
|
|
- <textarea :disabled="autoWatermark" v-model="watermarkTemplate" placeholder="水军弹幕内容模板,每行一条"></textarea>
|
|
|
|
|
- <div class="watermark-options">
|
|
|
|
|
- <label>
|
|
|
|
|
- 发送间隔:
|
|
|
|
|
- <input type="number" :disabled="autoWatermark" v-model.number="interval" min="1">
|
|
|
|
|
- 秒
|
|
|
|
|
- </label>
|
|
|
|
|
- <label>
|
|
|
|
|
- <input type="checkbox" v-model="autoWatermark" @change="changeAutoWatermark">
|
|
|
|
|
- 启用水军自动化
|
|
|
|
|
- </label>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="tool-block">
|
|
|
|
|
+ <div class="block-label">水军自动化</div>
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :disabled="autoWatermark"
|
|
|
|
|
+ v-model="watermarkTemplate"
|
|
|
|
|
+ placeholder="每行一条弹幕内容"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ resize="none"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="watermark-opts">
|
|
|
|
|
+ <span>间隔</span>
|
|
|
|
|
+ <el-input-number :disabled="autoWatermark" v-model.number="interval" :min="1" size="mini" controls-position="right" />
|
|
|
|
|
+ <span>秒</span>
|
|
|
|
|
+ <el-switch v-model="autoWatermark" @change="changeAutoWatermark" active-color="#6366f1" inactive-text="关闭" active-text="开启" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </aside>
|
|
|
|
|
|
|
|
<!-- 官方消息对话框 -->
|
|
<!-- 官方消息对话框 -->
|
|
|
- <el-dialog title="发送官方消息" :visible.sync="topMsgDialogVisible" width="500px">
|
|
|
|
|
- <el-form :model="topMsgForm" label-width="100px">
|
|
|
|
|
|
|
+ <el-dialog title="发送官方消息" :visible.sync="topMsgDialogVisible" width="480px">
|
|
|
|
|
+ <el-form :model="topMsgForm" label-width="80px">
|
|
|
<el-form-item label="消息内容">
|
|
<el-form-item label="消息内容">
|
|
|
- <el-input
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- v-model="topMsgForm.msg"
|
|
|
|
|
- placeholder="请输入消息内容"
|
|
|
|
|
- :rows="3"
|
|
|
|
|
- ></el-input>
|
|
|
|
|
|
|
+ <el-input type="textarea" v-model="topMsgForm.msg" placeholder="请输入消息内容" :rows="3" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="显示时长">
|
|
<el-form-item label="显示时长">
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="topMsgForm.duration"
|
|
|
|
|
- :min="1"
|
|
|
|
|
- :max="60"
|
|
|
|
|
- placeholder="请输入显示时长(分钟)"
|
|
|
|
|
- ></el-input-number>
|
|
|
|
|
- <span style="margin-left: 10px;">分钟</span>
|
|
|
|
|
|
|
+ <el-input-number v-model="topMsgForm.duration" :min="1" :max="60" size="small" />
|
|
|
|
|
+ <span class="unit-text">分钟</span>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
- <div slot="footer" class="dialog-footer">
|
|
|
|
|
- <el-button @click="topMsgDialogVisible = false">取 消</el-button>
|
|
|
|
|
- <el-button type="primary" @click="sendTopMessage">确 定</el-button>
|
|
|
|
|
|
|
+ <div slot="footer">
|
|
|
|
|
+ <el-button @click="topMsgDialogVisible = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="sendTopMessage">确定</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
|
|
@@ -1244,7 +1206,7 @@ export default {
|
|
|
const { rows, total } = response;
|
|
const { rows, total } = response;
|
|
|
params.total = total; // 记录总数据量
|
|
params.total = total; // 记录总数据量
|
|
|
this.syncUserTotalFromList(tabName, total);
|
|
this.syncUserTotalFromList(tabName, total);
|
|
|
- // 过滤重复数据(基于 userId)
|
|
|
|
|
|
|
+ // 过滤重复数据(基于userId)
|
|
|
const newRows = (rows || []).filter(row =>
|
|
const newRows = (rows || []).filter(row =>
|
|
|
!displayList.some(u => u.userId === row.userId)
|
|
!displayList.some(u => u.userId === row.userId)
|
|
|
);
|
|
);
|
|
@@ -1744,293 +1706,306 @@ export default {
|
|
|
.console {
|
|
.console {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
height: 100vh;
|
|
height: 100vh;
|
|
|
|
|
+ background: #f5f8fc;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.left-panel, .middle-panel, .right-panel {
|
|
|
|
|
- padding: 20px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
|
|
+.panel {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ border: 1px solid #e8eef5;
|
|
|
|
|
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.left-panel {
|
|
.left-panel {
|
|
|
- width: 30%;
|
|
|
|
|
- background: #f8fafc;
|
|
|
|
|
- border-right: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ width: 28%;
|
|
|
|
|
+ min-width: 260px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.middle-panel {
|
|
.middle-panel {
|
|
|
- width: 40%;
|
|
|
|
|
- background: #f8fafc;
|
|
|
|
|
- border-right: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ width: 44%;
|
|
|
|
|
+ flex: 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.right-panel {
|
|
.right-panel {
|
|
|
- width: 30%;
|
|
|
|
|
- background: #f8fafc;
|
|
|
|
|
|
|
+ width: 28%;
|
|
|
|
|
+ min-width: 280px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.search {
|
|
|
|
|
- margin: 10px 0;
|
|
|
|
|
|
|
+.panel-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ padding: 14px 16px 10px;
|
|
|
|
|
+ border-bottom: 1px solid #f1f5f9;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.search input {
|
|
|
|
|
- width: 70%;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- border: 1px solid #cbd5e1;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+.panel-title {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #1e293b;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.search button {
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.search-bar {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding: 10px 16px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.tabs {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- margin: 10px 0;
|
|
|
|
|
|
|
+.user-tabs {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ padding: 0 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.tabs button {
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- border: 1px solid #e2e8f0;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.user-tabs >>> .el-tabs__header {
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.tabs button.active {
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border-color: #3b82f6;
|
|
|
|
|
|
|
+.user-tabs >>> .el-tabs__item {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ padding: 0 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-list {
|
|
|
|
|
- max-height: 600px;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+.user-scroll {
|
|
|
|
|
+ height: calc(100vh - 160px);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-item {
|
|
|
|
|
|
|
+.user-card {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- padding: 10px;
|
|
|
|
|
- border-bottom: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ padding: 8px 10px;
|
|
|
|
|
+ margin: 4px 8px;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ transition: background 0.15s;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-item img {
|
|
|
|
|
- width: 40px;
|
|
|
|
|
- height: 40px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- margin-right: 10px;
|
|
|
|
|
|
|
+.user-card:hover {
|
|
|
|
|
+ background: #f8fafc;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.user-info {
|
|
.user-info {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.user-name {
|
|
.user-name {
|
|
|
- font-weight: bold;
|
|
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: #334155;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-status {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- color: #64748b;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.online {
|
|
|
|
|
- color: #10b981;
|
|
|
|
|
|
|
+.user-id {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: #94a3b8;
|
|
|
|
|
+ margin-top: 2px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.offline {
|
|
|
|
|
|
|
+.action-btn {
|
|
|
|
|
+ cursor: pointer;
|
|
|
color: #94a3b8;
|
|
color: #94a3b8;
|
|
|
|
|
+ padding: 4px;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-actions {
|
|
|
|
|
- display: flex;
|
|
|
|
|
|
|
+.action-btn:hover {
|
|
|
|
|
+ color: #6366f1;
|
|
|
|
|
+ background: #f1f5f9;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-actions button {
|
|
|
|
|
- padding: 5px 10px;
|
|
|
|
|
- margin-left: 5px;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+.global-toggle {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #64748b;
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.block {
|
|
|
|
|
- background: #ef4444;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+.chat-area {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ padding: 0 16px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.unblock {
|
|
|
|
|
- background: #10b981;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+.message-container {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ background: #f8fafc;
|
|
|
|
|
+ border-radius: 10px;
|
|
|
|
|
+ border: 1px solid #e8eef5;
|
|
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.mute {
|
|
|
|
|
- background: #f59e0b;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+.chat-scroll {
|
|
|
|
|
+ height: calc(100vh - 280px);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.unmute {
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+.msg-row {
|
|
|
|
|
+ padding: 6px 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.system-messages, .discussion-messages {
|
|
|
|
|
- margin: 20px 0;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- padding: 15px;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
+.msg-bubble {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ max-width: 85%;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.system-messages textarea {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100px;
|
|
|
|
|
- border: 1px solid #e2e8f0;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
|
|
+.msg-right {
|
|
|
|
|
+ flex-direction: row-reverse;
|
|
|
|
|
+ margin-left: auto;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-actions {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+.msg-left {
|
|
|
|
|
+ align-items: flex-start;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-actions button {
|
|
|
|
|
- padding: 5px 10px;
|
|
|
|
|
- margin-right: 5px;
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.msg-content {
|
|
|
|
|
+ max-width: 320px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-list {
|
|
|
|
|
- max-height: 300px;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+.msg-meta {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: #94a3b8;
|
|
|
|
|
+ margin-bottom: 3px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
- padding-bottom: 10px;
|
|
|
|
|
- border-bottom: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+.msg-reply {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: #94a3b8;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+ word-break: break-all;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-avatar img {
|
|
|
|
|
- width: 30px;
|
|
|
|
|
- height: 30px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- margin-right: 10px;
|
|
|
|
|
|
|
+.msg-text {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border: 1px solid #e8ecf0;
|
|
|
|
|
+ padding: 8px 12px;
|
|
|
|
|
+ border-radius: 0 10px 10px 10px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #334155;
|
|
|
|
|
+ word-break: break-word;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-content {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
|
|
+.admin-text {
|
|
|
|
|
+ background: #eef2ff;
|
|
|
|
|
+ border-color: #c7d2fe;
|
|
|
|
|
+ border-radius: 10px 0 10px 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-user {
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
|
|
+.msg-actions {
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-text {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #64748b;
|
|
|
|
|
|
|
+.msg-actions a {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: #6366f1;
|
|
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-actions button {
|
|
|
|
|
- padding: 3px 8px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.msg-actions a:hover {
|
|
|
|
|
+ text-decoration: underline;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.live-player, .automation, .watermark {
|
|
|
|
|
- margin: 20px 0;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- padding: 15px;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
+.load-latest-btn {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: 12px;
|
|
|
|
|
+ right: 12px;
|
|
|
|
|
+ z-index: 10;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.timeline-items {
|
|
|
|
|
- margin: 10px 0;
|
|
|
|
|
|
|
+.send-area {
|
|
|
|
|
+ padding: 12px 16px 16px;
|
|
|
|
|
+ border-top: 1px solid #f1f5f9;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.timeline-item {
|
|
|
|
|
|
|
+.send-actions {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding: 8px 0;
|
|
|
|
|
- border-bottom: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-top: 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.delete {
|
|
|
|
|
- background: #ef4444;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 3px 8px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.tool-block {
|
|
|
|
|
+ padding: 12px 16px;
|
|
|
|
|
+ border-bottom: 1px solid #f1f5f9;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.add {
|
|
|
|
|
- background: #10b981;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.block-label {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #64748b;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ text-transform: uppercase;
|
|
|
|
|
+ letter-spacing: 0.5px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.watermark-settings textarea {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100px;
|
|
|
|
|
- border: 1px solid #e2e8f0;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
|
|
+.timeline-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 6px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.watermark-options {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+.timeline-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ padding: 6px 8px;
|
|
|
|
|
+ background: #f8fafc;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.watermark-options label {
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-bottom: 5px;
|
|
|
|
|
|
|
+.timeline-time {
|
|
|
|
|
+ color: #6366f1;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
-/* 隐藏 el-scrollbar 的横向滚动条 */
|
|
|
|
|
-.el-scrollbar__wrap {
|
|
|
|
|
- overflow-x: hidden !important;
|
|
|
|
|
|
|
+
|
|
|
|
|
+.timeline-name {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ color: #475569;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
-.custom-scrollbar .el-scrollbar__wrap {
|
|
|
|
|
- overflow-x: hidden !important;
|
|
|
|
|
|
|
+
|
|
|
|
|
+.timeline-del {
|
|
|
|
|
+ color: #ef4444 !important;
|
|
|
}
|
|
}
|
|
|
-.scrollbar-demo-item{
|
|
|
|
|
|
|
+
|
|
|
|
|
+.watermark-opts {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
|
|
- height: 50px;
|
|
|
|
|
- margin: 10px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #64748b;
|
|
|
}
|
|
}
|
|
|
-.message-container {
|
|
|
|
|
- position: relative;
|
|
|
|
|
|
|
+
|
|
|
|
|
+.unit-text {
|
|
|
|
|
+ margin-left: 6px;
|
|
|
|
|
+ color: #94a3b8;
|
|
|
|
|
+ font-size: 13px;
|
|
|
}
|
|
}
|
|
|
-.load-latest-btn {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- bottom: 20px;
|
|
|
|
|
- right: 20px;
|
|
|
|
|
- z-index: 10;
|
|
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
|
+
|
|
|
|
|
+.el-scrollbar__wrap {
|
|
|
|
|
+ overflow-x: hidden !important;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|