添加通知公告
This commit is contained in:
63
mock/announcement.ts
Normal file
63
mock/announcement.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { MockMethod } from 'vite-plugin-mock'
|
||||
|
||||
const announcements = [
|
||||
{
|
||||
id: 1,
|
||||
title: '[人事任命] 北京分公司总经理...',
|
||||
category: '人事任命',
|
||||
content: '经董事会决定,由产品经理佳巍担任北京分公司总经理一职,负责全面工作。希望大家积极配合...',
|
||||
publishTime: '2023-12-22 10:34',
|
||||
status: 'published',
|
||||
statusText: '已发布'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '[人事任命] 上海分公司技术总监...',
|
||||
category: '人事任命',
|
||||
content: '经董事会决定,由技术专家张三担任上海分公司技术总监,负责研发团队管理...',
|
||||
publishTime: '2023-12-21 14:20',
|
||||
status: 'withdrawn',
|
||||
statusText: '已撤回'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '[公司制度] 2024年春节放假通知',
|
||||
category: '公司制度',
|
||||
content: '根据国家法定节假日安排,结合公司实际情况,现将2024年春节放假安排通知如下...',
|
||||
publishTime: '2023-12-20 09:00',
|
||||
status: 'draft',
|
||||
statusText: '草稿'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '[产品研发] Funtime Design v2.0...',
|
||||
category: '产品研发',
|
||||
content: 'Funtime Design v2.0 版本正式启动研发,请各部门配合需求调研工作...',
|
||||
publishTime: '2023-12-25 10:00',
|
||||
status: 'pending',
|
||||
statusText: '待发布'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '[企业文化] 季度团建活动通知',
|
||||
category: '企业文化',
|
||||
content: '为丰富员工生活,增强团队凝聚力,公司决定于本周五举行季度团建活动...',
|
||||
publishTime: '2023-12-18 16:00',
|
||||
status: 'published',
|
||||
statusText: '已发布'
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/api/announcements',
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return {
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: announcements
|
||||
}
|
||||
}
|
||||
}
|
||||
] as MockMethod[]
|
||||
@@ -18,6 +18,10 @@
|
||||
<el-icon><Shop /></el-icon>
|
||||
<span>巡店管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/announcement">
|
||||
<el-icon><Bell /></el-icon>
|
||||
<span>通知公告</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
|
||||
@@ -66,6 +66,12 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: 'StoreInspectionDetail',
|
||||
component: () => import('../views/store-inspection/detail.vue'),
|
||||
meta: { title: '巡店报告' }
|
||||
},
|
||||
{
|
||||
path: 'announcement',
|
||||
name: 'Announcement',
|
||||
component: () => import('../views/announcement/index.vue'),
|
||||
meta: { title: '通知公告' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
354
src/views/announcement/index.vue
Normal file
354
src/views/announcement/index.vue
Normal file
@@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<div class="announcement-container">
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<h2>公告管理</h2>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button type="primary" :icon="Plus">新建公告</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Section -->
|
||||
<el-card class="filter-card">
|
||||
<div class="search-row">
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="搜索标题/公告内容"
|
||||
prefix-icon="Search"
|
||||
class="search-input"
|
||||
clearable
|
||||
/>
|
||||
<el-button link type="primary" @click="toggleAdvancedFilter">
|
||||
筛选 <el-icon><Filter /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="category-tabs">
|
||||
<el-radio-group v-model="activeCategory" size="large">
|
||||
<el-radio-button label="全部" />
|
||||
<el-radio-button label="人事任命" />
|
||||
<el-radio-button label="公司制度" />
|
||||
<el-radio-button label="产品研发" />
|
||||
<el-radio-button label="企业文化" />
|
||||
<el-radio-button label="办公公告" />
|
||||
<el-radio-button label="人事调动" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<transition name="el-zoom-in-top">
|
||||
<div v-show="showAdvancedFilter" class="advanced-filters">
|
||||
<div class="filter-group">
|
||||
<span class="label">公告状态:</span>
|
||||
<el-radio-group v-model="filterStatus" size="default">
|
||||
<el-radio-button label="all">全部</el-radio-button>
|
||||
<el-radio-button label="draft">草稿</el-radio-button>
|
||||
<el-radio-button label="published">已发布</el-radio-button>
|
||||
<el-radio-button label="withdrawn">已撤销</el-radio-button>
|
||||
<el-radio-button label="pending">待发布</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<span class="label">创建时间:</span>
|
||||
<el-radio-group v-model="filterTime" size="default">
|
||||
<el-radio-button label="week">近一周</el-radio-button>
|
||||
<el-radio-button label="month">近一月</el-radio-button>
|
||||
<el-radio-button label="quarter">近三月</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-date-picker
|
||||
v-model="customDateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
size="default"
|
||||
style="margin-left: 10px; width: 240px;"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
<el-button type="primary" @click="applyFilters">提交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</el-card>
|
||||
|
||||
<!-- Announcement List -->
|
||||
<div class="list-container" v-loading="loading">
|
||||
<div v-for="item in announcementList" :key="item.id" class="announcement-item">
|
||||
<div class="item-icon">
|
||||
<div class="icon-bg">
|
||||
<el-icon :size="24" color="#fff"><Bell /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-header">
|
||||
<span class="item-title">{{ item.title }}</span>
|
||||
<span :class="['status-badge', item.status]">
|
||||
{{ item.statusText }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-summary">{{ item.content }}</div>
|
||||
<div class="item-meta">
|
||||
<span class="time">{{ item.publishTime }}</span>
|
||||
<div class="actions">
|
||||
<el-button link type="primary" size="small">编辑</el-button>
|
||||
<el-button link type="primary" size="small">详情</el-button>
|
||||
<el-button link type="danger" size="small">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Search, Plus, Filter, Bell } from '@element-plus/icons-vue'
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const loading = ref(false)
|
||||
const searchQuery = ref('')
|
||||
const activeCategory = ref('全部')
|
||||
const showAdvancedFilter = ref(false)
|
||||
const filterStatus = ref('all')
|
||||
const filterTime = ref('')
|
||||
const customDateRange = ref('')
|
||||
const announcementList = ref<any[]>([])
|
||||
|
||||
const toggleAdvancedFilter = () => {
|
||||
showAdvancedFilter.value = !showAdvancedFilter.value
|
||||
}
|
||||
|
||||
const resetFilters = () => {
|
||||
filterStatus.value = 'all'
|
||||
filterTime.value = ''
|
||||
customDateRange.value = ''
|
||||
}
|
||||
|
||||
const applyFilters = () => {
|
||||
// Mock filter logic
|
||||
ElMessage.success('筛选已应用')
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await axios.get('/api/announcements')
|
||||
if (res.data.code === 0) {
|
||||
announcementList.value = res.data.data
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('Failed to load announcements')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.announcement-container {
|
||||
padding: 24px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: calc(100vh - 60px);
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: #1d2129;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
margin-bottom: 24px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Custom styling for radio buttons to act like tabs */
|
||||
:deep(.el-radio-button__inner) {
|
||||
border: none;
|
||||
background: #f2f3f5;
|
||||
margin-right: 10px;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
color: #4e5969;
|
||||
}
|
||||
|
||||
:deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
|
||||
background-color: #e8f3ff;
|
||||
color: #165dff;
|
||||
box-shadow: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.el-radio-button:first-child .el-radio-button__inner),
|
||||
:deep(.el-radio-button:last-child .el-radio-button__inner) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.advanced-filters {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px dashed #e5e6eb;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-group .label {
|
||||
width: 80px;
|
||||
color: #86909c;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.announcement-item {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.announcement-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.icon-bg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #40c9c6 0%, #36a3f7 100%);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 10px rgba(54, 163, 247, 0.3);
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-badge.published {
|
||||
color: #00b42a;
|
||||
background: #e8ffea;
|
||||
}
|
||||
|
||||
.status-badge.withdrawn {
|
||||
color: #f53f3f;
|
||||
background: #ffece8;
|
||||
}
|
||||
|
||||
.status-badge.draft {
|
||||
color: #ff7d00;
|
||||
background: #fff7e8;
|
||||
}
|
||||
|
||||
.status-badge.pending {
|
||||
color: #165dff;
|
||||
background: #e8f3ff;
|
||||
}
|
||||
|
||||
.item-summary {
|
||||
font-size: 14px;
|
||||
color: #86909c;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.item-meta .actions {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.announcement-item:hover .item-meta .actions {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user