Showing
11 changed files
with
201 additions
and
393 deletions
@@ -43,120 +43,7 @@ export interface IDeviceConfigAddOrEditModel { | @@ -43,120 +43,7 @@ export interface IDeviceConfigAddOrEditModel { | ||
43 | icon?: string; | 43 | icon?: string; |
44 | id?: string; | 44 | id?: string; |
45 | name?: string; | 45 | name?: string; |
46 | - profileData?: { | ||
47 | - configuration: {}; | ||
48 | - transportConfiguration: {}; | ||
49 | - provisionConfiguration: { | ||
50 | - provisionDeviceSecret: string; | ||
51 | - }; | ||
52 | - //报警类型字段 | ||
53 | - alarms: [ | ||
54 | - { | ||
55 | - id: 'highTemperatureAlarmID'; | ||
56 | - alarmType: 'High Temperature Alarm'; | ||
57 | - createRules: { | ||
58 | - additionalProp1: { | ||
59 | - condition: { | ||
60 | - condition: [ | ||
61 | - { | ||
62 | - key: { | ||
63 | - type: 'TIME_SERIES'; | ||
64 | - key: 'temp'; | ||
65 | - }; | ||
66 | - valueType: 'NUMERIC'; | ||
67 | - value: {}; | ||
68 | - predicate: {}; | ||
69 | - } | ||
70 | - ]; | ||
71 | - spec: {}; | ||
72 | - }; | ||
73 | - schedule: { | ||
74 | - type: 'ANY_TIME'; | ||
75 | - }; | ||
76 | - alarmDetails: string; | ||
77 | - dashboardId: { | ||
78 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | ||
79 | - entityType: 'DASHBOARD'; | ||
80 | - }; | ||
81 | - }; | ||
82 | - additionalProp2: { | ||
83 | - condition: { | ||
84 | - condition: [ | ||
85 | - { | ||
86 | - key: { | ||
87 | - type: 'TIME_SERIES'; | ||
88 | - key: 'temp'; | ||
89 | - }; | ||
90 | - valueType: 'NUMERIC'; | ||
91 | - value: {}; | ||
92 | - predicate: {}; | ||
93 | - } | ||
94 | - ]; | ||
95 | - spec: {}; | ||
96 | - }; | ||
97 | - schedule: { | ||
98 | - type: 'ANY_TIME'; | ||
99 | - }; | ||
100 | - alarmDetails: string; | ||
101 | - dashboardId: { | ||
102 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | ||
103 | - entityType: 'DASHBOARD'; | ||
104 | - }; | ||
105 | - }; | ||
106 | - additionalProp3: { | ||
107 | - condition: { | ||
108 | - condition: [ | ||
109 | - { | ||
110 | - key: { | ||
111 | - type: 'TIME_SERIES'; | ||
112 | - key: 'temp'; | ||
113 | - }; | ||
114 | - valueType: 'NUMERIC'; | ||
115 | - value: {}; | ||
116 | - predicate: {}; | ||
117 | - } | ||
118 | - ]; | ||
119 | - spec: {}; | ||
120 | - }; | ||
121 | - schedule: { | ||
122 | - type: 'ANY_TIME'; | ||
123 | - }; | ||
124 | - alarmDetails: string; | ||
125 | - dashboardId: { | ||
126 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | ||
127 | - entityType: 'DASHBOARD'; | ||
128 | - }; | ||
129 | - }; | ||
130 | - }; | ||
131 | - clearRule: { | ||
132 | - condition: { | ||
133 | - condition: [ | ||
134 | - { | ||
135 | - key: { | ||
136 | - type: 'TIME_SERIES'; | ||
137 | - key: 'temp'; | ||
138 | - }; | ||
139 | - valueType: 'NUMERIC'; | ||
140 | - value: {}; | ||
141 | - predicate: {}; | ||
142 | - } | ||
143 | - ]; | ||
144 | - spec: {}; | ||
145 | - }; | ||
146 | - schedule: { | ||
147 | - type: 'ANY_TIME'; | ||
148 | - }; | ||
149 | - alarmDetails: string; | ||
150 | - dashboardId: { | ||
151 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | ||
152 | - entityType: 'DASHBOARD'; | ||
153 | - }; | ||
154 | - }; | ||
155 | - propagate: true; | ||
156 | - propagateRelationTypes: [string]; | ||
157 | - } | ||
158 | - ]; | ||
159 | - }; | 46 | + profileData?: ProfileData; |
160 | roleIds?: [string]; | 47 | roleIds?: [string]; |
161 | tbProfileId?: string; | 48 | tbProfileId?: string; |
162 | tenantExpireTime?: '2021-12-15T02:17:26.645Z'; | 49 | tenantExpireTime?: '2021-12-15T02:17:26.645Z'; |
@@ -219,7 +106,7 @@ export interface ProfileData { | @@ -219,7 +106,7 @@ export interface ProfileData { | ||
219 | configuration: Configuration; | 106 | configuration: Configuration; |
220 | transportConfiguration: TransportConfiguration; | 107 | transportConfiguration: TransportConfiguration; |
221 | provisionConfiguration: ProvisionConfiguration; | 108 | provisionConfiguration: ProvisionConfiguration; |
222 | - alarms?: any; | 109 | + alarms: any; |
223 | } | 110 | } |
224 | 111 | ||
225 | export interface ProfileRecord { | 112 | export interface ProfileRecord { |
@@ -297,8 +184,3 @@ export interface Configuration { | @@ -297,8 +184,3 @@ export interface Configuration { | ||
297 | export interface TransportConfiguration { | 184 | export interface TransportConfiguration { |
298 | type: string; | 185 | type: string; |
299 | } | 186 | } |
300 | - | ||
301 | -export interface ProvisionConfiguration { | ||
302 | - type: string; | ||
303 | - provisionDeviceSecret: any; | ||
304 | -} |
1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
2 | import { BasicForm, FormActionType, useForm } from '/@/components/Form'; | 2 | import { BasicForm, FormActionType, useForm } from '/@/components/Form'; |
3 | - import { List, ListProps, Button, Tooltip } from 'ant-design-vue'; | 3 | + import { List, ListProps, Button, Tooltip, DropdownButton, Menu } from 'ant-design-vue'; |
4 | import { computed, Ref, ref, toRaw, unref, useSlots, watch } from 'vue'; | 4 | import { computed, Ref, ref, toRaw, unref, useSlots, watch } from 'vue'; |
5 | import { useCardListScroll } from './hooks/useCardListScroll'; | 5 | import { useCardListScroll } from './hooks/useCardListScroll'; |
6 | import { BasicCardListPropsType, CardListActionType } from './types'; | 6 | import { BasicCardListPropsType, CardListActionType } from './types'; |
@@ -96,6 +96,7 @@ | @@ -96,6 +96,7 @@ | ||
96 | clearSelectedKeys, | 96 | clearSelectedKeys, |
97 | getSelectedKeys, | 97 | getSelectedKeys, |
98 | getSelectedRecords, | 98 | getSelectedRecords, |
99 | + getSelections, | ||
99 | } = useCardListSelected(getDataSourceRef, getProps, { | 100 | } = useCardListSelected(getDataSourceRef, getProps, { |
100 | updateTableDataRecord, | 101 | updateTableDataRecord, |
101 | findCardDataRecord, | 102 | findCardDataRecord, |
@@ -142,6 +143,7 @@ | @@ -142,6 +143,7 @@ | ||
142 | clearSelectedKeys, | 143 | clearSelectedKeys, |
143 | getSelectedKeys, | 144 | getSelectedKeys, |
144 | getSelectedRecords, | 145 | getSelectedRecords, |
146 | + getSelections, | ||
145 | }; | 147 | }; |
146 | 148 | ||
147 | function setProps(props: Partial<BasicCardListPropsType> = {}) { | 149 | function setProps(props: Partial<BasicCardListPropsType> = {}) { |
@@ -177,13 +179,21 @@ | @@ -177,13 +179,21 @@ | ||
177 | </span> | 179 | </span> |
178 | <slot v-else name="title"></slot> | 180 | <slot v-else name="title"></slot> |
179 | </div> | 181 | </div> |
180 | - <div class="flex flex-auto justify-end mr-4"> | 182 | + <div class="flex flex-auto justify-end mr-4 gap-4"> |
181 | <slot name="toolbar"></slot> | 183 | <slot name="toolbar"></slot> |
182 | </div> | 184 | </div> |
183 | <div v-if="showCardListSetting" class="flex gap-4"> | 185 | <div v-if="showCardListSetting" class="flex gap-4"> |
184 | - <Button v-if="getProps.selections" @click="selectAllToggle" type="primary"> | ||
185 | - {{ selectedKeys.length === getDataSourceRef.length ? '反选' : '全选' }} | ||
186 | - </Button> | 186 | + <DropdownButton v-if="getProps.selections" @click="selectAllToggle" type="primary"> |
187 | + <template #overlay> | ||
188 | + <Menu> | ||
189 | + <Menu.Item @click="selectedAll">全选</Menu.Item> | ||
190 | + <Menu.Item @click="clearSelectedKeys">反选</Menu.Item> | ||
191 | + </Menu> | ||
192 | + </template> | ||
193 | + <span> | ||
194 | + {{ selectedKeys.length === getDataSourceRef.length ? '反选' : '全选' }} | ||
195 | + </span> | ||
196 | + </DropdownButton> | ||
187 | <CardListLayout v-model:row="cardListLayout.row" v-model:col="cardListLayout.col" /> | 197 | <CardListLayout v-model:row="cardListLayout.row" v-model:col="cardListLayout.col" /> |
188 | <Tooltip title="刷新"> | 198 | <Tooltip title="刷新"> |
189 | <Button type="primary" @click="reload()" :loading="loading"> | 199 | <Button type="primary" @click="reload()" :loading="loading"> |
@@ -193,7 +203,7 @@ | @@ -193,7 +203,7 @@ | ||
193 | </div> | 203 | </div> |
194 | </section> | 204 | </section> |
195 | </template> | 205 | </template> |
196 | - <template #renderItem="{ item }: CardListRenderItem"> | 206 | + <template #renderItem="{ item }: BasicCardListRenderItem<any>"> |
197 | <List.Item | 207 | <List.Item |
198 | :class="selectedKeys.includes(item[getRowKey]) ? selectedClass : ''" | 208 | :class="selectedKeys.includes(item[getRowKey]) ? selectedClass : ''" |
199 | @click="handlerSelected($event, item)" | 209 | @click="handlerSelected($event, item)" |
@@ -8,7 +8,7 @@ import { PaginationProps } from 'ant-design-vue'; | @@ -8,7 +8,7 @@ import { PaginationProps } from 'ant-design-vue'; | ||
8 | import { FetchParams } from './useCardListData'; | 8 | import { FetchParams } from './useCardListData'; |
9 | import { tryOnUnmounted } from '@vueuse/core'; | 9 | import { tryOnUnmounted } from '@vueuse/core'; |
10 | 10 | ||
11 | -type UseTableMethod = CardListActionType & { | 11 | +type UseCardListMethod = CardListActionType & { |
12 | getForm: () => FormActionType; | 12 | getForm: () => FormActionType; |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -49,46 +49,49 @@ export function useCardList( | @@ -49,46 +49,49 @@ export function useCardList( | ||
49 | ); | 49 | ); |
50 | } | 50 | } |
51 | 51 | ||
52 | - function getTableInstance(): CardListActionType { | ||
53 | - const table = unref(cardListRef); | ||
54 | - if (!table) { | 52 | + function getCardListInstance(): CardListActionType { |
53 | + const cardList = unref(cardListRef); | ||
54 | + if (!cardList) { | ||
55 | error( | 55 | error( |
56 | - 'The CardList instance has not been obtained yet, please make sure the table is presented when performing the table operation!' | 56 | + 'The CardList instance has not been obtained yet, please make sure the card list is presented when performing the card list operation!' |
57 | ); | 57 | ); |
58 | } | 58 | } |
59 | - return table as CardListActionType; | 59 | + return cardList as CardListActionType; |
60 | } | 60 | } |
61 | 61 | ||
62 | - const cardListActionType: UseTableMethod = { | 62 | + const cardListActionType: UseCardListMethod = { |
63 | getForm: () => { | 63 | getForm: () => { |
64 | return unref(formRef) as unknown as FormActionType; | 64 | return unref(formRef) as unknown as FormActionType; |
65 | }, | 65 | }, |
66 | setLoading: (loading: boolean) => { | 66 | setLoading: (loading: boolean) => { |
67 | - return getTableInstance().setLoading(loading); | 67 | + return getCardListInstance().setLoading(loading); |
68 | }, | 68 | }, |
69 | setProps: (props: Partial<BasicCardListPropsType>) => { | 69 | setProps: (props: Partial<BasicCardListPropsType>) => { |
70 | - getTableInstance().setProps(props); | 70 | + getCardListInstance().setProps(props); |
71 | }, | 71 | }, |
72 | getPagination: () => { | 72 | getPagination: () => { |
73 | - return getTableInstance().getPagination(); | 73 | + return getCardListInstance().getPagination(); |
74 | }, | 74 | }, |
75 | setPagination: (pagination: Partial<PaginationProps>) => { | 75 | setPagination: (pagination: Partial<PaginationProps>) => { |
76 | - return getTableInstance().setPagination(pagination); | 76 | + return getCardListInstance().setPagination(pagination); |
77 | }, | 77 | }, |
78 | reload: (opt?: FetchParams) => { | 78 | reload: (opt?: FetchParams) => { |
79 | - return getTableInstance().reload(opt); | 79 | + return getCardListInstance().reload(opt); |
80 | }, | 80 | }, |
81 | selectedAll: () => { | 81 | selectedAll: () => { |
82 | - return getTableInstance().selectedAll(); | 82 | + return getCardListInstance().selectedAll(); |
83 | }, | 83 | }, |
84 | clearSelectedKeys: () => { | 84 | clearSelectedKeys: () => { |
85 | - return getTableInstance().clearSelectedKeys(); | 85 | + return getCardListInstance().clearSelectedKeys(); |
86 | }, | 86 | }, |
87 | getSelectedKeys: () => { | 87 | getSelectedKeys: () => { |
88 | - return getTableInstance().getSelectedKeys(); | 88 | + return getCardListInstance().getSelectedKeys(); |
89 | }, | 89 | }, |
90 | getSelectedRecords: () => { | 90 | getSelectedRecords: () => { |
91 | - return getTableInstance().getSelectedRecords(); | 91 | + return getCardListInstance().getSelectedRecords(); |
92 | + }, | ||
93 | + getSelections: () => { | ||
94 | + return getCardListInstance().getSelections(); | ||
92 | }, | 95 | }, |
93 | }; | 96 | }; |
94 | 97 |
1 | import { ComputedRef, computed, ref, toRaw, unref, watch } from 'vue'; | 1 | import { ComputedRef, computed, ref, toRaw, unref, watch } from 'vue'; |
2 | import { BasicCardListPropsType, CardListSelectionsType, UseCardListDataType } from '../types'; | 2 | import { BasicCardListPropsType, CardListSelectionsType, UseCardListDataType } from '../types'; |
3 | -import { isBoolean } from '/@/utils/is'; | 3 | +import { isBoolean, isFunction } from '/@/utils/is'; |
4 | import { cloneDeep } from 'lodash-es'; | 4 | import { cloneDeep } from 'lodash-es'; |
5 | 5 | ||
6 | export const CHECKED_FIELD = 'CARD_LIST_CHECKED_STATUS'; | 6 | export const CHECKED_FIELD = 'CARD_LIST_CHECKED_STATUS'; |
@@ -23,18 +23,27 @@ export function useCardListSelected( | @@ -23,18 +23,27 @@ export function useCardListSelected( | ||
23 | 23 | ||
24 | const selectedClass = ref(''); | 24 | const selectedClass = ref(''); |
25 | 25 | ||
26 | + const selectionRef = ref<CardListSelectionsType>({}); | ||
27 | + | ||
28 | + const getSelectionsRef = computed<CardListSelectionsType & { enabled: boolean }>(() => { | ||
29 | + const { selections } = unref(propsRef); | ||
30 | + return { | ||
31 | + enabled: !!selections, | ||
32 | + ...unref(selectionRef), | ||
33 | + ...(isBoolean(selections) ? {} : selections), | ||
34 | + }; | ||
35 | + }); | ||
36 | + | ||
26 | const getHasSelectedRecordStatus = computed(() => | 37 | const getHasSelectedRecordStatus = computed(() => |
27 | unref(getDataSourceRef).find((item) => item.checked) | 38 | unref(getDataSourceRef).find((item) => item.checked) |
28 | ); | 39 | ); |
29 | 40 | ||
30 | const getShouldUseDefaultStyle = computed( | 41 | const getShouldUseDefaultStyle = computed( |
31 | - () => | ||
32 | - isBoolean(unref(propsRef).selections) || | ||
33 | - !(unref(propsRef).selections as CardListSelectionsType).customCheckedStyle | 42 | + () => unref(getSelectionsRef).enabled && !unref(getSelectionsRef).customCheckedStyle |
34 | ); | 43 | ); |
35 | 44 | ||
36 | watch( | 45 | watch( |
37 | - () => unref(propsRef).selections, | 46 | + getSelectionsRef, |
38 | () => { | 47 | () => { |
39 | selectedClass.value = | 48 | selectedClass.value = |
40 | isBoolean(unref(propsRef).selections) || | 49 | isBoolean(unref(propsRef).selections) || |
@@ -48,24 +57,33 @@ export function useCardListSelected( | @@ -48,24 +57,33 @@ export function useCardListSelected( | ||
48 | ); | 57 | ); |
49 | 58 | ||
50 | function handlerSelected(_event: MouseEvent, record: Recordable) { | 59 | function handlerSelected(_event: MouseEvent, record: Recordable) { |
51 | - if (!unref(propsRef).selections) return; | 60 | + if (!unref(getSelectionsRef).enabled) return; |
61 | + | ||
62 | + if (unref(getSelectionsRef).beforeSelectValidate) { | ||
63 | + if (!unref(getSelectionsRef).beforeSelectValidate?.(record)) return; | ||
64 | + } | ||
65 | + | ||
52 | const rowKey = record[unref(getRowKey)]; | 66 | const rowKey = record[unref(getRowKey)]; |
53 | const index = unref(selectedKeys).findIndex((key) => key === rowKey); | 67 | const index = unref(selectedKeys).findIndex((key) => key === rowKey); |
54 | ~index ? selectedKeys.value.splice(index, 1) : unref(selectedKeys).push(rowKey); | 68 | ~index ? selectedKeys.value.splice(index, 1) : unref(selectedKeys).push(rowKey); |
55 | 69 | ||
56 | - if (isBoolean(unref(propsRef).selections)) return; | 70 | + if (!unref(getSelectionsRef).onSelect) return; |
57 | 71 | ||
58 | - (unref(propsRef).selections as CardListSelectionsType)?.onSelect?.(record, !!~index); | 72 | + unref(getSelectionsRef)?.onSelect?.(record, !!~index); |
59 | } | 73 | } |
60 | 74 | ||
61 | function selectedAll() { | 75 | function selectedAll() { |
62 | - selectedKeys.value = unref(getDataSourceRef).map((item) => item[unref(getRowKey)]); | 76 | + selectedKeys.value = unref(getDataSourceRef) |
77 | + .filter( | ||
78 | + (item) => | ||
79 | + isFunction(unref(getSelectionsRef).beforeSelectValidate) && | ||
80 | + unref(getSelectionsRef).beforeSelectValidate?.(item) | ||
81 | + ) | ||
82 | + .map((item) => item[unref(getRowKey)]); | ||
63 | 83 | ||
64 | - if (isBoolean(unref(propsRef).selections)) return; | 84 | + if (!unref(getSelectionsRef).onSelect) return; |
65 | 85 | ||
66 | - (unref(propsRef).selections as CardListSelectionsType)?.onSelectAll?.( | ||
67 | - toRaw(unref(getDataSourceRef)) | ||
68 | - ); | 86 | + unref(getSelectionsRef)?.onSelectAll?.(toRaw(unref(getDataSourceRef))); |
69 | } | 87 | } |
70 | 88 | ||
71 | function clearSelectedKeys() { | 89 | function clearSelectedKeys() { |
@@ -87,11 +105,16 @@ export function useCardListSelected( | @@ -87,11 +105,16 @@ export function useCardListSelected( | ||
87 | : selectedAll(); | 105 | : selectedAll(); |
88 | } | 106 | } |
89 | 107 | ||
108 | + function getSelections() { | ||
109 | + return unref(getSelectionsRef); | ||
110 | + } | ||
111 | + | ||
90 | return { | 112 | return { |
91 | selectedKeys, | 113 | selectedKeys, |
92 | selectedClass, | 114 | selectedClass, |
93 | getShouldUseDefaultStyle, | 115 | getShouldUseDefaultStyle, |
94 | getHasSelectedRecordStatus, | 116 | getHasSelectedRecordStatus, |
117 | + getSelections, | ||
95 | handlerSelected, | 118 | handlerSelected, |
96 | selectedAll, | 119 | selectedAll, |
97 | selectAllToggle, | 120 | selectAllToggle, |
@@ -52,6 +52,7 @@ export interface CardListActionType { | @@ -52,6 +52,7 @@ export interface CardListActionType { | ||
52 | clearSelectedKeys: UseCardListSelected['clearSelectedKeys']; | 52 | clearSelectedKeys: UseCardListSelected['clearSelectedKeys']; |
53 | getSelectedKeys: UseCardListSelected['getSelectedKeys']; | 53 | getSelectedKeys: UseCardListSelected['getSelectedKeys']; |
54 | getSelectedRecords: UseCardListSelected['getSelectedRecords']; | 54 | getSelectedRecords: UseCardListSelected['getSelectedRecords']; |
55 | + getSelections: UseCardListSelected['getSelections']; | ||
55 | } | 56 | } |
56 | 57 | ||
57 | export interface CardListEmitType { | 58 | export interface CardListEmitType { |
@@ -61,6 +62,7 @@ export interface CardListEmitType { | @@ -61,6 +62,7 @@ export interface CardListEmitType { | ||
61 | 62 | ||
62 | export interface CardListSelectionsType<T = Recordable> { | 63 | export interface CardListSelectionsType<T = Recordable> { |
63 | customCheckedStyle?: boolean; | 64 | customCheckedStyle?: boolean; |
65 | + beforeSelectValidate?: (record: Recordable) => boolean; | ||
64 | onSelect?: (record: T, selected: boolean) => any; | 66 | onSelect?: (record: T, selected: boolean) => any; |
65 | - onSelectAll: (selectedRecords: T[]) => any; | 67 | + onSelectAll?: (selectedRecords: T[]) => any; |
66 | } | 68 | } |
@@ -134,7 +134,7 @@ | @@ -134,7 +134,7 @@ | ||
134 | </Authority> | 134 | </Authority> |
135 | </div> | 135 | </div> |
136 | </template> | 136 | </template> |
137 | - <template #renderItem="{ item }: CardListRenderItem<ConfigurationCenterItemsModal>"> | 137 | + <template #renderItem="{ item }: BasicCardListRenderItem<ConfigurationCenterItemsModal>"> |
138 | <Card | 138 | <Card |
139 | :style="{ | 139 | :style="{ |
140 | '--viewType': item.viewType === ViewTypeEnum.PUBLIC_VIEW ? '#faad14' : '#1890ff', | 140 | '--viewType': item.viewType === ViewTypeEnum.PUBLIC_VIEW ? '#faad14' : '#1890ff', |
@@ -105,7 +105,7 @@ | @@ -105,7 +105,7 @@ | ||
105 | </Authority> | 105 | </Authority> |
106 | </div> | 106 | </div> |
107 | </template> | 107 | </template> |
108 | - <template #renderItem="{ item }: CardListRenderItem<ConfigurationCenterItemsModal>"> | 108 | + <template #renderItem="{ item }: BasicCardListRenderItem<ConfigurationCenterItemsModal>"> |
109 | <Card | 109 | <Card |
110 | :style="{ | 110 | :style="{ |
111 | '--viewType': '#1890ff', | 111 | '--viewType': '#1890ff', |
@@ -150,7 +150,7 @@ | @@ -150,7 +150,7 @@ | ||
150 | </Authority> | 150 | </Authority> |
151 | </section> | 151 | </section> |
152 | </template> | 152 | </template> |
153 | - <template #renderItem="{ item }: CardListRenderItem<BigScreenCenterItemsModel>"> | 153 | + <template #renderItem="{ item }: BasicCardListRenderItem<BigScreenCenterItemsModel>"> |
154 | <Card | 154 | <Card |
155 | :style="{ | 155 | :style="{ |
156 | '--viewType': item.viewType === ViewType.PUBLIC_VIEW ? '#1890ff' : '#faad14', | 156 | '--viewType': item.viewType === ViewType.PUBLIC_VIEW ? '#1890ff' : '#faad14', |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { PageWrapper } from '/@/components/Page'; | ||
3 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
4 | - import { List, Button, Tooltip, Card, PaginationProps, Image, Popconfirm } from 'ant-design-vue'; | ||
5 | - import { ReloadOutlined } from '@ant-design/icons-vue'; | ||
6 | - import { computed, onMounted, reactive, ref, unref } from 'vue'; | ||
7 | - import { | ||
8 | - AuthIcon, | ||
9 | - CardLayoutButton, | ||
10 | - EnumTableCardMode, | ||
11 | - ModeSwitchButton, | ||
12 | - } from '/@/components/Widget'; | 2 | + import { Button, Tooltip, Card, Image, Popconfirm } from 'ant-design-vue'; |
3 | + import { AuthIcon, EnumTableCardMode, ModeSwitchButton } from '/@/components/Widget'; | ||
13 | import { Authority } from '/@/components/Authority'; | 4 | import { Authority } from '/@/components/Authority'; |
14 | import { | 5 | import { |
15 | deviceConfigDelete, | 6 | deviceConfigDelete, |
@@ -29,8 +20,9 @@ | @@ -29,8 +20,9 @@ | ||
29 | import { useModal } from '/@/components/Modal'; | 20 | import { useModal } from '/@/components/Modal'; |
30 | import { useDrawer } from '/@/components/Drawer'; | 21 | import { useDrawer } from '/@/components/Drawer'; |
31 | import productDefault from '/@/assets/icons/product-default.svg'; | 22 | import productDefault from '/@/assets/icons/product-default.svg'; |
32 | - import { useRoute } from 'vue-router'; | ||
33 | import AuthDropDown from '/@/components/Widget/AuthDropDown.vue'; | 23 | import AuthDropDown from '/@/components/Widget/AuthDropDown.vue'; |
24 | + import { BasicCardList, useCardList } from '/@/components/CardList'; | ||
25 | + import { useRoute } from 'vue-router'; | ||
34 | 26 | ||
35 | defineProps<{ | 27 | defineProps<{ |
36 | mode: EnumTableCardMode; | 28 | mode: EnumTableCardMode; |
@@ -42,94 +34,39 @@ | @@ -42,94 +34,39 @@ | ||
42 | SET_DEFAULT = 'setDefault', | 34 | SET_DEFAULT = 'setDefault', |
43 | DELETE = 'delete', | 35 | DELETE = 'delete', |
44 | } | 36 | } |
37 | + | ||
45 | const IMAGE_FALLBACK = productDefault; | 38 | const IMAGE_FALLBACK = productDefault; |
46 | 39 | ||
47 | const { createMessage } = useMessage(); | 40 | const { createMessage } = useMessage(); |
48 | 41 | ||
49 | - const [register, { getFieldsValue, setFieldsValue }] = useForm({ | ||
50 | - showAdvancedButton: true, | ||
51 | - labelWidth: 100, | ||
52 | - compact: true, | ||
53 | - baseColProps: { span: 8 }, | ||
54 | - schemas: searchFormSchema, | ||
55 | - resetFunc: async () => { | ||
56 | - getDataSource({ name: '' }); | 42 | + const { query } = useRoute(); |
43 | + | ||
44 | + const [registerCardList, { reload, getSelectedKeys, clearSelectedKeys }] = useCardList({ | ||
45 | + api: deviceConfigGetQuery, | ||
46 | + useSearchForm: true, | ||
47 | + gutter: 4, | ||
48 | + rowKey: 'id', | ||
49 | + formConfig: { | ||
50 | + schemas: searchFormSchema, | ||
51 | + labelWidth: 100, | ||
52 | + model: { | ||
53 | + name: (query as Recordable)?.name ? decodeURIComponent((query as Recordable)?.name) : null, | ||
54 | + }, | ||
57 | }, | 55 | }, |
58 | - submitFunc: async () => { | ||
59 | - getDataSource({ pageSize: pagination.pageSize, page: 1 }); | 56 | + selections: { |
57 | + beforeSelectValidate: (record: ProfileRecord) => { | ||
58 | + return !record.default; | ||
59 | + }, | ||
60 | }, | 60 | }, |
61 | }); | 61 | }); |
62 | 62 | ||
63 | const [registerModal, { openModal }] = useModal(); | 63 | const [registerModal, { openModal }] = useModal(); |
64 | const [registerDrawer, { openDrawer }] = useDrawer(); | 64 | const [registerDrawer, { openDrawer }] = useDrawer(); |
65 | 65 | ||
66 | - const loading = ref(false); | ||
67 | - | ||
68 | - const colNumber = ref(5); | ||
69 | - | ||
70 | - const pagination = reactive<PaginationProps>({ | ||
71 | - size: 'small', | ||
72 | - showTotal: (total: number) => `共 ${total} 条数据`, | ||
73 | - current: 1, | ||
74 | - pageSize: unref(colNumber) * 2, | ||
75 | - onChange: (page: number) => { | ||
76 | - pagination.current = page; | ||
77 | - getDataSource(); | ||
78 | - }, | ||
79 | - }); | ||
80 | - | ||
81 | - const dataSource = ref<ProfileRecord[]>([]); | ||
82 | - | ||
83 | - const getSelectAllFlag = computed(() => { | ||
84 | - return unref(dataSource).every((item) => item.checked || item.default); | ||
85 | - }); | ||
86 | - | ||
87 | - const getCheckedRecord = computed(() => { | ||
88 | - return unref(dataSource) | ||
89 | - .filter((item) => item.checked && !item.default) | ||
90 | - .map((item) => item.id); | ||
91 | - }); | ||
92 | - | ||
93 | - const getDataSource = async (otherParams: Recordable = {}) => { | ||
94 | - try { | ||
95 | - loading.value = true; | ||
96 | - if (otherParams) { | ||
97 | - setFieldsValue(otherParams); | ||
98 | - } | ||
99 | - const params = getFieldsValue(); | ||
100 | - const pageSize = unref(colNumber) * 2; | ||
101 | - const { items, total } = await deviceConfigGetQuery({ | ||
102 | - page: pagination.current, | ||
103 | - pageSize: unref(colNumber) * 2, | ||
104 | - ...params, | ||
105 | - ...otherParams, | ||
106 | - }); | ||
107 | - Object.assign(pagination, { total, pageSize }); | ||
108 | - dataSource.value = items.map((item) => ({ ...item, checked: false })); | ||
109 | - } catch (error) { | ||
110 | - } finally { | ||
111 | - loading.value = false; | ||
112 | - } | ||
113 | - }; | ||
114 | - | ||
115 | const handleModeChange = (mode: EnumTableCardMode) => { | 66 | const handleModeChange = (mode: EnumTableCardMode) => { |
116 | emit('changeMode', mode); | 67 | emit('changeMode', mode); |
117 | }; | 68 | }; |
118 | 69 | ||
119 | - const handleCheckCard = (item: ProfileRecord) => { | ||
120 | - if (item.default || item.name == 'default') return; | ||
121 | - item.checked = !item.checked; | ||
122 | - }; | ||
123 | - | ||
124 | - const handleSelectAll = () => { | ||
125 | - dataSource.value = unref(dataSource).map((item) => { | ||
126 | - return { | ||
127 | - ...item, | ||
128 | - checked: !item.default ? !unref(getSelectAllFlag) : false, | ||
129 | - }; | ||
130 | - }); | ||
131 | - }; | ||
132 | - | ||
133 | const handleCreate = () => { | 70 | const handleCreate = () => { |
134 | openModal(true, { | 71 | openModal(true, { |
135 | isUpdate: false, | 72 | isUpdate: false, |
@@ -147,11 +84,13 @@ | @@ -147,11 +84,13 @@ | ||
147 | }); | 84 | }); |
148 | }; | 85 | }; |
149 | 86 | ||
150 | - const handleDelete = async (id: string[]) => { | 87 | + const handleDelete = async () => { |
151 | try { | 88 | try { |
152 | - await deviceConfigDelete(id); | 89 | + const ids = getSelectedKeys(); |
90 | + await deviceConfigDelete(ids); | ||
153 | createMessage.success('删除成功'); | 91 | createMessage.success('删除成功'); |
154 | - await getDataSource(); | 92 | + clearSelectedKeys(); |
93 | + await reload(); | ||
155 | } catch (error) { | 94 | } catch (error) { |
156 | throw error; | 95 | throw error; |
157 | } | 96 | } |
@@ -163,155 +102,108 @@ | @@ -163,155 +102,108 @@ | ||
163 | const data = await setDeviceProfileIsDefaultApi(tbProfileId, 'default', defaultObj); | 102 | const data = await setDeviceProfileIsDefaultApi(tbProfileId, 'default', defaultObj); |
164 | if (!data) return createMessage.error('设置该产品为默认失败'); | 103 | if (!data) return createMessage.error('设置该产品为默认失败'); |
165 | createMessage.success('设置该产品为默认成功'); | 104 | createMessage.success('设置该产品为默认成功'); |
166 | - await getDataSource(); | 105 | + await reload(); |
167 | } catch (error) { | 106 | } catch (error) { |
168 | throw error; | 107 | throw error; |
169 | } | 108 | } |
170 | }; | 109 | }; |
171 | - | ||
172 | - const handleCardLayoutChange = () => { | ||
173 | - pagination.current = 1; | ||
174 | - getDataSource(); | ||
175 | - }; | ||
176 | - | ||
177 | - const { query: routeParams } = useRoute(); | ||
178 | - onMounted(() => { | ||
179 | - routeParams.name = decodeURIComponent( | ||
180 | - ((routeParams as unknown as Recordable) || {})?.name || '' | ||
181 | - ); | ||
182 | - getDataSource(routeParams); | ||
183 | - }); | ||
184 | </script> | 110 | </script> |
185 | 111 | ||
186 | <template> | 112 | <template> |
187 | - <PageWrapper dense contentFullHeight contentClass="flex"> | ||
188 | - <section class="flex-auto p-4 w-full profile-list"> | ||
189 | - <div class="flex-auto w-full bg-light-50 dark:bg-dark-900 p-4"> | ||
190 | - <BasicForm @register="register" /> | ||
191 | - </div> | ||
192 | - <List | ||
193 | - ref="listEl" | ||
194 | - :loading="loading" | ||
195 | - class="flex-auto bg-light-50 dark:bg-dark-900 !p-2 !mt-4" | ||
196 | - position="bottom" | ||
197 | - :pagination="pagination" | ||
198 | - :data-source="dataSource" | ||
199 | - :grid="{ gutter: 4, column: colNumber }" | ||
200 | - > | ||
201 | - <template #header> | ||
202 | - <div class="flex gap-3 justify-end"> | ||
203 | - <Authority :value="ProductPermission.CREATE"> | ||
204 | - <Button type="primary" @click="handleCreate">新增产品</Button> | ||
205 | - </Authority> | ||
206 | - | ||
207 | - <Button type="primary" @click="handleSelectAll"> | ||
208 | - {{ getSelectAllFlag ? '反选' : '全选' }} | ||
209 | - </Button> | ||
210 | - <Authority :value="ProductPermission.DELETE"> | ||
211 | - <Popconfirm | ||
212 | - title="您确定要批量删除数据" | ||
213 | - ok-text="确定" | ||
214 | - cancel-text="取消" | ||
215 | - @confirm="handleDelete(getCheckedRecord)" | ||
216 | - :disabled="!getCheckedRecord.length" | ||
217 | - > | ||
218 | - <Button type="primary" danger :disabled="!getCheckedRecord.length"> | ||
219 | - 批量删除 | ||
220 | - </Button> | ||
221 | - </Popconfirm> | ||
222 | - </Authority> | ||
223 | - | ||
224 | - <ModeSwitchButton :value="$props.mode" @change="handleModeChange" /> | ||
225 | - <CardLayoutButton v-model:value="colNumber" @change="handleCardLayoutChange" /> | ||
226 | - | ||
227 | - <Tooltip title="刷新"> | ||
228 | - <Button type="primary" @click="getDataSource"> | ||
229 | - <ReloadOutlined :spin="loading" /> | ||
230 | - </Button> | 113 | + <section> |
114 | + <BasicCardList @register="registerCardList"> | ||
115 | + <template #toolbar> | ||
116 | + <Authority :value="ProductPermission.CREATE"> | ||
117 | + <Button type="primary" @click="handleCreate">新增产品</Button> | ||
118 | + </Authority> | ||
119 | + | ||
120 | + <ModeSwitchButton :value="$props.mode" @change="handleModeChange" /> | ||
121 | + | ||
122 | + <Authority :value="ProductPermission.DELETE"> | ||
123 | + <Popconfirm | ||
124 | + title="您确定要批量删除数据" | ||
125 | + ok-text="确定" | ||
126 | + cancel-text="取消" | ||
127 | + @confirm="handleDelete" | ||
128 | + > | ||
129 | + <Button type="primary" danger> 批量删除 </Button> | ||
130 | + </Popconfirm> | ||
131 | + </Authority> | ||
132 | + </template> | ||
133 | + <template #renderItem="{ item }: BasicCardListRenderItem<ProfileRecord>"> | ||
134 | + <Card hoverable> | ||
135 | + <template #cover> | ||
136 | + <div class="h-full w-full !flex justify-center items-center text-center p-1"> | ||
137 | + <Image | ||
138 | + @click.stop | ||
139 | + class="!w-32" | ||
140 | + :src="item.image || IMAGE_FALLBACK" | ||
141 | + placeholder | ||
142 | + :fallback="IMAGE_FALLBACK" | ||
143 | + /> | ||
144 | + </div> | ||
145 | + </template> | ||
146 | + <template class="ant-card-actions" #actions> | ||
147 | + <Tooltip title="详情"> | ||
148 | + <AuthIcon | ||
149 | + :auth="ProductPermission.DETAIL" | ||
150 | + class="!text-lg" | ||
151 | + icon="ant-design:eye-outlined" | ||
152 | + @click.stop="handleShowDetail(item)" | ||
153 | + /> | ||
154 | + </Tooltip> | ||
155 | + <Tooltip title="编辑"> | ||
156 | + <AuthIcon | ||
157 | + :auth="ProductPermission.UPDATE" | ||
158 | + class="!text-lg" | ||
159 | + icon="ant-design:form-outlined" | ||
160 | + @click.stop="handleUpdate(item)" | ||
161 | + :disabled="item.name == 'default'" | ||
162 | + /> | ||
231 | </Tooltip> | 163 | </Tooltip> |
232 | - </div> | ||
233 | - </template> | ||
234 | - <template #renderItem="{ item }"> | ||
235 | - <List.Item> | ||
236 | - <Card | ||
237 | - hoverable | ||
238 | - @click="handleCheckCard(item)" | ||
239 | - :class="item.checked ? '!border-blue-500 !border-2' : ''" | ||
240 | - > | ||
241 | - <template #cover> | ||
242 | - <div class="h-full w-full !flex justify-center items-center text-center p-1"> | ||
243 | - <Image | ||
244 | - @click.stop | ||
245 | - :height="144" | ||
246 | - :src="item.image || IMAGE_FALLBACK" | ||
247 | - placeholder | ||
248 | - :fallback="IMAGE_FALLBACK" | ||
249 | - /> | ||
250 | - </div> | ||
251 | - </template> | ||
252 | - <template class="ant-card-actions" #actions> | ||
253 | - <Tooltip title="详情"> | ||
254 | - <AuthIcon | ||
255 | - :auth="ProductPermission.DETAIL" | ||
256 | - class="!text-lg" | ||
257 | - icon="ant-design:eye-outlined" | ||
258 | - @click.stop="handleShowDetail(item)" | ||
259 | - /> | ||
260 | - </Tooltip> | ||
261 | - <Tooltip title="编辑"> | ||
262 | - <AuthIcon | ||
263 | - :auth="ProductPermission.UPDATE" | ||
264 | - class="!text-lg" | ||
265 | - icon="ant-design:form-outlined" | ||
266 | - @click.stop="handleUpdate(item)" | ||
267 | - :disabled="item.name == 'default'" | ||
268 | - /> | ||
269 | - </Tooltip> | ||
270 | - <AuthDropDown | ||
271 | - @click.stop | ||
272 | - :trigger="['hover']" | ||
273 | - :drop-menu-list="[ | ||
274 | - { | ||
275 | - text: '默认', | ||
276 | - event: DropMenuEvent.SET_DEFAULT, | ||
277 | - icon: 'ant-design:unordered-list-outlined', | ||
278 | - onClick: handleSetDefault.bind(null, item), | ||
279 | - disabled: item.default, | ||
280 | - }, | ||
281 | - { | ||
282 | - text: '删除', | ||
283 | - event: DropMenuEvent.DELETE, | ||
284 | - auth: ProductPermission.DELETE, | ||
285 | - icon: 'ant-design:delete-outlined', | ||
286 | - popconfirm: { | ||
287 | - title: '是否确认删除操作?', | ||
288 | - onConfirm: handleDelete.bind(null, [item.id]), | ||
289 | - disabled: item.default, | ||
290 | - }, | ||
291 | - disabled: item.default || item.name == 'default', | ||
292 | - }, | ||
293 | - ]" | ||
294 | - /> | ||
295 | - </template> | ||
296 | - <Card.Meta> | ||
297 | - <template #title> | ||
298 | - <span class="truncate"> {{ item.name }} </span> | ||
299 | - </template> | ||
300 | - <template #description> | ||
301 | - <div class="truncate h-11"> | ||
302 | - <div class="truncate">{{ DeviceTypeName[item.deviceType] }} </div> | ||
303 | - <div class="truncate">{{ item.transportType }} </div> | ||
304 | - </div> | ||
305 | - </template> | ||
306 | - </Card.Meta> | ||
307 | - </Card> | ||
308 | - </List.Item> | ||
309 | - </template> | ||
310 | - </List> | ||
311 | - </section> | ||
312 | - <DeviceProfileModal @register="registerModal" @success="getDataSource" /> | 164 | + <AuthDropDown |
165 | + @click.stop | ||
166 | + :trigger="['hover']" | ||
167 | + :drop-menu-list="[ | ||
168 | + { | ||
169 | + text: '默认', | ||
170 | + event: DropMenuEvent.SET_DEFAULT, | ||
171 | + icon: 'ant-design:unordered-list-outlined', | ||
172 | + onClick: handleSetDefault.bind(null, item), | ||
173 | + disabled: item.default, | ||
174 | + }, | ||
175 | + { | ||
176 | + text: '删除', | ||
177 | + event: DropMenuEvent.DELETE, | ||
178 | + auth: ProductPermission.DELETE, | ||
179 | + icon: 'ant-design:delete-outlined', | ||
180 | + popconfirm: { | ||
181 | + title: '是否确认删除操作?', | ||
182 | + onConfirm: handleDelete.bind(null, [item.id]), | ||
183 | + disabled: item.default, | ||
184 | + }, | ||
185 | + disabled: item.default || item.name == 'default', | ||
186 | + }, | ||
187 | + ]" | ||
188 | + /> | ||
189 | + </template> | ||
190 | + <Card.Meta> | ||
191 | + <template #title> | ||
192 | + <span class="truncate"> {{ item.name }} </span> | ||
193 | + </template> | ||
194 | + <template #description> | ||
195 | + <div class="truncate h-11"> | ||
196 | + <div class="truncate">{{ DeviceTypeName[item.deviceType] }} </div> | ||
197 | + <div class="truncate">{{ item.transportType }} </div> | ||
198 | + </div> | ||
199 | + </template> | ||
200 | + </Card.Meta> | ||
201 | + </Card> | ||
202 | + </template> | ||
203 | + </BasicCardList> | ||
204 | + <DeviceProfileModal @register="registerModal" @success="reload" /> | ||
313 | <DeviceProfileDrawer @register="registerDrawer" /> | 205 | <DeviceProfileDrawer @register="registerDrawer" /> |
314 | - </PageWrapper> | 206 | + </section> |
315 | </template> | 207 | </template> |
316 | 208 | ||
317 | <style lang="less" scoped> | 209 | <style lang="less" scoped> |
@@ -322,8 +214,4 @@ | @@ -322,8 +214,4 @@ | ||
322 | .profile-list:deep(.ant-card-body) { | 214 | .profile-list:deep(.ant-card-body) { |
323 | @apply !p-4; | 215 | @apply !p-4; |
324 | } | 216 | } |
325 | - | ||
326 | - :deep(.ant-spin-nested-loading) { | ||
327 | - height: 40rem; | ||
328 | - } | ||
329 | </style> | 217 | </style> |
@@ -154,7 +154,7 @@ | @@ -154,7 +154,7 @@ | ||
154 | <Button type="primary" @click="handleOpenDetailModal">新增看板</Button> | 154 | <Button type="primary" @click="handleOpenDetailModal">新增看板</Button> |
155 | </Authority> | 155 | </Authority> |
156 | </template> | 156 | </template> |
157 | - <template #renderItem="{ item }: CardListRenderItem<DataBoardRecord>"> | 157 | + <template #renderItem="{ item }: BasicCardListRenderItem<DataBoardRecord>"> |
158 | <Card class="data-card cursor-pointer"> | 158 | <Card class="data-card cursor-pointer"> |
159 | <template #title> | 159 | <template #title> |
160 | <div class="font-bold">{{ item.name }}</div> | 160 | <div class="font-bold">{{ item.name }}</div> |
@@ -20,7 +20,7 @@ export interface DefineComponentsBasicExpose<T = Recordable> { | @@ -20,7 +20,7 @@ export interface DefineComponentsBasicExpose<T = Recordable> { | ||
20 | } | 20 | } |
21 | 21 | ||
22 | declare global { | 22 | declare global { |
23 | - interface CardListRenderItem<T = Recordable & { checked?: boolean }> { | 23 | + interface BasicCardListRenderItem<T = Recordable> { |
24 | item: T; | 24 | item: T; |
25 | totalHeight: number; | 25 | totalHeight: number; |
26 | } | 26 | } |