Commit c16be2c499d90126dfa35d699da9294c21a4ab48

Authored by 无木
1 parent 0f28e803

feat(notice-list): add `pagination` support

为通知列表组件添加分页、超长自动省略、标题点击响应、标题删除线等功能

fixed: #894
CHANGELOG.zh_CN.md
  1 +### ✨ Features
  2 +
  3 +- **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能
  4 +
1 5 ### 🐛 Bug Fixes
2 6  
3 7 - **Table**
... ...
src/layouts/default/header/components/notify/NoticeList.vue
1 1 <template>
2   - <a-list :class="prefixCls">
3   - <template v-for="item in list" :key="item.id">
  2 + <a-list :class="prefixCls" bordered :pagination="getPagination" size="small">
  3 + <template v-for="item in getData" :key="item.id">
4 4 <a-list-item class="list-item">
5 5 <a-list-item-meta>
6 6 <template #title>
7 7 <div class="title">
8   - {{ item.title }}
  8 + <a-typography-paragraph
  9 + @click="handleTitleClick(item)"
  10 + style="width: 100%"
  11 + :style="{ cursor: isTitleClickable ? 'pointer' : '' }"
  12 + :delete="!!item.titleDelete"
  13 + :ellipsis="
  14 + $props.titleRows > 0 ? { rows: $props.titleRows, tooltip: item.title } : false
  15 + "
  16 + :content="item.title"
  17 + />
9 18 <div class="extra" v-if="item.extra">
10 19 <a-tag class="tag" :color="item.color">
11 20 {{ item.extra }}
... ... @@ -21,8 +30,16 @@
21 30  
22 31 <template #description>
23 32 <div>
24   - <div class="description">
25   - {{ item.description }}
  33 + <div class="description" v-if="item.description">
  34 + <a-typography-paragraph
  35 + style="width: 100%"
  36 + :ellipsis="
  37 + $props.descRows > 0
  38 + ? { rows: $props.descRows, tooltip: item.description }
  39 + : false
  40 + "
  41 + :content="item.description"
  42 + />
26 43 </div>
27 44 <div class="datetime">
28 45 {{ item.datetime }}
... ... @@ -35,16 +52,18 @@
35 52 </a-list>
36 53 </template>
37 54 <script lang="ts">
38   - import { defineComponent, PropType } from 'vue';
  55 + import { computed, defineComponent, PropType, ref, watch, unref } from 'vue';
39 56 import { ListItem } from './data';
40 57 import { useDesign } from '/@/hooks/web/useDesign';
41   - import { List, Avatar, Tag } from 'ant-design-vue';
  58 + import { List, Avatar, Tag, Typography } from 'ant-design-vue';
  59 + import { isNumber } from '/@/utils/is';
