|
@@ -0,0 +1,285 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="container">
|
|
|
|
|
+ <Breadcrumb :items="['公告管理', '首页弹窗公告']" />
|
|
|
|
|
+ <a-card title="首页弹窗公告">
|
|
|
|
|
+ <a-space style="margin-bottom: 12px;">
|
|
|
|
|
+ <a-button type="primary" @click="openCreate">新增公告</a-button>
|
|
|
|
|
+ </a-space>
|
|
|
|
|
+
|
|
|
|
|
+ <a-table
|
|
|
|
|
+ :data="data"
|
|
|
|
|
+ :loading="loading"
|
|
|
|
|
+ :pagination="{
|
|
|
|
|
+ showPageSize: true,
|
|
|
|
|
+ showJumper: true,
|
|
|
|
|
+ showTotal: true,
|
|
|
|
|
+ pageSize: pagination.pagesize,
|
|
|
|
|
+ current: pagination.current,
|
|
|
|
|
+ total: pagination.total
|
|
|
|
|
+ }"
|
|
|
|
|
+ @page-change="onPageChange"
|
|
|
|
|
+ @page-size-change="onPageSizeChange"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #columns>
|
|
|
|
|
+ <a-table-column title="ID" data-index="id" :width="80" />
|
|
|
|
|
+ <a-table-column title="标题" data-index="title" :width="220" ellipsis tooltip />
|
|
|
|
|
+ <a-table-column title="优先级" data-index="priority" :width="100" />
|
|
|
|
|
+ <a-table-column title="状态" :width="100">
|
|
|
|
|
+ <template #cell="{ record }">
|
|
|
|
|
+ <a-tag :color="record.is_active ? 'green' : 'red'">{{ record.is_active ? '启用' : '停用' }}</a-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ <a-table-column title="连续展示" :width="110">
|
|
|
|
|
+ <template #cell="{ record }">
|
|
|
|
|
+ <a-tag :color="record.repeat_show ? 'arcoblue' : 'gray'">{{ record.repeat_show ? '开启' : '关闭' }}</a-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ <a-table-column title="已读人数" data-index="read_count" :width="100" />
|
|
|
|
|
+ <a-table-column title="生效时间" :width="180">
|
|
|
|
|
+ <template #cell="{ record }">{{ record.start_at || '-' }}</template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ <a-table-column title="失效时间" :width="180">
|
|
|
|
|
+ <template #cell="{ record }">{{ record.end_at || '-' }}</template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ <a-table-column title="操作" :width="260">
|
|
|
|
|
+ <template #cell="{ record }">
|
|
|
|
|
+ <a-space>
|
|
|
|
|
+ <a-button size="mini" @click="openEdit(record)">编辑</a-button>
|
|
|
|
|
+ <a-button size="mini" @click="openReadList(record)">已读列表</a-button>
|
|
|
|
|
+ <a-button status="danger" size="mini" @click="remove(record)">删除</a-button>
|
|
|
|
|
+ </a-space>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table>
|
|
|
|
|
+ </a-card>
|
|
|
|
|
+
|
|
|
|
|
+ <a-modal v-model:visible="editorVisible" :title="form.id ? '编辑公告' : '新增公告'" width="980px" @before-ok="submitEditor">
|
|
|
|
|
+ <a-form :model="form" layout="vertical">
|
|
|
|
|
+ <a-form-item label="标题">
|
|
|
|
|
+ <a-input v-model="form.title" allow-clear />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="优先级">
|
|
|
|
|
+ <a-input-number v-model="form.priority" mode="button" />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="状态">
|
|
|
|
|
+ <a-switch v-model="form.is_active" :checked-value="1" :unchecked-value="0" />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="连续展示(已读后仍展示)">
|
|
|
|
|
+ <a-switch v-model="form.repeat_show" :checked-value="1" :unchecked-value="0" />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="生效时间">
|
|
|
|
|
+ <a-date-picker v-model="form.start_at" show-time format="YYYY-MM-DD HH:mm:ss" value-format="x" allow-clear />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="失效时间">
|
|
|
|
|
+ <a-date-picker v-model="form.end_at" show-time format="YYYY-MM-DD HH:mm:ss" value-format="x" allow-clear />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <a-form-item label="公告内容(富文本)">
|
|
|
|
|
+ <WangEditor v-model="form.content_html" />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ </a-form>
|
|
|
|
|
+ </a-modal>
|
|
|
|
|
+
|
|
|
|
|
+ <a-modal v-model:visible="readVisible" title="已读用户" :footer="false" width="860px" draggable>
|
|
|
|
|
+ <a-table
|
|
|
|
|
+ :data="readData"
|
|
|
|
|
+ :columns="readColumns"
|
|
|
|
|
+ :loading="readLoading"
|
|
|
|
|
+ :pagination="false"
|
|
|
|
|
+ :scroll="{ y: 420 }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #avatar="{ record }">
|
|
|
|
|
+ <a-avatar :size="28">
|
|
|
|
|
+ <img :src="record.avatar || 'https://lepao-cloud.xxoo365.top/view.php/25aa126dc406974ff3579a99a2c6501a.png'" />
|
|
|
|
|
+ </a-avatar>
|
|
|
|
|
+ {{ record.username || '-' }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #read_at="{ record }">{{ formatTime(record.read_at) }}</template>
|
|
|
|
|
+ <template #empty>
|
|
|
|
|
+ <a-empty description="暂无已读记录" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table>
|
|
|
|
|
+ </a-modal>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { reactive, ref, onMounted } from 'vue'
|
|
|
|
|
+import { Modal, Notification, Message } from '@arco-design/web-vue'
|
|
|
|
|
+import WangEditor from '@/components/Editor/WangEditor.vue'
|
|
|
|
|
+import {
|
|
|
|
|
+ getAdminPopupList,
|
|
|
|
|
+ createAdminPopup,
|
|
|
|
|
+ updateAdminPopup,
|
|
|
|
|
+ deleteAdminPopup,
|
|
|
|
|
+ getAdminPopupReadList
|
|
|
|
|
+} from '@/api/public'
|
|
|
|
|
+
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const data = ref([])
|
|
|
|
|
+const pagination = reactive({ total: 0, current: 1, pagesize: 20 })
|
|
|
|
|
+
|
|
|
|
|
+const editorVisible = ref(false)
|
|
|
|
|
+const form = reactive({
|
|
|
|
|
+ id: null,
|
|
|
|
|
+ title: '',
|
|
|
|
|
+ content_html: '',
|
|
|
|
|
+ priority: 0,
|
|
|
|
|
+ is_active: 1,
|
|
|
|
|
+ repeat_show: 0,
|
|
|
|
|
+ start_at: '',
|
|
|
|
|
+ end_at: ''
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const readVisible = ref(false)
|
|
|
|
|
+const readLoading = ref(false)
|
|
|
|
|
+const readData = ref([])
|
|
|
|
|
+const readColumns = [
|
|
|
|
|
+ { title: '用户', slotName: 'avatar', width: 140 },
|
|
|
|
|
+ { title: 'UUID', dataIndex: 'user_uuid', width: 220, ellipsis: true, tooltip: true },
|
|
|
|
|
+ { title: '已读时间', slotName: 'read_at', width: 180 }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const formatTime = (time) => new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
|
|
|
|
|
+
|
|
|
|
|
+const getList = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+ const res = await getAdminPopupList({
|
|
|
|
|
+ pagesize: pagination.pagesize,
|
|
|
|
|
+ current: pagination.current
|
|
|
|
|
+ })
|
|
|
|
|
+ if (!res || res.code !== 0) {
|
|
|
|
|
+ return Notification.error({
|
|
|
|
|
+ title: '获取公告列表失败',
|
|
|
|
|
+ content: res?.msg ?? '请稍后再试'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ data.value = res.data || []
|
|
|
|
|
+ pagination.total = res.pagination?.total || 0
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Notification.error({
|
|
|
|
|
+ title: '获取公告列表失败',
|
|
|
|
|
+ content: error.message || '请稍后再试'
|
|
|
|
|
+ })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const openCreate = () => {
|
|
|
|
|
+ form.id = null
|
|
|
|
|
+ form.title = ''
|
|
|
|
|
+ form.content_html = ''
|
|
|
|
|
+ form.priority = 0
|
|
|
|
|
+ form.is_active = 1
|
|
|
|
|
+ form.repeat_show = 0
|
|
|
|
|
+ form.start_at = ''
|
|
|
|
|
+ form.end_at = ''
|
|
|
|
|
+ editorVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const openEdit = (record) => {
|
|
|
|
|
+ form.id = record.id
|
|
|
|
|
+ form.title = record.title
|
|
|
|
|
+ form.content_html = record.content_html
|
|
|
|
|
+ form.priority = record.priority
|
|
|
|
|
+ form.is_active = record.is_active
|
|
|
|
|
+ form.repeat_show = record.repeat_show ? 1 : 0
|
|
|
|
|
+ form.start_at = record.start_at ? new Date(record.start_at).getTime() : ''
|
|
|
|
|
+ form.end_at = record.end_at ? new Date(record.end_at).getTime() : ''
|
|
|
|
|
+ editorVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const submitEditor = async () => {
|
|
|
|
|
+ if (!form.title || !form.content_html) {
|
|
|
|
|
+ Message.error('请填写标题和内容')
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ const payload = {
|
|
|
|
|
+ id: form.id,
|
|
|
|
|
+ title: form.title,
|
|
|
|
|
+ content_html: form.content_html,
|
|
|
|
|
+ priority: form.priority,
|
|
|
|
|
+ is_active: form.is_active,
|
|
|
|
|
+ repeat_show: form.repeat_show,
|
|
|
|
|
+ start_at: form.start_at,
|
|
|
|
|
+ end_at: form.end_at
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = form.id ? await updateAdminPopup(payload) : await createAdminPopup(payload)
|
|
|
|
|
+ if (!res || res.code !== 0) {
|
|
|
|
|
+ Notification.error({
|
|
|
|
|
+ title: form.id ? '更新公告失败' : '创建公告失败',
|
|
|
|
|
+ content: res?.msg ?? '请稍后再试'
|
|
|
|
|
+ })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ Message.success(form.id ? '更新成功' : '创建成功')
|
|
|
|
|
+ getList()
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const remove = (record) => {
|
|
|
|
|
+ Modal.confirm({
|
|
|
|
|
+ title: '删除公告',
|
|
|
|
|
+ content: `确定删除公告「${record.title}」吗?`,
|
|
|
|
|
+ onOk: async () => {
|
|
|
|
|
+ const res = await deleteAdminPopup({ id: record.id })
|
|
|
|
|
+ if (!res || res.code !== 0) {
|
|
|
|
|
+ return Notification.error({
|
|
|
|
|
+ title: '删除公告失败',
|
|
|
|
|
+ content: res?.msg ?? '请稍后再试'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ Message.success('删除成功')
|
|
|
|
|
+ getList()
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const openReadList = async (record) => {
|
|
|
|
|
+ readVisible.value = true
|
|
|
|
|
+ readLoading.value = true
|
|
|
|
|
+ readData.value = []
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getAdminPopupReadList({
|
|
|
|
|
+ popup_id: record.id,
|
|
|
|
|
+ pagesize: 200,
|
|
|
|
|
+ current: 1
|
|
|
|
|
+ })
|
|
|
|
|
+ if (!res || res.code !== 0)
|
|
|
|
|
+ return Notification.error({
|
|
|
|
|
+ title: '获取已读列表失败',
|
|
|
|
|
+ content: res?.msg ?? '请稍后再试'
|
|
|
|
|
+ })
|
|
|
|
|
+ readData.value = Array.isArray(res.data) ? res.data : []
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Notification.error({
|
|
|
|
|
+ title: '获取已读列表失败',
|
|
|
|
|
+ content: error.message || '请稍后再试'
|
|
|
|
|
+ })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ readLoading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onPageChange = (page) => {
|
|
|
|
|
+ pagination.current = page
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+const onPageSizeChange = (size) => {
|
|
|
|
|
+ pagination.pagesize = size
|
|
|
|
|
+ pagination.current = 1
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ getList()
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.container {
|
|
|
|
|
+ padding: 0 20px 20px 20px;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|