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 | 4 | import { nextTick, ref } from 'vue'; |
5 | 5 | import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue'; |
6 | 6 | import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue'; |
7 | - import InformationPanel from '../components/InformationPanel.vue'; | |
7 | + import InformationPanel from '../components/Other/InformationPanel.vue'; | |
8 | 8 | import { DropMenu } from '/@/components/Dropdown'; |
9 | 9 | import DataBindModal from './components/DataBindModal.vue'; |
10 | 10 | import { useModal } from '/@/components/Modal'; | ... | ... |
1 | 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 | 4 | import { PageWrapper } from '/@/components/Page'; |
5 | 5 | import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue'; |
6 | 6 | import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; |
7 | 7 | import { useMessage } from '/@/hooks/web/useMessage'; |
8 | - import { useRouter } from 'vue-router'; | |
9 | 8 | import Dropdown from '/@/components/Dropdown/src/Dropdown.vue'; |
10 | 9 | import { DropMenu } from '/@/components/Dropdown'; |
11 | 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 | 19 | const router = useRouter(); |
20 | + | |
15 | 21 | const { createMessage } = useMessage(); |
16 | 22 | |
17 | - const data = ref([{ title: 1 }, { title: 2 }, { title: 3 }]); | |
23 | + const dataBoardList = ref<DataBoardRecord[]>([]); | |
18 | 24 | //分页相关 |
19 | 25 | const page = ref(1); |
20 | 26 | const pageSize = ref(36); |
... | ... | @@ -39,8 +45,8 @@ |
39 | 45 | } |
40 | 46 | |
41 | 47 | const { clipboardRef } = useCopyToClipboard(); |
42 | - const handleCopyShareUrl = () => { | |
43 | - clipboardRef.value = '123'; | |
48 | + const handleCopyShareUrl = (record: DataBoardRecord) => { | |
49 | + clipboardRef.value = record.openUrl; | |
44 | 50 | unref(clipboardRef) && createMessage.success('复制成功'); |
45 | 51 | }; |
46 | 52 | |
... | ... | @@ -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 | 115 | </script> |
72 | 116 | |
73 | 117 | <template> |
74 | 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 | 123 | <List |
77 | 124 | :pagination="paginationProp" |
78 | - :data-source="data" | |
125 | + :data-source="dataBoardList" | |
79 | 126 | :grid="{ gutter: 5, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }" |
80 | 127 | > |
81 | 128 | <template #renderItem="{ item }"> |
82 | 129 | <ListItem> |
83 | - <Card class="data-card cursor-pointer"> | |
130 | + <Card class="data-card cursor-pointer" @click="handleViewBoard(item)"> | |
84 | 131 | <template #extra> |
85 | 132 | <Dropdown |
86 | 133 | :trigger="['click']" |
87 | - @menu-event="handleMenuEvent" | |
134 | + @menu-event="(event) => handleMenuEvent(event, item)" | |
88 | 135 | :drop-menu-list="dropMenuList" |
89 | 136 | > |
90 | 137 | <MoreOutlined class="rotate-90 transform cursor-pointer" /> |
... | ... | @@ -93,7 +140,7 @@ |
93 | 140 | <!-- <template #cover>title</template> --> |
94 | 141 | <section> |
95 | 142 | <div class="flex justify-between items-center"> |
96 | - <div>设备看板名</div> | |
143 | + <div>{{ item.name }}</div> | |
97 | 144 | <div class="flex content-center"> |
98 | 145 | <Statistic value="12"> |
99 | 146 | <template #suffix> |
... | ... | @@ -104,18 +151,23 @@ |
104 | 151 | </div> |
105 | 152 | <div class="flex justify-between mt-4"> |
106 | 153 | <div> |
107 | - <span>看板类型</span> | |
108 | 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 | 161 | </span> |
111 | 162 | </div> |
112 | - <div>2021-02-11 12:00:00</div> | |
163 | + <div>{{ item.updateTime || item.createTime }}</div> | |
113 | 164 | </div> |
114 | 165 | </section> |
115 | 166 | </Card> |
116 | 167 | </ListItem> |
117 | 168 | </template> |
118 | 169 | </List> |
170 | + <PanelDetailModal @register="registerModal" @change="getDatasource" /> | |
119 | 171 | </PageWrapper> |
120 | 172 | </template> |
121 | 173 | ... | ... |