mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 22:57:15 +08:00 
			
		
		
		
	Merge branch 'dev' of https://gitee.com/continew/continew-admin-ui into dev
This commit is contained in:
		| @@ -8,3 +8,4 @@ export * from './file' | ||||
| export * from './storage' | ||||
| export * from './option' | ||||
| export * from './user-center' | ||||
| export * from './message' | ||||
|   | ||||
							
								
								
									
										19
									
								
								src/apis/system/message.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/apis/system/message.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import type * as System from './type' | ||||
| import http from '@/utils/http' | ||||
|  | ||||
| const BASE_URL = '/system/message' | ||||
|  | ||||
| /** @desc 查询消息列表 */ | ||||
| export function listMessage(query: System.MessagePageQuery) { | ||||
|   return http.get<PageRes<System.MessageResp[]>>(`${BASE_URL}`, query) | ||||
| } | ||||
|  | ||||
| /** @desc 删除消息 */ | ||||
| export function deleteMessage(ids: string | Array<string>) { | ||||
|   return http.del(`${BASE_URL}/${ids}`) | ||||
| } | ||||
|  | ||||
| /** @desc 标记已读 */ | ||||
| export function readMessage(ids: string | Array<string>) { | ||||
|   return http.patch(`${BASE_URL}/read`, ids) | ||||
| } | ||||
| @@ -297,3 +297,25 @@ export interface BindSocialAccountRes { | ||||
|   source: string | ||||
|   description: string | ||||
| } | ||||
|  | ||||
| /** 系统消息类型 */ | ||||
| export interface MessageResp { | ||||
|   id: string | ||||
|   title: string | ||||
|   content: string | ||||
|   type: number | ||||
|   isRead: boolean | ||||
|   readTime: string | ||||
|   createUserString: string | ||||
|   createTime: string | ||||
| } | ||||
|  | ||||
| export interface MessageQuery { | ||||
|   title?: string | ||||
|   type?: number | ||||
|   isRead?: boolean | ||||
|   sort: Array<string> | ||||
| } | ||||
|  | ||||
| export interface MessagePageQuery extends MessageQuery, PageQuery { | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import type * as System from './type' | ||||
| import http from '@/utils/http' | ||||
| import type * as System from '@/apis/system/type' | ||||
|  | ||||
| const BASE_URL = '/system/user' | ||||
|  | ||||
|   | ||||
| @@ -63,6 +63,12 @@ export const constantRoutes: RouteRecordRaw[] = [ | ||||
|         name: 'SettingProfile', | ||||
|         component: () => import('@/views/setting/profile/index.vue'), | ||||
|         meta: { title: '个人中心', showInTabs: false } | ||||
|       }, | ||||
|       { | ||||
|         path: '/setting/message', | ||||
|         name: 'SettingMessage', | ||||
|         component: () => import('@/views/setting/message/index.vue'), | ||||
|         meta: { title: '消息中心', showInTabs: false } | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
|   | ||||
							
								
								
									
										140
									
								
								src/views/setting/message/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/views/setting/message/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| <template> | ||||
|   <div class="table-page"> | ||||
|     <GiTable | ||||
|       row-key="id" | ||||
|       title="消息中心" | ||||
|       :data="dataList" | ||||
|       :columns="columns" | ||||
|       :loading="loading" | ||||
|       :scroll="{ x: '100%', y: '100%', minWidth: 1000 }" | ||||
|       :pagination="pagination" | ||||
|       :disabled-tools="['size', 'setting']" | ||||
|       :disabled-column-keys="['name']" | ||||
|       :row-selection="{ type: 'checkbox', showCheckedAll: true }" | ||||
|       :selected-keys="selectedKeys" | ||||
|       @select-all="selectAll" | ||||
|       @select="select" | ||||
|       @refresh="search" | ||||
|     > | ||||
|       <template #custom-left> | ||||
|         <a-input v-model="queryForm.title" placeholder="请输入标题" allow-clear @change="search"> | ||||
|           <template #prefix><icon-search /></template> | ||||
|         </a-input> | ||||
|         <a-select | ||||
|           v-model="queryForm.isRead" | ||||
|           placeholder="请选择状态" | ||||
|           allow-clear | ||||
|           style="width: 150px" | ||||
|           @change="search" | ||||
|         > | ||||
|           <a-option :value="false">未读</a-option> | ||||
|           <a-option :value="true">已读</a-option> | ||||
|         </a-select> | ||||
|         <a-button @click="reset">重置</a-button> | ||||
|       </template> | ||||
|       <template #custom-right> | ||||
|         <a-button type="primary" status="danger" :disabled="!selectedKeys.length" @click="onDelete"> | ||||
|           <template #icon><icon-delete /></template> | ||||
|           <span>删除</span> | ||||
|         </a-button> | ||||
|         <a-button type="primary" :disabled="!selectedKeys.length" @click="onRead"> | ||||
|           <span>标记为已读</span> | ||||
|         </a-button> | ||||
|         <a-button type="primary" :disabled="selectedKeys.length" @click="onReadAll">全部已读</a-button> | ||||
|       </template> | ||||
|       <template #title="{ record }"> | ||||
|         <a-tooltip :content="record.content"><span>{{ record.title }}</span></a-tooltip> | ||||
|       </template> | ||||
|       <template #isRead="{ record }"> | ||||
|         <a-tag v-if="record.isRead">已读</a-tag> | ||||
|         <a-tag v-else color="arcoblue">未读</a-tag> | ||||
|       </template> | ||||
|       <template #type="{ record }"> | ||||
|         <GiCellTag :value="record.type" :dict="message_type" /> | ||||
|       </template> | ||||
|     </GiTable> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { Message, Modal } from '@arco-design/web-vue' | ||||
| import { type MessageQuery, deleteMessage, listMessage, readMessage } from '@/apis' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { useTable } from '@/hooks' | ||||
| import { useDict } from '@/hooks/app' | ||||
|  | ||||
| defineOptions({ name: 'SystemMessage' }) | ||||
|  | ||||
| const { message_type } = useDict('message_type') | ||||
|  | ||||
| const queryForm = reactive<MessageQuery>({ | ||||
|   sort: ['createTime,desc'] | ||||
| }) | ||||
|  | ||||
| const { | ||||
|   tableData: dataList, | ||||
|   loading, | ||||
|   pagination, | ||||
|   selectedKeys, | ||||
|   select, | ||||
|   selectAll, | ||||
|   search, | ||||
|   handleDelete | ||||
| } = useTable((page) => listMessage({ ...queryForm, ...page }), { immediate: true }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { | ||||
|     title: '序号', | ||||
|     width: 66, | ||||
|     align: 'center', | ||||
|     render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize) | ||||
|   }, | ||||
|   { title: '标题', dataIndex: 'title', slotName: 'title', ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', dataIndex: 'isRead', slotName: 'isRead', align: 'center', width: 80 }, | ||||
|   { title: '时间', dataIndex: 'createTime', width: 180 }, | ||||
|   { title: '类型', dataIndex: 'type', slotName: 'type', width: 180, ellipsis: true, tooltip: true } | ||||
| ] | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   queryForm.title = undefined | ||||
|   queryForm.type = undefined | ||||
|   queryForm.isRead = undefined | ||||
|   search() | ||||
| } | ||||
|  | ||||
| // 删除 | ||||
| const onDelete = () => { | ||||
|   if (!selectedKeys.value.length) { | ||||
|     return Message.warning('请选择数据') | ||||
|   } | ||||
|   return handleDelete(() => deleteMessage(selectedKeys.value), { showModal: false }) | ||||
| } | ||||
|  | ||||
| // 标记为已读 | ||||
| const onRead = async () => { | ||||
|   if (!selectedKeys.value.length) { | ||||
|     return Message.warning('请选择数据') | ||||
|   } | ||||
|   await readMessage(selectedKeys.value) | ||||
|   Message.success('操作成功') | ||||
|   search() | ||||
| } | ||||
|  | ||||
| // 全部已读 | ||||
| const onReadAll = async () => { | ||||
|   Modal.warning({ | ||||
|     title: '全部已读', | ||||
|     content: '确定要标记全部消息为已读吗?', | ||||
|     hideCancel: false, | ||||
|     maskClosable: false, | ||||
|     onOk: async () => { | ||||
|       await readMessage([]) | ||||
|       Message.success('操作成功') | ||||
|       search() | ||||
|     } | ||||
|   }) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped></style> | ||||
| @@ -94,7 +94,7 @@ const columns: TableInstanceColumns[] = [ | ||||
|   }, | ||||
|   { title: '标签', dataIndex: 'label', slotName: 'label', width: 100, align: 'center' }, | ||||
|   { title: '值', dataIndex: 'value', width: 100, align: 'center', ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', slotName: 'status', width: 90, align: 'center' }, | ||||
|   { title: '状态', slotName: 'status', width: 80, align: 'center' }, | ||||
|   { | ||||
|     title: '排序', | ||||
|     dataIndex: 'sort', | ||||
|   | ||||
| @@ -13,9 +13,9 @@ | ||||
|             <a-trigger v-model:popup-visible="node.popupVisible" trigger="contextMenu" align-point | ||||
|               animation-name="slide-dynamic-origin" auto-fit-transform-origin position="bl" scroll-to-close> | ||||
|               <a-tooltip v-if="node.description" :content="node.description" background-color="rgb(var(--primary-6))" position="right"> | ||||
|                 <div @contextmenu="onContextmenu(node)">{{ node.name }}({{ node.code }})</div> | ||||
|                 <div @contextmenu="onContextmenu(node)">{{ node.name }} ({{ node.code }})</div> | ||||
|               </a-tooltip> | ||||
|               <div v-else @contextmenu="onContextmenu(node)">{{ node.name }}({{ node.code }})</div> | ||||
|               <div v-else @contextmenu="onContextmenu(node)">{{ node.name }} ({{ node.code }})</div> | ||||
|               <template #content> | ||||
|                 <RightMenu v-if="has.hasPermOr(['system:dict:update', 'system:dict:delete'])" :data="node" | ||||
|                   @on-menu-item-click="onMenuItemClick" /> | ||||
|   | ||||
| @@ -85,7 +85,7 @@ import { | ||||
| import FileGrid from './FileGrid.vue' | ||||
| import useFileManage from './useFileManage' | ||||
| import { useTable } from '@/hooks' | ||||
| import { type FileItem, type FilePageQuery, type FileQuery, deleteFile, listFile, uploadFile } from '@/apis' | ||||
| import { type FileItem, type FileQuery, deleteFile, listFile, uploadFile } from '@/apis' | ||||
| import { ImageTypes } from '@/constant/file' | ||||
| import 'viewerjs/dist/viewer.css' | ||||
| import { downloadByUrl } from '@/utils/downloadFile' | ||||
| @@ -187,19 +187,19 @@ const handleMulDelete = () => { | ||||
| const handleUpload = (options: RequestOption) => { | ||||
|   const controller = new AbortController() | ||||
|     ; (async function requestWrap() { | ||||
|       const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options | ||||
|       onProgress(20) | ||||
|       const formData = new FormData() | ||||
|       formData.append(name as string, fileItem.file as Blob) | ||||
|       try { | ||||
|         const res = await uploadFile(formData) | ||||
|         Message.success('上传成功') | ||||
|         onSuccess(res) | ||||
|         search() | ||||
|       } catch (error) { | ||||
|         onError(error) | ||||
|       } | ||||
|     })() | ||||
|     const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options | ||||
|     onProgress(20) | ||||
|     const formData = new FormData() | ||||
|     formData.append(name as string, fileItem.file as Blob) | ||||
|     try { | ||||
|       const res = await uploadFile(formData) | ||||
|       Message.success('上传成功') | ||||
|       onSuccess(res) | ||||
|       search() | ||||
|     } catch (error) { | ||||
|       onError(error) | ||||
|     } | ||||
|   })() | ||||
|   return { | ||||
|     abort() { | ||||
|       controller.abort() | ||||
| @@ -243,7 +243,7 @@ onMounted(() => { | ||||
|   } | ||||
|  | ||||
|   .pagination { | ||||
|     margin: 10px 0; | ||||
|     padding: 0 var(--padding) var(--padding); | ||||
|  | ||||
|     :deep(.arco-pagination) { | ||||
|       justify-content: end; | ||||
|   | ||||
| @@ -71,7 +71,7 @@ | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:menu:update']" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-if="[1, 2].includes(record.type)" v-permission="['system:menu:add']" @click="onAdd(record.id)"> | ||||
|           <a-link v-permission="['system:menu:add']" :disabled="![1, 2].includes(record.type)" @click="onAdd(record.id)"> | ||||
|             新增 | ||||
|           </a-link> | ||||
|           <a-link v-permission="['system:menu:delete']" status="danger" @click="onDelete(record)">删除</a-link> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 秋帆
					秋帆