completepage: 🍻 消息功能完成

This commit is contained in:
bunny 2024-11-02 17:03:59 +08:00
parent a6eaa41226
commit 56534cdf67
19 changed files with 377 additions and 73 deletions

View File

@ -3,7 +3,7 @@
*/
(async function requestPath() {
// 获取基础paths对象
const response = await fetch('http://localhost:7070/v3/api-docs/admin%E7%AE%A1%E7%90%86%E5%91%98%E6%8E%A5%E5%8F%A3%E8%AF%B7%E6%B1%82', { method: 'GET' });
const response = await fetch('http://localhost:7070/v3/api-docs/%E9%BB%98%E8%AE%A4%E8%AF%B7%E6%B1%82%E6%8E%A5%E5%8F%A3', { method: 'GET' });
const json = await response.json();
const paths = json.paths;
@ -15,7 +15,7 @@
// 获取所有键
Object.keys(paths)
.filter(item => !item.includes('noAuth'))
.filter(item => !item.includes('noAuth') && !item.includes('noManage'))
.forEach(key => {
const pathKey = paths[key];
const { tags, description } = pathKey[Object.keys(pathKey)[0]];
@ -76,7 +76,7 @@ async function add(data) {
headers: {
'Content-Type': 'application/json',
token:
'eyJhbGciOiJIUzI1NiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAA_yWLywrCMBAA_2XPjeSxZEtP4s2L_7DpbqBCorUGLMV_N-BtGGYO2FqCCS6t1h0G0M8TJkfBhtFbHwdom76u0t0fb1y05yxlqed1Pc2P0q_7e-lS3JwUkQxhDgZzFMPZkUmWhTX6EYnh-wMBQi1DcgAAAA.II3lcc1R1pX8G6eaEVkCxxDXkscN4c6p89zn7FzFhaU',
'eyJhbGciOiJIUzI1NiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAA_yWLywqFIBQA_-WsE9Sjt2xXuz7jmAYGWfiAIu6_X-HuhmHmhVwtjDDXGB_owN8XjKJH3iMayTuo2afFNffHSIdv-eSOEEMuicqZ2raX0Kx22g4ciRkUyBRpw6yxgq1S0SBXubnPBt8fEjhnWnMAAAA.YwSm-NO_6Kg1k1GRwucIt50Y70FbPHoldsdTPVHK_Y4',
},
body: JSON.stringify(data),
});

View File

@ -23,6 +23,11 @@ export const fetchAssignRolesToRouter = (data: any) => {
return http.request<BaseResult<any>>('post', `routerRole/assignRolesToRouter`, { data });
};
/** 菜单管理-批量为菜单添加角色 */
export const fetchAssignAddBatchRolesToRouter = (data: any) => {
return http.request<BaseResult<any>>('post', `routerRole/assignAddBatchRolesToRouter`, { data });
};
/** 菜单管理-清除选中菜单所有角色 */
export const fetchClearAllRolesSelect = (data: any) => {
return http.request<BaseResult<any>>('delete', `routerRole/clearAllRolesSelect`, { data });

View File

@ -1,26 +1,16 @@
import { http } from '@/api/service/request';
import type { BaseResult, ResultTable } from '@/api/service/types';
/** 系统消息---获取系统消息列表 */
/** 系统消息---获取系统管理消息列表 */
export const fetchGetMessageList = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `message/getMessageList/${data.currentPage}/${data.pageSize}`, { params: data });
};
/** 系统消息---获取系统消息列表 */
export const fetchGetUserMessageList = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `message/noManage/getUserMessageList/${data.currentPage}/${data.pageSize}`, { params: data });
};
/** 系统消息---根据消息id获取接收人信息 */
export const fetchGetReceivedUserinfoByMessageId = (data: any) => {
return http.request<BaseResult<any>>('get', `messageReceived/noManage/getReceivedUserinfoByMessageId`, { params: data });
};
/** 系统消息---根据消息id查询消息详情 */
export const fetchGetMessageDetailById = (data: any) => {
return http.request<BaseResult<any>>('get', `message/noManage/getMessageDetailById`, { params: data });
};
/** 系统消息---添加系统消息 */
export const fetchAddMessage = (data: any) => {
return http.request<BaseResult<object>>('post', 'message/addMessage', { data });
@ -35,3 +25,23 @@ export const fetchUpdateMessage = (data: any) => {
export const fetchDeleteMessage = (data: any) => {
return http.request<BaseResult<object>>('delete', 'message/deleteMessage', { data });
};
/** 用户系统消息---根据消息id查询消息详情 */
export const fetchGetMessageDetailById = (data: any) => {
return http.request<BaseResult<any>>('get', `message/noManage/getMessageDetailById`, { params: data });
};
/** 用户系统消息---用户获取系统消息列表 */
export const fetchGetUserMessageList = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `message/noManage/getUserMessageList/${data.currentPage}/${data.pageSize}`, { params: data });
};
/** 系统消息---用户将消息标为已读 */
export const fetchUpdateUserMarkAsRead = (data: any) => {
return http.request<BaseResult<object>>('put', 'message/noManage/updateUserMarkAsRead', { data });
};
/** 系统消息---用户删除系统消息 */
export const fetchDeleteUserMessageByIds = (data: any) => {
return http.request<BaseResult<object>>('delete', 'message/noManage/deleteUserMessageByIds', { data });
};

