Browse Source

add:暂时隐藏数据刷新

add:消息推送接口对接
add:频道校验暂时关闭
lihui/feature-20251104165712-现金二期^2
lihui 2 weeks ago
parent
commit
d49019cb50
  1. 3
      src/main.ts
  2. 25
      src/router/index.js
  3. 33
      src/store/index.js
  4. 71
      src/utils/getMessage.js
  5. 36
      src/utils/goToCheck.js
  6. 275
      src/views/home.vue
  7. 20
      src/views/permissions/rolePermission.vue

3
src/main.ts

@ -17,6 +17,7 @@ import {useAdminStore} from './store'
import request from "@/util/request";
import "./global.css";
import '@/assets/css/btn.css';
import {useMessageStore} from "@/store";
const app = createApp(App)
@ -43,4 +44,6 @@ app.use(ElementPlus, {
// 在 app 挂载之后再使用 store
const adminStore = useAdminStore()
const messageStore = useMessageStore()
adminStore.initFromLocalStorage()
messageStore.initFromLocalStorage()

25
src/router/index.js

@ -1,7 +1,7 @@
import {createRouter, createWebHashHistory} from 'vue-router';
import axios from "axios";
import {storeToRefs} from "pinia";
import { useAdminStore } from "@/store/index.js";
import {useAdminStore, useMessageStore} from "@/store/index.js";
import API from '@/util/http.js';
// 路由定义(包含权限映射 meta.permissionId)
@ -457,7 +457,6 @@ const getAllPermissionIds = (menuTree) => {
return permissionIds;
};
// 全局路由守卫
router.beforeEach(async (to, from, next) => {
@ -520,4 +519,24 @@ router.beforeEach(async (to, from, next) => {
next();
});
// 全局后置守卫:每次路由切换后执行
router.afterEach(async () => {
try {
// 执行/getMessage请求
const newMessageRes = await API({
url: '/getMessage',
method: 'POST',
data: {}
});
console.log('newMessageRes=======================:', newMessageRes.data)
// 存入全局状态,供所有页面访问
const messageStore = useMessageStore();
// 过滤 flag=1的消息
newMessageRes.data = newMessageRes.data.filter(item => item.flag !== 1);
messageStore.setMessages(newMessageRes.data);
} catch (error) {
console.error('获取消息失败:', error);
}
});
export default router;

33
src/store/index.js

@ -1,4 +1,6 @@
// src/store/index.js
// useMessageStore
// 引入Pinia的defineStore
import {defineStore} from 'pinia'
export const useAdminStore = defineStore('admin', {
@ -69,3 +71,34 @@ export const useAdminStore = defineStore('admin', {
}
}
})
// 最基础的messageStore
// 模仿adminStore风格的messageStore
export const useMessageStore = defineStore('messages', {
state: () => ({
messages: [], // 消息列表数据
}),
actions: {
// 设置消息列表并同步到localStorage
setMessages(data) {
this.messages = data
localStorage.setItem('messages', JSON.stringify(data))
},
// 从localStorage初始化消息相关数据
initFromLocalStorage() {
const messages = localStorage.getItem('messages')
if (messages) {
this.messages = JSON.parse(messages)
}
},
// 清空消息状态并移除localStorage数据
clearMessages() {
this.messages = []
localStorage.removeItem('messages')
},
}
})

71
src/utils/getMessage.js

@ -0,0 +1,71 @@
// 所有导入放在顶部
import API from '@/util/http.js';
import {ref} from "vue";
// 声明变量
const messageList1 = ref()
/**
* 格式化时间为相对时间
* @param {String} timeStr - 原始时间字符串
* @returns {String} 格式化后的时间
*/
function formatTime(timeStr) {
// 函数逻辑不变...
const now = new Date();
const msgTime = new Date(timeStr);
const diffMs = now - msgTime;
const diffMins = Math.floor(diffMs / (1000 * 60));
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffHours < 1) {
return `${diffMins}分钟前`;
} else if (diffDays < 1) {
return `${diffHours}小时前`;
} else if (diffDays === 1) {
return '昨天';
} else {
return `${msgTime.getFullYear()}-${String(msgTime.getMonth() + 1).padStart(2, '0')}-${String(msgTime.getDate()).padStart(2, '0')}`;
}
}
/**
* 按日期分组消息返回扁平化列表每条消息包含group字段
* @param {Array} messages - 原始消息列表
* @returns {Array} 带分组信息的消息列表
*/
export function groupMessages(messages) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
// 直接返回处理后的消息数组(每条消息带group字段)
return messages
.filter(msg => msg.flag !== 1)
.map(msg => {
const msgTime = new Date(msg.czTime);
const formattedTime = formatTime(msg.czTime);
let group;
if (msgTime >= today) {
group = '今天';
} else if (msgTime >= yesterday) {
group = '昨天';
} else {
group = '更早';
}
return {...msg, czTime: formattedTime, group};
});
}
// // 异步获取消息
// export const newMessageRes = await API({
// url: '/getMessage',
// method: 'POST',
// data: {}
// });

36
src/utils/goToCheck.js

@ -0,0 +1,36 @@
export function getOrderPage(status) {
// 收款相关状态(0-5)
const receiveStatusMap = {
0: '/moneyManage/receiveDetail/receiveFinance', // 线下财务待审核
1: '/moneyManage/receiveDetail/receiveService', // 线下财务审核通过待填手续费
2: '/moneyManage/receiveDetail/receiveService', // 线下财务审核驳回
3: '/moneyManage/receiveDetail/receiveService', // link线上财务复核待填手续费
// 4: '/moneyManage/receiveDetail', // 收款流程全部结束
5: '/moneyManage/receiveDetail/receiveService' // 手动撤回待编辑提交
};
// 退款相关状态(6及10-41中退款流程状态)
const refundStatusMap = {
6: '/moneyManage/refundDetail', // 退款
10: '/moneyManage/refundDetail/refundFinance', // 地区财务待审核
11: '/moneyManage/refundDetail/refundFinance', // 地区财务手动撤回待编辑提交
12: '/moneyManage/refundDetail/refundFinance', // 地区财务驳回
20: '/moneyManage/refundDetail/refundCharge', // 地区负责人待审核
22: '/moneyManage/refundDetail/refundCharge', // 地区负责人驳回
30: '/moneyManage/refundDetail/refundHeader', // 总部财务待审核
32: '/moneyManage/refundDetail/refundHeader', // 总部财务驳回
40: '/moneyManage/refundDetail/refundService', // 执行人待处理
41: '/moneyManage/refundDetail/refundService' // 执行人已处理,退款结束
};
// 优先匹配退款状态(包含6和10-41区间)
if (refundStatusMap.hasOwnProperty(status)) {
return refundStatusMap[status];
}
// 匹配收款状态(0-5)
if (receiveStatusMap.hasOwnProperty(status)) {
return receiveStatusMap[status];
}
// 未知状态返回工作台
return '/workbench';
}

275
src/views/home.vue

@ -1,19 +1,23 @@
<script setup>
//
import {computed, onMounted, onUnmounted, ref} from 'vue'
import {computed, onMounted, ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {ElMessage} from 'element-plus'
import ChangePassword from '@/components/dialogs/changePassword.vue'
import {useAdminStore} from '@/store'
import {storeToRefs} from 'pinia'
import {filterMenu, getRoutePath} from "@/utils/menuUtils.js";
//
import API from '@/util/http.js'
import bell from '@/assets/SvgIcons/bell.svg'
import bell from '@/assets/SvgIcons/bell.svg'
import noMessage from '@/assets/images/no-message.svg'
import goTop from '@/assets/SvgIcons/go-top.svg'
import {getOrderPage} from '@/utils/goToCheck.js'
import {groupMessages} from "@/utils/getMessage.js"
// 使import.meta.globSVG
import {useMessageStore} from '@/store/index.js'
// ------------------ ICONS ------------------
const icons = import.meta.glob('@/assets/SvgIcons/*.svg', {eager: true})
const menuNameMap = {
@ -25,124 +29,83 @@ const menuNameMap = {
'权限管理': 'permission-management',
}
//
const getIconPath = (menuName) => {
const englishName = menuNameMap[menuName] || menuName;
// key
const possibleKeys = [
`@/assets/SvgIcons/${englishName}.svg`,
`./SvgIcons/${englishName}.svg`,
`/src/assets/SvgIcons/${englishName}.svg`
]
// icons
for (const key of possibleKeys) {
if (icons[key]) {
const iconModule = icons[key]
// default
return iconModule.default || iconModule
}
}
}
//
// ------------------ ------------------
const refreshData = async () => {
try {
// durationshowclose×
ElMessage({message: '数据刷新中,请稍候...', type: 'info'});
// Mysql
const response = await API({url: '/Mysql', method: 'POST', data: {}});
if (response && response.code === 200) {
if (response && response.code === 200) {
const currentRoute = route.fullPath;
router.replace('/blank'); //
setTimeout(() => {
router.replace(currentRoute); //
}, 10);
router.replace('/blank');
setTimeout(() => router.replace(currentRoute), 10);
ElMessage.success('数据刷新成功');
} else {
ElMessage.error('数据刷新失败:' + (response?.msg || '未知错误'));
}
} catch (error) {
console.error('数据刷新异常:', error);
console.error(error)
ElMessage.error('数据刷新异常,请重试');
}
}
//
const menuList = ref([])
// ------------------ ------------------
const route = useRoute()
const router = useRouter()
//
const adminStore = useAdminStore()
// adminData menuTree
const {adminData, menuTree, flag} = storeToRefs(adminStore)
// ,menuTree
menuList.value = filterMenu(menuTree.value)
const menuList = ref(filterMenu(menuTree.value))
console.log("menuList", menuList.value)
//
const route = useRoute()
// index
function findBestMatch(menuList, path) {
let bestMatch = ''
function traverse(menus) {
for (const item of menus) {
const itemPath = getRoutePath(item)
// path
if (path.startsWith(itemPath) && itemPath.length > bestMatch.length) {
bestMatch = itemPath
}
if (item.children && item.children.length > 0) {
traverse(item.children)
}
if (item.children?.length) traverse(item.children)
}
}
traverse(menuList)
return bestMatch || path // fallback
return bestMatch || path
}
//
const activeMenu = computed(() => {
return findBestMatch(menuList.value, route.path)
})
const router = useRouter()
const messageVisible = ref(false)
const activeMenu = computed(() => findBestMatch(menuList.value, route.path))
//
const openMessage = function () {
messageVisible.value = true
}
//
const closeMessage = function () {
messageVisible.value = false
}
const message = function () {
openMessage()
}
// ------------------ / ------------------
const messageVisible = ref(false)
const openMessage = () => (messageVisible.value = true)
const closeMessage = () => (messageVisible.value = false)
//
const showPasswordDialog = ref(false)
const pwdRef = ref()
//
const openChangePassword = () => {
showPasswordDialog.value = true
}
//
function onPwdDialogClosed() {
// resetFields
pwdRef.value?.resetFields()
}
const openChangePassword = () => (showPasswordDialog.value = true)
const onPwdDialogClosed = () => pwdRef.value?.resetFields()
// ------------------ 退 ------------------
function logout() {
const machineId = localStorage.getItem('machineId')
localStorage.removeItem('token')
@ -151,124 +114,117 @@ function logout() {
ElMessage.success('退出成功')
}
//
// ------------------ ------------------
const toggleFlag = () => {
const newFlag = flag.value === 1 ? 0 : 1
adminStore.setFlag(newFlag)
ElMessage.success(newFlag === 1 ? '员工数据已隐藏' : '员工数据已显示')
console.log('flag', newFlag)
}
// ------------------ ------------------
const messageStore = useMessageStore()
const {messages} = storeToRefs(messageStore)
//
// MessageDialog
//
const getMessage = async () => {
try {
const res = await API({
url: '/getMessage',
method: 'POST',
data: {}
});
const messageNum = ref(0)
const showMessageDialog = ref(false)
//
const messageDot = ref(true)
if (res?.data) {
const cleanList = res.data.filter(i => i.flag !== 1)
messageStore.setMessages(cleanList)
}
} catch (e) {
console.error("getMessage error:", e)
}
}
//
const openMessageDialog = () => {
//
const showMessageDialog = ref(false)
const openMessageDialog = async () => {
showMessageDialog.value = true
messageDot.value = false
}
//
const closeMessageDialog = () => {
showMessageDialog.value = false
}
//
const messageList = ref([
{id: 1, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2分钟前', group: '今天'},
{id: 2, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '1小时前', group: '今天'},
{id: 2, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '1小时前', group: '今天'},
{id: 2, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '1小时前', group: '今天'},
{id: 3, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '昨天 09:30', group: '昨天'},
{id: 4, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '昨天 08:30', group: '昨天'},
{id: 5, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 6, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 7, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 8, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 9, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 10, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 11, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 12, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 13, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 14, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 15, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
{id: 16, title: '现金管理—收款明细', desc: 'XXX(用户)的收款单被驳回', time: '2025-11-05 10:05:20', group: '更早'},
]);
//
const groupedMessages = ref({});
messageList.value.forEach(item => {
if (!groupedMessages.value[item.group]) {
groupedMessages.value[item.group] = [];
await getMessage() //
}
groupedMessages.value[item.group].push(item);
});
//
const showAll = ref(false);
//
const closeMessageDialog = () => showMessageDialog.value = false
//
const messageDot = computed(() => messages.value.length > 0)
// showAll
const displayMessages = computed(() => {
if (showAll.value) {
return groupedMessages.value;
}
//
const messageNum = computed(() => messages.value.length)
//
const limitedMessages = {};
let count = 0;
// computed
const messageList = computed(() => groupMessages(messages.value))
// -> ->
const groupOrder = ['今天', '昨天', '更早'];
// computed
const groupedMessages = computed(() => {
const result = {}
messageList.value.forEach(item => {
if (!result[item.group]) result[item.group] = []
result[item.group].push(item)
})
return result
})
// or
const showAll = ref(false)
const displayMessages = computed(() => {
if (showAll.value) return groupedMessages.value
for (const groupName of groupOrder) {
const group = groupedMessages.value[groupName];
if (!group) continue;
let count = 0
const limited = {}
const groupOrder = ['今天', '昨天', '更早']
limitedMessages[groupName] = [];
for (const g of groupOrder) {
const group = groupedMessages.value[g]
if (!group) continue
limited[g] = []
for (const item of group) {
if (count < 2) {
limitedMessages[groupName].push(item);
count++;
} else {
break;
limited[g].push(item)
count++
}
}
//
if (limitedMessages[groupName].length === 0) {
delete limitedMessages[groupName];
if (limited[g].length === 0) delete limited[g]
if (count >= 2) break
}
return limited
})
if (count >= 2) break;
}
const toggleShowAll = () => showAll.value = !showAll.value
return limitedMessages;
//
const scrollContainer = ref(null)
const scrollToTop = () => scrollContainer.value?.scrollTo({top: 0, behavior: 'smooth'})
// +
const handleMessageClick = async (item) => {
const res = await API({
url: '/getMessage/update',
method: 'POST',
data: {id: item.id}
});
// /
const toggleShowAll = () => {
showAll.value = !showAll.value;
};
//
const scrollContainer = ref(null)
const scrollToTop = () => {
scrollContainer.value?.scrollTo({top: 0, behavior: 'smooth'})
if (res.code === 200) {
closeMessageDialog()
await router.push(getOrderPage(item.status))
await getMessage()
ElMessage.success('跳转成功')
} else {
ElMessage.error('跳转失败')
}
}
import goTop from '@/assets/SvgIcons/go-top.svg'
onMounted(() => getMessage())
</script>
<template>
<div class="main-container">
<!-- 背景毛玻璃层作为内容容器 -->
@ -353,7 +309,7 @@ import goTop from '@/assets/SvgIcons/go-top.svg'
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="refreshData()">数据刷新</el-dropdown-item>
<!-- <el-dropdown-item @click="refreshData()">数据刷新</el-dropdown-item>-->
<!-- 员工数据开关 -->
<el-dropdown-item @click="toggleFlag()">
{{ flag === 1 ? '显示员工数据' : '隐藏员工数据' }}
@ -432,7 +388,7 @@ import goTop from '@/assets/SvgIcons/go-top.svg'
消息中心 ({{ messageNum }})
</div>
<!-- todo 这是为了样式显示 一定要改逻辑-->
<div v-if="messageNum !== 0">
<div v-if="messageNum === 0">
<div class="no-message">
<el-image :src="noMessage"></el-image>
<p class="no-message-text">暂无未办消息快去处理工作吧</p>
@ -461,13 +417,19 @@ import goTop from '@/assets/SvgIcons/go-top.svg'
<div style="display: flex; margin-bottom: 10px">
<span class="red-dot"></span>
<span class="message-card-title">{{ item.title }}</span>
<div class="message-time">{{ item.time }}</div>
<div
class="message-time"
:style="{ color: item.czTime.includes('分钟') ? 'red' : '' }"
>
{{ item.czTime }}
</div>
</div>
<p class="message-desc">{{ item.desc }}</p>
<el-button
type="primary"
style="margin: 0 auto; display: block;"
@click="handleMessageClick(item)"
>
前往查看
</el-button>
@ -481,6 +443,7 @@ import goTop from '@/assets/SvgIcons/go-top.svg'
<div>
<el-button
type="text"
v-if="messageNum > 2"
class="view-all"
@click="toggleShowAll"
>

20
src/views/permissions/rolePermission.vue

@ -6,6 +6,7 @@ import request from '@/util/http'
import API from '@/util/http'
import {useAdminStore} from "@/store/index.js"
import {storeToRefs} from "pinia"
const adminStore = useAdminStore();
const {adminData, menuTree} = storeToRefs(adminStore);
import {permissionMapping, findMenuById} from "@/utils/menuTreePermission.js"
@ -322,8 +323,7 @@ const filterPermission = (tree) => {
return tree.filter(item => {
if (item.id === permissionMapping.permission_management) {
return false
}
else if (item.children && item.children.length > 0) {
} else if (item.children && item.children.length > 0) {
item.children = filterPermission(item.children)
}
return true
@ -654,7 +654,8 @@ onMounted(async function () {
<el-card class="card2">
<div class="add-item">
<el-button style="color: #048efb; border: 1px solid #048efb" @click="permissionAddInit()" :disabled="!canAdd"
v-if="canAdd">新增角色</el-button>
v-if="canAdd">新增角色
</el-button>
</div>
<div>
<el-table :data="roleData" style="width: 82vw;height:71.3vh" show-overflow-tooltip
@ -693,8 +694,10 @@ onMounted(async function () {
<div style="margin-top: 20px;display: flex;">
<el-pagination background :current-page="getRoleObj.pageNum" :page-size="getRoleObj.pageSize"
:page-sizes="[5, 10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="roleTotal"
@size-change="handleRolePageSizeChange" @current-change="handleRoleCurrentChange"></el-pagination>
:page-sizes="[5, 10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
:total="roleTotal"
@size-change="handleRolePageSizeChange"
@current-change="handleRoleCurrentChange"></el-pagination>
</div>
</el-card>
</div>
@ -719,7 +722,8 @@ onMounted(async function () {
<el-input v-model="addRole.roleName" placeholder="请输入角色名称" style="width: 220px"/>
</el-form-item>
<el-form-item prop="parentName" label="上级角色:">
<el-select v-model="addRole.parentId" placeholder="请选择上级角色" style="width: 220px" @change="getLists" clearable>
<el-select v-model="addRole.parentId" placeholder="请选择上级角色" style="width: 220px" @change="getLists"
clearable>
<el-option v-for="item in permissionList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
@ -742,7 +746,7 @@ onMounted(async function () {
<span style="color: #999;">暂无数据</span>
</div>
</el-form-item>
<el-form-item v-show="ifHasChannel" prop="channel" label="频道名称:" required>
<el-form-item v-show="ifHasChannel" label="频道名称:" required>
<el-select v-model="addRole.channel" placeholder="请选择频道" style="width: 220px" filterable clearable>
<el-option v-for="item in channelList" :key="item" :label="item" :value="item"/>
</el-select>
@ -791,7 +795,7 @@ onMounted(async function () {
</div>
</el-form-item>
<el-form-item v-show="ifHasChannel" prop="channel" label="频道名称:" required>
<el-form-item v-show="ifHasChannel" label="频道名称:" required>
<el-select v-model="permissionEditRoleObj.channel" placeholder="请选择频道" style="width: 220px" clearable>
<el-option v-for="item in channelList" :key="item" :label="item" :value="item"/>
</el-select>

Loading…
Cancel
Save