mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-08 22:57:11 +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