Commit 742b336a8b043232f3ed1e49b4b841c6bfe949a9
Merge branch 'main_dev' of http://git.yunteng.com/yunteng/thingskit-front into fix/rule-chain-field
Showing
25 changed files
with
522 additions
and
601 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'; |
@@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
12 | import { FETCH_SETTING } from '../../Table/src/const'; | 12 | import { FETCH_SETTING } from '../../Table/src/const'; |
13 | import { useCardForm } from './hooks/useCardForm'; | 13 | import { useCardForm } from './hooks/useCardForm'; |
14 | import { Icon } from '/@/components/Icon'; | 14 | import { Icon } from '/@/components/Icon'; |
15 | + import { useCardListSelected } from './hooks/useCardListSelected'; | ||
15 | 16 | ||
16 | const emit = defineEmits<{ | 17 | const emit = defineEmits<{ |
17 | (eventName: 'fetchSuccess', result: { items: Recordable[]; total: number }): void; | 18 | (eventName: 'fetchSuccess', result: { items: Recordable[]; total: number }): void; |
@@ -33,6 +34,7 @@ | @@ -33,6 +34,7 @@ | ||
33 | immediate: true, | 34 | immediate: true, |
34 | fetchSetting: () => FETCH_SETTING, | 35 | fetchSetting: () => FETCH_SETTING, |
35 | autoCreateKey: true, | 36 | autoCreateKey: true, |
37 | + offsetHeight: 0, | ||
36 | }); | 38 | }); |
37 | 39 | ||
38 | const tableData = ref<Recordable[]>([]); | 40 | const tableData = ref<Recordable[]>([]); |
@@ -56,7 +58,15 @@ | @@ -56,7 +58,15 @@ | ||
56 | 58 | ||
57 | const { loading, setLoading, getLoading } = useLoading(); | 59 | const { loading, setLoading, getLoading } = useLoading(); |
58 | 60 | ||
59 | - const { getDataSourceRef, getRowKey, fetch, reload } = useCardListData(getProps, formActions, { | 61 | + const { |
62 | + getDataSourceRef, | ||
63 | + getRowKey, | ||
64 | + fetch, | ||
65 | + reload, | ||
66 | + updateTableDataRecord, | ||
67 | + findCardDataRecord, | ||
68 | + setCardListData, | ||
69 | + } = useCardListData(getProps, formActions, { | ||
60 | setLoading, | 70 | setLoading, |
61 | getPaginationInfo, | 71 | getPaginationInfo, |
62 | emit, | 72 | emit, |
@@ -77,14 +87,31 @@ | @@ -77,14 +87,31 @@ | ||
77 | getLoading | 87 | getLoading |
78 | ); | 88 | ); |
79 | 89 | ||
90 | + const { | ||
91 | + handlerSelected, | ||
92 | + selectedClass, | ||
93 | + selectedKeys, | ||
94 | + selectAllToggle, | ||
95 | + selectedAll, | ||
96 | + clearSelectedKeys, | ||
97 | + getSelectedKeys, | ||
98 | + getSelectedRecords, | ||
99 | + getSelections, | ||
100 | + } = useCardListSelected(getDataSourceRef, getProps, { | ||
101 | + updateTableDataRecord, | ||
102 | + findCardDataRecord, | ||
103 | + getRowKey, | ||
104 | + setCardListData, | ||
105 | + }); | ||
106 | + | ||
80 | function handleCardListChange() { | 107 | function handleCardListChange() { |
81 | reload(); | 108 | reload(); |
82 | } | 109 | } |
83 | 110 | ||
84 | - const getBindData = computed<ListProps>(() => { | 111 | + const getBindValues = computed<ListProps>(() => { |
85 | const dataSource = unref(getDataSourceRef); | 112 | const dataSource = unref(getDataSourceRef); |
86 | 113 | ||
87 | - return { | 114 | + const props = { |
88 | dataSource, | 115 | dataSource, |
89 | grid: unref(getListGrid), | 116 | grid: unref(getListGrid), |
90 | loading: unref(loading), | 117 | loading: unref(loading), |
@@ -95,8 +122,9 @@ | @@ -95,8 +122,9 @@ | ||
95 | handleCardListChange(); | 122 | handleCardListChange(); |
96 | }, | 123 | }, |
97 | }, | 124 | }, |
98 | - rowKey: unref(getRowKey), | 125 | + rowKey: (record: Recordable) => record[unref(getRowKey)], |
99 | } as ListProps; | 126 | } as ListProps; |
127 | + return props; | ||
100 | }); | 128 | }); |
101 | 129 | ||
102 | watch(cardListLayout, () => { | 130 | watch(cardListLayout, () => { |
@@ -111,6 +139,11 @@ | @@ -111,6 +139,11 @@ | ||
111 | setPagination, | 139 | setPagination, |
112 | getPagination, | 140 | getPagination, |
113 | reload, | 141 | reload, |
142 | + selectedAll, | ||
143 | + clearSelectedKeys, | ||
144 | + getSelectedKeys, | ||
145 | + getSelectedRecords, | ||
146 | + getSelections, | ||
114 | }; | 147 | }; |
115 | 148 | ||
116 | function setProps(props: Partial<BasicCardListPropsType> = {}) { | 149 | function setProps(props: Partial<BasicCardListPropsType> = {}) { |
@@ -137,7 +170,7 @@ | @@ -137,7 +170,7 @@ | ||
137 | </section> | 170 | </section> |
138 | 171 | ||
139 | <section class="w-full bg-light-50 dark:bg-dark-900 p-4 flex-auto h-full"> | 172 | <section class="w-full bg-light-50 dark:bg-dark-900 p-4 flex-auto h-full"> |
140 | - <List ref="listElRef" v-bind="getBindData"> | 173 | + <List ref="listElRef" v-bind="getBindValues"> |
141 | <template #header> | 174 | <template #header> |
142 | <section class="flex justify-between items-center"> | 175 | <section class="flex justify-between items-center"> |
143 | <div> | 176 | <div> |
@@ -146,10 +179,21 @@ | @@ -146,10 +179,21 @@ | ||
146 | </span> | 179 | </span> |
147 | <slot v-else name="title"></slot> | 180 | <slot v-else name="title"></slot> |
148 | </div> | 181 | </div> |
149 | - <div class="flex flex-auto justify-end mr-4"> | 182 | + <div class="flex flex-auto justify-end mr-4 gap-4"> |
150 | <slot name="toolbar"></slot> | 183 | <slot name="toolbar"></slot> |
151 | </div> | 184 | </div> |
152 | <div v-if="showCardListSetting" class="flex gap-4"> | 185 | <div v-if="showCardListSetting" class="flex gap-4"> |
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> | ||
153 | <CardListLayout v-model:row="cardListLayout.row" v-model:col="cardListLayout.col" /> | 197 | <CardListLayout v-model:row="cardListLayout.row" v-model:col="cardListLayout.col" /> |
154 | <Tooltip title="刷新"> | 198 | <Tooltip title="刷新"> |
155 | <Button type="primary" @click="reload()" :loading="loading"> | 199 | <Button type="primary" @click="reload()" :loading="loading"> |
@@ -159,9 +203,18 @@ | @@ -159,9 +203,18 @@ | ||
159 | </div> | 203 | </div> |
160 | </section> | 204 | </section> |
161 | </template> | 205 | </template> |
162 | - <template #renderItem="{ item }"> | ||
163 | - <List.Item :style="{ '--totalHeight': containerHeight }"> | ||
164 | - <slot name="renderItem" :item="item" :totalHeight="containerHeight"></slot> | 206 | + <template #renderItem="{ item }: BasicCardListRenderItem<any>"> |
207 | + <List.Item | ||
208 | + :class="selectedKeys.includes(item[getRowKey]) ? selectedClass : ''" | ||
209 | + @click="handlerSelected($event, item)" | ||
210 | + > | ||
211 | + <slot | ||
212 | + name="renderItem" | ||
213 | + :item="item" | ||
214 | + :totalHeight="containerHeight" | ||
215 | + :checked="item?.checked" | ||
216 | + > | ||
217 | + </slot> | ||
165 | </List.Item> | 218 | </List.Item> |
166 | </template> | 219 | </template> |
167 | </List> | 220 | </List> |
@@ -175,6 +228,38 @@ | @@ -175,6 +228,38 @@ | ||
175 | .ant-spin-container { | 228 | .ant-spin-container { |
176 | overflow-x: hidden; | 229 | overflow-x: hidden; |
177 | overflow-y: auto; | 230 | overflow-y: auto; |
231 | + | ||
232 | + .ant-list-item { | ||
233 | + border: 2px transparent solid; | ||
234 | + position: relative; | ||
235 | + | ||
236 | + &.basic-card-list-item-checked { | ||
237 | + border: 2px solid #377dff; | ||
238 | + | ||
239 | + &::after { | ||
240 | + position: absolute; | ||
241 | + inset-block-start: 2px; | ||
242 | + inset-inline-end: 2px; | ||
243 | + width: 0; | ||
244 | + height: 0; | ||
245 | + border: 10px solid #1677ff; | ||
246 | + border-block-end: 10px solid transparent; | ||
247 | + border-inline-start: 10px solid transparent; | ||
248 | + // border-start-end-radius: 6px; | ||
249 | + content: ''; | ||
250 | + } | ||
251 | + | ||
252 | + &::before { | ||
253 | + content: ''; | ||
254 | + position: absolute; | ||
255 | + background-color: #53a2fd; | ||
256 | + width: 100%; | ||
257 | + height: 100%; | ||
258 | + opacity: 0.2; | ||
259 | + z-index: 99; | ||
260 | + } | ||
261 | + } | ||
262 | + } | ||
178 | } | 263 | } |
179 | 264 | ||
180 | .ant-list-header { | 265 | .ant-list-header { |
@@ -35,7 +35,7 @@ | @@ -35,7 +35,7 @@ | ||
35 | }; | 35 | }; |
36 | 36 | ||
37 | watch( | 37 | watch( |
38 | - () => [props.row, props.col], | 38 | + () => [props.row, props.col, visible.value], |
39 | () => { | 39 | () => { |
40 | selectedLayout.row = props.row; | 40 | selectedLayout.row = props.row; |
41 | selectedLayout.col = props.col; | 41 | selectedLayout.col = props.col; |
1 | -import { WatchStopHandle, onUnmounted, ref, unref, watch } from 'vue'; | 1 | +import { WatchStopHandle, ref, unref, watch } from 'vue'; |
2 | import { BasicCardListPropsType, CardListActionType } from '../types'; | 2 | import { BasicCardListPropsType, CardListActionType } from '../types'; |
3 | import { isProdMode } from '/@/utils/env'; | 3 | import { isProdMode } from '/@/utils/env'; |
4 | import { error } from '/@/utils/log'; | 4 | import { error } from '/@/utils/log'; |
@@ -6,8 +6,9 @@ import { FormActionType } from '/@/components/Form'; | @@ -6,8 +6,9 @@ import { FormActionType } from '/@/components/Form'; | ||
6 | import { getDynamicProps } from '/@/utils'; | 6 | import { getDynamicProps } from '/@/utils'; |
7 | import { PaginationProps } from 'ant-design-vue'; | 7 | import { PaginationProps } from 'ant-design-vue'; |
8 | import { FetchParams } from './useCardListData'; | 8 | import { FetchParams } from './useCardListData'; |
9 | +import { tryOnUnmounted } from '@vueuse/core'; | ||
9 | 10 | ||
10 | -type UseTableMethod = CardListActionType & { | 11 | +type UseCardListMethod = CardListActionType & { |
11 | getForm: () => FormActionType; | 12 | getForm: () => FormActionType; |
12 | }; | 13 | }; |
13 | 14 | ||
@@ -22,7 +23,7 @@ export function useCardList( | @@ -22,7 +23,7 @@ export function useCardList( | ||
22 | 23 | ||
23 | function register(instance: CardListActionType, formInstance: FormActionType) { | 24 | function register(instance: CardListActionType, formInstance: FormActionType) { |
24 | isProdMode() && | 25 | isProdMode() && |
25 | - onUnmounted(() => { | 26 | + tryOnUnmounted(() => { |
26 | cardListRef.value = null; | 27 | cardListRef.value = null; |
27 | loadedRef.value = null; | 28 | loadedRef.value = null; |
28 | }); | 29 | }); |
@@ -48,34 +49,49 @@ export function useCardList( | @@ -48,34 +49,49 @@ export function useCardList( | ||
48 | ); | 49 | ); |
49 | } | 50 | } |
50 | 51 | ||
51 | - function getTableInstance(): CardListActionType { | ||
52 | - const table = unref(cardListRef); | ||
53 | - if (!table) { | 52 | + function getCardListInstance(): CardListActionType { |
53 | + const cardList = unref(cardListRef); | ||
54 | + if (!cardList) { | ||
54 | error( | 55 | error( |
55 | - '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!' |
56 | ); | 57 | ); |
57 | } | 58 | } |
58 | - return table as CardListActionType; | 59 | + return cardList as CardListActionType; |
59 | } | 60 | } |
60 | 61 | ||
61 | - const cardListActionType: UseTableMethod = { | 62 | + const cardListActionType: UseCardListMethod = { |
62 | getForm: () => { | 63 | getForm: () => { |
63 | return unref(formRef) as unknown as FormActionType; | 64 | return unref(formRef) as unknown as FormActionType; |
64 | }, | 65 | }, |
65 | setLoading: (loading: boolean) => { | 66 | setLoading: (loading: boolean) => { |
66 | - return getTableInstance().setLoading(loading); | 67 | + return getCardListInstance().setLoading(loading); |
67 | }, | 68 | }, |
68 | setProps: (props: Partial<BasicCardListPropsType>) => { | 69 | setProps: (props: Partial<BasicCardListPropsType>) => { |
69 | - getTableInstance().setProps(props); | 70 | + getCardListInstance().setProps(props); |
70 | }, | 71 | }, |
71 | getPagination: () => { | 72 | getPagination: () => { |
72 | - return getTableInstance().getPagination(); | 73 | + return getCardListInstance().getPagination(); |
73 | }, | 74 | }, |
74 | setPagination: (pagination: Partial<PaginationProps>) => { | 75 | setPagination: (pagination: Partial<PaginationProps>) => { |
75 | - return getTableInstance().setPagination(pagination); | 76 | + return getCardListInstance().setPagination(pagination); |
76 | }, | 77 | }, |
77 | reload: (opt?: FetchParams) => { | 78 | reload: (opt?: FetchParams) => { |
78 | - return getTableInstance().reload(opt); | 79 | + return getCardListInstance().reload(opt); |
80 | + }, | ||
81 | + selectedAll: () => { | ||
82 | + return getCardListInstance().selectedAll(); | ||
83 | + }, | ||
84 | + clearSelectedKeys: () => { | ||
85 | + return getCardListInstance().clearSelectedKeys(); | ||
86 | + }, | ||
87 | + getSelectedKeys: () => { | ||
88 | + return getCardListInstance().getSelectedKeys(); | ||
89 | + }, | ||
90 | + getSelectedRecords: () => { | ||
91 | + return getCardListInstance().getSelectedRecords(); | ||
92 | + }, | ||
93 | + getSelections: () => { | ||
94 | + return getCardListInstance().getSelections(); | ||
79 | }, | 95 | }, |
80 | }; | 96 | }; |
81 | 97 |
@@ -49,25 +49,22 @@ export function useCardListData( | @@ -49,25 +49,22 @@ export function useCardListData( | ||
49 | } | 49 | } |
50 | ); | 50 | ); |
51 | 51 | ||
52 | - function setTableKey(items: any[]) { | 52 | + function setCardKey(items: any[]) { |
53 | if (!items || !Array.isArray(items)) return; | 53 | if (!items || !Array.isArray(items)) return; |
54 | items.forEach((item) => { | 54 | items.forEach((item) => { |
55 | if (!item[ROW_KEY]) { | 55 | if (!item[ROW_KEY]) { |
56 | item[ROW_KEY] = buildUUID(); | 56 | item[ROW_KEY] = buildUUID(); |
57 | } | 57 | } |
58 | - if (item.children && item.children.length) { | ||
59 | - setTableKey(item.children); | ||
60 | - } | ||
61 | }); | 58 | }); |
62 | } | 59 | } |
63 | 60 | ||
64 | - const getAutoCreateKey = computed(() => { | ||
65 | - return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey; | 61 | + const getAutoCreateKey = computed<boolean>(() => { |
62 | + return !!(unref(propsRef).autoCreateKey && !unref(propsRef).rowKey); | ||
66 | }); | 63 | }); |
67 | 64 | ||
68 | - const getRowKey = computed(() => { | 65 | + const getRowKey = computed<string | number>(() => { |
69 | const { rowKey } = unref(propsRef); | 66 | const { rowKey } = unref(propsRef); |
70 | - return unref(getAutoCreateKey) ? ROW_KEY : rowKey; | 67 | + return unref(getAutoCreateKey) ? ROW_KEY : rowKey!; |
71 | }); | 68 | }); |
72 | 69 | ||
73 | const getDataSourceRef = computed(() => { | 70 | const getDataSourceRef = computed(() => { |
@@ -87,13 +84,14 @@ export function useCardListData( | @@ -87,13 +84,14 @@ export function useCardListData( | ||
87 | item[ROW_KEY] = buildUUID(); | 84 | item[ROW_KEY] = buildUUID(); |
88 | } | 85 | } |
89 | if (item.children && item.children.length) { | 86 | if (item.children && item.children.length) { |
90 | - setTableKey(item.children); | 87 | + setCardKey(item.children); |
91 | } | 88 | } |
92 | }); | 89 | }); |
93 | dataSourceRef.value = data; | 90 | dataSourceRef.value = data; |
94 | } | 91 | } |
95 | } | 92 | } |
96 | } | 93 | } |
94 | + | ||
97 | return unref(dataSourceRef); | 95 | return unref(dataSourceRef); |
98 | }); | 96 | }); |
99 | 97 | ||
@@ -176,10 +174,47 @@ export function useCardListData( | @@ -176,10 +174,47 @@ export function useCardListData( | ||
176 | } | 174 | } |
177 | } | 175 | } |
178 | 176 | ||
177 | + function findCardDataRecord(rowKey: string | number) { | ||
178 | + if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; | ||
179 | + | ||
180 | + const rowKeyName = unref(getRowKey); | ||
181 | + if (!rowKeyName) return; | ||
182 | + | ||
183 | + const findRow = (array: any[]) => { | ||
184 | + let ret; | ||
185 | + array.some(function iter(r) { | ||
186 | + if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) { | ||
187 | + ret = r; | ||
188 | + return true; | ||
189 | + } | ||
190 | + }); | ||
191 | + return ret; | ||
192 | + }; | ||
193 | + | ||
194 | + return findRow(dataSourceRef.value); | ||
195 | + } | ||
196 | + | ||
197 | + function updateTableDataRecord( | ||
198 | + rowKey: string | number, | ||
199 | + record: Recordable | ||
200 | + ): Recordable | undefined { | ||
201 | + const row = findCardDataRecord(rowKey); | ||
202 | + if (row) { | ||
203 | + for (const field in row) { | ||
204 | + if (Reflect.has(record, field)) row[field] = record[field]; | ||
205 | + } | ||
206 | + return row; | ||
207 | + } | ||
208 | + } | ||
209 | + | ||
179 | async function reload(opt?: FetchParams) { | 210 | async function reload(opt?: FetchParams) { |
180 | await fetch(opt); | 211 | await fetch(opt); |
181 | } | 212 | } |
182 | 213 | ||
214 | + function setCardListData<T = Recordable>(values: T[]) { | ||
215 | + dataSourceRef.value = values as Recordable[]; | ||
216 | + } | ||
217 | + | ||
183 | onMounted(() => { | 218 | onMounted(() => { |
184 | useTimeoutFn(() => { | 219 | useTimeoutFn(() => { |
185 | unref(propsRef).immediate && fetch(); | 220 | unref(propsRef).immediate && fetch(); |
@@ -191,5 +226,8 @@ export function useCardListData( | @@ -191,5 +226,8 @@ export function useCardListData( | ||
191 | fetch, | 226 | fetch, |
192 | getRowKey, | 227 | getRowKey, |
193 | getDataSourceRef, | 228 | getDataSourceRef, |
229 | + setCardListData, | ||
230 | + findCardDataRecord, | ||
231 | + updateTableDataRecord, | ||
194 | }; | 232 | }; |
195 | } | 233 | } |
1 | -import { ComputedRef, computed, unref } from 'vue'; | 1 | +import { ComputedRef, computed, ref, toRaw, unref, watch } from 'vue'; |
2 | +import { BasicCardListPropsType, CardListSelectionsType, UseCardListDataType } from '../types'; | ||
3 | +import { isBoolean, isFunction } from '/@/utils/is'; | ||
4 | +import { cloneDeep } from 'lodash-es'; | ||
5 | + | ||
6 | +export const CHECKED_FIELD = 'CARD_LIST_CHECKED_STATUS'; | ||
7 | + | ||
8 | +export const DEFAULT_SELECTED_CLASS = 'basic-card-list-item-checked'; | ||
9 | + | ||
10 | +interface CardListSelectActionType { | ||
11 | + updateTableDataRecord: UseCardListDataType['updateTableDataRecord']; | ||
12 | + findCardDataRecord: UseCardListDataType['findCardDataRecord']; | ||
13 | + getRowKey: UseCardListDataType['getRowKey']; | ||
14 | + setCardListData: UseCardListDataType['setCardListData']; | ||
15 | +} | ||
2 | 16 | ||
3 | export function useCardListSelected( | 17 | export function useCardListSelected( |
4 | - getDataSourceRef: ComputedRef<(Recordable & { checked?: boolean })[]> | 18 | + getDataSourceRef: ComputedRef<Recordable[]>, |
19 | + propsRef: ComputedRef<BasicCardListPropsType>, | ||
20 | + { getRowKey }: CardListSelectActionType | ||
5 | ) { | 21 | ) { |
22 | + const selectedKeys = ref<string[]>([]); | ||
23 | + | ||
24 | + const selectedClass = ref(''); | ||
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 | + | ||
6 | const getHasSelectedRecordStatus = computed(() => | 37 | const getHasSelectedRecordStatus = computed(() => |
7 | unref(getDataSourceRef).find((item) => item.checked) | 38 | unref(getDataSourceRef).find((item) => item.checked) |
8 | ); | 39 | ); |
9 | 40 | ||
10 | - // function handlerSelect | 41 | + const getShouldUseDefaultStyle = computed( |
42 | + () => unref(getSelectionsRef).enabled && !unref(getSelectionsRef).customCheckedStyle | ||
43 | + ); | ||
44 | + | ||
45 | + watch( | ||
46 | + getSelectionsRef, | ||
47 | + () => { | ||
48 | + selectedClass.value = | ||
49 | + isBoolean(unref(propsRef).selections) || | ||
50 | + !(unref(propsRef).selections as CardListSelectionsType).customCheckedStyle | ||
51 | + ? DEFAULT_SELECTED_CLASS | ||
52 | + : ''; | ||
53 | + }, | ||
54 | + { | ||
55 | + immediate: true, | ||
56 | + } | ||
57 | + ); | ||
58 | + | ||
59 | + function handlerSelected(_event: MouseEvent, record: Recordable) { | ||
60 | + if (!unref(getSelectionsRef).enabled) return; | ||
61 | + | ||
62 | + if (unref(getSelectionsRef).beforeSelectValidate) { | ||
63 | + if (!unref(getSelectionsRef).beforeSelectValidate?.(record)) return; | ||
64 | + } | ||
65 | + | ||
66 | + const rowKey = record[unref(getRowKey)]; | ||
67 | + const index = unref(selectedKeys).findIndex((key) => key === rowKey); | ||
68 | + ~index ? selectedKeys.value.splice(index, 1) : unref(selectedKeys).push(rowKey); | ||
69 | + | ||
70 | + if (!unref(getSelectionsRef).onSelect) return; | ||
71 | + | ||
72 | + unref(getSelectionsRef)?.onSelect?.(record, !!~index, getSelectedRecords()); | ||
73 | + } | ||
74 | + | ||
75 | + function selectedAll() { | ||
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)]); | ||
83 | + | ||
84 | + if (!unref(getSelectionsRef).onSelect) return; | ||
85 | + | ||
86 | + unref(getSelectionsRef)?.onSelectAll?.(toRaw(unref(getDataSourceRef))); | ||
87 | + } | ||
88 | + | ||
89 | + function clearSelectedKeys() { | ||
90 | + selectedKeys.value = []; | ||
91 | + } | ||
92 | + | ||
93 | + function getSelectedKeys() { | ||
94 | + return toRaw(unref(selectedKeys)); | ||
95 | + } | ||
96 | + | ||
97 | + function getSelectedRecords() { | ||
98 | + const data = cloneDeep(unref(getDataSourceRef)); | ||
99 | + return data.filter((item) => unref(selectedKeys).includes(item[unref(getRowKey)])); | ||
100 | + } | ||
101 | + | ||
102 | + function selectAllToggle() { | ||
103 | + unref(getDataSourceRef).length === unref(selectedKeys).length | ||
104 | + ? (selectedKeys.value = []) | ||
105 | + : selectedAll(); | ||
106 | + } | ||
107 | + | ||
108 | + function getSelections() { | ||
109 | + return unref(getSelectionsRef); | ||
110 | + } | ||
11 | 111 | ||
12 | return { | 112 | return { |
113 | + selectedKeys, | ||
114 | + selectedClass, | ||
115 | + getShouldUseDefaultStyle, | ||
13 | getHasSelectedRecordStatus, | 116 | getHasSelectedRecordStatus, |
117 | + getSelections, | ||
118 | + handlerSelected, | ||
119 | + selectedAll, | ||
120 | + selectAllToggle, | ||
121 | + clearSelectedKeys, | ||
122 | + getSelectedKeys, | ||
123 | + getSelectedRecords, | ||
14 | }; | 124 | }; |
15 | } | 125 | } |
@@ -4,6 +4,7 @@ import { FetchSetting } from '../../Table'; | @@ -4,6 +4,7 @@ import { FetchSetting } from '../../Table'; | ||
4 | import { useLoading } from './hooks/useLoading'; | 4 | import { useLoading } from './hooks/useLoading'; |
5 | import { usePagination } from './hooks/usePagination'; | 5 | import { usePagination } from './hooks/usePagination'; |
6 | import { FetchParams, useCardListData } from './hooks/useCardListData'; | 6 | import { FetchParams, useCardListData } from './hooks/useCardListData'; |
7 | +import { useCardListSelected } from './hooks/useCardListSelected'; | ||
7 | 8 | ||
8 | // export interface CardList | 9 | // export interface CardList |
9 | 10 | ||
@@ -22,11 +23,11 @@ export interface BasicCardListPropsType<T = Recordable> { | @@ -22,11 +23,11 @@ export interface BasicCardListPropsType<T = Recordable> { | ||
22 | fetchSetting?: FetchSetting; | 23 | fetchSetting?: FetchSetting; |
23 | afterFetch?: (items: T[], result: any) => Promise<any[]>; | 24 | afterFetch?: (items: T[], result: any) => Promise<any[]>; |
24 | autoCreateKey?: boolean; | 25 | autoCreateKey?: boolean; |
25 | - rowKey?: (item: T) => string | number; | 26 | + rowKey?: string | number; |
26 | immediate?: boolean; | 27 | immediate?: boolean; |
27 | handleSearchInfoFn?: Fn; | 28 | handleSearchInfoFn?: Fn; |
28 | baseLayout?: Record<'row' | 'col', number>; | 29 | baseLayout?: Record<'row' | 'col', number>; |
29 | - selections?: boolean; | 30 | + selections?: boolean | CardListSelectionsType; |
30 | } | 31 | } |
31 | 32 | ||
32 | export type ListGridType = Record<'column' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', number> & { | 33 | export type ListGridType = Record<'column' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', number> & { |
@@ -36,14 +37,22 @@ export type ListGridType = Record<'column' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | | @@ -36,14 +37,22 @@ export type ListGridType = Record<'column' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | | ||
36 | export type UseLoading = ReturnType<typeof useLoading>; | 37 | export type UseLoading = ReturnType<typeof useLoading>; |
37 | 38 | ||
38 | export type UsePaginationType = ReturnType<typeof usePagination>; | 39 | export type UsePaginationType = ReturnType<typeof usePagination>; |
40 | + | ||
39 | export type UseCardListDataType = ReturnType<typeof useCardListData>; | 41 | export type UseCardListDataType = ReturnType<typeof useCardListData>; |
40 | 42 | ||
43 | +export type UseCardListSelected = ReturnType<typeof useCardListSelected>; | ||
44 | + | ||
41 | export interface CardListActionType { | 45 | export interface CardListActionType { |
42 | setProps: (props: Partial<BasicCardListPropsType>) => void; | 46 | setProps: (props: Partial<BasicCardListPropsType>) => void; |
43 | setLoading: UseLoading['setLoading']; | 47 | setLoading: UseLoading['setLoading']; |
44 | setPagination: UsePaginationType['setPagination']; | 48 | setPagination: UsePaginationType['setPagination']; |
45 | getPagination: UsePaginationType['getPagination']; | 49 | getPagination: UsePaginationType['getPagination']; |
46 | reload: UseCardListDataType['reload']; | 50 | reload: UseCardListDataType['reload']; |
51 | + selectedAll: UseCardListSelected['selectedAll']; | ||
52 | + clearSelectedKeys: UseCardListSelected['clearSelectedKeys']; | ||
53 | + getSelectedKeys: UseCardListSelected['getSelectedKeys']; | ||
54 | + getSelectedRecords: UseCardListSelected['getSelectedRecords']; | ||
55 | + getSelections: UseCardListSelected['getSelections']; | ||
47 | } | 56 | } |
48 | 57 | ||
49 | export interface CardListEmitType { | 58 | export interface CardListEmitType { |
@@ -51,13 +60,9 @@ export interface CardListEmitType { | @@ -51,13 +60,9 @@ export interface CardListEmitType { | ||
51 | (eventName: 'fetchError', error: Error): void; | 60 | (eventName: 'fetchError', error: Error): void; |
52 | } | 61 | } |
53 | 62 | ||
54 | -export interface CardListRenderItem<T = Recordable & { checked?: boolean }> { | ||
55 | - item: T; | ||
56 | - totalHeight: number; | ||
57 | -} | ||
58 | - | ||
59 | -export interface CardListSelectionsType<T = Recordable> { | ||
60 | - onSelect?: (record: T, selected: boolean) => any; | ||
61 | - onSelectAll: (selectedRecords: T[]) => any; | ||
62 | - onSelectInvert: (selectedRecords: T[]) => any; | 63 | +export interface CardListSelectionsType { |
64 | + customCheckedStyle?: boolean; | ||
65 | + beforeSelectValidate?: (record: Recordable) => boolean; | ||
66 | + onSelect?: (record: Recordable, selected: boolean, allSelectedRecord: Recordable[]) => any; | ||
67 | + onSelectAll?: (selectedRecords: Recordable[]) => any; | ||
63 | } | 68 | } |
@@ -19,30 +19,12 @@ | @@ -19,30 +19,12 @@ | ||
19 | </Button> | 19 | </Button> |
20 | </template> | 20 | </template> |
21 | <template #toolbar> | 21 | <template #toolbar> |
22 | - <Tooltip> | ||
23 | - <template #title> | ||
24 | - <div>激活未确认: 可以处理,清除</div> | ||
25 | - <div>激活已确认: 只可清除,已经处理</div> | ||
26 | - <div>清除未确认: 只可处理,已经清除</div> | ||
27 | - <div>清除已确认: 不需要做处理和清除</div> | ||
28 | - </template> | ||
29 | - <Button @click="handleBatchClear" type="primary" :disabled="getCanBatchClear"> | ||
30 | - <QuestionCircleOutlined class="cursor-pointer" /> | ||
31 | - <span>批量清除</span> | ||
32 | - </Button> | ||
33 | - </Tooltip> | ||
34 | - <Tooltip> | ||
35 | - <template #title> | ||
36 | - <div>激活未确认: 可以处理,清除</div> | ||
37 | - <div>激活已确认: 只可清除,已经处理</div> | ||
38 | - <div>清除未确认: 只可处理,已经清除</div> | ||
39 | - <div>清除已确认: 不需要做处理和清除</div> | ||
40 | - </template> | ||
41 | - <Button @click="handleBatchAck" type="primary" :disabled="getCanBatchAck"> | ||
42 | - <QuestionCircleOutlined class="cursor-pointer" /> | ||
43 | - <span>批量处理</span> | ||
44 | - </Button> | ||
45 | - </Tooltip> | 22 | + <Button @click="handleBatchClear" type="primary" :disabled="getCanBatchClear"> |
23 | + <span>批量清除</span> | ||
24 | + </Button> | ||
25 | + <Button @click="handleBatchAck" type="primary" :disabled="getCanBatchAck"> | ||
26 | + <span>批量处理</span> | ||
27 | + </Button> | ||
46 | </template> | 28 | </template> |
47 | </BasicTable> | 29 | </BasicTable> |
48 | <AlarmDetailDrawer @register="registerDetailDrawer" @success="handleSuccess" /> | 30 | <AlarmDetailDrawer @register="registerDetailDrawer" @success="handleSuccess" /> |
@@ -55,7 +37,7 @@ | @@ -55,7 +37,7 @@ | ||
55 | import { doBatchAckAlarm, doBatchClearAlarm, getDeviceAlarm } from '/@/api/device/deviceManager'; | 37 | import { doBatchAckAlarm, doBatchClearAlarm, getDeviceAlarm } from '/@/api/device/deviceManager'; |
56 | import { useDrawer } from '/@/components/Drawer'; | 38 | import { useDrawer } from '/@/components/Drawer'; |
57 | import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue'; | 39 | import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue'; |
58 | - import { Modal, Button, Tooltip } from 'ant-design-vue'; | 40 | + import { Modal, Button } from 'ant-design-vue'; |
59 | import { JsonPreview } from '/@/components/CodeEditor'; | 41 | import { JsonPreview } from '/@/components/CodeEditor'; |
60 | import { getDeviceDetail } from '/@/api/device/deviceManager'; | 42 | import { getDeviceDetail } from '/@/api/device/deviceManager'; |
61 | import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; | 43 | import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; |
@@ -67,7 +49,6 @@ | @@ -67,7 +49,6 @@ | ||
67 | import { AlarmLogItem } from '/@/api/device/model/deviceConfigModel'; | 49 | import { AlarmLogItem } from '/@/api/device/model/deviceConfigModel'; |
68 | import { AlarmStatus } from '/@/enums/alarmEnum'; | 50 | import { AlarmStatus } from '/@/enums/alarmEnum'; |
69 | import { useMessage } from '/@/hooks/web/useMessage'; | 51 | import { useMessage } from '/@/hooks/web/useMessage'; |
70 | - import { QuestionCircleOutlined } from '@ant-design/icons-vue'; | ||
71 | 52 | ||
72 | const props = defineProps<{ | 53 | const props = defineProps<{ |
73 | deviceId?: string; | 54 | deviceId?: string; |
@@ -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', |
@@ -188,6 +188,12 @@ export const basicInfoForm: FormSchema[] = [ | @@ -188,6 +188,12 @@ export const basicInfoForm: FormSchema[] = [ | ||
188 | }, | 188 | }, |
189 | }, | 189 | }, |
190 | { | 190 | { |
191 | + field: FieldsEnum.TRANSPORT_TYPE, | ||
192 | + label: '', | ||
193 | + component: 'Input', | ||
194 | + show: false, | ||
195 | + }, | ||
196 | + { | ||
191 | field: FieldsEnum.GATEWAY_TB_DEVICE_ID, | 197 | field: FieldsEnum.GATEWAY_TB_DEVICE_ID, |
192 | component: 'ApiSelect', | 198 | component: 'ApiSelect', |
193 | label: '网关设备', | 199 | label: '网关设备', |
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,10 @@ | @@ -29,8 +20,10 @@ | ||
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'; | ||
26 | + import { ref } from 'vue-demi'; | ||
34 | 27 | ||
35 | defineProps<{ | 28 | defineProps<{ |
36 | mode: EnumTableCardMode; | 29 | mode: EnumTableCardMode; |
@@ -42,94 +35,44 @@ | @@ -42,94 +35,44 @@ | ||
42 | SET_DEFAULT = 'setDefault', | 35 | SET_DEFAULT = 'setDefault', |
43 | DELETE = 'delete', | 36 | DELETE = 'delete', |
44 | } | 37 | } |
38 | + | ||
45 | const IMAGE_FALLBACK = productDefault; | 39 | const IMAGE_FALLBACK = productDefault; |
46 | 40 | ||
47 | const { createMessage } = useMessage(); | 41 | const { createMessage } = useMessage(); |
48 | 42 | ||
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: '' }); | 43 | + const { query } = useRoute(); |
44 | + | ||
45 | + const disabledDeleteFlag = ref(true); | ||
46 | + | ||
47 | + const [registerCardList, { reload, getSelectedKeys, clearSelectedKeys }] = useCardList({ | ||
48 | + api: deviceConfigGetQuery, | ||
49 | + useSearchForm: true, | ||
50 | + gutter: 4, | ||
51 | + rowKey: 'id', | ||
52 | + formConfig: { | ||
53 | + schemas: searchFormSchema, | ||
54 | + labelWidth: 100, | ||
55 | + model: { | ||
56 | + name: (query as Recordable)?.name ? decodeURIComponent((query as Recordable)?.name) : null, | ||
57 | + }, | ||
57 | }, | 58 | }, |
58 | - submitFunc: async () => { | ||
59 | - getDataSource({ pageSize: pagination.pageSize, page: 1 }); | 59 | + selections: { |
60 | + beforeSelectValidate: (record: ProfileRecord) => { | ||
61 | + return !record.default; | ||
62 | + }, | ||
63 | + onSelect: (_record, _flag, allSelecteds) => { | ||
64 | + disabledDeleteFlag.value = !allSelecteds.length; | ||
65 | + }, | ||
60 | }, | 66 | }, |
61 | }); | 67 | }); |
62 | 68 | ||
63 | const [registerModal, { openModal }] = useModal(); | 69 | const [registerModal, { openModal }] = useModal(); |
64 | const [registerDrawer, { openDrawer }] = useDrawer(); | 70 | const [registerDrawer, { openDrawer }] = useDrawer(); |
65 | 71 | ||
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) => { | 72 | const handleModeChange = (mode: EnumTableCardMode) => { |
116 | emit('changeMode', mode); | 73 | emit('changeMode', mode); |
117 | }; | 74 | }; |
118 | 75 | ||
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 = () => { | 76 | const handleCreate = () => { |
134 | openModal(true, { | 77 | openModal(true, { |
135 | isUpdate: false, | 78 | isUpdate: false, |
@@ -147,11 +90,14 @@ | @@ -147,11 +90,14 @@ | ||
147 | }); | 90 | }); |
148 | }; | 91 | }; |
149 | 92 | ||
150 | - const handleDelete = async (id: string[]) => { | 93 | + const handleDelete = async (ids?: string[]) => { |
151 | try { | 94 | try { |
152 | - await deviceConfigDelete(id); | 95 | + ids = ids || getSelectedKeys(); |
96 | + await deviceConfigDelete(ids); | ||
153 | createMessage.success('删除成功'); | 97 | createMessage.success('删除成功'); |
154 | - await getDataSource(); | 98 | + clearSelectedKeys(); |
99 | + disabledDeleteFlag.value = true; | ||
100 | + await reload(); | ||
155 | } catch (error) { | 101 | } catch (error) { |
156 | throw error; | 102 | throw error; |
157 | } | 103 | } |
@@ -163,155 +109,109 @@ | @@ -163,155 +109,109 @@ | ||
163 | const data = await setDeviceProfileIsDefaultApi(tbProfileId, 'default', defaultObj); | 109 | const data = await setDeviceProfileIsDefaultApi(tbProfileId, 'default', defaultObj); |
164 | if (!data) return createMessage.error('设置该产品为默认失败'); | 110 | if (!data) return createMessage.error('设置该产品为默认失败'); |
165 | createMessage.success('设置该产品为默认成功'); | 111 | createMessage.success('设置该产品为默认成功'); |
166 | - await getDataSource(); | 112 | + await reload(); |
167 | } catch (error) { | 113 | } catch (error) { |
168 | throw error; | 114 | throw error; |
169 | } | 115 | } |
170 | }; | 116 | }; |
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> | 117 | </script> |
185 | 118 | ||
186 | <template> | 119 | <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> | 120 | + <section> |
121 | + <BasicCardList @register="registerCardList"> | ||
122 | + <template #toolbar> | ||
123 | + <Authority :value="ProductPermission.CREATE"> | ||
124 | + <Button type="primary" @click="handleCreate">新增产品</Button> | ||
125 | + </Authority> | ||
126 | + | ||
127 | + <ModeSwitchButton :value="$props.mode" @change="handleModeChange" /> | ||
128 | + | ||
129 | + <Authority :value="ProductPermission.DELETE"> | ||
130 | + <Popconfirm | ||
131 | + title="您确定要批量删除数据" | ||
132 | + ok-text="确定" | ||
133 | + cancel-text="取消" | ||
134 | + @confirm="handleDelete()" | ||
135 | + :disabled="disabledDeleteFlag" | ||
136 | + > | ||
137 | + <Button type="primary" danger :disabled="disabledDeleteFlag"> 批量删除 </Button> | ||
138 | + </Popconfirm> | ||
139 | + </Authority> | ||
140 | + </template> | ||
141 | + <template #renderItem="{ item }: BasicCardListRenderItem<ProfileRecord>"> | ||
142 | + <Card hoverable> | ||
143 | + <template #cover> | ||
144 | + <div class="h-full w-full !flex justify-center items-center text-center p-1"> | ||
145 | + <Image | ||
146 | + @click.stop | ||
147 | + wrapper-class-name="!w-32 !h-32 !flex !items-center" | ||
148 | + :src="item.image || IMAGE_FALLBACK" | ||
149 | + placeholder | ||
150 | + :fallback="IMAGE_FALLBACK" | ||
151 | + /> | ||
152 | + </div> | ||
153 | + </template> | ||
154 | + <template class="ant-card-actions" #actions> | ||
155 | + <Tooltip title="详情"> | ||
156 | + <AuthIcon | ||
157 | + :auth="ProductPermission.DETAIL" | ||
158 | + class="!text-lg" | ||
159 | + icon="ant-design:eye-outlined" | ||
160 | + @click.stop="handleShowDetail(item)" | ||
161 | + /> | ||
162 | + </Tooltip> | ||
163 | + <Tooltip title="编辑"> | ||
164 | + <AuthIcon | ||
165 | + :auth="ProductPermission.UPDATE" | ||
166 | + class="!text-lg" | ||
167 | + icon="ant-design:form-outlined" | ||
168 | + @click.stop="handleUpdate(item)" | ||
169 | + :disabled="item.name == 'default'" | ||
170 | + /> | ||
231 | </Tooltip> | 171 | </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" /> | 172 | + <AuthDropDown |
173 | + @click.stop | ||
174 | + :trigger="['hover']" | ||
175 | + :drop-menu-list="[ | ||
176 | + { | ||
177 | + text: '默认', | ||
178 | + event: DropMenuEvent.SET_DEFAULT, | ||
179 | + icon: 'ant-design:unordered-list-outlined', | ||
180 | + onClick: handleSetDefault.bind(null, item), | ||
181 | + disabled: item.default, | ||
182 | + }, | ||
183 | + { | ||
184 | + text: '删除', | ||
185 | + event: DropMenuEvent.DELETE, | ||
186 | + auth: ProductPermission.DELETE, | ||
187 | + icon: 'ant-design:delete-outlined', | ||
188 | + popconfirm: { | ||
189 | + title: '是否确认删除操作?', | ||
190 | + onConfirm: handleDelete.bind(null, [item.id]), | ||
191 | + disabled: item.default, | ||
192 | + }, | ||
193 | + disabled: item.default || item.name == 'default', | ||
194 | + }, | ||
195 | + ]" | ||
196 | + /> | ||
197 | + </template> | ||
198 | + <Card.Meta> | ||
199 | + <template #title> | ||
200 | + <span class="truncate"> {{ item.name }} </span> | ||
201 | + </template> | ||
202 | + <template #description> | ||
203 | + <div class="truncate h-11"> | ||
204 | + <div class="truncate">{{ DeviceTypeName[item.deviceType] }} </div> | ||
205 | + <div class="truncate">{{ item.transportType }} </div> | ||
206 | + </div> | ||
207 | + </template> | ||
208 | + </Card.Meta> | ||
209 | + </Card> | ||
210 | + </template> | ||
211 | + </BasicCardList> | ||
212 | + <DeviceProfileModal @register="registerModal" @success="reload" /> | ||
313 | <DeviceProfileDrawer @register="registerDrawer" /> | 213 | <DeviceProfileDrawer @register="registerDrawer" /> |
314 | - </PageWrapper> | 214 | + </section> |
315 | </template> | 215 | </template> |
316 | 216 | ||
317 | <style lang="less" scoped> | 217 | <style lang="less" scoped> |
@@ -322,8 +222,4 @@ | @@ -322,8 +222,4 @@ | ||
322 | .profile-list:deep(.ant-card-body) { | 222 | .profile-list:deep(.ant-card-body) { |
323 | @apply !p-4; | 223 | @apply !p-4; |
324 | } | 224 | } |
325 | - | ||
326 | - :deep(.ant-spin-nested-loading) { | ||
327 | - height: 40rem; | ||
328 | - } | ||
329 | </style> | 225 | </style> |
@@ -30,7 +30,7 @@ | @@ -30,7 +30,7 @@ | ||
30 | <Attribute | 30 | <Attribute |
31 | v-if="activeKey === FunctionType.PROPERTIES" | 31 | v-if="activeKey === FunctionType.PROPERTIES" |
32 | :openModalMode="openModalMode" | 32 | :openModalMode="openModalMode" |
33 | - :transportType="record.transportType" | 33 | + :transportType="record?.transportType" |
34 | ref="AttrRef" | 34 | ref="AttrRef" |
35 | /> | 35 | /> |
36 | <Service | 36 | <Service |
@@ -82,7 +82,7 @@ | @@ -82,7 +82,7 @@ | ||
82 | 82 | ||
83 | const isTCPGatewaySubDevice = computed(() => { | 83 | const isTCPGatewaySubDevice = computed(() => { |
84 | const { record } = props; | 84 | const { record } = props; |
85 | - const { deviceType, transportType } = record; | 85 | + const { deviceType, transportType } = record || {}; |
86 | return deviceType === DeviceTypeEnum.SENSOR && transportType === 'TCP'; | 86 | return deviceType === DeviceTypeEnum.SENSOR && transportType === 'TCP'; |
87 | }); | 87 | }); |
88 | 88 |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | 15 | import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config'; |
16 | import { TransportTypeEnum } from '../../../../components/TransportDescript/const'; | 16 | import { TransportTypeEnum } from '../../../../components/TransportDescript/const'; |
17 | 17 | ||
18 | - const props = defineProps<{ openModalMode: OpenModelMode; transportType: string }>(); | 18 | + const props = defineProps<{ openModalMode: OpenModelMode; transportType?: string | undefined }>(); |
19 | 19 | ||
20 | const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ | 20 | const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ |
21 | labelWidth: 100, | 21 | labelWidth: 100, |
@@ -18,7 +18,9 @@ | @@ -18,7 +18,9 @@ | ||
18 | 18 | ||
19 | const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ | 19 | const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ |
20 | labelWidth: 100, | 20 | labelWidth: 100, |
21 | - schemas: serviceSchemas(props.record.transportType === 'TCP'), | 21 | + schemas: serviceSchemas( |
22 | + props.record.transportType === 'TCP' || props.record.ifShowClass === true | ||
23 | + ), | ||
22 | actionColOptions: { | 24 | actionColOptions: { |
23 | span: 14, | 25 | span: 14, |
24 | }, | 26 | }, |
@@ -4,12 +4,6 @@ import { CreateModalDefineExposeType, AwaitPopupWindowReturnDataType } from '../ | @@ -4,12 +4,6 @@ import { CreateModalDefineExposeType, AwaitPopupWindowReturnDataType } from '../ | ||
4 | import { EdgeData, NodeData } from '../types/node'; | 4 | import { EdgeData, NodeData } from '../types/node'; |
5 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 5 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
6 | import { ElementsTypeEnum } from '../enum'; | 6 | import { ElementsTypeEnum } from '../enum'; |
7 | -import { FilterCategoryComponentEnum } from '../enum/category'; | ||
8 | -import { useMessage } from '/@/hooks/web/useMessage'; | ||
9 | -import { CheckExistenceFieldsNameEnum } from '../enum/formField/filter'; | ||
10 | -import { useI18n } from '/@/hooks/web/useI18n'; | ||
11 | - | ||
12 | -const { t } = useI18n(); | ||
13 | 7 | ||
14 | interface OptionsType { | 8 | interface OptionsType { |
15 | mode: DataActionModeEnum; | 9 | mode: DataActionModeEnum; |
@@ -108,25 +102,12 @@ export function useAwaitPopupWindowBindData( | @@ -108,25 +102,12 @@ export function useAwaitPopupWindowBindData( | ||
108 | ); | 102 | ); |
109 | }; | 103 | }; |
110 | 104 | ||
111 | - const { createMessage } = useMessage(); | ||
112 | - | ||
113 | const handleSubmit = async (extraData: Recordable = {}) => { | 105 | const handleSubmit = async (extraData: Recordable = {}) => { |
114 | const { flag } = await validate(); | 106 | const { flag } = await validate(); |
115 | if (!flag) return; | 107 | if (!flag) return; |
108 | + await unref(createComponentEl)?.beforeSubmit?.(); | ||
116 | const value = await unref(createComponentEl)?.getFieldsValue?.(); | 109 | const value = await unref(createComponentEl)?.getFieldsValue?.(); |
117 | - const { config } = nodeData.value || {}; | ||
118 | - if ( | ||
119 | - config?.key === FilterCategoryComponentEnum.CHECK_EXISTENCE_FIELDS && | ||
120 | - !value?.messageNames && | ||
121 | - !value?.messageNames | ||
122 | - ) { | ||
123 | - createMessage.warning( | ||
124 | - `${t(CheckExistenceFieldsNameEnum.MESSAGE_NAMES)}和${t( | ||
125 | - CheckExistenceFieldsNameEnum.METADATA_NAMES | ||
126 | - )}最少选填一个` | ||
127 | - ); | ||
128 | - return true; | ||
129 | - } | 110 | + |
130 | unref(resolveFn)?.({ | 111 | unref(resolveFn)?.({ |
131 | flag: true, | 112 | flag: true, |
132 | data: handleSubmitData(extraData, value), | 113 | data: handleSubmitData(extraData, value), |
@@ -3,11 +3,16 @@ | @@ -3,11 +3,16 @@ | ||
3 | import { BasicForm, useForm } from '/@/components/Form'; | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | import { formSchemas } from './create.config'; | 4 | import { formSchemas } from './create.config'; |
5 | import { NodeData } from '../../../types/node'; | 5 | import { NodeData } from '../../../types/node'; |
6 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
7 | + import { useI18n } from '/@/hooks/web/useI18n'; | ||
8 | + import { CheckExistenceFieldsNameEnum } from '../../../enum/formField/filter'; | ||
6 | 9 | ||
7 | defineProps<{ | 10 | defineProps<{ |
8 | config: NodeData; | 11 | config: NodeData; |
9 | }>(); | 12 | }>(); |
10 | 13 | ||
14 | + const { t } = useI18n(); | ||
15 | + | ||
11 | const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 16 | const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
12 | schemas: formSchemas, | 17 | schemas: formSchemas, |
13 | showActionButtonGroup: false, | 18 | showActionButtonGroup: false, |
@@ -24,9 +29,22 @@ | @@ -24,9 +29,22 @@ | ||
24 | setFieldsValue(value); | 29 | setFieldsValue(value); |
25 | }; | 30 | }; |
26 | 31 | ||
32 | + const { createMessage } = useMessage(); | ||
33 | + const beforeSubmit = async () => { | ||
34 | + const value = getFieldsValue(); | ||
35 | + if (!value?.messageNames && !value?.messageNames) { | ||
36 | + const errorMessage = `${t(CheckExistenceFieldsNameEnum.MESSAGE_NAMES)}和${t( | ||
37 | + CheckExistenceFieldsNameEnum.METADATA_NAMES | ||
38 | + )}最少选填一个`; | ||
39 | + createMessage.warning(errorMessage); | ||
40 | + return Promise.reject(errorMessage); | ||
41 | + } | ||
42 | + }; | ||
43 | + | ||
27 | defineExpose({ | 44 | defineExpose({ |
28 | setFieldsValue: setValue, | 45 | setFieldsValue: setValue, |
29 | getFieldsValue: getValue, | 46 | getFieldsValue: getValue, |
47 | + beforeSubmit, | ||
30 | } as CreateModalDefineExposeType); | 48 | } as CreateModalDefineExposeType); |
31 | </script> | 49 | </script> |
32 | 50 |
@@ -56,8 +56,7 @@ | @@ -56,8 +56,7 @@ | ||
56 | const handleModalOk = async () => { | 56 | const handleModalOk = async () => { |
57 | await validate(); | 57 | await validate(); |
58 | const data = getFieldsValue(); | 58 | const data = getFieldsValue(); |
59 | - const flag = await handleSubmit(data); | ||
60 | - if (flag) return; | 59 | + await handleSubmit(data); |
61 | resetFieldsValue(); | 60 | resetFieldsValue(); |
62 | }; | 61 | }; |
63 | 62 |
@@ -15,6 +15,8 @@ export interface CreateModalDefineExposeType { | @@ -15,6 +15,8 @@ export interface CreateModalDefineExposeType { | ||
15 | validate?: () => Promise<{ flag: boolean; error?: RuleError }>; | 15 | validate?: () => Promise<{ flag: boolean; error?: RuleError }>; |
16 | getFieldsValue?: () => Promise<Recordable>; | 16 | getFieldsValue?: () => Promise<Recordable>; |
17 | setFieldsValue?: (value?: DefaultNodeConfig, nodeData?: NodeData) => void; | 17 | setFieldsValue?: (value?: DefaultNodeConfig, nodeData?: NodeData) => void; |
18 | + clearValidate?: (name?: string | string[] | undefined) => Promise<void>; | ||
19 | + beforeSubmit?: () => Promise<any>; | ||
18 | } | 20 | } |
19 | 21 | ||
20 | export interface CreateModalDefineEmitType { | 22 | export interface CreateModalDefineEmitType { |
@@ -137,9 +137,10 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | @@ -137,9 +137,10 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | ||
137 | }, | 137 | }, |
138 | { | 138 | { |
139 | field: FormFieldsEnum.ENABLE_CLEAR_RULE, | 139 | field: FormFieldsEnum.ENABLE_CLEAR_RULE, |
140 | - label: '', | 140 | + label: ' ', |
141 | component: 'Checkbox', | 141 | component: 'Checkbox', |
142 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, | 142 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, |
143 | + labelWidth: 24, | ||
143 | renderComponentContent: () => ({ | 144 | renderComponentContent: () => ({ |
144 | default: () => [ | 145 | default: () => [ |
145 | h('span', FormFieldsNameEnum.ENABLE_CLEAR_RULE), | 146 | h('span', FormFieldsNameEnum.ENABLE_CLEAR_RULE), |
@@ -14,7 +14,7 @@ import { ConditionItemType } from '../ConditionFilter/type'; | @@ -14,7 +14,7 @@ import { ConditionItemType } from '../ConditionFilter/type'; | ||
14 | export function useFlipFlopData( | 14 | export function useFlipFlopData( |
15 | flipFlopListRef: Ref<FlipFlopItemType[]> | 15 | flipFlopListRef: Ref<FlipFlopItemType[]> |
16 | ): DefineComponentsBasicExpose<FlipFlopConditionType[]> { | 16 | ): DefineComponentsBasicExpose<FlipFlopConditionType[]> { |
17 | - const getEventTriggerCOndition = ( | 17 | + const getEventTriggerCondition = ( |
18 | basicRecord: FlipFlopFormRecordType, | 18 | basicRecord: FlipFlopFormRecordType, |
19 | _flipFlopItem: FlipFlopItemType | 19 | _flipFlopItem: FlipFlopItemType |
20 | ): ConditionItemType[] => { | 20 | ): ConditionItemType[] => { |
@@ -23,7 +23,7 @@ export function useFlipFlopData( | @@ -23,7 +23,7 @@ export function useFlipFlopData( | ||
23 | { | 23 | { |
24 | key: { | 24 | key: { |
25 | type: DeviceTriggerTypeEum.DEVICE_EVENT, | 25 | type: DeviceTriggerTypeEum.DEVICE_EVENT, |
26 | - key: deviceEventTriggerKey, | 26 | + key: 'eventIdentifier', |
27 | }, | 27 | }, |
28 | valueType: TriggerValueTypeEnum.STRING, | 28 | valueType: TriggerValueTypeEnum.STRING, |
29 | predicate: { | 29 | predicate: { |
@@ -41,7 +41,7 @@ export function useFlipFlopData( | @@ -41,7 +41,7 @@ export function useFlipFlopData( | ||
41 | const { conditionType } = basicRecord; | 41 | const { conditionType } = basicRecord; |
42 | return conditionType === DeviceTriggerTypeEum.TIME_SERIES | 42 | return conditionType === DeviceTriggerTypeEum.TIME_SERIES |
43 | ? flipFlopItem.conditionRef?.getFieldsValue() || [] | 43 | ? flipFlopItem.conditionRef?.getFieldsValue() || [] |
44 | - : getEventTriggerCOndition(basicRecord, flipFlopItem); | 44 | + : getEventTriggerCondition(basicRecord, flipFlopItem); |
45 | }; | 45 | }; |
46 | 46 | ||
47 | const crateFlipFlopCondition = (flipFlopItem: FlipFlopItemType): FlipFlopConditionType => { | 47 | const crateFlipFlopCondition = (flipFlopItem: FlipFlopItemType): FlipFlopConditionType => { |
@@ -106,6 +106,7 @@ export function useFlipFlopData( | @@ -106,6 +106,7 @@ export function useFlipFlopData( | ||
106 | const { key, valueType } = firstItem; | 106 | const { key, valueType } = firstItem; |
107 | const { type, unit, predicate } = condition.spec; | 107 | const { type, unit, predicate } = condition.spec; |
108 | const { defaultValue } = predicate || {}; | 108 | const { defaultValue } = predicate || {}; |
109 | + const { value } = firstItem.predicate; | ||
109 | 110 | ||
110 | const record = { | 111 | const record = { |
111 | ...data, | 112 | ...data, |
@@ -122,7 +123,7 @@ export function useFlipFlopData( | @@ -122,7 +123,7 @@ export function useFlipFlopData( | ||
122 | 123 | ||
123 | if (key.type === DeviceTriggerTypeEum.DEVICE_EVENT) { | 124 | if (key.type === DeviceTriggerTypeEum.DEVICE_EVENT) { |
124 | Object.assign(record, { | 125 | Object.assign(record, { |
125 | - [FormFieldEnum.DEVICE_EVENT_TRIGGER_KEY]: key.key, | 126 | + [FormFieldEnum.DEVICE_EVENT_TRIGGER_KEY]: value.defaultValue, |
126 | [FormFieldEnum.CONDITION_KEY]: null, | 127 | [FormFieldEnum.CONDITION_KEY]: null, |
127 | }); | 128 | }); |
128 | } | 129 | } |
1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
2 | - import { Button, List, Space, Tooltip } from 'ant-design-vue'; | ||
3 | - import { PageWrapper } from '/@/components/Page'; | ||
4 | - import { BasicForm, useForm } from '/@/components/Form'; | 2 | + import { Button } from 'ant-design-vue'; |
5 | import { PermissionEnum, formSchemas } from './config'; | 3 | import { PermissionEnum, formSchemas } from './config'; |
6 | import { TaskCard } from './components/TaskCard'; | 4 | import { TaskCard } from './components/TaskCard'; |
7 | import { getTaskCenterList } from '/@/api/task'; | 5 | import { getTaskCenterList } from '/@/api/task'; |
8 | - import { ref, unref } from 'vue'; | ||
9 | - import { onMounted } from 'vue'; | ||
10 | import { DetailModal } from './components/DetailModal'; | 6 | import { DetailModal } from './components/DetailModal'; |
11 | import { useModal } from '/@/components/Modal'; | 7 | import { useModal } from '/@/components/Modal'; |
12 | import { TaskRecordType } from '/@/api/task/model'; | 8 | import { TaskRecordType } from '/@/api/task/model'; |
13 | - import { reactive } from 'vue'; | ||
14 | - import { ReloadOutlined } from '@ant-design/icons-vue'; | ||
15 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 9 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
16 | import { Authority } from '/@/components/Authority'; | 10 | import { Authority } from '/@/components/Authority'; |
17 | - import { getBoundingClientRect } from '/@/utils/domUtils'; | ||
18 | import { RunTaskModal } from './components/RunTaskModal'; | 11 | import { RunTaskModal } from './components/RunTaskModal'; |
19 | import { DetailDrawer } from './components/DetailDrawer'; | 12 | import { DetailDrawer } from './components/DetailDrawer'; |
20 | import { useDrawer } from '/@/components/Drawer'; | 13 | import { useDrawer } from '/@/components/Drawer'; |
14 | + import { BasicCardList, useCardList } from '/@/components/CardList'; | ||
21 | 15 | ||
22 | const [registerModal, { openModal }] = useModal(); | 16 | const [registerModal, { openModal }] = useModal(); |
23 | const [registerDrawer, { openDrawer }] = useDrawer(); | 17 | const [registerDrawer, { openDrawer }] = useDrawer(); |
24 | const [registerRunTaskModal, { openModal: openRunTaskModal }] = useModal(); | 18 | const [registerRunTaskModal, { openModal: openRunTaskModal }] = useModal(); |
25 | 19 | ||
26 | - const [registerForm, { getFieldsValue }] = useForm({ | ||
27 | - schemas: formSchemas, | ||
28 | - baseColProps: { span: 8 }, | ||
29 | - compact: true, | ||
30 | - showAdvancedButton: true, | ||
31 | - labelWidth: 100, | ||
32 | - submitFunc: async () => { | ||
33 | - getDataSource(); | 20 | + const [registerCardList, { reload }] = useCardList({ |
21 | + title: '任务列表', | ||
22 | + api: getTaskCenterList, | ||
23 | + baseLayout: { col: 4, row: 4 }, | ||
24 | + useSearchForm: true, | ||
25 | + formConfig: { | ||
26 | + schemas: formSchemas, | ||
27 | + labelWidth: 100, | ||
28 | + baseColProps: { span: 8 }, | ||
34 | }, | 29 | }, |
35 | - resetFunc: async () => { | ||
36 | - getDataSource({}); | 30 | + beforeFetch: async (params: Recordable) => { |
31 | + return { ...params }; | ||
37 | }, | 32 | }, |
38 | }); | 33 | }); |
39 | 34 | ||
40 | - const paginationChange = (page: number, pageSize: number) => { | ||
41 | - pagination.current = page - 1 * pageSize > pagination.total ? 1 : page; | ||
42 | - pagination.pageSize = pageSize; | ||
43 | - getDataSource(); | ||
44 | - }; | ||
45 | - | ||
46 | - const pagination = reactive({ | ||
47 | - current: 1, | ||
48 | - total: 0, | ||
49 | - pageSize: 10, | ||
50 | - size: 'small', | ||
51 | - showSizeChanger: false, | ||
52 | - showTotal: (total: number) => `共 ${total} 条数据`, | ||
53 | - onChange: paginationChange, | ||
54 | - onShowSizeChange: paginationChange, | ||
55 | - }); | ||
56 | - | ||
57 | - const dataSource = ref<TaskRecordType[]>([]); | ||
58 | - const loading = ref(false); | ||
59 | - const getDataSource = async (params?: Recordable) => { | ||
60 | - try { | ||
61 | - loading.value = true; | ||
62 | - params = params || getFieldsValue(); | ||
63 | - const { items, total } = await getTaskCenterList({ | ||
64 | - page: pagination.current, | ||
65 | - pageSize: pagination.pageSize, | ||
66 | - ...params, | ||
67 | - }); | ||
68 | - dataSource.value = items; | ||
69 | - pagination.total = total; | ||
70 | - } catch (error) { | ||
71 | - throw error; | ||
72 | - } finally { | ||
73 | - loading.value = false; | ||
74 | - } | ||
75 | - }; | ||
76 | - | ||
77 | const handleEdit = (record: TaskRecordType) => { | 35 | const handleEdit = (record: TaskRecordType) => { |
78 | openModal(true, { | 36 | openModal(true, { |
79 | record, | 37 | record, |
@@ -88,95 +46,35 @@ | @@ -88,95 +46,35 @@ | ||
88 | const handleDetail = (record: TaskRecordType) => { | 46 | const handleDetail = (record: TaskRecordType) => { |
89 | openDrawer(true, { mode: DataActionModeEnum.READ, record } as ModalParamsType); | 47 | openDrawer(true, { mode: DataActionModeEnum.READ, record } as ModalParamsType); |
90 | }; | 48 | }; |
91 | - | ||
92 | - const reload = () => getDataSource(); | ||
93 | - | ||
94 | - const listElRef = ref<Nullable<ComponentElRef>>(null); | ||
95 | - | ||
96 | - const setListHeight = () => { | ||
97 | - const clientHeight = document.documentElement.clientHeight; | ||
98 | - const rect = getBoundingClientRect(unref(listElRef)!.$el!) as DOMRect; | ||
99 | - // margin-top 24 height 24 | ||
100 | - const paginationHeight = 24 + 24 + 8; | ||
101 | - // list pading top 8 maring-top 8 extra slot 56 | ||
102 | - const listContainerMarginBottom = 8 + 8 + 72; | ||
103 | - const listContainerHeight = | ||
104 | - clientHeight - rect.top - paginationHeight - listContainerMarginBottom; | ||
105 | - const listContainerEl = (unref(listElRef)!.$el as HTMLElement).querySelector( | ||
106 | - '.ant-spin-container' | ||
107 | - ) as HTMLElement; | ||
108 | - listContainerEl && | ||
109 | - (listContainerEl.style.height = listContainerHeight + 'px') && | ||
110 | - (listContainerEl.style.overflowY = 'auto') && | ||
111 | - (listContainerEl.style.overflowX = 'hidden'); | ||
112 | - }; | ||
113 | - | ||
114 | - onMounted(() => { | ||
115 | - setListHeight(); | ||
116 | - getDataSource(); | ||
117 | - }); | ||
118 | </script> | 49 | </script> |
119 | 50 | ||
120 | <template> | 51 | <template> |
121 | - <PageWrapper class="task-center-container"> | ||
122 | - <section | ||
123 | - class="bg-light-50 flex p-4 justify-between items-center dark:text-gray-300 dark:bg-dark-900" | ||
124 | - > | ||
125 | - <div class="text-2xl">任务中心</div> | ||
126 | - <Authority :value="PermissionEnum.CREATE"> | ||
127 | - <Button | ||
128 | - type="primary" | ||
129 | - @click="openModal(true, { mode: DataActionModeEnum.CREATE } as ModalParamsType)" | ||
130 | - > | ||
131 | - 创建任务 | ||
132 | - </Button> | ||
133 | - </Authority> | ||
134 | - </section> | ||
135 | - <section | ||
136 | - class="form-container bg-light-50 px-4 pt-4 mt-4 x dark:text-gray-300 dark:bg-dark-900" | ||
137 | - > | ||
138 | - <BasicForm @register="registerForm" /> | ||
139 | - </section> | ||
140 | - <section class="bg-light-50 mt-4 p-4 dark:text-gray-300 dark:bg-dark-900"> | ||
141 | - <List | ||
142 | - ref="listElRef" | ||
143 | - :dataSource="dataSource" | ||
144 | - :pagination="pagination" | ||
145 | - :grid="{ gutter: 16, xs: 1, sm: 1, md: 2, lg: 3, xl: 3, xxl: 4, column: 4 }" | ||
146 | - :loading="loading" | ||
147 | - > | ||
148 | - <template #header> | ||
149 | - <section class="flex justify-between gap-4 min-h-12 items-center"> | ||
150 | - <div> | ||
151 | - <span class="text-lg font-medium">任务列表</span> | ||
152 | - </div> | ||
153 | - <Space> | ||
154 | - <!-- <CardLayoutButton v-model:value="colNumber" :max="4" :min="1" @change="reload" /> --> | ||
155 | - <Tooltip v-if="dataSource.length" title="刷新"> | ||
156 | - <Button type="primary" @click="getDataSource"> | ||
157 | - <ReloadOutlined :spin="loading" /> | ||
158 | - </Button> | ||
159 | - </Tooltip> | ||
160 | - </Space> | ||
161 | - </section> | ||
162 | - </template> | ||
163 | - <template #renderItem="{ item }"> | ||
164 | - <List.Item :key="item.id"> | ||
165 | - <TaskCard | ||
166 | - :record="item" | ||
167 | - :reload="reload" | ||
168 | - @runTask="handleRunTask" | ||
169 | - @detail="handleDetail" | ||
170 | - @edit="handleEdit" | ||
171 | - /> | ||
172 | - </List.Item> | ||
173 | - </template> | ||
174 | - </List> | ||
175 | - </section> | 52 | + <section> |
53 | + <BasicCardList class="flex-auto w-full" @register="registerCardList"> | ||
54 | + <template #toolbar> | ||
55 | + <Authority :value="PermissionEnum.CREATE"> | ||
56 | + <Button | ||
57 | + type="primary" | ||
58 | + @click="openModal(true, { mode: DataActionModeEnum.CREATE } as ModalParamsType)" | ||
59 | + > | ||
60 | + 创建任务 | ||
61 | + </Button> | ||
62 | + </Authority> | ||
63 | + </template> | ||
64 | + <template #renderItem="{ item }"> | ||
65 | + <TaskCard | ||
66 | + :record="item" | ||
67 | + :reload="reload" | ||
68 | + @runTask="handleRunTask" | ||
69 | + @detail="handleDetail" | ||
70 | + @edit="handleEdit" | ||
71 | + /> | ||
72 | + </template> | ||
73 | + </BasicCardList> | ||
176 | <DetailDrawer @register="registerDrawer" /> | 74 | <DetailDrawer @register="registerDrawer" /> |
177 | <DetailModal @register="registerModal" :reload="reload" /> | 75 | <DetailModal @register="registerModal" :reload="reload" /> |
178 | <RunTaskModal :reload="reload" @register="registerRunTaskModal" /> | 76 | <RunTaskModal :reload="reload" @register="registerRunTaskModal" /> |
179 | - </PageWrapper> | 77 | + </section> |
180 | </template> | 78 | </template> |
181 | 79 | ||
182 | <style lang="less" scoped> | 80 | <style lang="less" scoped> |
@@ -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 | } |