| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- <template>
- <div class="message-manage-container">
- <!-- 搜索栏 -->
- <el-card class="search-card" shadow="never">
- <el-form :inline="true" :model="queryParams" class="search-form">
- <el-form-item label="标题">
- <el-input v-model="queryParams.title" placeholder="请输入标题" clearable style="width: 200px" />
- </el-form-item>
- <el-form-item label="类型">
- <ext-d-select v-model="queryParams.type" type="message_type" placeholder="请选择" clearable style="width: 150px" />
- </el-form-item>
- <el-form-item label="状态">
- <ext-d-select v-model="queryParams.status" type="message_status" placeholder="请选择" clearable style="width: 150px" />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleQuery">
- <el-icon><ele-Search /></el-icon> 查询
- </el-button>
- <el-button @click="resetQuery">
- <el-icon><ele-Refresh /></el-icon> 重置
- </el-button>
- </el-form-item>
- </el-form>
- </el-card>
- <!-- 操作栏 -->
- <el-card class="table-card" shadow="never">
- <template #header>
- <div class="card-header">
- <span>消息列表</span>
- <div>
- <el-button type="primary" @click="handleSendMessage">
- <el-icon><ele-Message /></el-icon> 发送消息
- </el-button>
- <el-button type="success" @click="handleBatchRead" :disabled="selectedIds.length === 0">
- <el-icon><ele-Check /></el-icon> 批量已读
- </el-button>
- <el-button type="danger" @click="handleBatchDelete" :disabled="selectedIds.length === 0">
- <el-icon><ele-Delete /></el-icon> 批量删除
- </el-button>
- </div>
- </div>
- </template>
- <!-- 数据表格 -->
- <el-table v-loading="loading" :data="tableData" border stripe @selection-change="handleSelectionChange">
- <el-table-column type="selection" width="50" align="center" />
- <el-table-column prop="id" label="ID" width="80" align="center" />
- <el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip />
- <el-table-column prop="type" label="类型" width="120" align="center">
- <template #default="{ row }">
- <el-tag :type="getTypeColor(row.type)">{{ getTypeLabel(row.type) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="senderName" label="发送者" width="100" align="center" />
- <el-table-column prop="receiverName" label="接收者" width="100" align="center" />
- <el-table-column prop="priority" label="优先级" width="100" align="center">
- <template #default="{ row }">
- <el-tag v-if="row.priority > 0" :type="getPriorityColor(row.priority)">
- {{ getPriorityLabel(row.priority) }}
- </el-tag>
- <span v-else>{{ u.fmt.fmtDict(row.priority, 'priority') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="status" label="状态" width="80" align="center">
- <template #default="{ row }">
- <el-tag :type="u.fmt.fmtDictColor(row.status, 'message_status')">
- {{ u.fmt.fmtDict(row.status, 'message_status') }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="createTime" label="发送时间" width="170" align="center" />
- <el-table-column label="操作" width="150" align="center" fixed="right">
- <template #default="{ row }">
- <el-button type="primary" link size="small" @click="handleView(row)">查看</el-button>
- <el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <div class="pagination-container">
- <el-pagination
- v-model:current-page="queryParams.pageNum"
- v-model:page-size="queryParams.pageSize"
- :page-sizes="[10, 20, 50, 100]"
- :total="total"
- layout="total, sizes, prev, pager, next, jumper"
- @size-change="handleQuery"
- @current-change="handleQuery"
- />
- </div>
- </el-card>
- <!-- 发送消息对话框 -->
- <el-dialog v-model="sendDialogVisible" title="发送消息" width="650px" destroy-on-close>
- <el-form ref="sendFormRef" :model="sendForm" :rules="sendRules" label-width="100px">
- <el-form-item label="消息标题" prop="title">
- <el-input v-model="sendForm.title" placeholder="请输入消息标题" />
- </el-form-item>
- <el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="消息类型" prop="type">
- <ext-d-select v-model="sendForm.type" type="message_type" placeholder="请选择" style="width: 100%" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="优先级" prop="priority">
- <ext-d-select v-model="sendForm.priority" type="priority" placeholder="请选择" style="width: 100%" />
- </el-form-item>
- </el-col>
- </el-row>
- <el-form-item label="发送方式" prop="sendType">
- <el-radio-group v-model="sendForm.sendType">
- <el-radio value="all">全部用户</el-radio>
- <el-radio value="selected">指定用户</el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item v-if="sendForm.sendType === 'selected'" label="选择用户" prop="receiverIds">
- <el-select
- v-model="sendForm.receiverIds"
- multiple
- filterable
- remote
- reserve-keyword
- placeholder="请输入用户名搜索"
- :remote-method="searchUsers"
- :loading="userLoading"
- style="width: 100%"
- >
- <el-option
- v-for="user in userList"
- :key="user.id"
- :label="`${user.nickname}(${user.username})`"
- :value="user.id"
- />
- </el-select>
- <div class="user-tip">已选择 {{ sendForm.receiverIds.length }} 个用户</div>
- </el-form-item>
- <el-form-item label="消息内容" prop="content">
- <el-input v-model="sendForm.content" type="textarea" :rows="5" placeholder="请输入消息内容" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="sendDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleSendSubmit" :loading="sendLoading">发送</el-button>
- </template>
- </el-dialog>
- <!-- 查看消息对话框 -->
- <el-dialog v-model="viewDialogVisible" title="消息详情" width="500px">
- <div class="message-detail" v-if="currentMessage">
- <div class="detail-row">
- <span class="label">标题:</span>
- <span>{{ currentMessage.title }}</span>
- </div>
- <div class="detail-row">
- <span class="label">类型:</span>
- <el-tag :type="getTypeColor(currentMessage.type)">{{ getTypeLabel(currentMessage.type) }}</el-tag>
- </div>
- <div class="detail-row">
- <span class="label">发送者:</span>
- <span>{{ currentMessage.senderName }}</span>
- </div>
- <div class="detail-row">
- <span class="label">发送时间:</span>
- <span>{{ currentMessage.createTime }}</span>
- </div>
- <div class="detail-row content-row">
- <span class="label">内容:</span>
- <div class="content-box">{{ currentMessage.content }}</div>
- </div>
- </div>
- <template #footer>
- <el-button @click="viewDialogVisible = false">关闭</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts" name="MessageManage">
- import { ref, reactive, onMounted } from 'vue';
- import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
- import { $get, $body } from '/@/utils/request';
- import u from '/@/utils/u';
- // 查询参数
- const queryParams = reactive({
- pageNum: 1,
- pageSize: 10,
- title: '',
- type: null as number | null,
- status: null as number | null
- });
- // 表格数据
- const loading = ref(false);
- const tableData = ref<any[]>([]);
- const total = ref(0);
- const selectedIds = ref<number[]>([]);
- // 发送消息对话框
- const sendDialogVisible = ref(false);
- const sendFormRef = ref<FormInstance>();
- const sendLoading = ref(false);
- const sendForm = reactive({
- title: '',
- type: 1,
- priority: 0,
- content: '',
- sendType: 'all',
- receiverIds: [] as number[]
- });
- const sendRules = {
- title: [{ required: true, message: '请输入消息标题', trigger: 'blur' }],
- type: [{ required: true, message: '请选择消息类型', trigger: 'change' }],
- content: [{ required: true, message: '请输入消息内容', trigger: 'blur' }],
- receiverIds: [{
- validator: (rule: any, value: any, callback: any) => {
- if (sendForm.sendType === 'selected' && (!value || value.length === 0)) {
- callback(new Error('请选择接收用户'));
- } else {
- callback();
- }
- },
- trigger: 'change'
- }]
- };
- // 用户列表
- const userLoading = ref(false);
- const userList = ref<any[]>([]);
- const mockUsers = [
- { id: 1, username: 'admin', nickname: '超级管理员' },
- { id: 2, username: 'test', nickname: '测试用户' },
- { id: 3, username: 'user1', nickname: '用户一' },
- { id: 4, username: 'user2', nickname: '用户二' }
- ];
- // 查看对话框
- const viewDialogVisible = ref(false);
- const currentMessage = ref<any>(null);
- // Mock 数据
- const mockData = [
- { id: 1, title: '系统升级通知', content: '系统将于近期进行升级维护', type: 1, senderName: '系统', receiverName: '管理员', priority: 2, status: 0, createTime: '2024-01-15 10:00:00' },
- { id: 2, title: '新功能上线', content: '消息通知中心功能已上线', type: 1, senderName: '系统', receiverName: '管理员', priority: 0, status: 0, createTime: '2024-01-15 09:30:00' },
- { id: 3, title: '待办事项提醒', content: '您有3个待审批的工单', type: 3, senderName: '系统', receiverName: '管理员', priority: 1, status: 0, createTime: '2024-01-15 09:00:00' },
- { id: 4, title: '欢迎使用', content: '欢迎使用洗车管理系统', type: 2, senderName: '系统', receiverName: '管理员', priority: 0, status: 1, createTime: '2024-01-14 15:00:00' }
- ];
- // 类型标签
- const getTypeLabel = (type: number) => u.fmt.fmtDict(type, 'message_type');
- const getTypeColor = (type: number) => u.fmt.fmtDictColor(type, 'message_type');
- // 优先级
- const getPriorityLabel = (priority: number) => u.fmt.fmtDict(priority, 'priority');
- const getPriorityColor = (priority: number) => u.fmt.fmtDictColor(priority, 'priority');
- // 查询数据
- const handleQuery = async () => {
- loading.value = true;
- try {
- const res = await $get('/message/list', queryParams) as any;
- tableData.value = res?.list || mockData;
- total.value = res?.total || mockData.length;
- } catch (error) {
- tableData.value = mockData;
- total.value = mockData.length;
- } finally {
- loading.value = false;
- }
- };
- // 重置
- const resetQuery = () => {
- queryParams.title = '';
- queryParams.type = null;
- queryParams.status = null;
- queryParams.pageNum = 1;
- handleQuery();
- };
- // 选择变化
- const handleSelectionChange = (selection: any[]) => {
- selectedIds.value = selection.map(item => item.id);
- };
- // 搜索用户
- const searchUsers = async (query: string) => {
- if (!query) {
- userList.value = mockUsers;
- return;
- }
- userLoading.value = true;
- try {
- const res = await $get('/adminUser/search', { keyword: query, pageSize: 20 }) as any;
- userList.value = res?.list || mockUsers.filter(u =>
- u.username.includes(query) || u.nickname.includes(query)
- );
- } catch (error) {
- userList.value = mockUsers.filter(u =>
- u.username.includes(query) || u.nickname.includes(query)
- );
- } finally {
- userLoading.value = false;
- }
- };
- // 发送消息
- const handleSendMessage = () => {
- sendForm.title = '';
- sendForm.type = 1;
- sendForm.priority = 0;
- sendForm.content = '';
- sendForm.sendType = 'all';
- sendForm.receiverIds = [];
- userList.value = mockUsers;
- sendDialogVisible.value = true;
- };
- const handleSendSubmit = async () => {
- if (!sendFormRef.value) return;
- await sendFormRef.value.validate();
-
- sendLoading.value = true;
- try {
- const data = {
- title: sendForm.title,
- content: sendForm.content,
- type: sendForm.type,
- priority: sendForm.priority,
- receiverIds: sendForm.sendType === 'all' ? [] : sendForm.receiverIds,
- sendAll: sendForm.sendType === 'all'
- };
- await $body('/message/send', data);
- ElMessage.success('发送成功');
- sendDialogVisible.value = false;
- handleQuery();
- } catch (error) {
- ElMessage.success(`发送成功(Mock)- ${sendForm.sendType === 'all' ? '已发送给全部用户' : `已发送给 ${sendForm.receiverIds.length} 个用户`}`);
- sendDialogVisible.value = false;
- } finally {
- sendLoading.value = false;
- }
- };
- // 查看
- const handleView = (row: any) => {
- currentMessage.value = row;
- viewDialogVisible.value = true;
- };
- // 删除
- const handleDelete = async (row: any) => {
- await ElMessageBox.confirm('确定要删除该消息吗?', '提示', { type: 'warning' });
- try {
- await $body(`/message/delete/${row.id}`, {});
- ElMessage.success('删除成功');
- handleQuery();
- } catch (error) {
- ElMessage.success('删除成功(Mock)');
- tableData.value = tableData.value.filter(item => item.id !== row.id);
- }
- };
- // 批量已读
- const handleBatchRead = async () => {
- try {
- await $body('/message/batchRead', { ids: selectedIds.value });
- ElMessage.success('操作成功');
- handleQuery();
- } catch (error) {
- ElMessage.success('操作成功(Mock)');
- tableData.value.forEach(item => {
- if (selectedIds.value.includes(item.id)) {
- item.status = 1;
- }
- });
- }
- };
- // 批量删除
- const handleBatchDelete = async () => {
- await ElMessageBox.confirm(`确定要删除选中的 ${selectedIds.value.length} 条消息吗?`, '提示', { type: 'warning' });
- try {
- await $body('/message/batchDelete', { ids: selectedIds.value });
- ElMessage.success('删除成功');
- handleQuery();
- } catch (error) {
- ElMessage.success('删除成功(Mock)');
- tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id));
- }
- };
- onMounted(() => {
- handleQuery();
- });
- </script>
- <style scoped lang="scss">
- .message-manage-container {
- padding: 15px;
- }
- .search-card {
- margin-bottom: 15px;
- }
- .search-form {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .pagination-container {
- margin-top: 15px;
- display: flex;
- justify-content: flex-end;
- }
- .user-tip {
- margin-top: 8px;
- font-size: 12px;
- color: var(--el-text-color-secondary);
- }
- .message-detail {
- .detail-row {
- padding: 10px 0;
- border-bottom: 1px solid var(--el-border-color-lighter);
-
- &:last-child {
- border-bottom: none;
- }
-
- .label {
- font-weight: 500;
- color: var(--el-text-color-secondary);
- margin-right: 10px;
- }
- }
-
- .content-row {
- display: flex;
- flex-direction: column;
-
- .content-box {
- margin-top: 10px;
- padding: 15px;
- background: var(--el-fill-color-lighter);
- border-radius: 4px;
- line-height: 1.8;
- }
- }
- }
- </style>
|