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.

856 lines
24 KiB

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