Commit 17b6b5b102accd7ecebe3bada627449d1c698dfb

Authored by ww
1 parent ec57584d

perf: 优化数据看板分页界面

... ... @@ -15,6 +15,8 @@ export interface UpdateDataBoardParams extends AddDataBoardParams {
15 15 export interface GetDataBoardParams {
16 16 page?: number;
17 17 pageSize?: number;
  18 + name?: string;
  19 + organizationId?: string;
18 20 orderFiled?: string;
19 21 orderType?: string;
20 22 }
... ... @@ -47,7 +49,9 @@ export interface DataBoardRecord {
47 49 layout: Layout[];
48 50 defaultConfig: string;
49 51 tenantStatus: string;
  52 + componentNum?: number;
50 53 publicId: string;
  54 + organizationId?: string;
51 55 accessCredentials?: string;
52 56 }
53 57
... ...
... ... @@ -96,7 +96,7 @@
96 96 },
97 97 },
98 98 rowKey: unref(getRowKey),
99   - };
  99 + } as ListProps;
100 100 });
101 101
102 102 watch(cardListLayout, () => {
... ... @@ -122,7 +122,7 @@
122 122
123 123 <template>
124 124 <main class="basic-card-list p-4 flex flex-col w-full h-full">
125   - <section class="w-full bg-light-50 dark:bg-dark-900 p-4 mb-4">
  125 + <section v-if="getProps.useSearchForm" class="w-full bg-light-50 dark:bg-dark-900 p-4 mb-4">
126 126 <BasicForm
127 127 @register="registerForm"
128 128 v-bind="getFormProps"
... ... @@ -141,8 +141,10 @@
141 141 <template #header>
142 142 <section class="flex justify-between items-center">
143 143 <div>
144   - <span v-if="title"></span>
145   - <slot v-else name="name"></slot>
  144 + <span v-if="getProps.title" class="font-bold text-base px-4">
  145 + {{ getProps.title }}
  146 + </span>
  147 + <slot v-else name="title"></slot>
146 148 </div>
147 149 <div class="flex flex-auto justify-end mr-4">
148 150 <slot name="toolbar"></slot>
... ...
... ... @@ -126,7 +126,6 @@ export function useCardListData(
126 126 ...(useSearchForm ? getFieldsValue() : {}),
127 127 ...searchInfo,
128 128 };
129   -
130 129 if (beforeFetch && isFunction(beforeFetch)) {
131 130 params = (await beforeFetch(params)) || params;
132 131 }
... ...
1   -import { Ref, computed, reactive, unref } from 'vue';
  1 +import { Ref, computed, reactive, unref, watch } from 'vue';
2 2 import { BasicCardListPropsType, ListGridType } from '../types';
3 3 import { getListGridByColumn } from '../utils';
4 4 import { screenMap, sizeEnum } from '/@/enums/breakpointEnum';
... ... @@ -16,6 +16,13 @@ export function useListGrid(getProps: Ref<BasicCardListPropsType>) {
16 16 } as ListGridType;
17 17 });
18 18
  19 + watch(
  20 + () => unref(getProps).baseLayout,
  21 + () => {
  22 + Object.assign(cardListLayout, unref(getProps).baseLayout);
  23 + }
  24 + );
  25 +