42 60 export default defineComponent({
43 61 components: {
44 62 [Avatar.name]: Avatar,
45 63 [List.name]: List,
46 64 [List.Item.name]: List.Item,
47 65 AListItemMeta: List.Item.Meta,
  66 + ATypographyParagraph: Typography.Paragraph,
48 67 [Tag.name]: Tag,
49 68 },
50 69 props: {
... ... @@ -52,10 +71,66 @@
52 71 type: Array as PropType<ListItem[]>,
53 72 default: () => [],
54 73 },
  74 + pageSize: {
  75 + type: [Boolean, Number] as PropType<Boolean | Number>,
  76 + default: 5,
  77 + },
  78 + currentPage: {
  79 + type: Number,
  80 + default: 1,
  81 + },
  82 + titleRows: {
  83 + type: Number,
  84 + default: 1,
  85 + },
  86 + descRows: {
  87 + type: Number,
  88 + default: 2,
  89 + },
  90 + onTitleClick: {
  91 + type: Function as PropType<(Recordable) => void>,
  92 + },
55 93 },
56   - setup() {
  94 + emits: ['update:currentPage'],
  95 + setup(props, { emit }) {
57 96 const { prefixCls } = useDesign('header-notify-list');
58   - return { prefixCls };
  97 + const current = ref(props.currentPage || 1);
  98 + const getData = computed(() => {
  99 + const { pageSize, list } = props;
  100 + console.log('refreshData', list);
  101 + if (pageSize === false) return [];
  102 + let size = isNumber(pageSize) ? pageSize : 5;
  103 + return list.slice(size * (unref(current) - 1), size * unref(current));
  104 + });
  105 + watch(
  106 + () => props.currentPage,
  107 + (v) => {
  108 + current.value = v;
  109 + }
  110 + );
  111 + const isTitleClickable = computed(() => !!props.onTitleClick);
  112 + const getPagination = computed(() => {
  113 + const { list, pageSize } = props;
  114 + if (pageSize > 0 && list && list.length > pageSize) {
  115 + return {
  116 + total: list.length,
  117 + pageSize,
  118 + current: unref(current),
  119 + onChange(page) {
  120 + current.value = page;
  121 + emit('update:currentPage', page);
  122 + },
  123 + };
  124 + } else {
  125 + return false;
  126 + }
  127 + });
  128 +
  129 + function handleTitleClick(item: ListItem) {
  130 + props.onTitleClick && props.onTitleClick(item);
  131 + }
  132 +
  133 + return { prefixCls, getPagination, getData, handleTitleClick, isTitleClickable };
59 134 },
60 135 });
61 136 </script>
... ...
src/layouts/default/header/components/notify/data.ts
1 1 export interface ListItem {
2 2 id: string;
3 3 avatar: string;
  4 + // 通知的标题内容
4 5 title: string;
  6 + // 是否在标题上显示删除线
  7 + titleDelete?: boolean;
5 8 datetime: string;
6 9 type: string;
7 10 read?: boolean;
... ... @@ -56,6 +59,55 @@ export const tabListData: TabItem[] = [
56 59 datetime: '2017-08-07',
57 60 type: '1',
58 61 },
  62 + {
  63 + id: '000000005',
  64 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  65 + title:
  66 + '标题可以设置自动显示省略号,本例中标题行数已设为1行,如果内容超过1行将自动截断并支持tooltip显示完整标题。',
  67 + description: '',
  68 + datetime: '2017-08-07',
  69 + type: '1',
  70 + },
  71 + {
  72 + id: '000000006',
  73 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  74 + title: '左侧图标用于区分不同的类型',
  75 + description: '',
  76 + datetime: '2017-08-07',
  77 + type: '1',
  78 + },
  79 + {
  80 + id: '000000007',
  81 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  82 + title: '左侧图标用于区分不同的类型',
  83 + description: '',
  84 + datetime: '2017-08-07',
  85 + type: '1',
  86 + },
  87 + {
  88 + id: '000000008',
  89 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  90 + title: '左侧图标用于区分不同的类型',
  91 + description: '',
  92 + datetime: '2017-08-07',
  93 + type: '1',
  94 + },
  95 + {
  96 + id: '000000009',
  97 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  98 + title: '左侧图标用于区分不同的类型',
  99 + description: '',
  100 + datetime: '2017-08-07',
  101 + type: '1',
  102 + },
  103 + {
  104 + id: '000000010',
  105 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  106 + title: '左侧图标用于区分不同的类型',
  107 + description: '',
  108 + datetime: '2017-08-07',
  109 + type: '1',
  110 + },
59 111 ],
60 112 },
61 113 {
... ... @@ -84,7 +136,8 @@ export const tabListData: TabItem[] = [
84 136 id: '000000008',
85 137 avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
86 138 title: '标题',
87   - description: '这种模板用于提醒谁与你发生了互动',
  139 + description:
  140 + '请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容',
88 141 datetime: '2017-08-07',
89 142 type: '2',
90 143 clickClose: true,
... ...
src/layouts/default/header/components/notify/index.vue
... ... @@ -6,13 +6,15 @@
6 6 </Badge>
7 7 <template #content>
8 8 <Tabs>
9   - <template v-for="item in tabListData" :key="item.key">
  9 + <template v-for="item in listData" :key="item.key">
10 10 <TabPane>
11 11 <template #tab>
12 12 {{ item.name }}
13 13 <span v-if="item.list.length !== 0">({{ item.list.length }})</span>
14 14 </template>
15   - <NoticeList :list="item.list" />
  15 + <!-- 绑定title-click事件的通知列表中标题是“可点击”的-->
  16 + <NoticeList :list="item.list" v-if="item.key === '1'" @title-click="onNoticeClick" />
  17 + <NoticeList :list="item.list" v-else />
16 18 </TabPane>
17 19 </template>
18 20 </Tabs>
... ... @@ -21,28 +23,40 @@
21 23 </div>
22 24 </template>
23 25 <script lang="ts">
24   - import { defineComponent } from 'vue';
  26 + import { computed, defineComponent, ref } from 'vue';
25 27 import { Popover, Tabs, Badge } from 'ant-design-vue';
26 28 import { BellOutlined } from '@ant-design/icons-vue';
27   - import { tabListData } from './data';
  29 + import { tabListData, ListItem } from './data';
28 30 import NoticeList from './NoticeList.vue';
29 31 import { useDesign } from '/@/hooks/web/useDesign';
  32 + import { useMessage } from '/@/hooks/web/useMessage';
30 33  
31 34 export default defineComponent({
32 35 components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
33 36 setup() {
34 37 const { prefixCls } = useDesign('header-notify');
  38 + const { createMessage } = useMessage();
  39 + const listData = ref(tabListData);
35 40  
36   - let count = 0;
  41 + const count = computed(() => {
  42 + let count = 0;
  43 + for (let i = 0; i < tabListData.length; i++) {
  44 + count += tabListData[i].list.length;
  45 + }
  46 + return count;
  47 + });
37 48  
38   - for (let i = 0; i < tabListData.length; i++) {
39   - count += tabListData[i].list.length;
  49 + function onNoticeClick(record: ListItem) {
  50 + createMessage.success('你点击了通知,ID=' + record.id);
  51 + // 可以直接将其标记为已读(为标题添加删除线),此处演示的代码会切换删除线状态
  52 + record.titleDelete = !record.titleDelete;
40 53 }
41 54  
42 55 return {
43 56 prefixCls,
44   - tabListData,
  57 + listData,
45 58 count,
  59 + onNoticeClick,
46 60 numberStyle: {},
47 61 };
48 62 },
... ...