Commit c158d755d19c4ba9dbb1d9aea35d94b9454bd4d9
1 parent
67a97b78
wip: implement data board list page
Showing
13 changed files
with
316 additions
and
22 deletions
src/api/dataBoard/index.ts
0 → 100644
1 | +import { | ||
2 | + AddDataBoardParams, | ||
3 | + DataBoardList, | ||
4 | + GetDataBoardParams, | ||
5 | + UpdateDataBoardParams, | ||
6 | +} from './model'; | ||
7 | +import { defHttp } from '/@/utils/http/axios'; | ||
8 | + | ||
9 | +enum DataBoardUrl { | ||
10 | + GET_DATA_BOARD = '/data_board', | ||
11 | + ADD_DATA_BOARD = '/data_board/add', | ||
12 | + DELETE_DATA_BOARD = '/data_board', | ||
13 | + UPDATE_DATA_BOARD = '/data_board/update', | ||
14 | +} | ||
15 | + | ||
16 | +/** | ||
17 | + * @description 获取数据看板 | ||
18 | + * @param params | ||
19 | + * @returns | ||
20 | + */ | ||
21 | +export const getDataBoardList = (params: GetDataBoardParams) => { | ||
22 | + return defHttp.get<DataBoardList>({ | ||
23 | + url: DataBoardUrl.GET_DATA_BOARD, | ||
24 | + params, | ||
25 | + }); | ||
26 | +}; | ||
27 | + | ||
28 | +/** | ||
29 | + * @description 新增数据看板 | ||
30 | + * @param params | ||
31 | + * @returns | ||
32 | + */ | ||
33 | +export const addDataBoard = (params: AddDataBoardParams) => { | ||
34 | + return defHttp.post({ | ||
35 | + url: DataBoardUrl.ADD_DATA_BOARD, | ||
36 | + params, | ||
37 | + }); | ||
38 | +}; | ||
39 | + | ||
40 | +/** | ||
41 | + * @description 编辑数据看吧 | ||
42 | + * @param params | ||
43 | + * @returns | ||
44 | + */ | ||
45 | +export const updateDataBoard = (params: UpdateDataBoardParams) => { | ||
46 | + return defHttp.post({ | ||
47 | + url: DataBoardUrl.UPDATE_DATA_BOARD, | ||
48 | + params, | ||
49 | + }); | ||
50 | +}; | ||
51 | + | ||
52 | +/** | ||
53 | + * @description 删除数据看板 | ||
54 | + * @param params | ||
55 | + * @returns | ||
56 | + */ | ||
57 | +export const deleteDataBoard = (params: string[]) => { | ||
58 | + return defHttp.delete({ | ||
59 | + url: DataBoardUrl.DELETE_DATA_BOARD, | ||
60 | + params: { ids: params }, | ||
61 | + }); | ||
62 | +}; |
src/api/dataBoard/model/index.ts
0 → 100644
1 | +export interface AddDataBoardParams { | ||
2 | + name: string; | ||
3 | + viewType: string; | ||
4 | + remark?: string; | ||
5 | +} | ||
6 | + | ||
7 | +export interface UpdateDataBoardParams extends AddDataBoardParams { | ||
8 | + id: string; | ||
9 | +} | ||
10 | + | ||
11 | +export interface GetDataBoardParams { | ||
12 | + page?: number; | ||
13 | + pageSize?: number; | ||
14 | + orderFiled?: string; | ||
15 | + orderType?: string; | ||
16 | +} | ||
17 | + | ||
18 | +export interface Layout { | ||
19 | + h: number; | ||
20 | + id: string; | ||
21 | + w: number; | ||
22 | + x: number; | ||
23 | + y: number; | ||
24 | +} | ||
25 | + | ||
26 | +export interface Layout { | ||
27 | + h: number; | ||
28 | + id: string; | ||
29 | + w: number; | ||
30 | + x: number; | ||
31 | + y: number; | ||
32 | +} | ||
33 | + | ||
34 | +export interface DataBoardRecord { | ||
35 | + name: string; | ||
36 | + roleIds: string[]; | ||
37 | + updater: string; | ||
38 | + description: string; | ||
39 | + remark: string; | ||
40 | + viewType: string; | ||
41 | + enabled: boolean; | ||
42 | + updateTime: string; | ||
43 | + createTime: string; | ||
44 | + tenantProfileId: string; | ||
45 | + id: string; | ||
46 | + tenantExpireTime: string; | ||
47 | + icon: string; | ||
48 | + openUrl: string; | ||
49 | + tenantId: string; | ||
50 | + creator: string; | ||
51 | + layout: Layout[]; | ||
52 | + defaultConfig: string; | ||
53 | + tenantStatus: string; | ||
54 | +} | ||
55 | + | ||
56 | +export interface DataBoardList { | ||
57 | + items: DataBoardRecord[]; | ||
58 | + total: number; | ||
59 | +} |
src/views/data/board/components/Other/CommandSendButton.vue
renamed from
src/views/data/board/components/CommandSendButton.vue
src/views/data/board/components/Other/IndicatorLight.vue
renamed from
src/views/data/board/components/IndicatorLight.vue
src/views/data/board/components/Other/InformationPanel.vue
renamed from
src/views/data/board/components/InformationPanel.vue
src/views/data/board/components/Other/LightBulbSwitch.vue
renamed from
src/views/data/board/components/LightBulbSwitch.vue
src/views/data/board/components/Other/RockerSwitch.vue
renamed from
src/views/data/board/components/RockerSwitch.vue
src/views/data/board/components/Other/SlidingSwitch.vue
renamed from
src/views/data/board/components/SlidingSwitch.vue
src/views/data/board/components/Other/ToggleSwitch.vue
renamed from
src/views/data/board/components/ToggleSwitch.vue
1 | +<script lang="ts" setup> | ||
2 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
3 | + import { BasicForm, useForm } from '/@/components/Form'; | ||
4 | + import { formSchema } from '../config/panelDetail'; | ||
5 | + import { addDataBoard, updateDataBoard } from '/@/api/dataBoard'; | ||
6 | + import { AddDataBoardParams } from '/@/api/dataBoard/model'; | ||
7 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
8 | + import type { DataBoardRecord, UpdateDataBoardParams } from '/@/api/dataBoard/model'; | ||
9 | + import { ref, unref } from 'vue'; | ||
10 | + | ||
11 | + const emit = defineEmits(['change']); | ||
12 | + | ||
13 | + const isEdit = ref(false); | ||
14 | + const recordId = ref<Nullable<string>>(null); | ||
15 | + | ||
16 | + const [registerModal, { changeLoading, closeModal }] = useModalInner( | ||
17 | + (record: DataBoardRecord & { isEdit: boolean }) => { | ||
18 | + method.setFieldsValue(record); | ||
19 | + recordId.value = record.id; | ||
20 | + isEdit.value = record.isEdit; | ||
21 | + } | ||
22 | + ); | ||
23 | + | ||
24 | + const [registerForm, method] = useForm({ | ||
25 | + showActionButtonGroup: false, | ||
26 | + schemas: formSchema, | ||
27 | + labelWidth: 80, | ||
28 | + }); | ||
29 | + | ||
30 | + const { createMessage } = useMessage(); | ||
31 | + | ||
32 | + const handleCreatePanel = async () => { | ||
33 | + try { | ||
34 | + const value = method.getFieldsValue() as AddDataBoardParams; | ||
35 | + changeLoading(true); | ||
36 | + await addDataBoard(value); | ||
37 | + createMessage.success('创建成功'); | ||
38 | + closeModal(); | ||
39 | + emit('change'); | ||
40 | + } catch (error) { | ||
41 | + createMessage.error('创建失败'); | ||
42 | + } finally { | ||
43 | + changeLoading(false); | ||
44 | + } | ||
45 | + }; | ||
46 | + | ||
47 | + const handleEditPanel = async () => { | ||
48 | + try { | ||
49 | + const value = method.getFieldsValue() as UpdateDataBoardParams; | ||
50 | + value.id = unref(recordId) as string; | ||
51 | + changeLoading(true); | ||
52 | + await updateDataBoard(value); | ||
53 | + createMessage.success('编辑成功'); | ||
54 | + closeModal(); | ||
55 | + emit('change'); | ||
56 | + } catch (error) { | ||
57 | + createMessage.error('编辑失败'); | ||
58 | + } finally { | ||
59 | + changeLoading(false); | ||
60 | + } | ||
61 | + }; | ||
62 | + | ||
63 | + const handleGetValue = () => { | ||
64 | + unref(isEdit) ? handleEditPanel() : handleCreatePanel(); | ||
65 | + }; | ||
66 | +</script> | ||
67 | + | ||
68 | +<template> | ||
69 | + <BasicModal | ||
70 | + v-bind="$attrs" | ||
71 | + :destroyOnClose="true" | ||
72 | + :title="isEdit ? '编辑看板' : '创建看板'" | ||
73 | + @register="registerModal" | ||
74 | + @ok="handleGetValue" | ||
75 | + > | ||
76 | + <BasicForm @register="registerForm" /> | ||
77 | + </BasicModal> | ||
78 | +</template> | ||
79 | + | ||
80 | +<style scoped></style> |
src/views/data/board/config/panelDetail.ts
0 → 100644
1 | +import { FormSchema } from '/@/components/Form'; | ||
2 | +export enum ViewType { | ||
3 | + PRIVATE_VIEW = 'PRIVATE_VIEW', | ||
4 | + PUBLIC_VIEW = 'PUBLIC_VIEW', | ||
5 | +} | ||
6 | + | ||
7 | +export const formSchema: FormSchema[] = [ | ||
8 | + { | ||
9 | + field: 'name', | ||
10 | + label: '名称', | ||
11 | + component: 'Input', | ||
12 | + required: true, | ||
13 | + componentProps: { | ||
14 | + placeholder: '请输入看板名称', | ||
15 | + }, | ||
16 | + }, | ||
17 | + { | ||
18 | + field: 'viewType', | ||
19 | + label: '名称', | ||
20 | + component: 'RadioGroup', | ||
21 | + defaultValue: ViewType.PRIVATE_VIEW, | ||
22 | + helpMessage: [ | ||
23 | + '私有视图只有项目成员可以浏览。公开视图拥有一个公开的 URL,任何人无需登录即可浏览。 ', | ||
24 | + ], | ||
25 | + componentProps: { | ||
26 | + placeholder: '请选择公开性', | ||
27 | + options: [ | ||
28 | + { label: '私有看板', value: ViewType.PRIVATE_VIEW }, | ||
29 | + { label: '公开看板', value: ViewType.PUBLIC_VIEW }, | ||
30 | + ], | ||
31 | + }, | ||
32 | + }, | ||
33 | + { | ||
34 | + field: 'remark', | ||
35 | + label: '备注', | ||
36 | + component: 'InputTextArea', | ||
37 | + componentProps: { | ||
38 | + placeholder: '请输入看板备注', | ||
39 | + }, | ||
40 | + }, | ||
41 | +]; |
@@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
4 | import { nextTick, ref } from 'vue'; | 4 | import { nextTick, ref } from 'vue'; |
5 | import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue'; | 5 | import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue'; |
6 | import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue'; | 6 | import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue'; |
7 | - import InformationPanel from '../components/InformationPanel.vue'; | 7 | + import InformationPanel from '../components/Other/InformationPanel.vue'; |
8 | import { DropMenu } from '/@/components/Dropdown'; | 8 | import { DropMenu } from '/@/components/Dropdown'; |
9 | import DataBindModal from './components/DataBindModal.vue'; | 9 | import DataBindModal from './components/DataBindModal.vue'; |
10 | import { useModal } from '/@/components/Modal'; | 10 | import { useModal } from '/@/components/Modal'; |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { List, Card, Statistic } from 'ant-design-vue'; | ||
3 | - import { ref, unref } from 'vue'; | 2 | + import { List, Card, Statistic, Button, Tooltip } from 'ant-design-vue'; |
3 | + import { onMounted, ref, unref } from 'vue'; | ||
4 | import { PageWrapper } from '/@/components/Page'; | 4 | import { PageWrapper } from '/@/components/Page'; |
5 | import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue'; | 5 | import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue'; |
6 | import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; | 6 | import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; |
7 | import { useMessage } from '/@/hooks/web/useMessage'; | 7 | import { useMessage } from '/@/hooks/web/useMessage'; |
8 | - import { useRouter } from 'vue-router'; | ||
9 | import Dropdown from '/@/components/Dropdown/src/Dropdown.vue'; | 8 | import Dropdown from '/@/components/Dropdown/src/Dropdown.vue'; |
10 | import { DropMenu } from '/@/components/Dropdown'; | 9 | import { DropMenu } from '/@/components/Dropdown'; |
11 | import { MoreActionEvent } from './config/config'; | 10 | import { MoreActionEvent } from './config/config'; |
12 | - const ListItem = List.Item; | 11 | + import { useModal } from '/@/components/Modal'; |
12 | + import PanelDetailModal from './components/PanelDetailModal.vue'; | ||
13 | + import { getDataBoardList, deleteDataBoard } from '/@/api/dataBoard'; | ||
14 | + import { DataBoardRecord } from '/@/api/dataBoard/model'; | ||
15 | + import { ViewType } from './config/panelDetail'; | ||
16 | + import { useRouter } from 'vue-router'; | ||
13 | 17 | ||
18 | + const ListItem = List.Item; | ||
14 | const router = useRouter(); | 19 | const router = useRouter(); |
20 | + | ||
15 | const { createMessage } = useMessage(); | 21 | const { createMessage } = useMessage(); |
16 | 22 | ||
17 | - const data = ref([{ title: 1 }, { title: 2 }, { title: 3 }]); | 23 | + const dataBoardList = ref<DataBoardRecord[]>([]); |
18 | //分页相关 | 24 | //分页相关 |
19 | const page = ref(1); | 25 | const page = ref(1); |
20 | const pageSize = ref(36); | 26 | const pageSize = ref(36); |
@@ -39,8 +45,8 @@ | @@ -39,8 +45,8 @@ | ||
39 | } | 45 | } |
40 | 46 | ||
41 | const { clipboardRef } = useCopyToClipboard(); | 47 | const { clipboardRef } = useCopyToClipboard(); |
42 | - const handleCopyShareUrl = () => { | ||
43 | - clipboardRef.value = '123'; | 48 | + const handleCopyShareUrl = (record: DataBoardRecord) => { |
49 | + clipboardRef.value = record.openUrl; | ||
44 | unref(clipboardRef) && createMessage.success('复制成功'); | 50 | unref(clipboardRef) && createMessage.success('复制成功'); |
45 | }; | 51 | }; |
46 | 52 | ||
@@ -57,34 +63,75 @@ | @@ -57,34 +63,75 @@ | ||
57 | }, | 63 | }, |
58 | ]; | 64 | ]; |
59 | 65 | ||
60 | - const handleMenuEvent = (event: DropMenu) => { | ||
61 | - if (event.event === MoreActionEvent.EDIT) { | ||
62 | - handleEdit(); | 66 | + const getDatasource = async () => { |
67 | + try { | ||
68 | + const { total, items } = await getDataBoardList({ | ||
69 | + page: unref(paginationProp).current, | ||
70 | + pageSize: unref(paginationProp).pageSize, | ||
71 | + }); | ||
72 | + dataBoardList.value = items; | ||
73 | + paginationProp.value.total = total; | ||
74 | + } catch (error) { | ||
75 | + } finally { | ||
63 | } | 76 | } |
64 | }; | 77 | }; |
65 | 78 | ||
66 | - const handleEdit = () => { | ||
67 | - router.push('/data/board/detail/1234'); | 79 | + const handleMenuEvent = (event: DropMenu, record: DataBoardRecord) => { |
80 | + if (event.event === MoreActionEvent.EDIT) handleEdit(record); | ||
81 | + if (event.event === MoreActionEvent.DELETE) handleRemove(record); | ||
82 | + }; | ||
83 | + | ||
84 | + const handleEdit = (record: DataBoardRecord) => { | ||
85 | + openModal(true, { | ||
86 | + isEdit: true, | ||
87 | + ...record, | ||
88 | + }); | ||
89 | + }; | ||
90 | + | ||
91 | + const handleOpenDetailModal = () => { | ||
92 | + openModal(); | ||
68 | }; | 93 | }; |
69 | 94 | ||
70 | - const handleRemove = () => {}; | 95 | + const handleRemove = async (record: DataBoardRecord) => { |
96 | + // TODO 删除确认 | ||
97 | + try { | ||
98 | + await deleteDataBoard([record.id]); | ||
99 | + createMessage.success('删除成功'); | ||
100 | + await getDatasource(); | ||
101 | + } catch (error) { | ||
102 | + createMessage.error('删除失败'); | ||
103 | + } | ||
104 | + }; | ||
105 | + | ||
106 | + const [registerModal, { openModal }] = useModal(); | ||
107 | + | ||
108 | + const handleViewBoard = (record: DataBoardRecord) => { | ||
109 | + router.push(`/data/board/detail/${record.id}`); | ||
110 | + }; | ||
111 | + | ||
112 | + onMounted(() => { | ||
113 | + getDatasource(); | ||
114 | + }); | ||
71 | </script> | 115 | </script> |
72 | 116 | ||
73 | <template> | 117 | <template> |
74 | <PageWrapper> | 118 | <PageWrapper> |
75 | - <template #header> 自定义看板 </template> | 119 | + <div class="flex mb-6 items-center"> |
120 | + <div class="text-lg mr-6 font-bold">自定义看板</div> | ||
121 | + <Button type="primary" @click="handleOpenDetailModal">创建看板</Button> | ||
122 | + </div> | ||
76 | <List | 123 | <List |
77 | :pagination="paginationProp" | 124 | :pagination="paginationProp" |
78 | - :data-source="data" | 125 | + :data-source="dataBoardList" |
79 | :grid="{ gutter: 5, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }" | 126 | :grid="{ gutter: 5, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }" |
80 | > | 127 | > |
81 | <template #renderItem="{ item }"> | 128 | <template #renderItem="{ item }"> |
82 | <ListItem> | 129 | <ListItem> |
83 | - <Card class="data-card cursor-pointer"> | 130 | + <Card class="data-card cursor-pointer" @click="handleViewBoard(item)"> |
84 | <template #extra> | 131 | <template #extra> |
85 | <Dropdown | 132 | <Dropdown |
86 | :trigger="['click']" | 133 | :trigger="['click']" |
87 | - @menu-event="handleMenuEvent" | 134 | + @menu-event="(event) => handleMenuEvent(event, item)" |
88 | :drop-menu-list="dropMenuList" | 135 | :drop-menu-list="dropMenuList" |
89 | > | 136 | > |
90 | <MoreOutlined class="rotate-90 transform cursor-pointer" /> | 137 | <MoreOutlined class="rotate-90 transform cursor-pointer" /> |
@@ -93,7 +140,7 @@ | @@ -93,7 +140,7 @@ | ||
93 | <!-- <template #cover>title</template> --> | 140 | <!-- <template #cover>title</template> --> |
94 | <section> | 141 | <section> |
95 | <div class="flex justify-between items-center"> | 142 | <div class="flex justify-between items-center"> |
96 | - <div>设备看板名</div> | 143 | + <div>{{ item.name }}</div> |
97 | <div class="flex content-center"> | 144 | <div class="flex content-center"> |
98 | <Statistic value="12"> | 145 | <Statistic value="12"> |
99 | <template #suffix> | 146 | <template #suffix> |
@@ -104,18 +151,23 @@ | @@ -104,18 +151,23 @@ | ||
104 | </div> | 151 | </div> |
105 | <div class="flex justify-between mt-4"> | 152 | <div class="flex justify-between mt-4"> |
106 | <div> | 153 | <div> |
107 | - <span>看板类型</span> | ||
108 | <span> | 154 | <span> |
109 | - <ShareAltOutlined @click="handleCopyShareUrl" /> | 155 | + {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }} |
156 | + </span> | ||
157 | + <span v-if="item.viewType === ViewType.PUBLIC_VIEW"> | ||
158 | + <Tooltip title="分享链接"> | ||
159 | + <ShareAltOutlined class="ml-2" @click="handleCopyShareUrl(item)" /> | ||
160 | + </Tooltip> | ||
110 | </span> | 161 | </span> |
111 | </div> | 162 | </div> |
112 | - <div>2021-02-11 12:00:00</div> | 163 | + <div>{{ item.updateTime || item.createTime }}</div> |
113 | </div> | 164 | </div> |
114 | </section> | 165 | </section> |
115 | </Card> | 166 | </Card> |
116 | </ListItem> | 167 | </ListItem> |
117 | </template> | 168 | </template> |
118 | </List> | 169 | </List> |
170 | + <PanelDetailModal @register="registerModal" @change="getDatasource" /> | ||
119 | </PageWrapper> | 171 | </PageWrapper> |
120 | </template> | 172 | </template> |
121 | 173 |