View File

@ -35,8 +35,9 @@ export const enabledOrNotStatus = [
/** 是否已读 */
export const isReadStatus = [
{ value: true, label: $t('readAlready') },
{ value: false, label: $t('unread') },
{ value: '', label: $t('all') },
{ value: 'true', label: $t('readAlready') },
{ value: 'false', label: $t('unread') },
];
/**

View File

@ -2,6 +2,7 @@ import { $t } from '@/plugins/i18n';
import { computed, ref } from 'vue';
import { fetchGetUserMessageList } from '@/api/v1/message';
import { throttle } from '@pureadmin/utils';
import { useWebNotification } from '@vueuse/core';
export interface ListItem {
messageId: string;
@ -27,6 +28,13 @@ const form = { status: false, currentPage: 1, pageSize: 100 };
// 响应内容
export const noticesData = ref<TabItem[]>([]);
// 通知消息数据
export const noticesNum = ref(0);
export const notices = ref(noticesData);
// 选择的消息栏目
export const activeKey = ref(noticesData.value[0]?.key);
export const getLabel = computed(() => item => item.name + (item.list.length > 0 ? `(${item.list.length})` : ''));
/** 获取所有消息 */
export const getAllMessageList = async () => {
const baseResult = await fetchGetUserMessageList(form);
@ -82,14 +90,21 @@ export const getAllMessageList = async () => {
{ key: '2', name: $t('status.pureMessage'), list: notify, emptyText: $t('status.pureNoMessage') },
{ key: '3', name: $t('status.systemMessage'), list: system, emptyText: $t('status.systemMessage') },
];
};
// 通知消息数据
export const noticesNum = ref(0);
export const notices = ref(noticesData);
// 选择的消息栏目
export const activeKey = ref(noticesData.value[0]?.key);
export const getLabel = computed(() => item => item.name + (item.list.length > 0 ? `(${item.list.length})` : ''));
// 调用浏览器系统通知
const { isSupported, show, close } = useWebNotification({
title: system[0]?.title,
dir: 'auto',
lang: 'zh',
renotify: true,
tag: system[0]?.extra,
});
if (system.length <= 0 || !isSupported.value) {
close();
return;
}
await show();
};
/** 计算消息数量 */
export const computedNoticesNum = throttle(async () => {

View File

@ -4,17 +4,24 @@ import { onMounted } from 'vue';
import { activeKey, computedNoticesNum, getLabel, notices, noticesNum } from './data';
import NoticeList from './components/NoticeList.vue';
import BellIcon from '@iconify-icons/ep/bell';
import { useMessageTypeStore } from '@/store/message/messageType';
const { t } = useI18n();
const messageTypeStore = useMessageTypeStore();
onMounted(() => {
messageTypeStore.getAllMessageTypeList();
computedNoticesNum();
});
</script>
<template>
<el-dropdown placement="bottom-end" trigger="click">
<span :class="['dropdown-badge', 'navbar-bg-hover', 'select-none', Number(noticesNum) !== 0 && 'mr-[10px]']" @click="computedNoticesNum">
<span
:class="['dropdown-badge', 'navbar-bg-hover', 'select-none', Number(noticesNum) !== 0 && 'mr-[10px]']"
@click="computedNoticesNum"
@dblclick="$router.push(`/message-detail/${messageTypeStore?.allMessageTypeList[0]?.messageType}`)"
>
<el-badge :max="99" :value="Number(noticesNum) === 0 ? '' : noticesNum">
<span class="header-notice-icon">
<IconifyIconOffline :icon="BellIcon" />

View File

@ -0,0 +1,65 @@
import { defineStore } from 'pinia';
import { fetchDeleteUserMessageByIds, fetchGetUserMessageList, fetchUpdateUserMarkAsRead } from '@/api/v1/message';
import { pageSizes } from '@/enums/baseConstant';
import { storePagination } from '@/store/useStorePagination';
import { storeMessage } from '@/utils/message';
/**
* Store
*/
export const useMessageUserStore = defineStore('messageUserStore', {
state() {
return {
// 系统消息列表
datalist: [],
// 查询表单
form: {
// 消息标题
title: undefined,
// 0:未读 1:已读
status: '',
// 消息类型
messageType: undefined,
},
// 分页查询结果
pagination: {
currentPage: 1,
pageSize: 150,
total: 100,
pageSizes,
},
// 加载
loading: false,
};
},
getters: {},
actions: {
/** 获取系统消息 */
async getMessageList() {
// 整理请求参数
const data = { ...this.pagination, ...this.form };
delete data.pageSizes;
delete data.total;
delete data.background;
// 获取系统消息列表
const result = await fetchGetUserMessageList(data);
// 公共页面函数hook
const pagination = storePagination.bind(this);
return pagination(result);
},
/** 用户将消息标为已读 */
async updateUserMarkAsRead(data: any) {
const result = await fetchUpdateUserMarkAsRead(data);
return storeMessage(result);
},
/** 用户删除系统消息 */
async deleteUserMessageByIds(data: any) {
const result = await fetchDeleteUserMessageByIds(data);
return storeMessage(result);
},
},
});

View File

@ -3,6 +3,7 @@ import { storeMessage } from '@/utils/message';
import { handleTree } from '@/utils/tree';
import {
fetchAddMenu,
fetchAssignAddBatchRolesToRouter,
fetchAssignRolesToRouter,
fetchClearAllRolesSelect,
fetchDeletedMenuByIds,
@ -76,6 +77,12 @@ export const userMenuStore = defineStore('menuStore', {
return storeMessage(result);
},
/** 批量为菜单添加角色 */
async assignAddBatchRolesToRouter(data: any) {
const result = await fetchAssignAddBatchRolesToRouter(data);
return storeMessage(result);
},
/** 清除选中菜单所有角色 */
async clearAllRolesSelect(data: any) {
const result = await fetchClearAllRolesSelect(data);

View File

@ -1,37 +1,117 @@
<script lang="ts" setup>
import { useRoute } from 'vue-router';
import { onMounted, ref } from 'vue';
import { PlusCheckCardGroup } from 'plus-pro-components';
import 'plus-pro-components/es/components/check-card-group/style/css';
import { columns } from '@/views/message-management/message-detail/utils/columns';
import PureTableBar from '@/components/TableBar/src/bar';
import PureTable from '@pureadmin/table';
import { markAsAllRead, markAsRead, onDelete, onSearch, selectids } from '@/views/message-management/message-detail/utils/hooks';
import Delete from '@iconify-icons/ep/delete';
import { $t } from '@/plugins/i18n';
import { Message, MessageBox } from '@element-plus/icons-vue';
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
import { isReadStatus } from '@/enums/baseConstant';
import { useMessageUserStore } from '@/store/message/messageUser';
import { useMessageTypeStore } from '@/store/message/messageType';
import { useRoute, useRouter } from 'vue-router';
const tableRef = ref();
const formRef = ref();
const messageTypeStore = useMessageTypeStore();
const messageUserStore = useMessageUserStore();
const route = useRoute();
const router = useRouter();
/** 获取消息详情 */
const getMessageDetail = () => {
const messageId = route.params.messageId;
/** 当前页改变时 */
const onCurrentPageChange = (value: number) => {
messageUserStore.pagination.currentPage = value;
onSearch(route.params.messageType);
};
const options = [
{
title: '标题一',
value: '0',
description: '坚持梦想,成就不凡的自己',
avatar: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
},
{
title: '标题二',
value: '1',
description: '每一次努力,都是成长的契机',
avatar: 'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
},
];
const size = ref('default');
const dynamicSize = ref();
const list = ref('0');
/**
* * 当分页发生变化
* @param value
*/
const onPageSizeChange = (value: number) => {
messageUserStore.pagination.pageSize = value;
onSearch(route.params.messageType);
};
/**
* * 选择多行
* @param rows
*/
const onSelectionChange = (rows: Array<any>) => {
selectids.value = rows.map((row: any) => row.id);
};
onMounted(() => {
getMessageDetail();
onSearch(route.params.messageType);
});
</script>
<template>
<PlusCheckCardGroup v-model="list" :disabled="size === 'disabled'" :options="options" :size="dynamicSize" />
<div class="main">
<PureTableBar :columns="columns" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch(route.params.messageType)">
<template #title>
<el-segmented v-model="messageUserStore.form.status" :options="isReadStatus" @change="onSearch(route.params.messageType)" />
</template>
<template #buttons>
<!-- 删除按钮 -->
<el-button :disabled="!(selectids.length > 0)" :icon="useRenderIcon(Delete)" type="danger" @click="onDelete">
{{ $t('delete') }}
</el-button>
<!-- 标为已读 -->
<el-button :disabled="!(selectids.length > 0)" :icon="useRenderIcon('octicon:read-24')" type="primary" @click="markAsRead">
{{ $t('markAsRead') }}
</el-button>
<!-- 全部标为已读 -->
<el-button :icon="useRenderIcon('octicon:read-24')" type="primary" @click="markAsAllRead">
{{ $t('allMarkAsRead') }}
</el-button>
<!-- 标题搜索 -->
<el-input v-model="messageUserStore.form.title" :placeholder="`${$t('input')}${$t('title')}`" class="!w-[180px] ml-3" clearable @input="onSearch(route.params.messageType)" />
</template>
<template v-slot="{ size, dynamicColumns }">
<pure-table
ref="tableRef"
:adaptiveConfig="{ offsetBottom: 96 }"
:columns="dynamicColumns"
:data="messageUserStore.datalist"
:header-cell-style="{ background: 'var(--el-fill-color-light)', color: 'var(--el-text-color-primary)' }"
:loading="messageUserStore.loading"
:pagination="messageUserStore.pagination"
:size="size"
adaptive
highlight-current-row
row-key="id"
showOverflowTooltip
table-layout="auto"
@page-size-change="onPageSizeChange"
@selection-change="onSelectionChange"
@page-current-change="onCurrentPageChange"
>
<template #title="{ row }">
<el-link v-if="row.status" :icon="MessageBox" :underline="false" type="info" @click="router.push(`/message-detail/${messageTypeStore?.form?.messageType}/${row.id}`)">
{{ row.title }}
</el-link>
<el-link v-else :icon="Message" :underline="false" type="primary" @click="router.push(`/message-detail/${messageTypeStore?.form?.messageType}/${row.id}`)">
{{ row.title }}
</el-link>
</template>
</pure-table>
</template>
</PureTableBar>
</div>
</template>
<style lang="scss" scoped>
.el-link {
:deep(.el-icon) {
margin: 0 5px 0 0;
}
}
</style>

View File

@ -6,26 +6,27 @@ import { useDataThemeChange } from '@/layout/hooks/useDataThemeChange';
import leftLine from '@iconify-icons/ri/arrow-left-s-line';
import { $t } from '@/plugins/i18n';
import { useMessageTypeStore } from '@/store/message/messageType';
import { onSearch } from '@/views/message-management/message-detail/utils/hooks';
const router = useRouter();
const isOpen = ref(!deviceDetection());
const { $storage } = useGlobal<GlobalPropertiesApi>();
const witchPane = ref('profile');
const messageTypeStore = useMessageTypeStore();
const router = useRouter();
const route = useRoute();
const routerMessageType = ref('');
const messageTypeStore = useMessageTypeStore();
/** 点击菜单时 */
const onMenuClick = (item: any) => {
const messageType = item.messageType;
const messageId = item.messageId;
routerMessageType.value = messageType;
messageTypeStore.form.messageType = item.messageType;
router.push({ path: `/message-detail/${item.messageType}` });
onSearch(item.messageType);
};
onBeforeMount(() => {
//
messageTypeStore.getAllMessageTypeList();
routerMessageType.value = route.params.messageType as string;
//
messageTypeStore.form.messageType = route.params.messageType as string;
useDataThemeChange().dataThemeChange($storage.layout?.overallStyle);
});
</script>
@ -34,25 +35,31 @@ onBeforeMount(() => {
<el-container class="h-full">
<el-aside
v-if="isOpen"
:width="deviceDetection() ? '180px' : '240px'"
:width="deviceDetection() ? '180 px' : '240px'"
class="pure-account-settings overflow-hidden px-2 dark:!bg-[var(--el-bg-color)] border-r-[1px] border-[var(--pure-border-color)]"
>
<el-menu :default-active="witchPane" class="pure-account-settings-menu">
<!--<el-menu-item class="hover:!transition-all hover:!duration-200 hover:!text-base !h-[50px]" @click="router.go(-1)">-->
<el-menu-item class="hover:!transition-all hover:!duration-200 hover:!text-base !h-[50px]" @click="router.push('/')">
<div class="flex items-center">
<el-menu :default-active="messageTypeStore.form.messageType" class="pure-account-settings-menu">
<el-menu-item class="!h-[50px]" @click="$router.go(-1)">
<div class="flex items-center hover:!transition-all hover:!duration-200 hover:!text-base">
<IconifyIconOffline :icon="leftLine" />
<span class="ml-2">{{ $t('back') }}</span>
<span>{{ $t('back') }}</span>
</div>
<el-button class="ml-2" link type="primary" @click="$router.push('/')">{{ $t('returnToHomepage') }} </el-button>
</el-menu-item>
<el-menu-item v-for="item in messageTypeStore.allMessageTypeList" :key="item.id" :class="routerMessageType === item.messageType ? 'is-active' : ''" :index="item.id" @click="onMenuClick(item)">
<el-menu-item
v-for="item in messageTypeStore.allMessageTypeList"
:key="item.id"
:class="messageTypeStore.form.messageType === item.messageType ? 'is-active' : ''"
:index="item.id"
@click="onMenuClick(item)"
>
<div class="flex items-center z-10">
<span>{{ item.messageName }}</span>
</div>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<el-main class="w-[100%]">
<RouterView />
</el-main>
</el-container>

View File

@ -0,0 +1,9 @@
import { $t } from '@/plugins/i18n';
// 表格列
export const columns: TableColumnList = [
{ type: 'selection', align: 'left' },
{ label: $t('title'), prop: 'title', slot: 'title' },
{ label: $t('table.acceptanceTime'), prop: 'acceptanceTime', sortable: true },
{ label: $t('extra'), prop: 'extra' },
];

View File

@ -0,0 +1,72 @@
import { ref } from 'vue';
import { messageBox } from '@/utils/message';
import { $t } from '@/plugins/i18n';
import { useMessageUserStore } from '@/store/message/messageUser';
// 删除ids
export const selectids = ref([]);
const messageUserStore = useMessageUserStore();
/** 搜索初始化系统消息 */
export const onSearch = async (messageType?: string) => {
messageUserStore.loading = true;
if (messageType) {
messageUserStore.form.messageType = messageType;
}
await messageUserStore.getMessageList();
messageUserStore.loading = false;
};
/** 删除系统消息 */
export const onDelete = async () => {
const ids = selectids.value;
// 是否确认删除
const result = await messageBox({
title: $t('confirmDelete'),
showMessage: false,
confirmMessage: undefined,
cancelMessage: $t('cancel_delete'),
});
if (!result) return;
// 删除数据
await messageUserStore.deleteUserMessageByIds(ids);
await onSearch();
};
/** 标为已读 */
export const markAsRead = async () => {
const ids = selectids.value;
// 是否确认标为已读
const result = await messageBox({
title: $t('markAsRead'),
showMessage: false,
confirmMessage: undefined,
cancelMessage: $t('cancel'),
});
if (!result) return;
// 标为已读
await messageUserStore.updateUserMarkAsRead(ids);
await onSearch();
};
/** 全部标为已读 */
export const markAsAllRead = async () => {
const ids = messageUserStore.datalist.map(message => message.id);
// 是否确认标为已读
const result = await messageBox({
title: $t('allMarkAsRead'),
showMessage: false,
confirmMessage: undefined,
cancelMessage: $t('cancel'),
});
if (!result) return;
// 标为已读
await messageUserStore.updateUserMarkAsRead(ids);
await onSearch();
};

View File

@ -0,0 +1,26 @@
// 添加或者修改表单元素
export interface FormItemProps {
// 消息标题
title: string;
// 接收人用户ID
receivedUserIds: string[];
// 发送人用户ID
sendUserId: string;
// 消息类型
messageType: string;
// 消息内容
content: string;
// 消息等级
level: string;
// 消息等级类型
extra: string;
// 编辑器类型
editorType: string;
// 0:未读 1:已读
status: boolean;
}
// 添加或修改表单Props
export interface FormProps {
formInline: FormItemProps;
}

View File

@ -1,8 +1,8 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { FormInstance, genFileId, UploadProps, UploadRawFile } from 'element-plus';
import { addRules, uploadRules } from '@/views/monitor/files/utils/columns';
import { FormProps } from '@/views/monitor/files/utils/types';
import { addRules, uploadRules } from '@/views/system/files/utils/columns';
import { FormProps } from '@/views/system/files/utils/types';
import { $t } from '@/plugins/i18n';
import { useFilesStore } from '@/store/monitor/files';
import { UploadFilled } from '@element-plus/icons-vue';

View File

@ -1,10 +1,10 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { columns } from '@/views/monitor/files/utils/columns';
import { columns } from '@/views/system/files/utils/columns';
import PureTableBar from '@/components/TableBar/src/bar';
import AddFill from '@iconify-icons/ri/add-circle-line';
import PureTable from '@pureadmin/table';
import { onAdd, onDelete, onDeleteBatch, onDownload, onDownloadBatch, onSearch, onUpdate, selectRows } from '@/views/monitor/files/utils/hooks';
import { onAdd, onDelete, onDeleteBatch, onDownload, onDownloadBatch, onSearch, onUpdate, selectRows } from '@/views/system/files/utils/hooks';
import Delete from '@iconify-icons/ep/delete';
import Download from '@iconify-icons/ep/download';
import EditPen from '@iconify-icons/ep/edit-pen';

View File

@ -1,9 +1,9 @@
import { addDialog } from '@/components/BaseDialog/index';
import FilesDialog from '@/views/monitor/files/files-dialog.vue';
import FilesDialog from '@/views/system/files/files-dialog.vue';
import { useFilesStore } from '@/store/monitor/files';
import { h, ref } from 'vue';
import { message, messageBox } from '@/utils/message';
import type { FormItemProps } from '@/views/monitor/files/utils/types';
import type { FormItemProps } from '@/views/system/files/utils/types';
import { $t } from '@/plugins/i18n';
import { downloadFilesByFileId, downloadFilesByFilepath } from '@/api/v1/files';
import { download } from '@/utils/sso';

View File

@ -235,7 +235,7 @@ export const assignBatchRolesToRouter = () => {
draggable: true,
closeOnClickModal: false,
fullscreenIcon: true,
props: { warning: $t('assignBatchRolesToRouterTip') },
// props: { warning: $t('assignBatchRolesToRouterTip') },
contentRenderer: () => <AssignRouterToRole ref={assignRouterToRolesRef} />,
beforeSure: async (done: any) => {
// 表格功能
@ -243,7 +243,7 @@ export const assignBatchRolesToRouter = () => {
// 分配用户角色
const data = { routerIds: selectIds.value, roleIds: assignRouterToRolesRef.value.assignRoles };
const result = await menuStore.assignRolesToRouter(data);
const result = await menuStore.assignAddBatchRolesToRouter(data);
// 更新成功关闭弹窗
if (!result) return;