Commit c16be2c499d90126dfa35d699da9294c21a4ab48
1 parent
0f28e803
feat(notice-list): add `pagination` support
为通知列表组件添加分页、超长自动省略、标题点击响应、标题删除线等功能 fixed: #894
Showing
4 changed files
with
164 additions
and
18 deletions
CHANGELOG.zh_CN.md
src/layouts/default/header/components/notify/NoticeList.vue
1 | <template> | 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 | <a-list-item class="list-item"> | 4 | <a-list-item class="list-item"> |
5 | <a-list-item-meta> | 5 | <a-list-item-meta> |
6 | <template #title> | 6 | <template #title> |
7 | <div class="title"> | 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 | <div class="extra" v-if="item.extra"> | 18 | <div class="extra" v-if="item.extra"> |
10 | <a-tag class="tag" :color="item.color"> | 19 | <a-tag class="tag" :color="item.color"> |
11 | {{ item.extra }} | 20 | {{ item.extra }} |
@@ -21,8 +30,16 @@ | @@ -21,8 +30,16 @@ | ||
21 | 30 | ||
22 | <template #description> | 31 | <template #description> |
23 | <div> | 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 | </div> | 43 | </div> |
27 | <div class="datetime"> | 44 | <div class="datetime"> |
28 | {{ item.datetime }} | 45 | {{ item.datetime }} |
@@ -35,16 +52,18 @@ | @@ -35,16 +52,18 @@ | ||
35 | </a-list> | 52 | </a-list> |
36 | </template> | 53 | </template> |
37 | <script lang="ts"> | 54 | <script lang="ts"> |
38 | - import { defineComponent, PropType } from 'vue'; | 55 | + import { computed, defineComponent, PropType, ref, watch, unref } from 'vue'; |
39 | import { ListItem } from './data'; | 56 | import { ListItem } from './data'; |
40 | import { useDesign } from '/@/hooks/web/useDesign'; | 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 | export default defineComponent({ | 60 | export default defineComponent({ |
43 | components: { | 61 | components: { |
44 | [Avatar.name]: Avatar, | 62 | [Avatar.name]: Avatar, |
45 | [List.name]: List, | 63 | [List.name]: List, |
46 | [List.Item.name]: List.Item, | 64 | [List.Item.name]: List.Item, |
47 | AListItemMeta: List.Item.Meta, | 65 | AListItemMeta: List.Item.Meta, |
66 | + ATypographyParagraph: Typography.Paragraph, | ||
48 | [Tag.name]: Tag, | 67 | [Tag.name]: Tag, |
49 | }, | 68 | }, |
50 | props: { | 69 | props: { |
@@ -52,10 +71,66 @@ | @@ -52,10 +71,66 @@ | ||
52 | type: Array as PropType<ListItem[]>, | 71 | type: Array as PropType<ListItem[]>, |
53 | default: () => [], | 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 | const { prefixCls } = useDesign('header-notify-list'); | 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 | </script> | 136 | </script> |
src/layouts/default/header/components/notify/data.ts
1 | export interface ListItem { | 1 | export interface ListItem { |
2 | id: string; | 2 | id: string; |
3 | avatar: string; | 3 | avatar: string; |
4 | + // 通知的标题内容 | ||
4 | title: string; | 5 | title: string; |
6 | + // 是否在标题上显示删除线 | ||
7 | + titleDelete?: boolean; | ||
5 | datetime: string; | 8 | datetime: string; |
6 | type: string; | 9 | type: string; |
7 | read?: boolean; | 10 | read?: boolean; |
@@ -56,6 +59,55 @@ export const tabListData: TabItem[] = [ | @@ -56,6 +59,55 @@ export const tabListData: TabItem[] = [ | ||
56 | datetime: '2017-08-07', | 59 | datetime: '2017-08-07', |
57 | type: '1', | 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,7 +136,8 @@ export const tabListData: TabItem[] = [ | ||
84 | id: '000000008', | 136 | id: '000000008', |
85 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', | 137 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', |
86 | title: '标题', | 138 | title: '标题', |
87 | - description: '这种模板用于提醒谁与你发生了互动', | 139 | + description: |
140 | + '请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容', | ||
88 | datetime: '2017-08-07', | 141 | datetime: '2017-08-07', |
89 | type: '2', | 142 | type: '2', |
90 | clickClose: true, | 143 | clickClose: true, |
src/layouts/default/header/components/notify/index.vue
@@ -6,13 +6,15 @@ | @@ -6,13 +6,15 @@ | ||
6 | </Badge> | 6 | </Badge> |
7 | <template #content> | 7 | <template #content> |
8 | <Tabs> | 8 | <Tabs> |
9 | - <template v-for="item in tabListData" :key="item.key"> | 9 | + <template v-for="item in listData" :key="item.key"> |
10 | <TabPane> | 10 | <TabPane> |
11 | <template #tab> | 11 | <template #tab> |
12 | {{ item.name }} | 12 | {{ item.name }} |
13 | <span v-if="item.list.length !== 0">({{ item.list.length }})</span> | 13 | <span v-if="item.list.length !== 0">({{ item.list.length }})</span> |
14 | </template> | 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 | </TabPane> | 18 | </TabPane> |
17 | </template> | 19 | </template> |
18 | </Tabs> | 20 | </Tabs> |
@@ -21,28 +23,40 @@ | @@ -21,28 +23,40 @@ | ||
21 | </div> | 23 | </div> |
22 | </template> | 24 | </template> |
23 | <script lang="ts"> | 25 | <script lang="ts"> |
24 | - import { defineComponent } from 'vue'; | 26 | + import { computed, defineComponent, ref } from 'vue'; |
25 | import { Popover, Tabs, Badge } from 'ant-design-vue'; | 27 | import { Popover, Tabs, Badge } from 'ant-design-vue'; |
26 | import { BellOutlined } from '@ant-design/icons-vue'; | 28 | import { BellOutlined } from '@ant-design/icons-vue'; |
27 | - import { tabListData } from './data'; | 29 | + import { tabListData, ListItem } from './data'; |
28 | import NoticeList from './NoticeList.vue'; | 30 | import NoticeList from './NoticeList.vue'; |
29 | import { useDesign } from '/@/hooks/web/useDesign'; | 31 | import { useDesign } from '/@/hooks/web/useDesign'; |
32 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
30 | 33 | ||
31 | export default defineComponent({ | 34 | export default defineComponent({ |
32 | components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList }, | 35 | components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList }, |
33 | setup() { | 36 | setup() { |
34 | const { prefixCls } = useDesign('header-notify'); | 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 | return { | 55 | return { |
43 | prefixCls, | 56 | prefixCls, |
44 | - tabListData, | 57 | + listData, |
45 | count, | 58 | count, |
59 | + onNoticeClick, | ||
46 | numberStyle: {}, | 60 | numberStyle: {}, |
47 | }; | 61 | }; |
48 | }, | 62 | }, |