feat: add user notification center
This commit is contained in:
@@ -15,6 +15,26 @@ export function setNoticeConfig(params: any) {
|
||||
return request.post({ url: '/notice.notice/set', params })
|
||||
}
|
||||
|
||||
// 用户站内消息列表
|
||||
export function userNoticeLists(params: any) {
|
||||
return request.get({ url: '/notice.userNotice/lists', params })
|
||||
}
|
||||
|
||||
// 用户站内消息类型选项
|
||||
export function userNoticeOptions() {
|
||||
return request.get({ url: '/notice.userNotice/options' })
|
||||
}
|
||||
|
||||
// 发布系统站内消息
|
||||
export function sendUserNotice(params: any) {
|
||||
return request.post({ url: '/notice.userNotice/send', params })
|
||||
}
|
||||
|
||||
// 删除站内消息记录
|
||||
export function deleteUserNotice(params: { ids: number[] }) {
|
||||
return request.post({ url: '/notice.userNotice/delete', params })
|
||||
}
|
||||
|
||||
// 短信设置列表
|
||||
export function smsLists() {
|
||||
return request.get({ url: '/notice.sms_config/getConfig' })
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card class="!border-none" shadow="never">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-lg font-medium">站内消息</div>
|
||||
<div class="mt-1 text-sm text-gray-400">查看互动通知,并向全部或指定用户发布系统消息。</div>
|
||||
</div>
|
||||
<el-button v-perms="['notice.userNotice/send']" type="primary" @click="openSendDialog">
|
||||
发布系统消息
|
||||
</el-button>
|
||||
</div>
|
||||
<el-form class="mb-[-16px]" :model="queryParams" :inline="true">
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="queryParams.keyword" class="w-[240px]" placeholder="标题、内容、用户昵称或账号"
|
||||
clearable @keyup.enter="resetPage" />
|
||||
</el-form-item>
|
||||
<el-form-item label="消息类型">
|
||||
<el-select v-model="queryParams.scene_id" class="w-[160px]" clearable placeholder="全部">
|
||||
<el-option v-for="item in sceneOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="阅读状态">
|
||||
<el-select v-model="queryParams.read" class="w-[130px]" clearable placeholder="全部">
|
||||
<el-option label="未读" :value="0" />
|
||||
<el-option label="已读" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||
<el-button @click="resetParams">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="!border-none mt-4" shadow="never">
|
||||
<el-table size="large" v-loading="pager.loading" :data="pager.lists">
|
||||
<el-table-column label="ID" prop="id" width="76" />
|
||||
<el-table-column label="接收用户" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.user_nickname || '用户' }}</div>
|
||||
<div class="text-xs text-gray-400">ID: {{ row.user_id }} {{ row.user_account ? `· ${row.user_account}` : '' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" prop="type_desc" min-width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small">{{ row.type_desc }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="消息内容" min-width="300" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<div class="font-medium">{{ row.title }}</div>
|
||||
<div class="mt-1 text-sm text-gray-400">{{ row.content }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="互动用户" min-width="130">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.source_user_id">{{ row.source_nickname || `用户 ${row.source_user_id}` }}</span>
|
||||
<span v-else class="text-gray-400">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="跳转路径" prop="target_url" min-width="210" show-overflow-tooltip />
|
||||
<el-table-column label="状态" min-width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.read ? 'success' : 'warning'" size="small">
|
||||
{{ row.read ? '已读' : '未读' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发送时间" prop="create_time" min-width="165" />
|
||||
<el-table-column label="操作" width="90" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-perms="['notice.userNotice/delete']" type="danger" link @click="handleDelete(row.id)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<pagination v-model="pager" @change="getLists" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="sendDialog.visible" title="发布系统消息" width="560px" destroy-on-close>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="接收范围">
|
||||
<el-radio-group v-model="sendDialog.recipient_scope">
|
||||
<el-radio value="all">全部启用用户</el-radio>
|
||||
<el-radio value="specified">指定用户</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="sendDialog.recipient_scope === 'specified'" label="用户 ID">
|
||||
<el-input v-model="sendDialog.user_ids_text" type="textarea" :rows="2"
|
||||
placeholder="多个用户 ID 用逗号或换行分隔" />
|
||||
</el-form-item>
|
||||
<el-form-item label="消息标题" required>
|
||||
<el-input v-model="sendDialog.title" maxlength="50" show-word-limit placeholder="例如:平台公告" />
|
||||
</el-form-item>
|
||||
<el-form-item label="消息内容" required>
|
||||
<el-input v-model="sendDialog.content" type="textarea" :rows="5" maxlength="1000" show-word-limit
|
||||
placeholder="请输入消息内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="跳转路径">
|
||||
<el-input v-model="sendDialog.target_url" placeholder="可选,例如 /pages/community/community" />
|
||||
<div class="mt-1 text-xs text-gray-400">留空则只标记已读;填写时必须是站内页面路径。</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="sendDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="sendDialog.submitting" @click="handleSend">发布</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="messageUserNotice">
|
||||
import { deleteUserNotice, sendUserNotice, userNoticeLists, userNoticeOptions } from '@/api/message'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import feedback from '@/utils/feedback'
|
||||
|
||||
const queryParams = reactive({
|
||||
keyword: '',
|
||||
scene_id: '',
|
||||
read: '' as number | string
|
||||
})
|
||||
const sceneOptions = ref<Array<{ value: number; label: string }>>([])
|
||||
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
fetchFun: userNoticeLists,
|
||||
params: queryParams
|
||||
})
|
||||
|
||||
const sendDialog = reactive({
|
||||
visible: false,
|
||||
submitting: false,
|
||||
recipient_scope: 'all',
|
||||
user_ids_text: '',
|
||||
title: '',
|
||||
content: '',
|
||||
target_url: ''
|
||||
})
|
||||
|
||||
const getOptions = async () => {
|
||||
const result: any = await userNoticeOptions()
|
||||
sceneOptions.value = result?.scene_options || []
|
||||
}
|
||||
|
||||
const openSendDialog = () => {
|
||||
sendDialog.visible = true
|
||||
sendDialog.recipient_scope = 'all'
|
||||
sendDialog.user_ids_text = ''
|
||||
sendDialog.title = ''
|
||||
sendDialog.content = ''
|
||||
sendDialog.target_url = ''
|
||||
}
|
||||
|
||||
const handleSend = async () => {
|
||||
const userIds = sendDialog.user_ids_text
|
||||
.split(/[,,\s]+/)
|
||||
.map((item) => Number(item))
|
||||
.filter((item) => Number.isInteger(item) && item > 0)
|
||||
if (sendDialog.recipient_scope === 'specified' && userIds.length === 0) {
|
||||
feedback.msgError('请至少填写一位指定用户ID')
|
||||
return
|
||||
}
|
||||
|
||||
sendDialog.submitting = true
|
||||
try {
|
||||
await sendUserNotice({
|
||||
recipient_scope: sendDialog.recipient_scope,
|
||||
user_ids: userIds,
|
||||
title: sendDialog.title.trim(),
|
||||
content: sendDialog.content.trim(),
|
||||
target_url: sendDialog.target_url.trim()
|
||||
})
|
||||
sendDialog.visible = false
|
||||
getLists()
|
||||
} finally {
|
||||
sendDialog.submitting = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
await feedback.confirm('确定删除这条站内消息记录?')
|
||||
await deleteUserNotice({ ids: [id] })
|
||||
getLists()
|
||||
}
|
||||
|
||||
getOptions()
|
||||
getLists()
|
||||
</script>
|
||||
Reference in New Issue
Block a user