feat(bulletin-card): 添加公告通知卡片功能
- 新增公告通知卡片页面和相关组件 - 实现公告列表查询、筛选和分页功能 - 添加公告详情页面 - 优化公告卡片样式和布局
This commit is contained in:
parent
9f98fe832a
commit
8fa3b8f0c3
@ -12,6 +12,14 @@ export interface BulletinsRecord {
|
||||
current: number;
|
||||
}
|
||||
|
||||
export interface BulletinsList extends BulletinsRecord{
|
||||
id?: number;
|
||||
remark: string;
|
||||
createBy: string;
|
||||
content: string;
|
||||
publishTime: string;
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
export function queryBulletinsListAll(id: number) {
|
||||
return axios.get(`/api/rest/bulletin/self/${id}`);
|
||||
|
@ -44,6 +44,17 @@ const USER: AppRouteRecordRaw = {
|
||||
permissions: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'bulletinCard',
|
||||
name: 'BulletinCard',
|
||||
component: () => import('@/views/user/bulletin-card/index.vue'),
|
||||
meta: {
|
||||
locale: '公告通知(卡片)',
|
||||
title: '公告通知(卡片)',
|
||||
requiresAuth: true,
|
||||
permissions: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'messages',
|
||||
name: 'Messages',
|
||||
|
148
src/views/user/bulletin-card/components/bulletin-detail.vue
Normal file
148
src/views/user/bulletin-card/components/bulletin-detail.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="['个人中心', '公告通知','公告详情']" />
|
||||
<a-card class="general-card" title=" ">
|
||||
<div class="announcement-detail">
|
||||
<div class="header">
|
||||
<h1>{{ renderData.title }}</h1>
|
||||
<div class="meta">
|
||||
<span>作者: {{ renderData.createBy }}</span>
|
||||
<span
|
||||
>时间: {{ dayjs(renderData.publishTime).format('YYYY-MM-DD') }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<a-divider style="margin-top: 0" />
|
||||
<div v-html="renderData.content" class="content"></div>
|
||||
<div>
|
||||
<ul class="attachments">
|
||||
<li v-for="(item, index) in renderData.attachments" :key="index">
|
||||
<a
|
||||
:href="item.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<icon-download />
|
||||
{{ item.fileName }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import dayjs from 'dayjs';
|
||||
import { useBulletinStore } from '@/store';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const bulletinStore = useBulletinStore();
|
||||
const route = useRoute();
|
||||
const id = Number(route.params.id);
|
||||
const renderData = ref<any>([]);
|
||||
// const attachmentList = ref<any>([]);
|
||||
const fetchData = async (Id: number) => {
|
||||
const res = await bulletinStore.queryBulletinListAll(Id);
|
||||
// attachmentList.value = await bulletinStore.queryAttachmentInfo(
|
||||
// '28452d83420650425d45110c6417bf693b966b29'
|
||||
// );
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
renderData.value = res.data;
|
||||
};
|
||||
onMounted(() => {
|
||||
fetchData(id);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.announcement-detail {
|
||||
min-width: 800px;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #fff; /* 白色背景,与淡蓝色背景区分开来 */
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-bottom: 20px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.meta span {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
line-height: 1.6;
|
||||
}
|
||||
.attachments li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.attachments a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.attachments a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.attachments .arco-icon {
|
||||
margin-right: 8px;
|
||||
color: #1890ff;
|
||||
}
|
||||
.layout-demo :deep(.arco-layout-header),
|
||||
.layout-demo :deep(.arco-layout-footer),
|
||||
.layout-demo :deep(.arco-layout-sider-children),
|
||||
.layout-demo :deep(.arco-layout-content) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
color: var(--color-white);
|
||||
font-size: 16px;
|
||||
font-stretch: condensed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.layout-demo :deep(.arco-layout-header),
|
||||
.layout-demo :deep(.arco-layout-footer) {
|
||||
height: 64px;
|
||||
background-color: var(--color-primary-light-4);
|
||||
}
|
||||
|
||||
.layout-demo :deep(.arco-layout-sider) {
|
||||
width: 206px;
|
||||
background-color: var(--color-primary-light-3);
|
||||
}
|
||||
|
||||
.layout-demo :deep(.arco-layout-content) {
|
||||
background-color: rgb(var(--arcoblue-6));
|
||||
}
|
||||
</style>
|
||||
|
||||
|
143
src/views/user/bulletin-card/components/card-wrap.vue
Normal file
143
src/views/user/bulletin-card/components/card-wrap.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="card-wrap">
|
||||
<a-card v-if="loading" :bordered="false" hoverable>
|
||||
<slot name="skeleton"></slot>
|
||||
</a-card>
|
||||
<a-card v-else :bordered="false" hoverable>
|
||||
<a-space align="start">
|
||||
<a-card-meta>
|
||||
<template #title>
|
||||
<a-typography-text style="margin-right: 10px">
|
||||
{{ title }}
|
||||
</a-typography-text>
|
||||
</template>
|
||||
<template #description>
|
||||
<a-descriptions :column="1">
|
||||
<a-descriptions-item label="创建人">
|
||||
{{ createBy }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
{{ dayjs(publishTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="备注">
|
||||
{{ remark }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</template>
|
||||
</a-card-meta>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<div>
|
||||
<a-space>
|
||||
<a-button type="outline" @click="handleDetail(id)">
|
||||
详情
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import router from '@/router';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const props = defineProps({
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
remark: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
createBy: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
closeTxt: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
publishTime: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
// 详情
|
||||
const handleDetail = async (id: number) => {
|
||||
await router.push({ name: 'Details', params: { id } });
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.card-wrap {
|
||||
height: 100%;
|
||||
transition: all 0.3s;
|
||||
border: 1px solid var(--color-neutral-3);
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
// box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
:deep(.arco-card) {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
.arco-card-body {
|
||||
height: 100%;
|
||||
.arco-space {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.arco-space-item {
|
||||
height: 100%;
|
||||
&:last-child {
|
||||
flex: 1;
|
||||
}
|
||||
.arco-card-meta {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
.arco-card-meta-content {
|
||||
flex: 1;
|
||||
.arco-card-meta-description {
|
||||
margin-top: 8px;
|
||||
color: rgb(var(--gray-6));
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.arco-card-meta-footer {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.arco-card-meta-title) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// To prevent the shaking
|
||||
line-height: 28px;
|
||||
}
|
||||
:deep(.arco-skeleton-line) {
|
||||
&:last-child {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
210
src/views/user/bulletin-card/index.vue
Normal file
210
src/views/user/bulletin-card/index.vue
Normal file
@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="['个人中心', '公告通知']" />
|
||||
<a-card class="general-card" title=" ">
|
||||
<a-row>
|
||||
<a-col :flex="1">
|
||||
<a-form
|
||||
:model="formModel"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
label-align="right"
|
||||
>
|
||||
<a-row :gutter="18">
|
||||
<a-col :span="9">
|
||||
<a-form-item field="title" label="标题">
|
||||
<a-input
|
||||
v-model="formModel.title"
|
||||
style="width: 360px"
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="10">
|
||||
<a-form-item field="Time" label="时间">
|
||||
<a-range-picker
|
||||
show-time
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@ok="timeRangs"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- <a-col :span="9">-->
|
||||
<!-- <a-form-item field="isRead" label="状态">-->
|
||||
<!-- <a-select-->
|
||||
<!-- v-model="formModel.isRead"-->
|
||||
<!-- style="width: 360px"-->
|
||||
<!-- placeholder="请选择状态"-->
|
||||
<!-- :options="statusOptions"-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
<!-- </a-col>-->
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-col>
|
||||
<!-- <a-divider style="height: 84px" direction="vertical" />-->
|
||||
<a-col :flex="'46px'" style="text-align: right">
|
||||
<a-space :size="18">
|
||||
<a-button type="primary" @click="search">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button @click="reset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- <a-divider style="margin-top: 0" />-->
|
||||
<div class="list-wrap">
|
||||
<a-row class="list-row" :gutter="20">
|
||||
<a-col
|
||||
v-for="item in renderData"
|
||||
:key="item.id"
|
||||
class="list-col"
|
||||
:xs="12"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="6"
|
||||
:xl="6"
|
||||
:xxl="6"
|
||||
>
|
||||
<CardWrap
|
||||
:loading="loading"
|
||||
:id="item.id"
|
||||
:title="item.title"
|
||||
:remark="item.remark"
|
||||
:createBy="item.createBy"
|
||||
:publishTime="item.publishTime"
|
||||
open-txt="详情"
|
||||
>
|
||||
<template #skeleton>
|
||||
<a-skeleton :animation="true">
|
||||
<a-skeleton-line
|
||||
:widths="['50%', '50%', '100%', '40%']"
|
||||
:rows="4"
|
||||
/>
|
||||
<a-skeleton-line :widths="['40%']" :rows="1" />
|
||||
</a-skeleton>
|
||||
</template>
|
||||
</CardWrap>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<a-pagination
|
||||
style="float: right; position: relative; right: 1px; bottom: 25px"
|
||||
:total="pagination.total"
|
||||
show-total
|
||||
show-jumper
|
||||
show-page-size
|
||||
@page-size-change="onSizeChange"
|
||||
@change="onPageChange"
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { useBulletinsStore } from '@/store';
|
||||
import usePagination from '@/hooks/pagination';
|
||||
import { BulletinsList, BulletinsRecord } from '@/api/bulletins';
|
||||
import CardWrap from './components/card-wrap.vue';
|
||||
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
title: '',
|
||||
state: '',
|
||||
publishTimeBegin: '',
|
||||
publishTimeEnd: '',
|
||||
};
|
||||
};
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const renderData = ref<BulletinsList[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
const { pagination, setPagination} = usePagination();
|
||||
|
||||
|
||||
const bulletinsStore = useBulletinsStore();
|
||||
|
||||
|
||||
// 获取公告列表
|
||||
const fetchData = async (
|
||||
params: BulletinsRecord = { size: 8, current: 1 }
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await bulletinsStore.getBulletinsList(params);
|
||||
renderData.value = res.data.records;
|
||||
setPagination(res.data);
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 时间范围
|
||||
const timeRangs = (dateString: string[]) => {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
formModel.value.publishTimeBegin = dateString[0];
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
formModel.value.publishTimeEnd = dateString[1];
|
||||
};
|
||||
|
||||
|
||||
// 查询
|
||||
const search = () => {
|
||||
fetchData({
|
||||
...pagination,
|
||||
...formModel.value,
|
||||
} as unknown as BulletinsRecord);
|
||||
};
|
||||
|
||||
// 分页发生改变
|
||||
const onPageChange = (current: number) => {
|
||||
setPagination({
|
||||
current,
|
||||
})
|
||||
search();
|
||||
};
|
||||
|
||||
// 数据条数改变
|
||||
const onSizeChange = (total: number) => {
|
||||
setPagination({
|
||||
total,
|
||||
})
|
||||
search();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
})
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
formModel.value = generateFormModel();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
:deep(.list-wrap) {
|
||||
.list-row {
|
||||
align-items: stretch;
|
||||
.list-col {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user