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.

825 lines
23 KiB

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