Commit 742b336a8b043232f3ed1e49b4b841c6bfe949a9

Authored by loveumiko
2 parents b68289fb 37b5417e

Merge branch 'main_dev' of http://git.yunteng.com/yunteng/thingskit-front into fix/rule-chain-field

@@ -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 }