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.

841 lines
23 KiB

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