You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

778 lines
23 KiB

5 months ago
5 months ago
2 months ago
2 months ago
2 months ago
5 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <script setup>
  2. // 导航栏在这
  3. import {computed, onMounted, onUnmounted, ref} from 'vue'
  4. import {useRoute, useRouter} from 'vue-router'
  5. import {ElMessage} from 'element-plus'
  6. import ChangePassword from '@/components/dialogs/changePassword.vue'
  7. import {useAdminStore} from '@/store'
  8. import {storeToRefs} from 'pinia'
  9. import {filterMenu, getRoutePath} from "@/utils/menuUtils.js";
  10. // 刷新数据功能
  11. import API from '@/util/http.js'
  12. import bell from '@/assets/SvgIcons/bell.svg'
  13. import noMessage from '@/assets/images/no-message.svg'
  14. // 使用import.meta.glob导入所有SVG图标(修复版本)
  15. const icons = import.meta.glob('@/assets/SvgIcons/*.svg', {eager: true})
  16. const menuNameMap = {
  17. '工作台': 'workbench',
  18. '金币管理': 'gold-management',
  19. '现金管理': 'cash-management',
  20. '活动管理': 'activity-management',
  21. '频道管理': 'channel-management',
  22. '权限管理': 'permission-management',
  23. }
  24. // 创建获取图标路径的函数(修复版本)
  25. const getIconPath = (menuName) => {
  26. const englishName = menuNameMap[menuName] || menuName;
  27. // 构建可能的key格式
  28. const possibleKeys = [
  29. `@/assets/SvgIcons/${englishName}.svg`,
  30. `./SvgIcons/${englishName}.svg`,
  31. `/src/assets/SvgIcons/${englishName}.svg`
  32. ]
  33. // 在icons对象中查找对应的图标
  34. for (const key of possibleKeys) {
  35. if (icons[key]) {
  36. const iconModule = icons[key]
  37. // 返回模块的default属性或直接返回值
  38. return iconModule.default || iconModule
  39. }
  40. }
  41. }
  42. // 数据刷新功能实现
  43. const refreshData = async () => {
  44. try {
  45. // 显示加载提示,duration保持打开提示不自动关闭,showclose显示未×,点击可关闭提示
  46. ElMessage({message: '数据刷新中,请稍候...', type: 'info'});
  47. // 调用Mysql接口获取最新数据
  48. const response = await API({url: '/Mysql', method: 'POST', data: {}});
  49. if (response && response.code === 200) {
  50. const currentRoute = route.fullPath;
  51. router.replace('/blank'); // 临时跳转到一个空页面
  52. setTimeout(() => {
  53. router.replace(currentRoute); // 跳转回原页面
  54. }, 10);
  55. ElMessage.success('数据刷新成功');
  56. } else {
  57. ElMessage.error('数据刷新失败:' + (response?.msg || '未知错误'));
  58. }
  59. } catch (error) {
  60. console.error('数据刷新异常:', error);
  61. ElMessage.error('数据刷新异常,请重试');
  62. }
  63. }
  64. // 存储接口返回的菜单数据
  65. const menuList = ref([])
  66. // 获取仓库实例
  67. const adminStore = useAdminStore()
  68. // 解构状态(保持响应式) 获得 adminData(用户信息) 和 menuTree(菜单树)
  69. const {adminData, menuTree, flag} = storeToRefs(adminStore)
  70. // 筛选权限菜单 ,menuTree 是组件通信拿的
  71. menuList.value = filterMenu(menuTree.value)
  72. console.log("menuList", menuList.value)
  73. // 获取当前路由
  74. const route = useRoute()
  75. // 通用函数:从菜单树中递归找出最匹配的 index
  76. function findBestMatch(menuList, path) {
  77. let bestMatch = ''
  78. function traverse(menus) {
  79. for (const item of menus) {
  80. const itemPath = getRoutePath(item)
  81. // 如果当前菜单的 path 是当前路径的前缀,可能是候选项
  82. if (path.startsWith(itemPath) && itemPath.length > bestMatch.length) {
  83. bestMatch = itemPath
  84. }
  85. if (item.children && item.children.length > 0) {
  86. traverse(item.children)
  87. }
  88. }
  89. }
  90. traverse(menuList)
  91. return bestMatch || path // fallback 到当前路径
  92. }
  93. // 响应式高亮菜单
  94. const activeMenu = computed(() => {
  95. return findBestMatch(menuList.value, route.path)
  96. })
  97. const router = useRouter()
  98. const messageVisible = ref(false)
  99. // 查看个人信息弹出框
  100. const openMessage = function () {
  101. messageVisible.value = true
  102. }
  103. // 关闭个人信息
  104. const closeMessage = function () {
  105. messageVisible.value = false
  106. }
  107. const message = function () {
  108. openMessage()
  109. }
  110. // 显示修改密码弹窗
  111. const showPasswordDialog = ref(false)
  112. const pwdRef = ref()
  113. //打开修改密码弹窗
  114. const openChangePassword = () => {
  115. showPasswordDialog.value = true
  116. }
  117. //关闭后清空密码表单
  118. function onPwdDialogClosed() {
  119. // 调用子组件暴露的 resetFields
  120. pwdRef.value?.resetFields()
  121. }
  122. function logout() {
  123. const machineId = localStorage.getItem('machineId')
  124. localStorage.removeItem('token')
  125. adminStore.clearState()
  126. router.push('/login?machineId=' + machineId)
  127. ElMessage.success('退出成功')
  128. }
  129. // 切换员工数据开关状态
  130. const toggleFlag = () => {
  131. const newFlag = flag.value === 1 ? 0 : 1
  132. adminStore.setFlag(newFlag)
  133. ElMessage.success(newFlag === 1 ? '员工数据已隐藏' : '员工数据已显示')
  134. console.log('flag', newFlag)
  135. }
  136. // 消息推送逻辑
  137. // 控制 MessageDialog 显示状态
  138. const messageNum = ref(0)
  139. const showMessageDialog = ref(false)
  140. // 消息小红点状态
  141. const messageDot = ref(true)
  142. // 点击铃铛图标时打开
  143. const openMessageDialog = () => {
  144. showMessageDialog.value = true
  145. messageDot.value = false
  146. }
  147. // 点击关闭时关闭
  148. const closeMessageDialog = () => {
  149. showMessageDialog.value = false
  150. }
  151. // 模拟消息数据
  152. const messageList = ref([
  153. {id: 1, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2分钟前', group: '今天'},
  154. {id: 2, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '1小时前', group: '今天'},
  155. {id: 3, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '昨天 09:30', group: '昨天'},
  156. {id: 4, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '昨天 08:30', group: '昨天'},
  157. {id: 5, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  158. {id: 6, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  159. {id: 7, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  160. {id: 8, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  161. {id: 9, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  162. {id: 10, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  163. {id: 11, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  164. {id: 12, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  165. {id: 13, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  166. {id: 14, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  167. {id: 15, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  168. {id: 16, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
  169. ]);
  170. // 按时间分组
  171. const groupedMessages = ref({});
  172. messageList.value.forEach(item => {
  173. if (!groupedMessages.value[item.group]) {
  174. groupedMessages.value[item.group] = [];
  175. }
  176. groupedMessages.value[item.group].push(item);
  177. });
  178. // 控制是否显示全部
  179. const showAll = ref(false);
  180. // 计算属性:根据showAll状态返回需要显示的消息
  181. const displayMessages = computed(() => {
  182. if (showAll.value) {
  183. return groupedMessages.value;
  184. }
  185. // 默认只展示前两条消息(跨分组)
  186. const limitedMessages = {};
  187. let count = 0;
  188. // 保持分组顺序(今天 -> 昨天 -> 更早)
  189. const groupOrder = ['今天', '昨天', '更早'];
  190. for (const groupName of groupOrder) {
  191. const group = groupedMessages.value[groupName];
  192. if (!group) continue;
  193. limitedMessages[groupName] = [];
  194. for (const item of group) {
  195. if (count < 2) {
  196. limitedMessages[groupName].push(item);
  197. count++;
  198. } else {
  199. break;
  200. }
  201. }
  202. // 如果该分组没有数据了就删除
  203. if (limitedMessages[groupName].length === 0) {
  204. delete limitedMessages[groupName];
  205. }
  206. if (count >= 2) break;
  207. }
  208. return limitedMessages;
  209. });
  210. // 切换显示全部/收起
  211. const toggleShowAll = () => {
  212. showAll.value = !showAll.value;
  213. };
  214. // 控制返回顶部按钮状态
  215. const scrollContainer = ref(null)
  216. const scrollToTop = () => {
  217. scrollContainer.value?.scrollTo({top: 0, behavior: 'smooth'})
  218. }
  219. </script>
  220. <template>
  221. <div class="main-container">
  222. <iframe src="http://192.168.40.8:8081/Money/ceshi" style="display:none"></iframe>
  223. <!-- 背景毛玻璃层作为内容容器 -->
  224. <div class="background-glass">
  225. <!-- 侧边栏 -->
  226. <div class="sidebar-container">
  227. <el-aside class="sidebar-layout">
  228. <div class="logo">
  229. <img src="../assets/新logo.png" alt="logo" style="width: 9vh; height: 9vh"/>
  230. </div>
  231. <div class="menu-scroll-container">
  232. <el-menu :router="true" :default-active="activeMenu" style="min-height: 80vh;border:none;">
  233. <!-- 递归渲染菜单层级 -->
  234. <template v-for="menu in menuList" :key="menu.id">
  235. <!-- 有子菜单的父级菜单menuType=2 且存在children -->
  236. <el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.id.toString()">
  237. <template #title>
  238. <img
  239. :src="getIconPath(menu.menuName)"
  240. :alt="`${menu.menuName}图标`"
  241. style="width: 4vh; height: 4vh; margin-right: 4px;"
  242. >
  243. <span>{{ menu.menuName }}</span>
  244. </template>
  245. <!-- 子菜单 -->
  246. <template v-for="child in menu.children" :key="child.id">
  247. <!-- 子菜单为叶子节点无children -->
  248. <el-menu-item v-if="!child.children || child.children.length === 0" :index="getRoutePath(child)">
  249. <el-icon style="margin-right: 4px;">
  250. <Folder/>
  251. </el-icon>
  252. <span>{{ child.menuName }}</span>
  253. </el-menu-item>
  254. <!-- 子菜单有下级 -->
  255. <el-sub-menu v-else :index="child.id.toString()">
  256. <template #title>
  257. <el-icon style="margin-right: 4px;">
  258. <Folder/>
  259. </el-icon>
  260. <span>{{ child.menuName }}</span>
  261. </template>
  262. <!-- 递归 下一级-->
  263. <template v-for="grandChild in child.children" :key="grandChild.id">
  264. <el-menu-item :index="getRoutePath(grandChild)">
  265. <el-icon style="margin-right: 4px;">
  266. <Folder/>
  267. </el-icon>
  268. <span>{{ grandChild.menuName }}</span>
  269. </el-menu-item>
  270. </template>
  271. </el-sub-menu>
  272. </template>
  273. </el-sub-menu>
  274. <!-- 无子菜单的一级菜单 -->
  275. <el-menu-item v-else :index="getRoutePath(menu)">
  276. <img
  277. :src="getIconPath(menu.menuName)"
  278. :alt="`${menu.menuName}图标`"
  279. style="width: 4vh; height: 4vh; margin-right: 4px;"
  280. >
  281. <span>{{ menu.menuName }}</span>
  282. </el-menu-item>
  283. </template>
  284. </el-menu>
  285. </div>
  286. <div style="display: flex">
  287. <!-- 底部固定的设置中心 -->
  288. <div class="settings-container">
  289. <el-dropdown placement="top-start">
  290. <span class="el-dropdown-link">
  291. <!-- 暂时使用静态路径确保设置图标正常显示 -->
  292. <img src="@/assets/SvgIcons/设置.svg" alt="设置" style="width: 4vh; height: 4vh"/>
  293. <span>设置中心</span>
  294. <el-icon class="arrow-icon">
  295. <ArrowUp/>
  296. </el-icon>
  297. </span>
  298. <template #dropdown>
  299. <el-dropdown-menu>
  300. <el-dropdown-item @click="refreshData()">数据刷新</el-dropdown-item>
  301. <!-- 员工数据开关 -->
  302. <el-dropdown-item @click="toggleFlag()">
  303. {{ flag === 1 ? '显示员工数据' : '隐藏员工数据' }}
  304. </el-dropdown-item>
  305. <el-dropdown-item @click="message()">查看个人信息</el-dropdown-item>
  306. <el-dropdown-item @click="openChangePassword">修改密码</el-dropdown-item>
  307. <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
  308. </el-dropdown-menu>
  309. </template>
  310. </el-dropdown>
  311. </div>
  312. <!-- 消息提示 这里的 小红点不用el-badge 他在切换状态会抽一下 应该是dom问题 -->
  313. <div class="message-container">
  314. <div style="position: relative;">
  315. <el-image :src="bell" style="width: 28px; height: 28px;" @click="openMessageDialog"></el-image>
  316. <span v-show="messageDot" class="dot"></span>
  317. </div>
  318. </div>
  319. </div>
  320. </el-aside>
  321. </div>
  322. <!-- 右侧内容区域 -->
  323. <div class="content-container">
  324. <!-- 头部
  325. <el-header class="header">
  326. </el-header> -->
  327. <!-- 主内容区域 -->
  328. <div class="main-area">
  329. <el-main>
  330. <router-view></router-view>
  331. </el-main>
  332. </div>
  333. </div>
  334. </div>
  335. <!-- 查看个人信息 -->
  336. <el-dialog v-model="messageVisible" title="查看个人信息" width="500px">
  337. <el-form :model="adminData">
  338. <el-form-item label="用户姓名" label-width="100px" label-position="left">
  339. <span class="message-font">{{ adminData.adminName }}</span>
  340. </el-form-item>
  341. <el-form-item label="精网号" label-width="100px" label-position="left">
  342. <span class="message-font">{{ adminData.account }}</span>
  343. </el-form-item>
  344. <el-form-item label="地区" label-width="100px" label-position="left">
  345. <span class="message-font">{{ adminData.markets }}</span>
  346. </el-form-item>
  347. <el-form-item label="注册时间" label-width="100px" label-position="left">
  348. <span class="message-font">{{ adminData.createTime }}</span>
  349. </el-form-item>
  350. </el-form>
  351. <template #footer>
  352. <div>
  353. <el-button text @click="closeMessage()">关闭</el-button>
  354. </div>
  355. </template>
  356. </el-dialog>
  357. <!-- 自定义密码修改弹窗组件 -->
  358. <el-dialog v-model="showPasswordDialog" :center="true" width="470px" @closed="onPwdDialogClosed">
  359. <ChangePassword ref="pwdRef" @confirm="showPasswordDialog = false"/>
  360. </el-dialog>
  361. <!--消息推送的弹窗-->
  362. <el-dialog style="background: #F3FAFE" v-model="showMessageDialog" title="" width="500px">
  363. <div class="message-title">
  364. <el-divider
  365. class="divider"
  366. direction="vertical"
  367. ></el-divider>
  368. 消息中心 ({{ messageNum }})
  369. </div>
  370. <!-- todo 这是为了样式显示 一定要改逻辑-->
  371. <div v-if="messageNum !== 0">
  372. <div class="no-message">
  373. <el-image :src="noMessage"></el-image>
  374. <p class="no-message-text">暂无未办消息快去处理工作吧</p>
  375. </div>
  376. </div>
  377. <div v-else
  378. ref="scrollContainer"
  379. style="max-height: 60vh; overflow-y: auto;">
  380. <!-- 按时间分组的消息列表 -->
  381. <div
  382. v-for="(group, time) in displayMessages"
  383. :key="time"
  384. style="margin-bottom: 16px;"
  385. >
  386. <div class="time-header">
  387. {{ time }} <span class="little-dot"></span>
  388. <el-divider
  389. style="height: 2px; align-self: stretch;flex: 1;background: #CEE5FE; border: none;"
  390. ></el-divider>
  391. </div>
  392. <div
  393. v-for="item in group"
  394. :key="item.id"
  395. class="message-item"
  396. >
  397. <div style="display: flex; margin-bottom: 10px">
  398. <span class="red-dot"></span>
  399. <span class="message-card-title">{{ item.title }}</span>
  400. <div class="message-time">{{ item.time }}</div>
  401. </div>
  402. <p class="message-desc">{{ item.desc }}</p>
  403. <el-button
  404. type="primary"
  405. style="margin: 0 auto; display: block;"
  406. >
  407. 前往查看
  408. </el-button>
  409. </div>
  410. </div>
  411. <!-- 控制按钮 -->
  412. <div class="message-actions">
  413. <el-button
  414. type="text"
  415. class="view-all"
  416. @click="toggleShowAll"
  417. >
  418. {{ showAll ? '收起' : '查看全部' }}
  419. </el-button>
  420. <image src="@" @click="scrollToTop" style="width: 20px; height: 20px;"/>
  421. 返回顶部
  422. </div>
  423. </div>
  424. </el-dialog>
  425. </div>
  426. </template>
  427. <style scoped>
  428. /* 主容器,设置背景图并居中 */
  429. .main-container {
  430. position: fixed;
  431. top: 0;
  432. left: 0;
  433. right: 0;
  434. bottom: 0;
  435. background-image: url('@/assets/backgroundBlue.png');
  436. background-size: cover;
  437. background-position: center center;
  438. background-repeat: no-repeat;
  439. overflow: hidden;
  440. }
  441. /* 背景毛玻璃层(作为内容容器) */
  442. .background-glass {
  443. position: absolute;
  444. top: 1vh;
  445. left: 1vh;
  446. right: 1vh;
  447. bottom: 1vh;
  448. background-image: url('@/assets/半透明background.png');
  449. background-size: cover;
  450. z-index: 1;
  451. display: flex;
  452. flex-direction: row;
  453. padding: 10px;
  454. border-radius: 12px;
  455. }
  456. /* 侧边栏容器 */
  457. .sidebar-container {
  458. flex-shrink: 0;
  459. }
  460. .logo {
  461. display: flex;
  462. align-items: center;
  463. justify-content: center;
  464. height: 12vh;
  465. }
  466. /* 中间可滚动菜单容器 */
  467. .menu-scroll-container {
  468. flex: 1;
  469. overflow-y: auto;
  470. padding: 10px 0;
  471. }
  472. /* 底部设置中心样式 */
  473. .settings-container {
  474. padding: 10px 0 10px 20px; /* 上,右, 下,左 */
  475. display: flex;
  476. align-items: center; /* 垂直居中 */
  477. }
  478. /* 调整下拉菜单的样式,确保它向上弹出 */
  479. .el-dropdown-link:focus {
  480. /* 移除底部的异常效果 */
  481. outline: none;
  482. text-decoration: none;
  483. }
  484. .el-dropdown-link {
  485. display: flex;
  486. align-items: center;
  487. cursor: pointer;
  488. gap: 10px; /* 图标和文字左右间距 */
  489. }
  490. .sidebar-layout {
  491. width: 15vw;
  492. height: 100%;
  493. background: #E7F4FD; /* 浅蓝色背景 */
  494. /* backdrop-filter: blur(5px); 毛玻璃效果 --消耗性能 */
  495. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); /* 添加阴影增强层次感 */
  496. border-radius: 12px;
  497. display: flex;
  498. flex-direction: column;
  499. position: relative;
  500. transition: all 0.3s ease;
  501. }
  502. /* 内容区域容器 */
  503. .content-container {
  504. flex: 1;
  505. display: flex;
  506. flex-direction: column;
  507. margin-left: 5px;
  508. gap: 5px;
  509. height: 100%;
  510. overflow: hidden;
  511. }
  512. /* 主内容区域容器 */
  513. .main-area {
  514. flex: 1;
  515. background: #E7F4FD;
  516. /* 半透明浅色背景 */
  517. /* backdrop-filter: blur(5px); */
  518. /* 毛玻璃效果 */
  519. border-radius: 12px;
  520. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  521. /* 添加阴影增强层次感 */
  522. overflow: hidden;
  523. display: flex;
  524. flex-direction: column;
  525. }
  526. /* 主内容区域样式 */
  527. .el-main {
  528. height: 100%;
  529. padding: 1px 8px 10px 8px;
  530. background: transparent;
  531. overflow-y: auto;
  532. /* 应用自定义滚动条 */
  533. }
  534. /* 确保el-menu撑满容器 */
  535. .sidebar-layout .el-menu {
  536. width: 100%;
  537. }
  538. /* 侧边栏菜单样式 适配浅色背景 */
  539. .el-menu {
  540. background: transparent !important;
  541. }
  542. /* 工作台,金币管理,现金管理 */
  543. ::v-deep(.el-sub-menu__title:hover),
  544. ::v-deep(.el-menu-item:hover) {
  545. background: #E5EBFE;
  546. }
  547. /* 子菜单展开时和背景同色 */
  548. ::v-deep(.el-sub-menu__title),
  549. ::v-deep(.el-menu-item) {
  550. background: #E7F4FD;
  551. }
  552. .message-font {
  553. /* 个人信息字体样式 */
  554. font-size: 16px;
  555. font-weight: bold;
  556. }
  557. /* 确保全局el-container适应容器 */
  558. :deep(.el-container) {
  559. /* vue3的深度选择器,用于覆盖element-plus的默认样式 */
  560. min-height: 100%;
  561. width: 100%;
  562. background: transparent;
  563. }
  564. /* 为侧边栏和主内容区域添加滚动条样式 */
  565. .menu-scroll-container,
  566. .el-main {
  567. scrollbar-width: thin;
  568. /* Firefox */
  569. scrollbar-color: rgba(0, 0, 0, 0.3) rgba(255, 255, 255, 0.2);
  570. /* Firefox滑块和轨道颜色 */
  571. }
  572. /* 小红点 */
  573. .dot {
  574. position: absolute;
  575. top: -2px;
  576. right: -2px;
  577. width: 8px;
  578. height: 8px;
  579. background: #F23C39;
  580. border-radius: 50%;
  581. }
  582. /* 消息中心整体容器 */
  583. .message-container {
  584. padding: 10px 50px 10px 50px; /* 上,右, 下,左 */
  585. display: flex;
  586. align-items: center; /* 垂直居中 */
  587. }
  588. /* 消息中心标题 */
  589. .message-title {
  590. display: flex;
  591. font-size: 16px;
  592. color: black;
  593. .divider {
  594. align-items: flex-start;
  595. gap: 36px;
  596. align-self: stretch;
  597. height: 20px;
  598. border-left: 3px solid #266EFF;
  599. }
  600. }
  601. /* 无消息的样式 */
  602. .no-message {
  603. text-align: center;
  604. position: relative;
  605. .no-message-text {
  606. position: absolute;
  607. top: 60%;
  608. left: 50%;
  609. /* 水平垂直居中 */
  610. transform: translate(-50%, -50%);
  611. /* 文字样式 */
  612. font-weight: bold;
  613. margin: 0;
  614. /* 可添加更多样式(如字体大小、阴影等) */
  615. font-size: 14px;
  616. }
  617. }
  618. /* 有消息的样式 */
  619. .time-header {
  620. font-size: 14px;
  621. color: #666;
  622. display: flex;
  623. align-items: center;
  624. gap: 8px;
  625. }
  626. .message-item {
  627. height: 100px;
  628. padding: 10px 10px 10px 10px;
  629. border-radius: 4px;
  630. border: 1px solid #E5E5E5;
  631. background: #FCFEFF;
  632. margin-bottom: 10px;
  633. }
  634. .message-card-title {
  635. font-weight: bold;
  636. margin-right: 4px;
  637. }
  638. /* 圆点样式 */
  639. .red-dot {
  640. width: 6px;
  641. height: 6px;
  642. margin-right: 9px;
  643. border-radius: 50%;
  644. background-color: red;
  645. }
  646. .message-desc {
  647. font-size: 13px;
  648. color: #666;
  649. margin: 4px 0 15px 15px;
  650. }
  651. .message-time {
  652. margin-left: auto;
  653. font-size: 13px;
  654. color: #999;
  655. }
  656. .view-all {
  657. display: block;
  658. margin: 0 auto 16px;
  659. }
  660. .little-dot {
  661. display: inline-block;
  662. width: 8px; /* 圆点直径 */
  663. height: 8px;
  664. border-radius: 50%; /* 圆形 */
  665. background-color: #CEE5FE;;
  666. }
  667. </style>