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.

900 lines
26 KiB

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