feat(distribution): 公告中心支持分站自定义
公告中心(前台公告列表/详情)此前完全没有站点概念,分站访客只能看到主站公告。 本次按「默认继承主站、站长可切仅本站」的口径支持分站自定义。 数据与口径: - 新增 announcements.distribution_site_id(0=主站),前台按站点隔离 - 新增 distribution_sites.announcement_mode:inherit(默认,主站+本站)/ own_only(仅本站) 后端: - 公告模块新增 ResolvePublicSiteScope:主站 [0]、inherit [0, siteID]、own_only [siteID], 站点编码无效/已停用回落主站;列表与详情都按该范围过滤,详情不再允许跨站按 ID 读取 - 管理端公告列表支持按站点筛选(site_id:不传=全部,0=主站,>0=分站)并带出站点名 - 分销模块新增本站公告 CRUD(distribution/announcement.go):强制 distribution_site_id=本站, 先过 ensureSiteAccess,跨站更新/删除/上下架一律 ErrAnnouncementNotFound - UpdateSiteBranding 支持 announcement_mode 切换(非法值报错,读侧非法历史值回落 inherit) - 新增权限 distribution:announcement_view|manage 并授予 super_admin/ops/finance, 站长开通时继承 ops 角色即自动获得 - 路由:站长侧 /api/distribution/announcements*,平台侧 /api/admin/distribution/announcements* 前端: - 分销后台新增「公告中心」页:来源开关(继承主站+本站 / 仅本站)、本站公告列表与 新建/编辑/发布/下架/归档/恢复/删除,空态按来源模式给出引导 - 平台后台公告页新增「站点」列与筛选,新建公告可选择归属站点(主站或指定分站代发) - 前台 PC/移动无需改动:请求已带 X-Distribution-Site-Code,后端按站点隔离,页面已有空态 测试: - 公告模块:主站/继承/仅本站三种可见范围、跨站详情拦截、无效站点回落、管理端站点筛选 - 分销模块:本站公告 CRUD 与跨站防护、公告来源开关校验与非法值容错
This commit is contained in:
@@ -23,6 +23,10 @@ import {
|
||||
type CreateAnnouncementRequest,
|
||||
type UpdateAnnouncementRequest,
|
||||
} from '@/features/admin/api/adminAnnouncements'
|
||||
import {
|
||||
fetchDistributionSites,
|
||||
type DistributionSite,
|
||||
} from '@/features/admin/api/adminDistribution'
|
||||
import type { Announcement } from '@/features/announcement'
|
||||
import { formatDateTime } from '@/shared/utils/time'
|
||||
import AnnouncementDialog from '../components/AnnouncementDialog.vue'
|
||||
@@ -35,6 +39,9 @@ const currentPageSize = ref(20)
|
||||
const total = ref(0)
|
||||
const statusFilter = ref('')
|
||||
const categoryFilter = ref('')
|
||||
// 站点筛选:'' = 全部,0 = 主站,>0 = 指定分站
|
||||
const siteFilter = ref<number | ''>('')
|
||||
const distributionSites = ref<DistributionSite[]>([])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogMode = ref<'create' | 'edit'>('create')
|
||||
@@ -54,7 +61,23 @@ const statusOptions = [
|
||||
{ value: 'archived', label: '已归档' },
|
||||
]
|
||||
|
||||
onMounted(loadAnnouncements)
|
||||
onMounted(() => {
|
||||
void loadDistributionSites()
|
||||
void loadAnnouncements()
|
||||
})
|
||||
|
||||
async function loadDistributionSites() {
|
||||
try {
|
||||
distributionSites.value = await fetchDistributionSites()
|
||||
} catch {
|
||||
distributionSites.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function siteLabel(row: Announcement): string {
|
||||
if (!row.distribution_site_id) return '主站'
|
||||
return row.distribution_site_name || `分站 #${row.distribution_site_id}`
|
||||
}
|
||||
|
||||
async function loadAnnouncements() {
|
||||
loading.value = true
|
||||
@@ -62,6 +85,7 @@ async function loadAnnouncements() {
|
||||
const result = await fetchAdminAnnouncements({
|
||||
status: statusFilter.value || undefined,
|
||||
category: categoryFilter.value || undefined,
|
||||
site_id: siteFilter.value === '' ? undefined : Number(siteFilter.value),
|
||||
page: currentPage.value,
|
||||
page_size: currentPageSize.value,
|
||||
})
|
||||
@@ -222,6 +246,22 @@ function getStatusType(status: string): '' | 'success' | 'info' | 'warning' {
|
||||
</div>
|
||||
|
||||
<div class="filter-bar">
|
||||
<el-select
|
||||
v-model="siteFilter"
|
||||
placeholder="筛选站点"
|
||||
clearable
|
||||
@change="handleFilterChange"
|
||||
style="width: 170px"
|
||||
>
|
||||
<el-option label="全部站点" :value="''" />
|
||||
<el-option label="主站" :value="0" />
|
||||
<el-option
|
||||
v-for="site in distributionSites"
|
||||
:key="site.id"
|
||||
:value="site.id"
|
||||
:label="`${site.site_name}(${site.site_code})`"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="statusFilter"
|
||||
placeholder="筛选状态"
|
||||
@@ -272,6 +312,14 @@ function getStatusType(status: string): '' | 'success' | 'info' | 'warning' {
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="站点" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.distribution_site_id" type="success" effect="plain" size="small">
|
||||
{{ siteLabel(row) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="info" effect="plain" size="small">主站</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
|
||||
Reference in New Issue
Block a user