|
@@ -2,230 +2,189 @@
|
|
|
<div class="console">
|
|
<div class="console">
|
|
|
|
|
|
|
|
<div class="left-panel">
|
|
<div class="left-panel">
|
|
|
- <h2>学员列表</h2>
|
|
|
|
|
- <div class="search">
|
|
|
|
|
- <input type="text" placeholder="搜索用户昵称" v-model="searchKeyword">
|
|
|
|
|
- <button @click="searchUsers()">搜索</button>
|
|
|
|
|
|
|
+ <h2 class="panel-title">学员列表</h2>
|
|
|
|
|
+ <div class="search-bar">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="searchKeyword"
|
|
|
|
|
+ placeholder="搜索用户昵称"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @keyup.enter.native="searchUsers"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-button type="primary" size="small" @click="searchUsers">搜索</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- <el-tabs class="live-console-tab-right" v-model="tabLeft.activeName" @tab-click="handleUserClick" :stretch="true">
|
|
|
|
|
|
|
+ <el-tabs class="console-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="panel-scroll" ref="manageLeftRef_al">
|
|
|
|
|
+ <div class="user-list-item" v-for="u in alDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" class="user-avatar" />
|
|
|
|
|
+ <div class="user-meta">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">{{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-popover width="120" trigger="click" popper-class="user-action-popover">
|
|
|
|
|
+ <a class="action-link" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
+ <a class="action-link" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
+ <i class="el-icon-more user-more-btn" slot="reference"></i>
|
|
|
|
|
+ </el-popover>
|
|
|
|
|
+ </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="panel-scroll" ref="manageLeftRef_online">
|
|
|
|
|
+ <div class="user-list-item" v-for="u in onlineDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" class="user-avatar" />
|
|
|
|
|
+ <div class="user-meta">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">{{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-popover width="120" trigger="click" popper-class="user-action-popover">
|
|
|
|
|
+ <a class="action-link" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
+ <a class="action-link" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
+ <i class="el-icon-more user-more-btn" slot="reference"></i>
|
|
|
|
|
+ </el-popover>
|
|
|
|
|
+ </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="panel-scroll" ref="manageLeftRef_offline">
|
|
|
|
|
+ <div class="user-list-item" v-for="u in offlineDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" class="user-avatar" />
|
|
|
|
|
+ <div class="user-meta">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">{{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-popover width="120" trigger="click" popper-class="user-action-popover">
|
|
|
|
|
+ <a class="action-link" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
+ <a class="action-link" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
+ <i class="el-icon-more user-more-btn" slot="reference"></i>
|
|
|
|
|
+ </el-popover>
|
|
|
|
|
+ </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="panel-scroll" ref="manageLeftRef_silenced">
|
|
|
|
|
+ <div class="user-list-item" v-for="u in silencedDisplayList" :key="u.userId">
|
|
|
|
|
+ <el-avatar :size="36" :src="u.avatar" class="user-avatar" />
|
|
|
|
|
+ <div class="user-meta">
|
|
|
|
|
+ <div class="user-name">{{ u.nickName }}</div>
|
|
|
|
|
+ <div class="user-id">{{ u.userId }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-popover width="120" trigger="click" popper-class="user-action-popover">
|
|
|
|
|
+ <a class="action-link" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
+ <a class="action-link" @click="blockUser(u)">拉黑</a>
|
|
|
|
|
+ <i class="el-icon-more user-more-btn" slot="reference"></i>
|
|
|
|
|
+ </el-popover>
|
|
|
|
|
+ </div>
|
|
|
</el-scrollbar>
|
|
</el-scrollbar>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
</el-tabs>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="middle-panel">
|
|
<div class="middle-panel">
|
|
|
- <h2>消息管理</h2>
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- <div class="discussion-messages">
|
|
|
|
|
- <h3>讨论区消息</h3>
|
|
|
|
|
- <div class="message-settings">
|
|
|
|
|
- <label>
|
|
|
|
|
- <input type="checkbox" v-model="globalVisible" @change="globalVisibleChange">
|
|
|
|
|
- 全局用户自见
|
|
|
|
|
- </label>
|
|
|
|
|
|
|
+ <div class="panel-card live-player-card">
|
|
|
|
|
+ <h3 class="section-title">直播观看</h3>
|
|
|
|
|
+ <div class="live-player-wrapper">
|
|
|
|
|
+ <div v-if="showLivePlaceholder" class="live-placeholder">
|
|
|
|
|
+ <span class="live-placeholder-text">{{ livePlaceholderText }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <LivePlayer v-else ref="livePlayer" :videoParam="videoParam" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="message-container" @click="handleMessageBoxClick">
|
|
|
|
|
- <el-scrollbar class="custom-scrollbar" style="height: 500px; width: 100%;" ref="manageRightRef">
|
|
|
|
|
- <el-row v-for="m in msgList" :key="m.msgId">
|
|
|
|
|
- <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 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="m.singleVisible === 1" 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="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 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>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="panel-card ops-panel">
|
|
|
|
|
+ <h3 class="section-title">运营自动化</h3>
|
|
|
|
|
+ <div class="automation-panel">
|
|
|
|
|
+ <p class="section-subtitle">时间轴设置</p>
|
|
|
|
|
+ <div class="automation-body">
|
|
|
|
|
+ <div class="timeline-items">
|
|
|
|
|
+ <div class="timeline-item" v-for="item in timelineItems.slice(0, 2)" :key="item.id || item.absValue">
|
|
|
|
|
+ <div class="timeline-info">
|
|
|
|
|
+ <div class="timeline-time">{{ formatDate(item.absValue) }}</div>
|
|
|
|
|
+ <div class="timeline-action">{{ item.taskName }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <el-avatar :src="m.avatar" style="margin-left: 10px; margin-right: 10px;"/>
|
|
|
|
|
|
|
+ <el-button type="text" size="mini" class="action-link" @click="removeTimelineItem(item)">删除</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- </el-row>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- <!-- 底部留白 -->
|
|
|
|
|
- <div style="height: 20px;"></div>
|
|
|
|
|
- </el-scrollbar>
|
|
|
|
|
- <!-- 加载最新消息按钮 -->
|
|
|
|
|
- <el-button
|
|
|
|
|
- v-if="showLoadLatestBtn"
|
|
|
|
|
- class="load-latest-btn"
|
|
|
|
|
- type="primary"
|
|
|
|
|
- size="small"
|
|
|
|
|
- @click.stop="loadLatestMessages"
|
|
|
|
|
- :disabled="isLoadingLatest"
|
|
|
|
|
- :loading="isLoadingLatest"
|
|
|
|
|
- icon="el-icon-refresh">
|
|
|
|
|
- 加载最新消息
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </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 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>
|
|
|
|
|
|
|
+ <el-empty v-if="!timelineItems.length" description="暂无任务" :image-size="48" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="right-panel">
|
|
<div class="right-panel">
|
|
|
- <h2>运营工具</h2>
|
|
|
|
|
- <div class="live-player">
|
|
|
|
|
- <h3>直播观看</h3>
|
|
|
|
|
- <LivePlayer ref="livePlayer" :videoParam="videoParam" />
|
|
|
|
|
- </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 class="discussion-messages">
|
|
|
|
|
+ <div class="discussion-header">
|
|
|
|
|
+ <h3 class="discussion-title">讨论</h3>
|
|
|
|
|
+ <div class="discussion-header-line"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="discussion-toolbar">
|
|
|
|
|
+ <el-checkbox v-model="globalVisible" @change="globalVisibleChange">全局用户自见</el-checkbox>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="message-container" @click="handleMessageBoxClick">
|
|
|
|
|
+ <el-scrollbar class="msg-scroll" ref="manageRightRef">
|
|
|
|
|
+ <div v-for="(m, index) in msgList" :key="getMsgKey(m, index)" class="msg-item">
|
|
|
|
|
+ <div v-if="isSelfMessage(m)" class="msg-row msg-row--self">
|
|
|
|
|
+ <div class="msg-content msg-content--self">
|
|
|
|
|
+ <div class="msg-nickname">{{ m.nickName }}</div>
|
|
|
|
|
+ <div class="msg-bubble msg-bubble--self">{{ m.msg }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-avatar :size="32" :src="m.avatar" class="msg-avatar" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-else class="msg-row">
|
|
|
|
|
+ <el-avatar :size="32" :src="m.avatar" class="msg-avatar" />
|
|
|
|
|
+ <div class="msg-content">
|
|
|
|
|
+ <div class="msg-nickname">{{ m.nickName }}</div>
|
|
|
|
|
+ <div class="msg-bubble">{{ m.msg }}</div>
|
|
|
|
|
+ <div class="msg-actions">
|
|
|
|
|
+ <a class="action-link" @click.stop="changeUserState(m)">{{ m.msgStatus === 1 ? '解禁' : '禁言' }}</a>
|
|
|
|
|
+ <a class="action-link" @click.stop="blockUser(m)">拉黑</a>
|
|
|
|
|
+ <a class="action-link" @click.stop="singleVisible(m)">{{ isSingleVisible(m) ? '解除用户自见' : '用户自见' }}</a>
|
|
|
|
|
+ <a class="action-link" @click.stop="deleteMsg(m)">删除</a>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="scroll-bottom-space"></div>
|
|
|
|
|
+ </el-scrollbar>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="discussion-input">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ v-model="newMsg"
|
|
|
|
|
+ placeholder="请输入消息..."
|
|
|
|
|
+ :rows="4"
|
|
|
|
|
+ resize="none"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="discussion-input-actions">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-if="showLoadLatestBtn"
|
|
|
|
|
+ class="discussion-action-btn"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="loadLatestMessages"
|
|
|
|
|
+ :disabled="isLoadingLatest"
|
|
|
|
|
+ :loading="isLoadingLatest"
|
|
|
|
|
+ >加载最新</el-button>
|
|
|
|
|
+ <el-button class="discussion-send-btn" type="primary" size="small" @click="sendMessage">发送</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="panel-card watermark">
|
|
|
|
|
+ <h3 class="section-title">直播氛围自动化</h3>
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ :disabled="autoWatermark"
|
|
|
|
|
+ v-model="watermarkTemplate"
|
|
|
|
|
+ placeholder="水军弹幕内容模板,每行一条"
|
|
|
|
|
+ resize="none"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="watermark-options">
|
|
|
|
|
+ <span class="toolbar-label">发送间隔</span>
|
|
|
|
|
+ <el-input-number v-model="interval" :min="1" :disabled="autoWatermark" size="mini" controls-position="right" />
|
|
|
|
|
+ <span class="toolbar-label">秒</span>
|
|
|
|
|
+ <el-checkbox v-model="autoWatermark" @change="changeAutoWatermark">启用水军自动化</el-checkbox>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -285,6 +244,9 @@ export default {
|
|
|
watermarkList:[],
|
|
watermarkList:[],
|
|
|
watermarkTemplate: '',
|
|
watermarkTemplate: '',
|
|
|
liveInfo: {},
|
|
liveInfo: {},
|
|
|
|
|
+ isAudit: false,
|
|
|
|
|
+ liveStatus: 0,
|
|
|
|
|
+ loading: false,
|
|
|
videoParam:{
|
|
videoParam:{
|
|
|
startTime:'',
|
|
startTime:'',
|
|
|
livingUrl: '',
|
|
livingUrl: '',
|
|
@@ -415,7 +377,8 @@ export default {
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
pageSize: 30,
|
|
pageSize: 30,
|
|
|
liveId: null
|
|
liveId: null
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ chatScrollTop: 0
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
@@ -436,6 +399,24 @@ export default {
|
|
|
},
|
|
},
|
|
|
silencedUserLabel() {
|
|
silencedUserLabel() {
|
|
|
return `禁言(${this.userTotal.silenced})`;
|
|
return `禁言(${this.userTotal.silenced})`;
|
|
|
|
|
+ },
|
|
|
|
|
+ showLivePlaceholder() {
|
|
|
|
|
+ if (!this.isAudit) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.liveStatus === 4) {
|
|
|
|
|
+ return !this.videoParam.videoUrl;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.liveStatus !== 2) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.videoParam.liveType === 1) {
|
|
|
|
|
+ return !this.videoParam.livingUrl;
|
|
|
|
|
+ }
|
|
|
|
|
+ return !this.videoParam.videoUrl;
|
|
|
|
|
+ },
|
|
|
|
|
+ livePlaceholderText() {
|
|
|
|
|
+ return '直播未开启';
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
@@ -456,6 +437,29 @@ export default {
|
|
|
this.initScrollListeners();
|
|
this.initScrollListeners();
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
|
|
+ isSameUser(id1, id2) {
|
|
|
|
|
+ if (id1 == null || id2 == null) return false;
|
|
|
|
|
+ return String(id1) === String(id2);
|
|
|
|
|
+ },
|
|
|
|
|
+ isSelfMessage(m) {
|
|
|
|
|
+ return this.isSameUser(m.userId, this.userId);
|
|
|
|
|
+ },
|
|
|
|
|
+ isSingleVisible(m) {
|
|
|
|
|
+ return Number(m.singleVisible) === 1;
|
|
|
|
|
+ },
|
|
|
|
|
+ getMsgKey(m, index) {
|
|
|
|
|
+ if (m.msgId != null && m.msgId !== '') {
|
|
|
|
|
+ return m.msgId;
|
|
|
|
|
+ }
|
|
|
|
|
+ return `msg-${index}-${m.userId || 'unknown'}`;
|
|
|
|
|
+ },
|
|
|
|
|
+ ensureSocket() {
|
|
|
|
|
+ if (!this.socket || !this.socket.ws || this.socket.ws.readyState !== WebSocket.OPEN) {
|
|
|
|
|
+ this.$message.error('WebSocket 未连接,请稍后重试');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ },
|
|
|
// 点击消息框
|
|
// 点击消息框
|
|
|
handleMessageBoxClick() {
|
|
handleMessageBoxClick() {
|
|
|
// 点击消息框时,停止自动滚动
|
|
// 点击消息框时,停止自动滚动
|
|
@@ -496,7 +500,7 @@ export default {
|
|
|
// 强制滚动或启用自动滚动时,直接滚动到底部并隐藏按钮
|
|
// 强制滚动或启用自动滚动时,直接滚动到底部并隐藏按钮
|
|
|
if (forceScroll || this.isAutoScrollEnabled) {
|
|
if (forceScroll || this.isAutoScrollEnabled) {
|
|
|
this.showLoadLatestBtn = false;
|
|
this.showLoadLatestBtn = false;
|
|
|
- this.$refs.manageRightRef.wrap.scrollTop = this.$refs.manageRightRef.wrap.scrollHeight - this.$refs.manageRightRef.wrap.clientHeight
|
|
|
|
|
|
|
+ wrap.scrollTop = wrap.scrollHeight - wrap.clientHeight;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
@@ -517,18 +521,22 @@ export default {
|
|
|
this.loadMsgList();
|
|
this.loadMsgList();
|
|
|
},
|
|
},
|
|
|
singleVisible(m){
|
|
singleVisible(m){
|
|
|
- // 过滤当前所有消息 找到userId的相同的消息 更改他们的自可见状态
|
|
|
|
|
- m.singleVisible= m.singleVisible === 1 ? 0 : 1
|
|
|
|
|
- this.msgList.forEach(m1 => {m1.singleVisible = m1.userId === m.userId ? m.singleVisible : !m.singleVisible})
|
|
|
|
|
- // 消息自可见
|
|
|
|
|
- let msg = {
|
|
|
|
|
|
|
+ const newStatus = this.isSingleVisible(m) ? 0 : 1;
|
|
|
|
|
+ m.singleVisible = newStatus;
|
|
|
|
|
+ this.msgList.forEach(m1 => {
|
|
|
|
|
+ if (this.isSameUser(m1.userId, m.userId)) {
|
|
|
|
|
+ m1.singleVisible = newStatus;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ if (!this.ensureSocket()) return;
|
|
|
|
|
+ const msg = {
|
|
|
liveId: this.liveId,
|
|
liveId: this.liveId,
|
|
|
userId: m.userId,
|
|
userId: m.userId,
|
|
|
userType: 0,
|
|
userType: 0,
|
|
|
cmd: 'singleVisible',
|
|
cmd: 'singleVisible',
|
|
|
- status:m.singleVisible
|
|
|
|
|
- }
|
|
|
|
|
- this.socket.send(JSON.stringify(msg))
|
|
|
|
|
|
|
+ status: newStatus
|
|
|
|
|
+ };
|
|
|
|
|
+ this.socket.send(JSON.stringify(msg));
|
|
|
},
|
|
},
|
|
|
deleteMsg(m){
|
|
deleteMsg(m){
|
|
|
// 1. 弹出确认对话框
|
|
// 1. 弹出确认对话框
|
|
@@ -547,9 +555,10 @@ export default {
|
|
|
let msg = {
|
|
let msg = {
|
|
|
liveId: this.liveId,
|
|
liveId: this.liveId,
|
|
|
userId: m.userId,
|
|
userId: m.userId,
|
|
|
- msg: m.msgId, // 关键:将消息ID发送给后台
|
|
|
|
|
|
|
+ msg: m.msgId,
|
|
|
cmd: 'deleteMsg',
|
|
cmd: 'deleteMsg',
|
|
|
};
|
|
};
|
|
|
|
|
+ if (!this.ensureSocket()) return;
|
|
|
this.socket.send(JSON.stringify(msg));
|
|
this.socket.send(JSON.stringify(msg));
|
|
|
// 可以在这里给用户一个删除成功的提示
|
|
// 可以在这里给用户一个删除成功的提示
|
|
|
this.$message({
|
|
this.$message({
|
|
@@ -566,16 +575,19 @@ export default {
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
- globalVisibleChange( val){
|
|
|
|
|
- // 消息自可见
|
|
|
|
|
- let msg = {
|
|
|
|
|
|
|
+ globalVisibleChange(){
|
|
|
|
|
+ if (!this.ensureSocket()) {
|
|
|
|
|
+ this.globalVisible = !this.globalVisible;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const msg = {
|
|
|
liveId: this.liveId,
|
|
liveId: this.liveId,
|
|
|
userId: '9999',
|
|
userId: '9999',
|
|
|
userType: 0,
|
|
userType: 0,
|
|
|
cmd: 'globalVisible',
|
|
cmd: 'globalVisible',
|
|
|
- status:this.globalVisible ? 1 :0
|
|
|
|
|
- }
|
|
|
|
|
- this.socket.send(JSON.stringify(msg))
|
|
|
|
|
|
|
+ status: this.globalVisible ? 1 : 0
|
|
|
|
|
+ };
|
|
|
|
|
+ this.socket.send(JSON.stringify(msg));
|
|
|
},
|
|
},
|
|
|
changeAutoWatermark( val){
|
|
changeAutoWatermark( val){
|
|
|
this.watermarkList = this.watermarkTemplate
|
|
this.watermarkList = this.watermarkTemplate
|
|
@@ -616,6 +628,7 @@ export default {
|
|
|
if (curMsg.trim() === '') {
|
|
if (curMsg.trim() === '') {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (!this.ensureSocket()) return;
|
|
|
let msg = {
|
|
let msg = {
|
|
|
msg: curMsg,
|
|
msg: curMsg,
|
|
|
liveId: this.liveId,
|
|
liveId: this.liveId,
|
|
@@ -685,10 +698,10 @@ export default {
|
|
|
this.$message.success('顶部消息发送成功');
|
|
this.$message.success('顶部消息发送成功');
|
|
|
},
|
|
},
|
|
|
sendMessage() {
|
|
sendMessage() {
|
|
|
- // 发送前简单校验
|
|
|
|
|
if (this.newMsg.trim() === '') {
|
|
if (this.newMsg.trim() === '') {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (!this.ensureSocket()) return;
|
|
|
|
|
|
|
|
let msg = {
|
|
let msg = {
|
|
|
msg: this.newMsg,
|
|
msg: this.newMsg,
|
|
@@ -711,12 +724,13 @@ export default {
|
|
|
if (cmd === 'sendMsg') {
|
|
if (cmd === 'sendMsg') {
|
|
|
let message = JSON.parse(data.data)
|
|
let message = JSON.parse(data.data)
|
|
|
|
|
|
|
|
- let user = this.alDisplayList.find(u => u.userId === message.userId)
|
|
|
|
|
|
|
+ let user = this.alDisplayList.find(u => this.isSameUser(u.userId, message.userId))
|
|
|
if (user) {
|
|
if (user) {
|
|
|
message.msgStatus = user.msgStatus
|
|
message.msgStatus = user.msgStatus
|
|
|
} else {
|
|
} else {
|
|
|
message.msgStatus = 0
|
|
message.msgStatus = 0
|
|
|
}
|
|
}
|
|
|
|
|
+ message.singleVisible = message.singleVisible == null ? 0 : Number(message.singleVisible)
|
|
|
delete message.params
|
|
delete message.params
|
|
|
if(this.msgList.length > 50){
|
|
if(this.msgList.length > 50){
|
|
|
this.msgList.shift()
|
|
this.msgList.shift()
|
|
@@ -781,6 +795,24 @@ export default {
|
|
|
|
|
|
|
|
} else if (cmd === 'live_end') {
|
|
} else if (cmd === 'live_end') {
|
|
|
|
|
|
|
|
|
|
+ } else if (cmd === 'deleteMsg') {
|
|
|
|
|
+ const msgId = data.msg || data.msgId;
|
|
|
|
|
+ if (msgId != null) {
|
|
|
|
|
+ const index = this.msgList.findIndex(item => String(item.msgId) === String(msgId));
|
|
|
|
|
+ if (index !== -1) {
|
|
|
|
|
+ this.msgList.splice(index, 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (cmd === 'singleVisible') {
|
|
|
|
|
+ const targetUserId = data.userId;
|
|
|
|
|
+ const status = Number(data.status) === 1 ? 1 : 0;
|
|
|
|
|
+ this.msgList.forEach(m1 => {
|
|
|
|
|
+ if (this.isSameUser(m1.userId, targetUserId)) {
|
|
|
|
|
+ m1.singleVisible = status;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } else if (cmd === 'globalVisible') {
|
|
|
|
|
+ this.globalVisible = Number(data.status) === 1 || data.status === true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
@@ -793,7 +825,7 @@ export default {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
this.isAudit = true
|
|
this.isAudit = true
|
|
|
- this.status = res.data.status
|
|
|
|
|
|
|
+ this.liveStatus = res.data.status
|
|
|
this.videoParam.startTime = new Date(res.data.startTime).getTime()
|
|
this.videoParam.startTime = new Date(res.data.startTime).getTime()
|
|
|
if(res.data.status == 4){
|
|
if(res.data.status == 4){
|
|
|
this.videoParam.liveType = 3
|
|
this.videoParam.liveType = 3
|
|
@@ -807,21 +839,16 @@ export default {
|
|
|
if (res.data.liveType == 1) {
|
|
if (res.data.liveType == 1) {
|
|
|
this.videoParam.livingUrl = res.data.flvHlsUrl
|
|
this.videoParam.livingUrl = res.data.flvHlsUrl
|
|
|
this.videoParam.livingUrl = this.videoParam.livingUrl.replace("flv","m3u8")
|
|
this.videoParam.livingUrl = this.videoParam.livingUrl.replace("flv","m3u8")
|
|
|
- // this.$nextTick(() => {
|
|
|
|
|
- // this.initPlayer()
|
|
|
|
|
- // })
|
|
|
|
|
- // this.processInterval = setInterval(this.updateLiveProgress, 1000);
|
|
|
|
|
} else {
|
|
} else {
|
|
|
this.videoParam.liveType = 2
|
|
this.videoParam.liveType = 2
|
|
|
this.videoParam.videoUrl = res.data.videoUrl;
|
|
this.videoParam.videoUrl = res.data.videoUrl;
|
|
|
- // this.$nextTick(() => {
|
|
|
|
|
- // this.initVideoPlayer(res.data.startTime)
|
|
|
|
|
- // })
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
this.$nextTick(() => {
|
|
this.$nextTick(() => {
|
|
|
- this.globalVisible = res.data.globalVisible
|
|
|
|
|
- this.$refs.livePlayer.initPlayer()
|
|
|
|
|
|
|
+ this.globalVisible = res.data.globalVisible === 1 || res.data.globalVisible === true
|
|
|
|
|
+ if (this.$refs.livePlayer) {
|
|
|
|
|
+ this.$refs.livePlayer.initPlayer()
|
|
|
|
|
+ }
|
|
|
})
|
|
})
|
|
|
this.loading = false
|
|
this.loading = false
|
|
|
} else {
|
|
} else {
|
|
@@ -836,9 +863,12 @@ export default {
|
|
|
liveWsUrl: this.liveWsUrl,
|
|
liveWsUrl: this.liveWsUrl,
|
|
|
liveId: this.liveId,
|
|
liveId: this.liveId,
|
|
|
userId: this.userId
|
|
userId: this.userId
|
|
|
|
|
+ }).then((ws) => {
|
|
|
|
|
+ this.socket = ws || this.$store.state.liveWs[this.liveId]
|
|
|
|
|
+ if (this.socket) {
|
|
|
|
|
+ this.socket.onmessage = (event) => this.handleWsMessage(event)
|
|
|
|
|
+ }
|
|
|
})
|
|
})
|
|
|
- this.socket = this.$store.state.liveWs[this.liveId]
|
|
|
|
|
- this.socket.onmessage = (event) => this.handleWsMessage(event)
|
|
|
|
|
},
|
|
},
|
|
|
changeUserState(u) {
|
|
changeUserState(u) {
|
|
|
const displayList = this[`${this.currentTab}DisplayList`];
|
|
const displayList = this[`${this.currentTab}DisplayList`];
|
|
@@ -1166,12 +1196,13 @@ export default {
|
|
|
let totalPage = (total % this.msgParams.pageSize == 0) ? Math.floor(total / this.msgParams.pageSize) : Math.floor(total / this.msgParams.pageSize + 1);
|
|
let totalPage = (total % this.msgParams.pageSize == 0) ? Math.floor(total / this.msgParams.pageSize) : Math.floor(total / this.msgParams.pageSize + 1);
|
|
|
rows.forEach(row => {
|
|
rows.forEach(row => {
|
|
|
if (!this.msgList.some(m => m.msgId === row.msgId)) {
|
|
if (!this.msgList.some(m => m.msgId === row.msgId)) {
|
|
|
- let user = this.alDisplayList.find(u => u.userId === row.userId)
|
|
|
|
|
|
|
+ let user = this.alDisplayList.find(u => this.isSameUser(u.userId, row.userId))
|
|
|
if (user) {
|
|
if (user) {
|
|
|
row.msgStatus = user.msgStatus
|
|
row.msgStatus = user.msgStatus
|
|
|
} else {
|
|
} else {
|
|
|
row.msgStatus = 0
|
|
row.msgStatus = 0
|
|
|
}
|
|
}
|
|
|
|
|
+ row.singleVisible = row.singleVisible == null ? 0 : Number(row.singleVisible)
|
|
|
this.msgList.push(row)
|
|
this.msgList.push(row)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -1179,7 +1210,7 @@ export default {
|
|
|
this.msgList.reverse()
|
|
this.msgList.reverse()
|
|
|
// 同步更新消息列表中相同用户的状态
|
|
// 同步更新消息列表中相同用户的状态
|
|
|
this.alDisplayList.forEach(u => {
|
|
this.alDisplayList.forEach(u => {
|
|
|
- this.msgList.filter(m => m.userId === u.userId).forEach(m => m.msgStatus = u.msgStatus)
|
|
|
|
|
|
|
+ this.msgList.filter(m => this.isSameUser(m.userId, u.userId)).forEach(m => m.msgStatus = u.msgStatus)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 所有消息加载完成后,根据自动滚动状态决定是否滚动
|
|
// 所有消息加载完成后,根据自动滚动状态决定是否滚动
|
|
@@ -1490,296 +1521,500 @@ export default {
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
|
|
+/* 布局 */
|
|
|
.console {
|
|
.console {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- height: 100vh;
|
|
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: #f5f7fa;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.left-panel, .middle-panel, .right-panel {
|
|
|
|
|
- padding: 20px;
|
|
|
|
|
|
|
+.left-panel,
|
|
|
|
|
+.middle-panel,
|
|
|
|
|
+.right-panel {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: 12px;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.left-panel {
|
|
.left-panel {
|
|
|
- width: 30%;
|
|
|
|
|
- background: #f8fafc;
|
|
|
|
|
- border-right: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ width: 22%;
|
|
|
|
|
+ border-right: 1px solid #ebeef5;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.middle-panel {
|
|
.middle-panel {
|
|
|
- width: 40%;
|
|
|
|
|
- background: #f8fafc;
|
|
|
|
|
- border-right: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ width: 48%;
|
|
|
|
|
+ border-right: 1px solid #ebeef5;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.right-panel {
|
|
.right-panel {
|
|
|
width: 30%;
|
|
width: 30%;
|
|
|
- background: #f8fafc;
|
|
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.search {
|
|
|
|
|
- margin: 10px 0;
|
|
|
|
|
|
|
+/* 通用标题与卡片 */
|
|
|
|
|
+.panel-title {
|
|
|
|
|
+ margin: 0 0 8px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.search input {
|
|
|
|
|
- width: 70%;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- border: 1px solid #cbd5e1;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+.section-title {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.search button {
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
|
|
+.section-subtitle {
|
|
|
|
|
+ margin: 0 0 8px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.panel-card {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.toolbar-label {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.action-link {
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ margin-right: 10px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #f56c6c;
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.tabs {
|
|
|
|
|
|
|
+.action-link:hover {
|
|
|
|
|
+ opacity: 0.85;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 学员列表 */
|
|
|
|
|
+.search-bar {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- margin: 10px 0;
|
|
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.tabs button {
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- border: 1px solid #e2e8f0;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.search-bar .el-input {
|
|
|
|
|
+ flex: 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.tabs button.active {
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border-color: #3b82f6;
|
|
|
|
|
|
|
+.console-tabs {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-list {
|
|
|
|
|
- max-height: 600px;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+.console-tabs ::v-deep .el-tabs__content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-item {
|
|
|
|
|
|
|
+.console-tabs ::v-deep .el-tab-pane {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.panel-scroll {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.panel-scroll ::v-deep .el-scrollbar {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.user-list-item {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- padding: 10px;
|
|
|
|
|
- border-bottom: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ padding: 10px 8px;
|
|
|
|
|
+ border-bottom: 1px solid #f2f6fc;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-item img {
|
|
|
|
|
- width: 40px;
|
|
|
|
|
- height: 40px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- margin-right: 10px;
|
|
|
|
|
|
|
+.user-list-item:hover {
|
|
|
|
|
+ background: #f5f7fa;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-info {
|
|
|
|
|
|
|
+.user-meta {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.user-name {
|
|
.user-name {
|
|
|
- font-weight: bold;
|
|
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-status {
|
|
|
|
|
|
|
+.user-id {
|
|
|
|
|
+ margin-top: 2px;
|
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
|
- color: #64748b;
|
|
|
|
|
|
|
+ color: #909399;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.online {
|
|
|
|
|
- color: #10b981;
|
|
|
|
|
|
|
+.user-more-btn {
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ padding: 4px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.offline {
|
|
|
|
|
- color: #94a3b8;
|
|
|
|
|
|
|
+.user-more-btn:hover {
|
|
|
|
|
+ color: #409eff;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-actions {
|
|
|
|
|
|
|
+/* 中间直播画面 */
|
|
|
|
|
+.live-player-card {
|
|
|
|
|
+ flex: 3;
|
|
|
|
|
+ min-height: 0;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ padding: 10px 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.user-actions button {
|
|
|
|
|
- padding: 5px 10px;
|
|
|
|
|
- margin-left: 5px;
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+.live-player-card .section-title {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.block {
|
|
|
|
|
- background: #ef4444;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.unblock {
|
|
|
|
|
- background: #10b981;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+.live-player-wrapper {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.mute {
|
|
|
|
|
- background: #f59e0b;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+.live-placeholder {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ max-height: 100%;
|
|
|
|
|
+ background: #000;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.unmute {
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
|
|
+.live-placeholder-text {
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
|
|
+ font-size: 26px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ letter-spacing: 3px;
|
|
|
|
|
+ user-select: none;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.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);
|
|
|
|
|
|
|
+.live-player-card ::v-deep .live-player {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ max-height: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ background: #000;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.system-messages textarea {
|
|
|
|
|
|
|
+.live-player-card ::v-deep .player {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
- height: 100px;
|
|
|
|
|
- border: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ max-height: 100%;
|
|
|
|
|
+ min-height: 0;
|
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
|
- padding: 8px;
|
|
|
|
|
|
|
+ object-fit: contain;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 运营自动化 */
|
|
|
|
|
+.ops-panel {
|
|
|
|
|
+ flex: 2;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ padding: 8px 10px 10px;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-actions {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+.ops-panel .section-title {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-actions button {
|
|
|
|
|
- padding: 5px 10px;
|
|
|
|
|
- margin-right: 5px;
|
|
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
|
|
+.automation-panel {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.automation-body {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.timeline-items {
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.timeline-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding: 8px 0;
|
|
|
|
|
+ border-bottom: 1px solid #f2f6fc;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.timeline-time {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.timeline-action {
|
|
|
|
|
+ margin-top: 2px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 讨论区 */
|
|
|
|
|
+.discussion-messages {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.discussion-header {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ padding: 12px 16px 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-list {
|
|
|
|
|
- max-height: 300px;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+.discussion-title {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #36cfc9;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.discussion-header-line {
|
|
|
margin-top: 10px;
|
|
margin-top: 10px;
|
|
|
|
|
+ height: 2px;
|
|
|
|
|
+ background: #36cfc9;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-item {
|
|
|
|
|
|
|
+.discussion-toolbar {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
- padding-bottom: 10px;
|
|
|
|
|
- border-bottom: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ padding: 8px 16px 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-avatar img {
|
|
|
|
|
- width: 30px;
|
|
|
|
|
- height: 30px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- margin-right: 10px;
|
|
|
|
|
|
|
+.message-container {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: 8px 16px 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-content {
|
|
|
|
|
|
|
+.msg-scroll {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ height: auto;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-user {
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
|
|
+.msg-scroll ::v-deep .el-scrollbar {
|
|
|
|
|
+ height: 100%;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-text {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #64748b;
|
|
|
|
|
|
|
+.msg-scroll ::v-deep .el-scrollbar__wrap {
|
|
|
|
|
+ height: 100% !important;
|
|
|
|
|
+ max-height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-item + .msg-item {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-row--self {
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-content {
|
|
|
|
|
+ max-width: 72%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-content--self {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.message-actions button {
|
|
|
|
|
- padding: 3px 8px;
|
|
|
|
|
|
|
+.msg-nickname {
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
|
- background: #3b82f6;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: none;
|
|
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-bubble {
|
|
|
|
|
+ padding: 8px 10px;
|
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+ background: #f4f4f5;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ word-break: break-word;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-bubble--self {
|
|
|
|
|
+ background: #ecf5ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-actions {
|
|
|
|
|
+ margin-top: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.msg-avatar {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.live-player, .automation, .watermark {
|
|
|
|
|
- margin: 20px 0;
|
|
|
|
|
|
|
+.scroll-bottom-space {
|
|
|
|
|
+ height: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.discussion-input {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ padding: 12px 16px 16px;
|
|
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
background: #fff;
|
|
background: #fff;
|
|
|
- padding: 15px;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.timeline-items {
|
|
|
|
|
- margin: 10px 0;
|
|
|
|
|
|
|
+.discussion-input ::v-deep .el-textarea__inner {
|
|
|
|
|
+ background: #f5f5f5;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #303133;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.timeline-item {
|
|
|
|
|
|
|
+.discussion-input ::v-deep .el-textarea__inner:focus {
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ box-shadow: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.discussion-input-actions {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding: 8px 0;
|
|
|
|
|
- border-bottom: 1px solid #e2e8f0;
|
|
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-top: 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.delete {
|
|
|
|
|
- background: #ef4444;
|
|
|
|
|
|
|
+.discussion-action-btn {
|
|
|
|
|
+ background: #36cfc9;
|
|
|
|
|
+ border-color: #36cfc9;
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 3px 8px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.add {
|
|
|
|
|
- background: #10b981;
|
|
|
|
|
|
|
+.discussion-action-btn:hover,
|
|
|
|
|
+.discussion-action-btn:focus {
|
|
|
|
|
+ background: #2eb8ab;
|
|
|
|
|
+ border-color: #2eb8ab;
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.watermark-settings textarea {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100px;
|
|
|
|
|
- border: 1px solid #e2e8f0;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
|
|
+.discussion-send-btn {
|
|
|
|
|
+ background: #36cfc9;
|
|
|
|
|
+ border-color: #36cfc9;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.discussion-send-btn:hover,
|
|
|
|
|
+.discussion-send-btn:focus {
|
|
|
|
|
+ background: #2eb8ab;
|
|
|
|
|
+ border-color: #2eb8ab;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 右侧氛围自动化 */
|
|
|
|
|
+.watermark {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ margin: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.watermark-options {
|
|
.watermark-options {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 8px;
|
|
|
margin-top: 10px;
|
|
margin-top: 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.watermark-options label {
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-bottom: 5px;
|
|
|
|
|
-}
|
|
|
|
|
-/* 隐藏 el-scrollbar 的横向滚动条 */
|
|
|
|
|
-.el-scrollbar__wrap {
|
|
|
|
|
- overflow-x: hidden !important;
|
|
|
|
|
|
|
+/* 隐藏滚动条,保留滚动能力 */
|
|
|
|
|
+.console ::v-deep .el-scrollbar__bar {
|
|
|
|
|
+ display: none !important;
|
|
|
}
|
|
}
|
|
|
-.custom-scrollbar .el-scrollbar__wrap {
|
|
|
|
|
|
|
+
|
|
|
|
|
+.console ::v-deep .el-scrollbar__wrap {
|
|
|
overflow-x: hidden !important;
|
|
overflow-x: hidden !important;
|
|
|
|
|
+ scrollbar-width: none;
|
|
|
|
|
+ -ms-overflow-style: none;
|
|
|
}
|
|
}
|
|
|
-.scrollbar-demo-item{
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- height: 50px;
|
|
|
|
|
- margin: 10px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+
|
|
|
|
|
+.console ::v-deep .el-scrollbar__wrap::-webkit-scrollbar {
|
|
|
|
|
+ width: 0;
|
|
|
|
|
+ height: 0;
|
|
|
|
|
+ display: none;
|
|
|
}
|
|
}
|
|
|
-.message-container {
|
|
|
|
|
- position: relative;
|
|
|
|
|
|
|
+</style>
|
|
|
|
|
+
|
|
|
|
|
+<style>
|
|
|
|
|
+.user-action-popover .action-link {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin: 0 0 8px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
}
|
|
|
-.load-latest-btn {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- bottom: 20px;
|
|
|
|
|
- right: 20px;
|
|
|
|
|
- z-index: 10;
|
|
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
|
+
|
|
|
|
|
+.user-action-popover .action-link:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|