Showing
7 changed files
with
481 additions
and
791 deletions
src/components/Form/src/components/TransferTableModal.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | ||
| 2 | - import { Button, Tabs, Badge, ButtonProps, Tag } from 'ant-design-vue'; | ||
| 3 | - import { get, isFunction, set, uniqBy } from 'lodash-es'; | ||
| 4 | - import { ExtractPropTypes, computed, unref, ref, nextTick, onMounted, watch } from 'vue'; | ||
| 5 | - import { DynamicProps } from '/#/utils'; | ||
| 6 | - import { BasicModal, useModal } from '/@/components/Modal'; | ||
| 7 | - import { BasicTable, BasicTableProps, TableRowSelection, useTable } from '/@/components/Table'; | ||
| 8 | - import { FETCH_SETTING } from '/@/components/Table/src/const'; | ||
| 9 | - import { useDesign } from '/@/hooks/web/useDesign'; | ||
| 10 | - | ||
| 11 | - interface Options extends Recordable { | ||
| 12 | - primaryKey?: string; | ||
| 13 | - disabled?: boolean; | ||
| 14 | - } | ||
| 15 | - | ||
| 16 | - enum Active { | ||
| 17 | - PENDING = 'pending', | ||
| 18 | - SELECTED = 'selected', | ||
| 19 | - } | ||
| 20 | - | ||
| 21 | - interface ActionType { | ||
| 22 | - setSelectedOptions: (options: Recordable[]) => void; | ||
| 23 | - } | ||
| 24 | - | ||
| 25 | - const emit = defineEmits(['change', 'update:value']); | ||
| 26 | - | ||
| 27 | - const props = withDefaults( | ||
| 28 | - defineProps<{ | ||
| 29 | - value?: string[]; | ||
| 30 | - labelField?: string; | ||
| 31 | - valueField?: string; | ||
| 32 | - primaryKey?: string; | ||
| 33 | - params?: Recordable; | ||
| 34 | - buttonName?: string; | ||
| 35 | - pendingTableProps?: BasicTableProps; | ||
| 36 | - selectedTableProps?: BasicTableProps; | ||
| 37 | - maxTagLength?: number; | ||
| 38 | - modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; | ||
| 39 | - buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>; | ||
| 40 | - initSelectedOptions?: (actionType: ActionType) => Promise<Recordable[]>; | ||
| 41 | - transformValue?: (selectedRowKeys: string[], selectedRows: Options[]) => any[]; | ||
| 42 | - onValueChange?: (selectedRowkeys: string[]) => any[]; | ||
| 43 | - onRemoveAfter?: (actionType: ActionType) => Promise<any>; | ||
| 44 | - onSelectedAfter?: (actionType: ActionType) => Promise<any>; | ||
| 45 | - }>(), | ||
| 46 | - { | ||
| 47 | - buttonName: '选择设备', | ||
| 48 | - primaryKey: 'id', | ||
| 49 | - maxTagLength: 2, | ||
| 50 | - labelField: 'label', | ||
| 51 | - valueField: 'value', | ||
| 52 | - } | ||
| 53 | - ); | ||
| 54 | - | ||
| 55 | - const { prefixCls } = useDesign('transfer-table-modal'); | ||
| 56 | - | ||
| 57 | - const activeKey = ref<Active>(Active.PENDING); | ||
| 58 | - | ||
| 59 | - const selectedRows = ref<Options[]>([]); | ||
| 60 | - | ||
| 61 | - const selectedRowKeys = ref<string[]>(props.value || []); | ||
| 62 | - | ||
| 63 | - const pendingOptions = ref<Options[]>([]); | ||
| 64 | - | ||
| 65 | - const selectedConfirmQueue = ref<Options[]>([]); | ||
| 66 | - | ||
| 67 | - const pendingConfirmQueue = ref<Options[]>([]); | ||
| 68 | - | ||
| 69 | - const selectedTotal = ref(0); | ||
| 70 | - | ||
| 71 | - const pendingTotal = ref(0); | ||
| 72 | - | ||
| 73 | - const getFetchSetting = computed(() => { | ||
| 74 | - const { pendingTableProps } = props; | ||
| 75 | - return pendingTableProps?.fetchSetting || FETCH_SETTING; | ||
| 76 | - }); | ||
| 77 | - | ||
| 78 | - const getPendingRowSelection = computed<TableRowSelection>(() => { | ||
| 79 | - const rowKeys = unref(selectedRowKeys); | ||
| 80 | - return { | ||
| 81 | - type: 'checkbox', | ||
| 82 | - getCheckboxProps: (record: Recordable) => { | ||
| 83 | - const { primaryKey } = props; | ||
| 84 | - return { | ||
| 85 | - ...record, | ||
| 86 | - disabled: rowKeys.includes(record[primaryKey]), | ||
| 87 | - }; | ||
| 88 | - }, | ||
| 89 | - onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => { | ||
| 90 | - pendingConfirmQueue.value = selectedRows; | ||
| 91 | - }, | ||
| 92 | - onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => { | ||
| 93 | - pendingConfirmQueue.value = selectedRows; | ||
| 94 | - }, | ||
| 95 | - }; | ||
| 96 | - }); | ||
| 97 | - | ||
| 98 | - const getPendingTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => { | ||
| 99 | - const { pendingTableProps, primaryKey } = props; | ||
| 100 | - return { | ||
| 101 | - ...pendingTableProps, | ||
| 102 | - rowKey: primaryKey, | ||
| 103 | - api: handlePendingApiIntercept, | ||
| 104 | - clickToRowSelect: false, | ||
| 105 | - rowSelection: getPendingRowSelection, | ||
| 106 | - }; | ||
| 107 | - }); | ||
| 108 | - | ||
| 109 | - const getSelectedTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => { | ||
| 110 | - const { selectedTableProps, primaryKey } = props; | ||
| 111 | - return { | ||
| 112 | - ...selectedTableProps, | ||
| 113 | - dataSource: selectedRows, | ||
| 114 | - clickToRowSelect: false, | ||
| 115 | - rowKey: primaryKey, | ||
| 116 | - api: selectedTableProps!.api ? handleSelectedApiIntercept : undefined, | ||
| 117 | - rowSelection: { | ||
| 118 | - type: 'checkbox', | ||
| 119 | - onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => { | ||
| 120 | - selectedConfirmQueue.value = selectedRows; | ||
| 121 | - }, | ||
| 122 | - onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => { | ||
| 123 | - selectedConfirmQueue.value = selectedRows; | ||
| 124 | - }, | ||
| 125 | - }, | ||
| 126 | - }; | ||
| 127 | - }); | ||
| 128 | - | ||
| 129 | - const getModalBindProps = computed(() => { | ||
| 130 | - const { modalProps = {} } = props; | ||
| 131 | - return { | ||
| 132 | - width: '60%', | ||
| 133 | - title: '穿梭表格', | ||
| 134 | - wrapClassName: prefixCls, | ||
| 135 | - ...modalProps, | ||
| 136 | - showOkBtn: false, | ||
| 137 | - }; | ||
| 138 | - }); | ||
| 139 | - | ||
| 140 | - const getBindButtonProps = computed<ButtonProps>(() => { | ||
| 141 | - const { buttonProps = {} } = props; | ||
| 142 | - return { | ||
| 143 | - type: 'link', | ||
| 144 | - ...buttonProps, | ||
| 145 | - }; | ||
| 146 | - }); | ||
| 147 | - | ||
| 148 | - const getShowTagOptions = computed(() => { | ||
| 149 | - const { maxTagLength } = props; | ||
| 150 | - return unref(selectedRows).slice(0, maxTagLength); | ||
| 151 | - }); | ||
| 152 | - | ||
| 153 | - const getSurplusOptionsLength = computed(() => { | ||
| 154 | - const { maxTagLength } = props; | ||
| 155 | - const surplusValue = unref(selectedRows).length - maxTagLength; | ||
| 156 | - return surplusValue < 0 ? 0 : surplusValue; | ||
| 157 | - }); | ||
| 158 | - | ||
| 159 | - const [registerModal, { openModal }] = useModal(); | ||
| 160 | - | ||
| 161 | - const [ | ||
| 162 | - regsterPendingTable, | ||
| 163 | - { | ||
| 164 | - getSelectRows: getPendingSelectRows, | ||
| 165 | - getSelectRowKeys: getPendingSelectRowKeys, | ||
| 166 | - reload: reloadPending, | ||
| 167 | - clearSelectedRowKeys: clearPendingSelectedRowKeys, | ||
| 168 | - }, | ||
| 169 | - ] = useTable(unref(getPendingTableBindProps)); | ||
| 170 | - | ||
| 171 | - const [ | ||
| 172 | - registerSelectedTable, | ||
| 173 | - { getSelectRowKeys, setProps, clearSelectedRowKeys, reload: reloadSelected }, | ||
| 174 | - ] = useTable(unref(getSelectedTableBindProps)); | ||
| 175 | - | ||
| 176 | - async function handlePendingApiIntercept(params?: Recordable) { | ||
| 177 | - try { | ||
| 178 | - const { api } = props.pendingTableProps || {}; | ||
| 179 | - if (api && isFunction(api)) { | ||
| 180 | - let options = await api(params); | ||
| 181 | - pendingOptions.value = options; | ||
| 182 | - const { totalField, listField } = unref(getFetchSetting); | ||
| 183 | - const total = get(options, totalField!); | ||
| 184 | - if (unref(selectedTotal) + unref(pendingTotal) !== total) { | ||
| 185 | - pendingTotal.value = total; | ||
| 186 | - } | ||
| 187 | - let list: Recordable[] = get(options, listField!); | ||
| 188 | - list = getSelectedRows(list); | ||
| 189 | - options = set(options, listField!, list); | ||
| 190 | - return options; | ||
| 191 | - } | ||
| 192 | - } catch (error) { | ||
| 193 | - console.error(error); | ||
| 194 | - return []; | ||
| 195 | - } | ||
| 196 | - return []; | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - async function handleSelectedApiIntercept(params?: Recordable) { | ||
| 200 | - try { | ||
| 201 | - const { api } = props.selectedTableProps || {}; | ||
| 202 | - if (api && isFunction(api)) { | ||
| 203 | - let options = await api(params); | ||
| 204 | - pendingOptions.value = options; | ||
| 205 | - const { totalField, listField } = unref(getFetchSetting); | ||
| 206 | - selectedTotal.value = get(options, totalField!); | ||
| 207 | - let list: Recordable[] = get(options, listField!); | ||
| 208 | - list = getSelectedRows(list); | ||
| 209 | - options = set(options, listField!, list); | ||
| 210 | - return options; | ||
| 211 | - } | ||
| 212 | - } catch (error) { | ||
| 213 | - console.error(error); | ||
| 214 | - return []; | ||
| 215 | - } | ||
| 216 | - return []; | ||
| 217 | - } | ||
| 218 | - | ||
| 219 | - const handleOpenModal = async () => { | ||
| 220 | - openModal(true); | ||
| 221 | - await nextTick(); | ||
| 222 | - if (props.value && !props.value.length) { | ||
| 223 | - activeKey.value = Active.PENDING; | ||
| 224 | - reloadPending(); | ||
| 225 | - } | ||
| 226 | - }; | ||
| 227 | - | ||
| 228 | - const handleTriggerEmit = (selectedRowKeys: string[], selectedRows: Options[]) => { | ||
| 229 | - const { transformValue } = props; | ||
| 230 | - let value = selectedRowKeys; | ||
| 231 | - if (transformValue && isFunction(transformValue)) { | ||
| 232 | - value = transformValue(selectedRowKeys, selectedRows); | ||
| 233 | - } | ||
| 234 | - emit('change', unref(selectedRowKeys), unref(selectedRows)); | ||
| 235 | - emit('update:value', unref(value)); | ||
| 236 | - }; | ||
| 237 | - | ||
| 238 | - const handleSelected = async () => { | ||
| 239 | - const { onSelectedAfter } = props; | ||
| 240 | - const currentPageSelectRows = getPendingSelectRows(); | ||
| 241 | - const currentPageSelectRowKeys = getPendingSelectRowKeys(); | ||
| 242 | - const { primaryKey } = props; | ||
| 243 | - selectedRows.value = uniqBy([...unref(selectedRows), ...currentPageSelectRows], primaryKey); | ||
| 244 | - selectedRowKeys.value = [...new Set([...unref(selectedRowKeys), ...currentPageSelectRowKeys])]; | ||
| 245 | - pendingConfirmQueue.value = []; | ||
| 246 | - // selectedTotal.value = unref(selectedRowKeys).length; | ||
| 247 | - pendingTotal.value = unref(pendingTotal) - currentPageSelectRows.length; | ||
| 248 | - selectedTotal.value = unref(selectedTotal) + currentPageSelectRows.length; | ||
| 249 | - | ||
| 250 | - clearPendingSelectedRowKeys(); | ||
| 251 | - handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows)); | ||
| 252 | - | ||
| 253 | - if (onSelectedAfter && isFunction(onSelectedAfter)) { | ||
| 254 | - await onSelectedAfter(actionType); | ||
| 255 | - } | ||
| 256 | - reloadPending(); | ||
| 257 | - }; | ||
| 258 | - | ||
| 259 | - const handleRemoveSelected = async () => { | ||
| 260 | - const { onRemoveAfter } = props; | ||
| 261 | - const removeRowKeys = getSelectRowKeys(); | ||
| 262 | - selectedRowKeys.value = unref(selectedRowKeys).filter((key) => !removeRowKeys.includes(key)); | ||
| 263 | - selectedRows.value = unref(selectedRows).filter((item) => { | ||
| 264 | - const { primaryKey } = props; | ||
| 265 | - return unref(selectedRowKeys).includes(item[primaryKey]); | ||
| 266 | - }); | ||
| 267 | - pendingTotal.value = unref(pendingTotal) + removeRowKeys.length; | ||
| 268 | - selectedTotal.value = unref(selectedTotal) - removeRowKeys.length; | ||
| 269 | - | ||
| 270 | - clearSelectedRowKeys(); | ||
| 271 | - selectedConfirmQueue.value = []; | ||
| 272 | - setProps({ dataSource: unref(selectedRows) }); | ||
| 273 | - handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows)); | ||
| 274 | - | ||
| 275 | - if (onRemoveAfter && isFunction(onRemoveAfter)) { | ||
| 276 | - await onRemoveAfter(actionType); | ||
| 277 | - } | ||
| 278 | - }; | ||
| 279 | - | ||
| 280 | - const actionType = { | ||
| 281 | - setSelectedOptions, | ||
| 282 | - setSelectedTotal, | ||
| 283 | - reloadPending, | ||
| 284 | - reloadSelected, | ||
| 285 | - }; | ||
| 286 | - | ||
| 287 | - const getSelectedRows = (options: Recordable[]) => { | ||
| 288 | - const { labelField, valueField } = props; | ||
| 289 | - return options.map((item) => ({ ...item, label: item[labelField], value: item[valueField] })); | ||
| 290 | - }; | ||
| 291 | - | ||
| 292 | - const getSelectedKeys = (options: Recordable[]) => { | ||
| 293 | - const { primaryKey } = props; | ||
| 294 | - return options.map((item) => item[primaryKey]); | ||
| 295 | - }; | ||
| 296 | - | ||
| 297 | - function setSelectedOptions(options: Recordable[]) { | ||
| 298 | - selectedRows.value = getSelectedRows(options); | ||
| 299 | - selectedRowKeys.value = getSelectedKeys(options); | ||
| 300 | - } | ||
| 301 | - | ||
| 302 | - function setSelectedTotal(number: number) { | ||
| 303 | - selectedTotal.value = number; | ||
| 304 | - } | ||
| 305 | - | ||
| 306 | - const handleCheckoutPanel = async (keys: Active) => { | ||
| 307 | - await nextTick(); | ||
| 308 | - if (keys === Active.PENDING) { | ||
| 309 | - reloadPending(); | ||
| 310 | - } else { | ||
| 311 | - reloadSelected(); | ||
| 312 | - setProps({ | ||
| 313 | - dataSource: unref(selectedRows), | ||
| 314 | - }); | ||
| 315 | - } | ||
| 316 | - }; | ||
| 317 | - | ||
| 318 | - watch( | ||
| 319 | - () => props.value, | ||
| 320 | - () => { | ||
| 321 | - if (props.value && !props.value.length) { | ||
| 322 | - selectedRowKeys.value = []; | ||
| 323 | - selectedRows.value = []; | ||
| 324 | - // pendingTotal.value = 0; | ||
| 325 | - selectedTotal.value = 0; | ||
| 326 | - } | ||
| 327 | - } | ||
| 328 | - ); | ||
| 329 | - | ||
| 330 | - onMounted(async () => { | ||
| 331 | - const { initSelectedOptions } = props; | ||
| 332 | - if (initSelectedOptions && isFunction(initSelectedOptions)) { | ||
| 333 | - const options = await initSelectedOptions(actionType); | ||
| 334 | - setSelectedOptions(options); | ||
| 335 | - } | ||
| 336 | - }); | ||
| 337 | -</script> | ||
| 338 | - | ||
| 339 | -<template> | ||
| 340 | - <section> | ||
| 341 | - <BasicModal @register="registerModal" v-bind="getModalBindProps"> | ||
| 342 | - <section class="bg-gray-100"> | ||
| 343 | - <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel"> | ||
| 344 | - <Tabs.TabPane :key="Active.PENDING"> | ||
| 345 | - <template #tab> | ||
| 346 | - <div class="flex items-center justify-center"> | ||
| 347 | - <span>待选设备</span> | ||
| 348 | - <Badge show-zero :count="pendingTotal" /> | ||
| 349 | - </div> | ||
| 350 | - </template> | ||
| 351 | - <BasicTable @register="regsterPendingTable"> | ||
| 352 | - <template #toolbar> | ||
| 353 | - <section class="flex w-full justify-end items-center"> | ||
| 354 | - <!-- <Button type="primary">全选</Button> --> | ||
| 355 | - <div class="text-blue-400"> | ||
| 356 | - <span class="mr-2">选择设备:</span> | ||
| 357 | - <span>{{ pendingConfirmQueue.length }}</span> | ||
| 358 | - </div> | ||
| 359 | - </section> | ||
| 360 | - </template> | ||
| 361 | - </BasicTable> | ||
| 362 | - <section class="flex justify-end px-4 pb-4"> | ||
| 363 | - <Button | ||
| 364 | - type="primary" | ||
| 365 | - @click="handleSelected" | ||
| 366 | - :disabled="!pendingConfirmQueue.length" | ||
| 367 | - > | ||
| 368 | - <span>确定已选</span> | ||
| 369 | - </Button> | ||
| 370 | - </section> | ||
| 371 | - </Tabs.TabPane> | ||
| 372 | - <Tabs.TabPane :key="Active.SELECTED"> | ||
| 373 | - <template #tab> | ||
| 374 | - <div class="flex items-center justify-center"> | ||
| 375 | - <span>已选设备</span> | ||
| 376 | - <Badge show-zero :count="selectedTotal" /> | ||
| 377 | - </div> | ||
| 378 | - </template> | ||
| 379 | - <BasicTable @register="registerSelectedTable"> | ||
| 380 | - <template #toolbar> | ||
| 381 | - <section class="flex w-full justify-end items-center"> | ||
| 382 | - <!-- <Button type="primary">全选</Button> --> | ||
| 383 | - <div class="text-blue-400"> | ||
| 384 | - <span class="mr-2">选择设备:</span> | ||
| 385 | - <span>{{ selectedConfirmQueue.length }}</span> | ||
| 386 | - </div> | ||
| 387 | - </section> | ||
| 388 | - </template> | ||
| 389 | - </BasicTable> | ||
| 390 | - <section class="flex justify-end px-4 pb-4"> | ||
| 391 | - <Button | ||
| 392 | - type="primary" | ||
| 393 | - :disabled="!selectedConfirmQueue.length" | ||
| 394 | - @click="handleRemoveSelected" | ||
| 395 | - > | ||
| 396 | - <span>移除已选</span> | ||
| 397 | - </Button> | ||
| 398 | - </section> | ||
| 399 | - </Tabs.TabPane> | ||
| 400 | - </Tabs> | ||
| 401 | - </section> | ||
| 402 | - </BasicModal> | ||
| 403 | - <Button @click="handleOpenModal" v-bind="getBindButtonProps"> | ||
| 404 | - <span v-if="!selectedRowKeys.length">选择设备</span> | ||
| 405 | - <div v-if="selectedRowKeys.length"> | ||
| 406 | - <Tag | ||
| 407 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
| 408 | - v-for="item in getShowTagOptions" | ||
| 409 | - :key="item.value" | ||
| 410 | - > | ||
| 411 | - <span> | ||
| 412 | - {{ item.alias || item.name }} | ||
| 413 | - </span> | ||
| 414 | - </Tag> | ||
| 415 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
| 416 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
| 417 | - </Tag> | ||
| 418 | - </div> | ||
| 419 | - </Button> | ||
| 420 | - </section> | ||
| 421 | -</template> | ||
| 422 | - | ||
| 423 | -<style lang="less"> | ||
| 424 | - @prefix-cls: ~'@{namespace}-transfer-table-modal'; | ||
| 425 | - | ||
| 426 | - .@{prefix-cls} { | ||
| 427 | - .vben-basic-table { | ||
| 428 | - padding-top: 0; | ||
| 429 | - } | ||
| 430 | - | ||
| 431 | - .vben-basic-form > .ant-row { | ||
| 432 | - width: 100%; | ||
| 433 | - } | ||
| 434 | - | ||
| 435 | - .ant-tabs-top-bar { | ||
| 436 | - background-color: #fff; | ||
| 437 | - } | ||
| 438 | - } | ||
| 439 | -</style> |
| @@ -61,7 +61,6 @@ | @@ -61,7 +61,6 @@ | ||
| 61 | //input动态数据 | 61 | //input动态数据 |
| 62 | const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({ params: [] }); | 62 | const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({ params: [] }); |
| 63 | 63 | ||
| 64 | - console.log(dynamicInput, 'dynamicInput'); | ||
| 65 | //删除Input | 64 | //删除Input |
| 66 | const remove = (item: Params) => { | 65 | const remove = (item: Params) => { |
| 67 | let index = dynamicInput.params.indexOf(item); | 66 | let index = dynamicInput.params.indexOf(item); |
| @@ -37,7 +37,7 @@ | @@ -37,7 +37,7 @@ | ||
| 37 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 37 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
| 38 | import { add } from '/@/components/Form/src/componentMap'; | 38 | import { add } from '/@/components/Form/src/componentMap'; |
| 39 | import TransferModal from '/@/components/Form/src/components/TransferModal.vue'; | 39 | import TransferModal from '/@/components/Form/src/components/TransferModal.vue'; |
| 40 | - import TransferTableModal from '/@/components/Form/src/components/TransferTableModal.vue'; | 40 | + import TransferTableModal from './TransferTableModal.vue'; |
| 41 | import { DataFlowMethod, DataFlowParams } from './index'; | 41 | import { DataFlowMethod, DataFlowParams } from './index'; |
| 42 | import { stepConfig, removeFieldByModeForm } from './config'; | 42 | import { stepConfig, removeFieldByModeForm } from './config'; |
| 43 | import { postAddConvertApi } from '/@/api/datamanager/dataManagerApi'; | 43 | import { postAddConvertApi } from '/@/api/datamanager/dataManagerApi'; |
| 1 | +import { h, unref } from 'vue'; | ||
| 2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
| 3 | +import { FETCH_SETTING } from '/@/components/Table/src/const'; | ||
| 4 | +import { DeviceStatusEnum, DeviceStatusNameEnum, DeviceTypeNameEnum } from './enum'; | ||
| 5 | +import { Tag } from 'ant-design-vue'; | ||
| 6 | +import { BasicColumn, BasicTableProps, FormSchema } from '/@/components/Table'; | ||
| 7 | +import { useClipboard } from '@vueuse/core'; | ||
| 8 | +import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 9 | + | ||
| 10 | +export const deviceTableFormSchema: FormSchema[] = [ | ||
| 11 | + { | ||
| 12 | + field: 'name', | ||
| 13 | + label: '设备名称', | ||
| 14 | + component: 'Input', | ||
| 15 | + colProps: { span: 9 }, | ||
| 16 | + componentProps: { | ||
| 17 | + placeholder: '请输入设备名称', | ||
| 18 | + }, | ||
| 19 | + }, | ||
| 20 | + { | ||
| 21 | + field: 'deviceType', | ||
| 22 | + label: '设备类型', | ||
| 23 | + component: 'ApiSelect', | ||
| 24 | + colProps: { span: 9 }, | ||
| 25 | + componentProps: { | ||
| 26 | + placeholder: '请选择设备类型', | ||
| 27 | + api: findDictItemByCode, | ||
| 28 | + params: { | ||
| 29 | + dictCode: 'device_type', | ||
| 30 | + }, | ||
| 31 | + labelField: 'itemText', | ||
| 32 | + valueField: 'itemValue', | ||
| 33 | + }, | ||
| 34 | + }, | ||
| 35 | +]; | ||
| 36 | + | ||
| 37 | +const { copied, copy } = useClipboard({ legacy: true }); | ||
| 38 | + | ||
| 39 | +const { createMessage } = useMessage(); | ||
| 40 | + | ||
| 41 | +export const deviceTableColumn: BasicColumn[] = [ | ||
| 42 | + { | ||
| 43 | + title: '状态', | ||
| 44 | + dataIndex: 'deviceState', | ||
| 45 | + customRender: ({ text }) => { | ||
| 46 | + return h( | ||
| 47 | + Tag, | ||
| 48 | + { | ||
| 49 | + color: | ||
| 50 | + text === DeviceStatusEnum.INACTIVE | ||
| 51 | + ? 'warning' | ||
| 52 | + : text === DeviceStatusEnum.OFFLINE | ||
| 53 | + ? 'error' | ||
| 54 | + : 'success', | ||
| 55 | + }, | ||
| 56 | + () => DeviceStatusNameEnum[text] | ||
| 57 | + ); | ||
| 58 | + }, | ||
| 59 | + }, | ||
| 60 | + { | ||
| 61 | + title: '别名/设备名称', | ||
| 62 | + dataIndex: 'name', | ||
| 63 | + customRender: ({ record }) => { | ||
| 64 | + return h('div', [ | ||
| 65 | + h( | ||
| 66 | + 'div', | ||
| 67 | + { | ||
| 68 | + class: 'cursor-pointer', | ||
| 69 | + onClick: async () => { | ||
| 70 | + await copy(record.name); | ||
| 71 | + if (unref(copied)) createMessage.success('复制成功~'); | ||
| 72 | + }, | ||
| 73 | + }, | ||
| 74 | + [ | ||
| 75 | + record.alias && h('div', { class: 'truncate' }, record.alias), | ||
| 76 | + h('div', { class: 'text-blue-400 truncate' }, record.name), | ||
| 77 | + ] | ||
| 78 | + ), | ||
| 79 | + ]); | ||
| 80 | + }, | ||
| 81 | + }, | ||
| 82 | + { | ||
| 83 | + title: '设备类型', | ||
| 84 | + dataIndex: 'deviceType', | ||
| 85 | + customRender: ({ text }) => { | ||
| 86 | + return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]); | ||
| 87 | + }, | ||
| 88 | + }, | ||
| 89 | + { | ||
| 90 | + title: '所属产品', | ||
| 91 | + dataIndex: 'deviceProfile.name', | ||
| 92 | + }, | ||
| 93 | + { | ||
| 94 | + title: '所属组织', | ||
| 95 | + dataIndex: 'organizationDTO.name', | ||
| 96 | + }, | ||
| 97 | +]; | ||
| 98 | + | ||
| 99 | +export const TransferTableProps: BasicTableProps = { | ||
| 100 | + formConfig: { | ||
| 101 | + layout: 'inline', | ||
| 102 | + labelWidth: 80, | ||
| 103 | + schemas: deviceTableFormSchema, | ||
| 104 | + actionColOptions: { span: 6 }, | ||
| 105 | + }, | ||
| 106 | + size: 'small', | ||
| 107 | + maxHeight: 240, | ||
| 108 | + useSearchForm: true, | ||
| 109 | + columns: deviceTableColumn, | ||
| 110 | + showIndexColumn: false, | ||
| 111 | + fetchSetting: FETCH_SETTING, | ||
| 112 | +} as BasicTableProps; |
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { Button, Tabs, Tag } from 'ant-design-vue'; | ||
| 3 | + import { remove, uniqBy, cloneDeep } from 'lodash'; | ||
| 4 | + import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue'; | ||
| 5 | + import { | ||
| 6 | + deviceTableColumn, | ||
| 7 | + deviceTableFormSchema, | ||
| 8 | + TransferTableProps, | ||
| 9 | + } from './TransferTableModal.config'; | ||
| 10 | + import { devicePage } from '/@/api/device/deviceManager'; | ||
| 11 | + import { DeviceModel as RawDeviceModal } from '/@/api/device/model/deviceModel'; | ||
| 12 | + import { BasicModal, useModal } from '/@/components/Modal'; | ||
| 13 | + import { BasicTable, useTable } from '/@/components/Table'; | ||
| 14 | + import { FETCH_SETTING } from '/@/components/Table/src/const'; | ||
| 15 | + import { useDesign } from '/@/hooks/web/useDesign'; | ||
| 16 | + import { isFunction } from '/@/utils/is'; | ||
| 17 | + | ||
| 18 | + interface DeviceModel extends RawDeviceModal { | ||
| 19 | + disabled?: boolean; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + const props = withDefaults( | ||
| 23 | + defineProps<{ | ||
| 24 | + getPendingTableParams: (params: Recordable) => any; | ||
| 25 | + getSelectedTableParams: (params: Recordable) => any; | ||
| 26 | + value?: (Recordable & DeviceModel)[]; | ||
| 27 | + maxTagLength?: number; | ||
| 28 | + openModalValidate?: () => boolean; | ||
| 29 | + primaryKey?: string; | ||
| 30 | + transformValue?: (list: Recordable[]) => any; | ||
| 31 | + }>(), | ||
| 32 | + { | ||
| 33 | + value: () => [], | ||
| 34 | + maxTagLength: 2, | ||
| 35 | + primaryKey: 'tbDeviceId', | ||
| 36 | + } | ||
| 37 | + ); | ||
| 38 | + | ||
| 39 | + const emit = defineEmits(['update:value']); | ||
| 40 | + | ||
| 41 | + enum Active { | ||
| 42 | + PENDING = 'pending', | ||
| 43 | + SELECTED = 'selected', | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + const activeKey = ref(Active.PENDING); | ||
| 47 | + | ||
| 48 | + const { prefixCls } = useDesign('transfer-table-modal'); | ||
| 49 | + | ||
| 50 | + const pendingTotalList = ref<DeviceModel[]>([]); | ||
| 51 | + | ||
| 52 | + const pendingConfirmQueue = ref<DeviceModel[]>([]); | ||
| 53 | + | ||
| 54 | + const selectedTotalList = ref<DeviceModel[]>([]); | ||
| 55 | + | ||
| 56 | + const selectedConfirmQueue = ref<DeviceModel[]>([]); | ||
| 57 | + | ||
| 58 | + const getShowTagOptions = computed(() => { | ||
| 59 | + const { maxTagLength } = props; | ||
| 60 | + return unref(selectedTotalList).slice(0, maxTagLength); | ||
| 61 | + }); | ||
| 62 | + | ||
| 63 | + const getSurplusOptionsLength = computed(() => { | ||
| 64 | + const { maxTagLength } = props; | ||
| 65 | + const surplusValue = unref(selectedTotalList).length - maxTagLength; | ||
| 66 | + return surplusValue < 0 ? 0 : surplusValue; | ||
| 67 | + }); | ||
| 68 | + | ||
| 69 | + // const pendingListCount = computed(() => { | ||
| 70 | + // const { value } = props; | ||
| 71 | + // const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id)); | ||
| 72 | + // return unref(pendingTotalList).length - selectedList.length; | ||
| 73 | + // }); | ||
| 74 | + | ||
| 75 | + const [registerModal, { openModal }] = useModal(); | ||
| 76 | + | ||
| 77 | + const [regsterPendingTable, pendingTableActionType] = useTable({ | ||
| 78 | + ...TransferTableProps, | ||
| 79 | + rowKey: props.primaryKey, | ||
| 80 | + api: devicePage, | ||
| 81 | + immediate: false, | ||
| 82 | + clickToRowSelect: false, | ||
| 83 | + beforeFetch: (params) => { | ||
| 84 | + const { getPendingTableParams } = props; | ||
| 85 | + const data = getPendingTableParams?.(params) || {}; | ||
| 86 | + Object.assign(params, { ...data, selected: false }); | ||
| 87 | + return params; | ||
| 88 | + }, | ||
| 89 | + afterFetch: (list: DeviceModel[]) => { | ||
| 90 | + pendingTotalList.value = list; | ||
| 91 | + return unref(pendingTotalList); | ||
| 92 | + }, | ||
| 93 | + rowSelection: { | ||
| 94 | + type: 'checkbox', | ||
| 95 | + getCheckboxProps: (record: DeviceModel) => { | ||
| 96 | + const { primaryKey } = props; | ||
| 97 | + const checked = unref(selectedTotalList).map((item) => item[primaryKey]); | ||
| 98 | + return { | ||
| 99 | + disabled: checked.includes(record[props.primaryKey]!), | ||
| 100 | + }; | ||
| 101 | + }, | ||
| 102 | + onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => { | ||
| 103 | + const { primaryKey } = props; | ||
| 104 | + const checked = unref(selectedTotalList).map((item) => item[primaryKey]); | ||
| 105 | + pendingConfirmQueue.value = selectedRows.filter( | ||
| 106 | + (item) => !checked.includes(item[primaryKey]!) | ||
| 107 | + ); | ||
| 108 | + }, | ||
| 109 | + onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => { | ||
| 110 | + const { primaryKey } = props; | ||
| 111 | + const checked = unref(selectedTotalList).map((item) => item[primaryKey]); | ||
| 112 | + pendingConfirmQueue.value = selectedRows.filter( | ||
| 113 | + (item) => !checked.includes(item[primaryKey]!) | ||
| 114 | + ); | ||
| 115 | + }, | ||
| 116 | + }, | ||
| 117 | + }); | ||
| 118 | + | ||
| 119 | + const [registerSelectedTable, selectedTableActionType] = useTable({ | ||
| 120 | + formConfig: { | ||
| 121 | + layout: 'inline', | ||
| 122 | + labelWidth: 80, | ||
| 123 | + schemas: deviceTableFormSchema, | ||
| 124 | + actionColOptions: { span: 6 }, | ||
| 125 | + }, | ||
| 126 | + size: 'small', | ||
| 127 | + maxHeight: 240, | ||
| 128 | + useSearchForm: true, | ||
| 129 | + columns: deviceTableColumn, | ||
| 130 | + api: async (params) => { | ||
| 131 | + const { name = '', deviceType = '' } = params || {}; | ||
| 132 | + const items = unref(selectedTotalList).filter((item) => { | ||
| 133 | + return ( | ||
| 134 | + item.name.toUpperCase().includes(name.toUpperCase()) && | ||
| 135 | + item.deviceType.toUpperCase().includes(deviceType.toUpperCase()) | ||
| 136 | + ); | ||
| 137 | + }); | ||
| 138 | + return { | ||
| 139 | + items, | ||
| 140 | + total: items.length, | ||
| 141 | + }; | ||
| 142 | + }, | ||
| 143 | + showIndexColumn: false, | ||
| 144 | + pagination: { hideOnSinglePage: false }, | ||
| 145 | + fetchSetting: FETCH_SETTING, | ||
| 146 | + rowKey: props.primaryKey, | ||
| 147 | + dataSource: selectedTotalList, | ||
| 148 | + clickToRowSelect: false, | ||
| 149 | + beforeFetch: (params) => { | ||
| 150 | + const { getSelectedTableParams } = props; | ||
| 151 | + const data = getSelectedTableParams?.(params) || {}; | ||
| 152 | + Object.assign(params, { ...data, selected: false }); | ||
| 153 | + return params; | ||
| 154 | + }, | ||
| 155 | + rowSelection: { | ||
| 156 | + type: 'checkbox', | ||
| 157 | + onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => { | ||
| 158 | + selectedConfirmQueue.value = selectedRows; | ||
| 159 | + }, | ||
| 160 | + onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => { | ||
| 161 | + selectedConfirmQueue.value = selectedRows; | ||
| 162 | + }, | ||
| 163 | + }, | ||
| 164 | + }); | ||
| 165 | + | ||
| 166 | + const handleTriggerUpdateValue = () => { | ||
| 167 | + let list: Recordable[] = cloneDeep(toRaw(unref(selectedTotalList))); | ||
| 168 | + const { transformValue } = props; | ||
| 169 | + if (transformValue && isFunction(transformValue)) list = transformValue(list); | ||
| 170 | + | ||
| 171 | + emit('update:value', list); | ||
| 172 | + }; | ||
| 173 | + | ||
| 174 | + const handleSelected = () => { | ||
| 175 | + const { primaryKey } = props; | ||
| 176 | + const _list = [...unref(selectedTotalList), ...unref(pendingConfirmQueue)]; | ||
| 177 | + selectedTotalList.value = uniqBy(_list, primaryKey); | ||
| 178 | + pendingConfirmQueue.value = []; | ||
| 179 | + | ||
| 180 | + handleTriggerUpdateValue(); | ||
| 181 | + pendingTableActionType.setTableData([]); | ||
| 182 | + nextTick(() => pendingTableActionType.setTableData(unref(pendingTotalList))); | ||
| 183 | + }; | ||
| 184 | + | ||
| 185 | + const handleRemoveSelected = () => { | ||
| 186 | + const { primaryKey } = props; | ||
| 187 | + const selectedIds = unref(selectedConfirmQueue).map((selected) => selected[primaryKey]); | ||
| 188 | + remove(unref(selectedTotalList), (item) => selectedIds.includes(item[primaryKey])); | ||
| 189 | + | ||
| 190 | + handleTriggerUpdateValue(); | ||
| 191 | + | ||
| 192 | + selectedTableActionType.clearSelectedRowKeys(); | ||
| 193 | + selectedConfirmQueue.value = []; | ||
| 194 | + selectedTableActionType.reload(); | ||
| 195 | + }; | ||
| 196 | + | ||
| 197 | + const handleCheckoutPanel = async () => { | ||
| 198 | + await nextTick(); | ||
| 199 | + selectedTableActionType.reload(); | ||
| 200 | + }; | ||
| 201 | + | ||
| 202 | + const handleOpenModal = async () => { | ||
| 203 | + const { openModalValidate } = props; | ||
| 204 | + | ||
| 205 | + if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return; | ||
| 206 | + | ||
| 207 | + openModal(true); | ||
| 208 | + await nextTick(); | ||
| 209 | + pendingTableActionType.reload(); | ||
| 210 | + }; | ||
| 211 | + | ||
| 212 | + onMounted(async () => { | ||
| 213 | + const { getSelectedTableParams } = props; | ||
| 214 | + const data = getSelectedTableParams?.({}) || {}; | ||
| 215 | + if (!data?.convertConfigId || !data?.deviceProfileIds) { | ||
| 216 | + return; | ||
| 217 | + } | ||
| 218 | + const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true }); | ||
| 219 | + selectedTotalList.value = items; | ||
| 220 | + }); | ||
| 221 | +</script> | ||
| 222 | + | ||
| 223 | +<template> | ||
| 224 | + <section> | ||
| 225 | + <BasicModal | ||
| 226 | + @register="registerModal" | ||
| 227 | + title="穿梭表格" | ||
| 228 | + width="60%" | ||
| 229 | + :wrapClassName="prefixCls" | ||
| 230 | + :showOkBtn="false" | ||
| 231 | + cancelText="关闭" | ||
| 232 | + > | ||
| 233 | + <section class="bg-gray-100"> | ||
| 234 | + <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel"> | ||
| 235 | + <Tabs.TabPane :key="Active.PENDING"> | ||
| 236 | + <template #tab> | ||
| 237 | + <div class="flex items-center justify-center"> | ||
| 238 | + <span>待选设备</span> | ||
| 239 | + <!-- <Badge show-zero :count="pendingListCount" /> --> | ||
| 240 | + </div> | ||
| 241 | + </template> | ||
| 242 | + <BasicTable @register="regsterPendingTable"> | ||
| 243 | + <template #toolbar> | ||
| 244 | + <section class="flex w-full justify-end items-center"> | ||
| 245 | + <!-- <Button type="primary">全选</Button> --> | ||
| 246 | + <div class="text-blue-400"> | ||
| 247 | + <span class="mr-2">选择设备:</span> | ||
| 248 | + <span>{{ pendingConfirmQueue.length }}</span> | ||
| 249 | + </div> | ||
| 250 | + </section> | ||
| 251 | + </template> | ||
| 252 | + </BasicTable> | ||
| 253 | + <section class="flex justify-end px-4 pb-4"> | ||
| 254 | + <Button | ||
| 255 | + type="primary" | ||
| 256 | + @click="handleSelected" | ||
| 257 | + :disabled="!pendingConfirmQueue.length" | ||
| 258 | + > | ||
| 259 | + <span>确定已选</span> | ||
| 260 | + </Button> | ||
| 261 | + </section> | ||
| 262 | + </Tabs.TabPane> | ||
| 263 | + <Tabs.TabPane :key="Active.SELECTED"> | ||
| 264 | + <template #tab> | ||
| 265 | + <div class="flex items-center justify-center"> | ||
| 266 | + <span>已选设备</span> | ||
| 267 | + <!-- <Badge show-zero :count="selectedTotalList.length" /> --> | ||
| 268 | + </div> | ||
| 269 | + </template> | ||
| 270 | + <BasicTable @register="registerSelectedTable"> | ||
| 271 | + <template #toolbar> | ||
| 272 | + <section class="flex w-full justify-end items-center"> | ||
| 273 | + <div class="text-blue-400"> | ||
| 274 | + <span class="mr-2">选择设备:</span> | ||
| 275 | + <span>{{ selectedConfirmQueue.length }}</span> | ||
| 276 | + </div> | ||
| 277 | + </section> | ||
| 278 | + </template> | ||
| 279 | + </BasicTable> | ||
| 280 | + <section class="flex justify-end px-4 pb-4"> | ||
| 281 | + <Button | ||
| 282 | + type="primary" | ||
| 283 | + :disabled="!selectedConfirmQueue.length" | ||
| 284 | + @click="handleRemoveSelected" | ||
| 285 | + > | ||
| 286 | + <span>移除已选</span> | ||
| 287 | + </Button> | ||
| 288 | + </section> | ||
| 289 | + </Tabs.TabPane> | ||
| 290 | + </Tabs> | ||
| 291 | + </section> | ||
| 292 | + </BasicModal> | ||
| 293 | + <Button @click="handleOpenModal" type="link"> | ||
| 294 | + <span v-if="!selectedTotalList.length">选择设备</span> | ||
| 295 | + <div v-if="selectedTotalList.length"> | ||
| 296 | + <Tag | ||
| 297 | + class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
| 298 | + v-for="item in getShowTagOptions" | ||
| 299 | + :key="item[primaryKey]" | ||
| 300 | + > | ||
| 301 | + <span> | ||
| 302 | + {{ item.alias || item.name }} | ||
| 303 | + </span> | ||
| 304 | + </Tag> | ||
| 305 | + <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
| 306 | + <span> +{{ getSurplusOptionsLength }}... </span> | ||
| 307 | + </Tag> | ||
| 308 | + </div> | ||
| 309 | + </Button> | ||
| 310 | + </section> | ||
| 311 | +</template> | ||
| 312 | + | ||
| 313 | +<style lang="less"> | ||
| 314 | + @prefix-cls: ~'@{namespace}-transfer-table-modal'; | ||
| 315 | + | ||
| 316 | + .@{prefix-cls} { | ||
| 317 | + .vben-basic-table { | ||
| 318 | + padding-top: 0; | ||
| 319 | + } | ||
| 320 | + | ||
| 321 | + .vben-basic-form > .ant-row { | ||
| 322 | + width: 100%; | ||
| 323 | + } | ||
| 324 | + | ||
| 325 | + .ant-tabs-top-bar { | ||
| 326 | + background-color: #fff; | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + .transfer-table-disabled-row { | ||
| 330 | + :deep(.ant-checkbox) { | ||
| 331 | + cursor: not-allowed; | ||
| 332 | + | ||
| 333 | + .ant-checkbox-inner { | ||
| 334 | + background-color: #f5f5f5; | ||
| 335 | + border-color: #d9d9d9 !important; | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | +</style> |
| 1 | import { FormSchema } from '/@/components/Form'; | 1 | import { FormSchema } from '/@/components/Form'; |
| 2 | import { findDictItemByCode } from '/@/api/system/dict'; | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 3 | -import { h, unref } from 'vue'; | ||
| 4 | import { getDeviceProfile } from '/@/api/alarm/position'; | 3 | import { getDeviceProfile } from '/@/api/alarm/position'; |
| 5 | -import { BasicColumn, BasicTableProps } from '/@/components/Table'; | ||
| 6 | -import { devicePage } from '/@/api/device/deviceManager'; | ||
| 7 | -import { Tag } from 'ant-design-vue'; | 4 | +import { BasicInfoFormField, DataSourceType } from '../enum'; |
| 8 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 5 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 9 | -import { FETCH_SETTING } from '/@/components/Table/src/const'; | ||
| 10 | -import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 11 | -import { | ||
| 12 | - BasicInfoFormField, | ||
| 13 | - DataSourceType, | ||
| 14 | - DeviceStatusEnum, | ||
| 15 | - DeviceStatusNameEnum, | ||
| 16 | - DeviceTypeNameEnum, | ||
| 17 | -} from '../enum'; | ||
| 18 | -import { useClipboard } from '@vueuse/core'; | ||
| 19 | 6 | ||
| 20 | export const stepConfig = ['选择流转方式', '完善配置参数']; | 7 | export const stepConfig = ['选择流转方式', '完善配置参数']; |
| 21 | 8 | ||
| 22 | export const removeFieldByModeForm = ['name', 'description']; | 9 | export const removeFieldByModeForm = ['name', 'description']; |
| 23 | 10 | ||
| 24 | -//表单通用配置 | ||
| 25 | -export const modelFormPublicConfig = { | ||
| 26 | - labelWidth: 120, | ||
| 27 | - actionColOptions: { | ||
| 28 | - span: 14, | ||
| 29 | - }, | ||
| 30 | - showResetButton: false, | ||
| 31 | - showSubmitButton: false, | ||
| 32 | -}; | ||
| 33 | - | ||
| 34 | const handleGroupDevice = (options: DeviceRecord[]) => { | 11 | const handleGroupDevice = (options: DeviceRecord[]) => { |
| 35 | const map = new Map<string, string[]>(); | 12 | const map = new Map<string, string[]>(); |
| 36 | options.forEach((item) => { | 13 | options.forEach((item) => { |
| @@ -46,111 +23,17 @@ const handleGroupDevice = (options: DeviceRecord[]) => { | @@ -46,111 +23,17 @@ const handleGroupDevice = (options: DeviceRecord[]) => { | ||
| 46 | return value; | 23 | return value; |
| 47 | }; | 24 | }; |
| 48 | 25 | ||
| 49 | -const deviceTableFormSchema: FormSchema[] = [ | ||
| 50 | - { | ||
| 51 | - field: 'name', | ||
| 52 | - label: '设备名称', | ||
| 53 | - component: 'Input', | ||
| 54 | - colProps: { span: 9 }, | ||
| 55 | - componentProps: { | ||
| 56 | - placeholder: '请输入设备名称', | ||
| 57 | - }, | ||
| 58 | - }, | ||
| 59 | - { | ||
| 60 | - field: 'deviceType', | ||
| 61 | - label: '设备类型', | ||
| 62 | - component: 'ApiSelect', | ||
| 63 | - colProps: { span: 9 }, | ||
| 64 | - componentProps: { | ||
| 65 | - placeholder: '请选择设备类型', | ||
| 66 | - api: findDictItemByCode, | ||
| 67 | - params: { | ||
| 68 | - dictCode: 'device_type', | ||
| 69 | - }, | ||
| 70 | - labelField: 'itemText', | ||
| 71 | - valueField: 'itemValue', | ||
| 72 | - }, | ||
| 73 | - }, | ||
| 74 | -]; | ||
| 75 | - | ||
| 76 | -const { copied, copy } = useClipboard({ legacy: true }); | ||
| 77 | - | ||
| 78 | -const { createMessage } = useMessage(); | ||
| 79 | - | ||
| 80 | -const deviceTableColumn: BasicColumn[] = [ | ||
| 81 | - { | ||
| 82 | - title: '状态', | ||
| 83 | - dataIndex: 'deviceState', | ||
| 84 | - customRender: ({ text }) => { | ||
| 85 | - return h( | ||
| 86 | - Tag, | ||
| 87 | - { | ||
| 88 | - color: | ||
| 89 | - text === DeviceStatusEnum.INACTIVE | ||
| 90 | - ? 'warning' | ||
| 91 | - : text === DeviceStatusEnum.OFFLINE | ||
| 92 | - ? 'error' | ||
| 93 | - : 'success', | ||
| 94 | - }, | ||
| 95 | - () => DeviceStatusNameEnum[text] | ||
| 96 | - ); | ||
| 97 | - }, | ||
| 98 | - }, | ||
| 99 | - { | ||
| 100 | - title: '别名/设备名称', | ||
| 101 | - dataIndex: 'name', | ||
| 102 | - customRender: ({ record }) => { | ||
| 103 | - return h('div', [ | ||
| 104 | - h( | ||
| 105 | - 'div', | ||
| 106 | - { | ||
| 107 | - class: 'cursor-pointer', | ||
| 108 | - onClick: async () => { | ||
| 109 | - await copy(record.name); | ||
| 110 | - if (unref(copied)) createMessage.success('复制成功~'); | ||
| 111 | - }, | ||
| 112 | - }, | ||
| 113 | - [ | ||
| 114 | - record.alias && h('div', { class: 'truncate' }, record.alias), | ||
| 115 | - h('div', { class: 'text-blue-400 truncate' }, record.name), | ||
| 116 | - ] | ||
| 117 | - ), | ||
| 118 | - ]); | ||
| 119 | - }, | ||
| 120 | - }, | ||
| 121 | - { | ||
| 122 | - title: '设备类型', | ||
| 123 | - dataIndex: 'deviceType', | ||
| 124 | - customRender: ({ text }) => { | ||
| 125 | - return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]); | ||
| 126 | - }, | ||
| 127 | - }, | ||
| 128 | - { | ||
| 129 | - title: '所属产品', | ||
| 130 | - dataIndex: 'deviceProfile.name', | ||
| 131 | - }, | ||
| 132 | - { | ||
| 133 | - title: '所属组织', | ||
| 134 | - dataIndex: 'organizationDTO.name', | ||
| 135 | - }, | ||
| 136 | -]; | ||
| 137 | - | ||
| 138 | -const TransferTableProps: BasicTableProps = { | ||
| 139 | - formConfig: { | ||
| 140 | - layout: 'inline', | ||
| 141 | - labelWidth: 80, | ||
| 142 | - schemas: deviceTableFormSchema, | ||
| 143 | - actionColOptions: { span: 6 }, | 26 | +//表单通用配置 |
| 27 | +export const modelFormPublicConfig = { | ||
| 28 | + labelWidth: 120, | ||
| 29 | + actionColOptions: { | ||
| 30 | + span: 14, | ||
| 144 | }, | 31 | }, |
| 145 | - size: 'small', | ||
| 146 | - maxHeight: 240, | ||
| 147 | - useSearchForm: true, | ||
| 148 | - columns: deviceTableColumn, | ||
| 149 | - showIndexColumn: false, | ||
| 150 | - fetchSetting: FETCH_SETTING, | ||
| 151 | -} as BasicTableProps; | 32 | + showResetButton: false, |
| 33 | + showSubmitButton: false, | ||
| 34 | +}; | ||
| 152 | 35 | ||
| 153 | -export const modeForm = (submitFn?: Function): FormSchema[] => { | 36 | +export const modeForm = (): FormSchema[] => { |
| 154 | return [ | 37 | return [ |
| 155 | { | 38 | { |
| 156 | field: BasicInfoFormField.CONVERT_CONFIG_ID, | 39 | field: BasicInfoFormField.CONVERT_CONFIG_ID, |
| @@ -214,69 +97,20 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { | @@ -214,69 +97,20 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { | ||
| 214 | rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], | 97 | rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], |
| 215 | componentProps: ({ formActionType }) => { | 98 | componentProps: ({ formActionType }) => { |
| 216 | const { getFieldsValue } = formActionType; | 99 | const { getFieldsValue } = formActionType; |
| 217 | - const values = getFieldsValue(); | ||
| 218 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 219 | - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE); | ||
| 220 | - | ||
| 221 | return { | 100 | return { |
| 222 | - labelField: 'name', | ||
| 223 | - valueField: 'tbDeviceId', | ||
| 224 | - primaryKey: 'tbDeviceId', | ||
| 225 | - pendingTableProps: { | ||
| 226 | - ...TransferTableProps, | ||
| 227 | - api: devicePage, | ||
| 228 | - beforeFetch: (params) => { | ||
| 229 | - const values = getFieldsValue(); | ||
| 230 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 231 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 232 | - if (convertConfigId) { | ||
| 233 | - Object.assign(params, { convertConfigId, selected: false }); | ||
| 234 | - } | ||
| 235 | - return { ...params, deviceProfileIds }; | ||
| 236 | - }, | ||
| 237 | - } as BasicTableProps, | ||
| 238 | - selectedTableProps: { | ||
| 239 | - ...TransferTableProps, | ||
| 240 | - // api | ||
| 241 | - api: !!(convertConfigId && devices) ? devicePage : undefined, | ||
| 242 | - beforeFetch: (params) => { | ||
| 243 | - const values = getFieldsValue(); | ||
| 244 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 245 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 246 | - if (convertConfigId) { | ||
| 247 | - Object.assign(params, { convertConfigId, selected: true }); | ||
| 248 | - } | ||
| 249 | - return { ...params, deviceProfileIds }; | ||
| 250 | - }, | ||
| 251 | - } as BasicTableProps, | ||
| 252 | - initSelectedOptions: async ({ setSelectedTotal }) => { | 101 | + getPendingTableParams: () => { |
| 253 | const values = getFieldsValue(); | 102 | const values = getFieldsValue(); |
| 254 | const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | 103 | const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); |
| 255 | const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | 104 | const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); |
| 256 | - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE); | ||
| 257 | - if (convertConfigId && devices) { | ||
| 258 | - const { items, total } = await devicePage({ | ||
| 259 | - page: 1, | ||
| 260 | - pageSize: 10, | ||
| 261 | - convertConfigId: values[BasicInfoFormField.CONVERT_CONFIG_ID], | ||
| 262 | - deviceProfileIds, | ||
| 263 | - selected: true, | ||
| 264 | - }); | ||
| 265 | - setSelectedTotal(total); | ||
| 266 | - return items; | ||
| 267 | - } | ||
| 268 | - return []; | 105 | + return { convertConfigId, deviceProfileIds }; |
| 269 | }, | 106 | }, |
| 270 | - onSelectedAfter: async () => { | ||
| 271 | - submitFn && (await submitFn(false)); | ||
| 272 | - }, | ||
| 273 | - onRemoveAfter: async ({ reloadSelected }) => { | ||
| 274 | - submitFn && (await submitFn(false)); | ||
| 275 | - reloadSelected(); | ||
| 276 | - }, | ||
| 277 | - transformValue: (_selectedRowKeys: string[], selectedRows: DeviceRecord[]) => { | ||
| 278 | - return handleGroupDevice(selectedRows); | 107 | + getSelectedTableParams: () => { |
| 108 | + const values = getFieldsValue(); | ||
| 109 | + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 110 | + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 111 | + return { convertConfigId, deviceProfileIds }; | ||
| 279 | }, | 112 | }, |
| 113 | + transformValue: handleGroupDevice, | ||
| 280 | }; | 114 | }; |
| 281 | }, | 115 | }, |
| 282 | }, | 116 | }, |
| 1 | import { FormSchema } from '/@/components/Form'; | 1 | import { FormSchema } from '/@/components/Form'; |
| 2 | import { findDictItemByCode } from '/@/api/system/dict'; | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 3 | -import { h, ref, unref } from 'vue'; | 3 | +import { ref } from 'vue'; |
| 4 | import { isExistDataManagerNameApi } from '/@/api/datamanager/dataManagerApi'; | 4 | import { isExistDataManagerNameApi } from '/@/api/datamanager/dataManagerApi'; |
| 5 | import { getDeviceProfile } from '/@/api/alarm/position'; | 5 | import { getDeviceProfile } from '/@/api/alarm/position'; |
| 6 | -import { BasicColumn, BasicTableProps } from '/@/components/Table'; | ||
| 7 | -import { devicePage } from '/@/api/device/deviceManager'; | ||
| 8 | -import { Tag } from 'ant-design-vue'; | ||
| 9 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 6 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 10 | -import { FETCH_SETTING } from '/@/components/Table/src/const'; | ||
| 11 | -import { useClipboard } from '@vueuse/core'; | ||
| 12 | -import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 13 | 7 | ||
| 14 | const typeValue = ref(''); | 8 | const typeValue = ref(''); |
| 15 | export enum CredentialsEnum { | 9 | export enum CredentialsEnum { |
| @@ -76,108 +70,7 @@ const handleGroupDevice = (options: DeviceRecord[]) => { | @@ -76,108 +70,7 @@ const handleGroupDevice = (options: DeviceRecord[]) => { | ||
| 76 | return value; | 70 | return value; |
| 77 | }; | 71 | }; |
| 78 | 72 | ||
| 79 | -const deviceTableFormSchema: FormSchema[] = [ | ||
| 80 | - { | ||
| 81 | - field: 'name', | ||
| 82 | - label: '设备名称', | ||
| 83 | - component: 'Input', | ||
| 84 | - colProps: { span: 9 }, | ||
| 85 | - componentProps: { | ||
| 86 | - placeholder: '请输入设备名称', | ||
| 87 | - }, | ||
| 88 | - }, | ||
| 89 | - { | ||
| 90 | - field: 'deviceType', | ||
| 91 | - label: '设备类型', | ||
| 92 | - component: 'ApiSelect', | ||
| 93 | - colProps: { span: 9 }, | ||
| 94 | - componentProps: { | ||
| 95 | - placeholder: '请选择设备类型', | ||
| 96 | - api: findDictItemByCode, | ||
| 97 | - params: { | ||
| 98 | - dictCode: 'device_type', | ||
| 99 | - }, | ||
| 100 | - labelField: 'itemText', | ||
| 101 | - valueField: 'itemValue', | ||
| 102 | - }, | ||
| 103 | - }, | ||
| 104 | -]; | ||
| 105 | -const { copied, copy } = useClipboard({ legacy: true }); | ||
| 106 | -const { createMessage } = useMessage(); | ||
| 107 | -const deviceTableColumn: BasicColumn[] = [ | ||
| 108 | - { | ||
| 109 | - title: '状态', | ||
| 110 | - dataIndex: 'deviceState', | ||
| 111 | - customRender: ({ text }) => { | ||
| 112 | - return h( | ||
| 113 | - Tag, | ||
| 114 | - { | ||
| 115 | - color: | ||
| 116 | - text === DeviceStatusEnum.INACTIVE | ||
| 117 | - ? 'warning' | ||
| 118 | - : text === DeviceStatusEnum.OFFLINE | ||
| 119 | - ? 'error' | ||
| 120 | - : 'success', | ||
| 121 | - }, | ||
| 122 | - () => DeviceStatusNameEnum[text] | ||
| 123 | - ); | ||
| 124 | - }, | ||
| 125 | - }, | ||
| 126 | - { | ||
| 127 | - title: '别名/设备名称', | ||
| 128 | - dataIndex: 'name', | ||
| 129 | - customRender: ({ record }) => { | ||
| 130 | - return h('div', [ | ||
| 131 | - h( | ||
| 132 | - 'div', | ||
| 133 | - { | ||
| 134 | - class: 'cursor-pointer', | ||
| 135 | - onClick: async () => { | ||
| 136 | - await copy(record.name); | ||
| 137 | - if (unref(copied)) createMessage.success('复制成功~'); | ||
| 138 | - }, | ||
| 139 | - }, | ||
| 140 | - [ | ||
| 141 | - record.alias && h('div', { class: 'truncate' }, record.alias), | ||
| 142 | - h('div', { class: 'text-blue-400 truncate' }, record.name), | ||
| 143 | - ] | ||
| 144 | - ), | ||
| 145 | - ]); | ||
| 146 | - }, | ||
| 147 | - }, | ||
| 148 | - { | ||
| 149 | - title: '设备类型', | ||
| 150 | - dataIndex: 'deviceType', | ||
| 151 | - customRender: ({ text }) => { | ||
| 152 | - return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]); | ||
| 153 | - }, | ||
| 154 | - }, | ||
| 155 | - { | ||
| 156 | - title: '所属产品', | ||
| 157 | - dataIndex: 'deviceProfile.name', | ||
| 158 | - }, | ||
| 159 | - { | ||
| 160 | - title: '所属组织', | ||
| 161 | - dataIndex: 'organizationDTO.name', | ||
| 162 | - }, | ||
| 163 | -]; | ||
| 164 | - | ||
| 165 | -const TransferTableProps: BasicTableProps = { | ||
| 166 | - formConfig: { | ||
| 167 | - layout: 'inline', | ||
| 168 | - labelWidth: 80, | ||
| 169 | - schemas: deviceTableFormSchema, | ||
| 170 | - actionColOptions: { span: 6 }, | ||
| 171 | - }, | ||
| 172 | - size: 'small', | ||
| 173 | - maxHeight: 240, | ||
| 174 | - useSearchForm: true, | ||
| 175 | - columns: deviceTableColumn, | ||
| 176 | - showIndexColumn: false, | ||
| 177 | - fetchSetting: FETCH_SETTING, | ||
| 178 | -} as BasicTableProps; | ||
| 179 | - | ||
| 180 | -export const modeForm = (submitFn?: Function): FormSchema[] => { | 73 | +export const modeForm = (): FormSchema[] => { |
| 181 | return [ | 74 | return [ |
| 182 | { | 75 | { |
| 183 | field: BasicInfoFormField.CONVERT_CONFIG_ID, | 76 | field: BasicInfoFormField.CONVERT_CONFIG_ID, |
| @@ -239,69 +132,20 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { | @@ -239,69 +132,20 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { | ||
| 239 | changeEvent: 'update:value', | 132 | changeEvent: 'update:value', |
| 240 | componentProps: ({ formActionType }) => { | 133 | componentProps: ({ formActionType }) => { |
| 241 | const { getFieldsValue } = formActionType; | 134 | const { getFieldsValue } = formActionType; |
| 242 | - const values = getFieldsValue(); | ||
| 243 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 244 | - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE); | ||
| 245 | - | ||
| 246 | return { | 135 | return { |
| 247 | - labelField: 'name', | ||
| 248 | - valueField: 'tbDeviceId', | ||
| 249 | - primaryKey: 'tbDeviceId', | ||
| 250 | - pendingTableProps: { | ||
| 251 | - ...TransferTableProps, | ||
| 252 | - api: devicePage, | ||
| 253 | - beforeFetch: (params) => { | ||
| 254 | - const values = getFieldsValue(); | ||
| 255 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 256 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 257 | - if (convertConfigId) { | ||
| 258 | - Object.assign(params, { convertConfigId, selected: false }); | ||
| 259 | - } | ||
| 260 | - return { ...params, deviceProfileIds }; | ||
| 261 | - }, | ||
| 262 | - } as BasicTableProps, | ||
| 263 | - selectedTableProps: { | ||
| 264 | - ...TransferTableProps, | ||
| 265 | - // api | ||
| 266 | - api: !!(convertConfigId && devices) ? devicePage : undefined, | ||
| 267 | - beforeFetch: (params) => { | ||
| 268 | - const values = getFieldsValue(); | ||
| 269 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 270 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 271 | - if (convertConfigId) { | ||
| 272 | - Object.assign(params, { convertConfigId, selected: true }); | ||
| 273 | - } | ||
| 274 | - return { ...params, deviceProfileIds }; | ||
| 275 | - }, | ||
| 276 | - } as BasicTableProps, | ||
| 277 | - initSelectedOptions: async ({ setSelectedTotal }) => { | 136 | + getPendingTableParams: () => { |
| 278 | const values = getFieldsValue(); | 137 | const values = getFieldsValue(); |
| 279 | const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | 138 | const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); |
| 280 | const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | 139 | const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); |
| 281 | - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE); | ||
| 282 | - if (convertConfigId && devices) { | ||
| 283 | - const { items, total } = await devicePage({ | ||
| 284 | - page: 1, | ||
| 285 | - pageSize: 10, | ||
| 286 | - convertConfigId: values[BasicInfoFormField.CONVERT_CONFIG_ID], | ||
| 287 | - deviceProfileIds, | ||
| 288 | - selected: true, | ||
| 289 | - }); | ||
| 290 | - setSelectedTotal(total); | ||
| 291 | - return items; | ||
| 292 | - } | ||
| 293 | - return []; | ||
| 294 | - }, | ||
| 295 | - onSelectedAfter: async () => { | ||
| 296 | - submitFn && (await submitFn(false)); | 140 | + return { convertConfigId, deviceProfileIds }; |
| 297 | }, | 141 | }, |
| 298 | - onRemoveAfter: async ({ reloadSelected }) => { | ||
| 299 | - submitFn && (await submitFn(false)); | ||
| 300 | - reloadSelected(); | ||
| 301 | - }, | ||
| 302 | - transformValue: (_selectedRowKeys: string[], selectedRows: DeviceRecord[]) => { | ||
| 303 | - return handleGroupDevice(selectedRows); | 142 | + getSelectedTableParams: () => { |
| 143 | + const values = getFieldsValue(); | ||
| 144 | + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 145 | + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 146 | + return { convertConfigId, deviceProfileIds }; | ||
| 304 | }, | 147 | }, |
| 148 | + transformValue: handleGroupDevice, | ||
| 305 | }; | 149 | }; |
| 306 | }, | 150 | }, |
| 307 | }, | 151 | }, |