19 26 function getScreenSize() {
20 27 const width = document.body.clientWidth;
21 28 const xs = screenMap.get(sizeEnum.XS)!;
... ...
... ... @@ -25,6 +25,7 @@ export interface BasicCardListPropsType<T = Recordable> {
25 25 rowKey?: (item: T) => string | number;
26 26 immediate?: boolean;
27 27 handleSearchInfoFn?: Fn;
  28 + baseLayout?: Record<'row' | 'col', number>;
28 29 }
29 30
30 31 export type ListGridType = Record<'column' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', number> & {
... ...
... ... @@ -11,6 +11,9 @@ export const formSchema: FormSchema[] = [
11 11 component: 'Input',
12 12 // colProps: { span: 10 },
13 13 colProps: useGridLayout(2, 3, 4) as unknown as ColEx,
  14 + componentProps: {
  15 + placeholder: '请输入看板名称',
  16 + },
14 17 },
15 18 {
16 19 field: 'organizationId',
... ...
1 1 <script lang="ts" setup>
2   - import { List, Card, Statistic, Button, Tooltip, Spin } from 'ant-design-vue';
3   - import { onMounted, ref, unref } from 'vue';
4   - import { PageWrapper } from '/@/components/Page';
  2 + import { Card, Statistic, Button, Tooltip } from 'ant-design-vue';
  3 + import { unref, computed } from 'vue';
5 4 import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue';
6 5 import { useMessage } from '/@/hooks/web/useMessage';
7 6 import Dropdown from '/@/components/Dropdown/src/Dropdown.vue';
... ... @@ -13,12 +12,8 @@
13 12 import { DataBoardRecord } from '/@/api/dataBoard/model';
14 13 import { ViewType } from './config/panelDetail';
15 14 import { useRouter } from 'vue-router';
16   - import { getBoundingClientRect } from '/@/utils/domUtils';
17   - import Authority from '/@/components/Authority/src/Authority.vue';
18   - import { computed } from '@vue/reactivity';
19 15 import { usePermission } from '/@/hooks/web/usePermission';
20 16 import { encode } from './config/config';
21   - import { useForm, BasicForm } from '/@/components/Form';
22 17 import { formSchema } from './config/searchForm';
23 18 import { ShareModal } from '/@/views/common/ShareModal';
24 19 import { ModalParamsType } from '/#/utils';
... ... @@ -26,62 +21,23 @@
26 21 import { useRole } from '/@/hooks/business/useRole';
27 22 import { useClipboard } from '@vueuse/core';
28 23 import { DATA_BOARD_SHARE_URL } from '../palette';
  24 + import { BasicCardList, useCardList } from '/@/components/CardList';
29 25
30   - const ListItem = List.Item;
31 26 const router = useRouter();
32 27
33 28 const { createMessage, createConfirm } = useMessage();
34 29
35   - const listEL = ref();
36   - const loading = ref(false);
37   - const dataBoardList = ref<DataBoardRecord[]>([]);
38   -
39   - const [searchFormRegister, searchFormMethod] = useForm({
40   - schemas: formSchema,
41   - labelWidth: 80,
42   - layout: 'inline',
43   - submitButtonOptions: {
44   - loading: loading as unknown as boolean,
45   - },
46   - submitFunc: async () => {
47   - try {
48   - const params = searchFormMethod.getFieldsValue();
49   - await getDatasource(params);
50   - } catch (error) {}
51   - },
52   - resetFunc: async () => {
53   - try {
54   - await getDatasource();
55   - } catch (error) {}
  30 + const [registerCardList, { reload }] = useCardList({
  31 + title: '数据看板',
  32 + api: getDataBoardList,
  33 + baseLayout: { col: 3, row: 3 },
  34 + useSearchForm: true,
  35 + formConfig: {
  36 + schemas: formSchema,
  37 + labelWidth: 80,
56 38 },
57 39 });
58 40
59   - // about pagination
60   - const page = ref(1);
61   - const pageSize = ref(10);
62   - const total = ref(0);
63   - const paginationProp = ref({
64   - showSizeChanger: false,
65   - showQuickJumper: true,
66   - pageSize,
67   - current: page,
68   - size: 'small',
69   - total,
70   - showTotal: (total) => `总 ${total} 条`,
71   - onChange: pageChange,
72   - onShowSizeChange: pageSizeChange,
73   - });
74   -
75   - function pageChange(p, pz) {
76   - page.value = p;
77   - pageSize.value = pz;
78   - getDatasource();
79   - }
80   - function pageSizeChange(_current, size) {
81   - pageSize.value = size;
82   - getDatasource();
83   - }
84   -
85 41 const createShareUrl = (record: DataBoardRecord) => {
86 42 const { origin } = location;
87 43 const { id, publicId } = record;
... ... @@ -124,22 +80,6 @@
124 80 return basicMenu;
125 81 });
126 82
127   - const getDatasource = async (params: Recordable = {}) => {
128   - try {
129   - loading.value = true;
130   - const { total, items } = await getDataBoardList({
131   - page: unref(paginationProp).current,
132   - pageSize: unref(paginationProp).pageSize,
133   - ...params,
134   - });
135   - dataBoardList.value = items;
136   - paginationProp.value.total = total;
137   - } catch (error) {
138   - } finally {
139   - loading.value = false;
140   - }
141   - };
142   -
143 83 const handleOpenShareModal = (record: DataBoardRecord) => {
144 84 openShareModal(true, {
145 85 record,
... ... @@ -175,7 +115,7 @@
175 115 try {
176 116 await deleteDataBoard([record.id]);
177 117 createMessage.success('删除成功');
178   - await getDatasource();
  118 + reload();
179 119 } catch (error) {}
180 120 };
181 121
... ... @@ -188,106 +128,67 @@
188 128 if (hasDetailPermission) {
189 129 const boardId = encode(record.id);
190 130 const boardName = encode(record.name);
191   - const organizationId = encode(record?.organizationId);
  131 + const organizationId = encode(record!.organizationId!);
192 132
193 133 router.push(`/visual/board/detail/${boardId}/${boardName}/${organizationId}`);
194 134 } else createMessage.warning('没有权限');
195 135 };
196   -
197   - const handlePagenationPosition = () => {
198   - const clientHeight = document.documentElement.clientHeight;
199   - const rect = getBoundingClientRect(unref(listEL).$el!) as DOMRect;
200   - const paginationHeight = 32 + 24 + 16;
201   - const listContainerMarginBottom = 16;
202   - const listContainerHeight =
203   - clientHeight - rect.top - paginationHeight - listContainerMarginBottom;
204   - const listContainerEl = (unref(listEL).$el as HTMLElement).querySelector(
205   - '.ant-spin-container'
206   - ) as HTMLElement;
207   - listContainerEl &&
208   - (listContainerEl.style.height = listContainerHeight + 'px') &&
209   - (listContainerEl.style.overflowY = 'auto') &&
210   - (listContainerEl.style.overflowX = 'hidden');
211   - };
212   -
213   - onMounted(() => {
214   - getDatasource();
215   - handlePagenationPosition();
216   - });
217 136 </script>
218 137
219 138 <template>
220   - <PageWrapper>
221   - <div class="flex items-center mb-3 bg-light-100 h-78px dark:text-gray-300 dark:bg-dark-900">
222   - <div class="text-lg ml-30px mr-9px font-bold">自定义看板</div>
223   - <Authority value="api:yt:data_board:add:post">
224   - <Button v-if="!isCustomerUser" type="primary" @click="handleOpenDetailModal"
225   - >创建看板</Button
226   - >
227   - </Authority>
228   - </div>
229   - <div class="bg-light-100 mb-6 w-full p-3 search-form dark:text-gray-300 dark:bg-dark-900">
230   - <BasicForm class="flex-auto w-full" @register="searchFormRegister" />
231   - </div>
232   - <Spin :spinning="loading">
233   - <List
234   - ref="listEL"
235   - :pagination="paginationProp"
236   - :data-source="dataBoardList"
237   - :grid="{ gutter: 20, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }"
238   - class="data-board-list"
239   - >
240   - <template #renderItem="{ item }">
241   - <ListItem>
242   - <Card class="data-card cursor-pointer">
243   - <template #title>
244   - <div class="font-bold">{{ item.name }}</div>
245   - </template>
246   - <template #extra>
247   - <Dropdown
248   - v-if="!isCustomerUser && dropMenuList.length"
249   - :trigger="['click']"
250   - @menu-event="(event) => handleMenuEvent(event, item)"
251   - :drop-menu-list="dropMenuList"
252   - >
253   - <MoreOutlined class="rotate-90 transform cursor-pointer" />
254   - </Dropdown>
255   - </template>
256   - <section @click="handleViewBoard(item)">
257   - <div class="flex data-card__info">
258   - <div>
259   - <div>组件数量</div>
260   - <Statistic style="font-size: 22px" :value="item.componentNum">
261   - <template #suffix>
262   - <span class="text-sm">个</span>
263   - </template>
264   - </Statistic>
265   - </div>
266   - </div>
267   - <div class="flex justify-between mt-4 text-sm" style="color: #999">
268   - <div class="flex min-w-20 mr-3">
269   - <span>
270   - {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }}
271   - </span>
272   - <span v-if="item.viewType === ViewType.PUBLIC_VIEW">
273   - <Tooltip title="点击复制分享链接">
274   - <ShareAltOutlined class="ml-1" @click.stop="handleCopyShareUrl(item)" />
275   - </Tooltip>
276   - </span>
277   - </div>
278   - <Tooltip placement="topLeft" :title="item.createTime">
279   - <div class="truncate">{{ item.createTime }}</div>
  139 + <section>
  140 + <BasicCardList @register="registerCardList">
  141 + <template #toolbar>
  142 + <Button type="primary" @click="handleOpenDetailModal">新增看板</Button>
  143 + </template>
  144 + <template #renderItem="{ item }: CardListRenderItem<DataBoardRecord>">
  145 + <Card class="data-card cursor-pointer">
  146 + <template #title>
  147 + <div class="font-bold">{{ item.name }}</div>
  148 + </template>
  149 + <template #extra>
  150 + <Dropdown
  151 + v-if="!isCustomerUser && dropMenuList.length"
  152 + :trigger="['click']"
  153 + @menu-event="(event) => handleMenuEvent(event, item)"
  154 + :drop-menu-list="dropMenuList"
  155 + >
  156 + <MoreOutlined class="rotate-90 transform cursor-pointer" />
  157 + </Dropdown>
  158 + </template>
  159 + <section @click="handleViewBoard(item)">
  160 + <div class="flex data-card__info">
  161 + <div>
  162 + <div>组件数量</div>
  163 + <Statistic class="text-2xl" :value="item.componentNum">
  164 + <template #suffix>
  165 + <span class="text-sm">个</span>
  166 + </template>
  167 + </Statistic>
  168 + </div>
  169 + </div>
  170 + <div class="flex justify-between mt-4 text-sm" style="color: #999">
  171 + <div class="flex min-w-20 mr-3">
  172 + <span>
  173 + {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }}
  174 + </span>
  175 + <span v-if="item.viewType === ViewType.PUBLIC_VIEW">
  176 + <Tooltip title="点击复制分享链接">
  177 + <ShareAltOutlined class="ml-1" @click.stop="handleCopyShareUrl(item)" />
280 178 </Tooltip>
281   - </div>
282   - </section>
283   - </Card>
284   - </ListItem>
285   - </template>
286   - </List>
287   - </Spin>
288   - <ShareModal @register="registerShareModal" :shareApi="shareBoard" @success="getDatasource" />
289   - <PanelDetailModal @register="registerModal" @change="getDatasource" />
290   - </PageWrapper>
  179 + </span>
  180 + </div>
  181 + <Tooltip placement="topLeft" :title="item.createTime">
  182 + <div class="truncate">{{ item.createTime }}</div>
  183 + </Tooltip>
  184 + </div>
  185 + </section>
  186 + </Card>
  187 + </template>
  188 + </BasicCardList>
  189 + <ShareModal @register="registerShareModal" :shareApi="shareBoard" @success="reload()" />
  190 + <PanelDetailModal @register="registerModal" @change="reload()" />
  191 + </section>
291 192 </template>
292 193
293 194 <style scoped lang="less">
... ...