Files
cz-web/src/views/message/index.vue
2026-09-30 15:55:36 +08:00

256 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { AppButton, FilterPanel, PaginationBar } from '@/components'
// 页面容器:只做组件引入、props 分发、事件中转,数据只经 store
import { useMessageStore } from '@/stores/modules/message'
import type { MessageType } from '@/types/business/message'
import MessageTabs from './cpns/MessageTabs.vue'
import MessageList from './cpns/MessageList.vue'
const router = useRouter()
const messageStore = useMessageStore()
const { query, list, total, unreadCount, tabCounts } = storeToRefs(messageStore)
const keyword = computed({
get: () => query.value.keyword,
set: (value: string) => messageStore.setQuery({ ...query.value, keyword: value }),
})
function onSearch(): void {
void messageStore.search()
}
function onReset(): void {
void messageStore.resetQuery()
}
function onPageChange(page: number): void {
void messageStore.changePage(page)
}
function onTabChange(type: MessageType | 'all'): void {
messageStore.setQuery({ ...query.value, type })
void messageStore.search()
}
function onView(id: string): void {
void router.push(`/message/${id}`)
}
function onMarkAllRead(): void {
void messageStore.markAllRead()
}
onMounted(() => {
void Promise.all([
messageStore.fetchList(),
messageStore.fetchUnreadCount(),
messageStore.fetchTabCounts(),
])
})
</script>
<template>
<div class="message">
<header class="message__head">
<h1 class="message__title">
消息中心
</h1>
</header>
<p class="message__subtitle text-secondary">
统一接收项目进度、售后工单、账单待办等站内消息,重要业务动态实时提醒
</p>
<div class="message__body">
<MessageTabs
class="message__tabs"
:active="query.type"
:counts="tabCounts"
@tab-change="onTabChange"
/>
<div class="message__main">
<div class="message__toolbar">
<FilterPanel
class="message__filter"
v-model:keyword="keyword"
keyword-placeholder="搜索消息标题、内容"
@search="onSearch"
@reset="onReset"
/>
<AppButton
class="message__mark-read"
type="default"
:disabled="unreadCount === 0"
@click="onMarkAllRead"
>
全部已读
</AppButton>
</div>
<MessageList :messages="list" @view="onView" />
<PaginationBar
class="message__pagination"
:page="query.page"
:page-size="query.pageSize"
:total="total"
@page-change="onPageChange"
/>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.message {
.flex(column, flex-start, stretch);
gap: @spacing-base;
max-width: @container-width;
margin: 0 auto;
padding: @spacing-xl @spacing-base @spacing-xxl;
&__head {
.flex(row, flex-start);
margin-bottom: -10px; // 标题→副标题净距 8 × 0.77(叠加父级 gap 16)
}
// 设计 43:标题 22 × 0.77,700,#1F2937
&__title {
font-size: 17px;
font-weight: 700;
color: @heading-color;
}
// 设计 43:副标题整行居中,13 → 可读下限 11,#6B7280(text-secondary)
&__subtitle {
text-align: center;
font-size: 11px;
}
&__body {
.flex(row, flex-start);
gap: 26px; // 左右栏间距 34 × 0.77
align-items: flex-start;
}
&__tabs {
flex-shrink: 0;
width: 156px; // 左分类卡宽 202 × 0.77
}
&__main {
.flex(column, flex-start, stretch);
gap: 15px; // 卡片纵向间距 19 × 0.77
flex: 1;
min-width: 0;
}
// 搜索行白卡:设计 43 x243..1897 / y370..435,输入与「全部已读」同卡
&__toolbar {
.flex(row, flex-start);
align-items: center;
gap: 11px;
padding: 10px 17px; // 卡内边距 22/16 × 0.77
border-radius: @radius-base;
background: @white;
}
& .message__filter {
flex: 1;
min-width: 0;
padding: 0;
background: transparent;
}
// 「全部已读」:设计 43 白底浅描边 12 → 11 / 500 #4B5563
& .message__mark-read {
flex-shrink: 0;
height: 26px; // 34 × 0.77
padding: 0 16px;
border: 1px solid @border-color;
border-radius: 6px;
background: @white;
color: @text-color;
font-size: 11px;
font-weight: 500;
&:hover:not(:disabled) {
border-color: @primary-color;
background: @white;
color: @primary-color;
}
}
& .message__pagination {
justify-content: flex-end;
align-self: stretch;
padding: 0;
}
// FilterPanel 内部按设计 43 覆写(灰底输入框 / 灰重置 / 蓝搜索,重置在前)
.message__filter {
:deep(.filter-panel__bar) {
gap: 6px;
}
:deep(.filter-panel__body) {
margin-top: 0;
}
:deep(.filter-panel__input) {
order: 0;
flex: 0 1 349px; // 输入框宽 453 × 0.77
max-width: 349px;
height: 29px; // 38 × 0.77
margin-right: 2px; // 叠加 bar gap 6 ≈ 输入→按钮 8 × 0.77
padding: 0 10px;
border: none;
border-radius: 6px;
background: @finance-field-bg; // 设计 43 实取 @input-bg
color: @text-color;
font-size: 11px; // 设计 13 → 可读下限 11
&::placeholder {
color: @contract-placeholder; // 设计 43 实取 #B3B9C4
}
&:focus {
border: none;
}
}
:deep(.filter-panel__btn) {
order: 1;
min-width: 46px; // 重置 57 × 0.77
height: 29px;
padding: 0 12px;
border: none;
border-radius: 6px;
background: @contract-reset-bg; // 设计 43 重置底 #EEF0F3
color: @text-color;
font-size: 11px;
font-weight: 500;
&:hover {
background: @contract-reset-bg;
color: @text-color;
}
}
:deep(.filter-panel__btn--primary) {
order: 2;
background: @primary-strong; // 设计 43 搜索底 @primary-strong
color: @white;
&:hover {
background: @primary-strong;
color: @white;
}
}
}
}
</style>