Showing
5 changed files
with
72 additions
and
553 deletions
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | - import { Button, Transfer, Tag } from 'ant-design-vue'; | 2 | + import { Transfer, Select } from 'ant-design-vue'; |
| 3 | import { cloneDeep, get } from 'lodash-es'; | 3 | import { cloneDeep, get } from 'lodash-es'; |
| 4 | import { computed, CSSProperties, ExtractPropTypes, onMounted, ref, unref, watch } from 'vue'; | 4 | import { computed, CSSProperties, ExtractPropTypes, onMounted, ref, unref, watch } from 'vue'; |
| 5 | import { BasicModal, useModal } from '/@/components/Modal'; | 5 | import { BasicModal, useModal } from '/@/components/Modal'; |
| @@ -29,7 +29,7 @@ | @@ -29,7 +29,7 @@ | ||
| 29 | buttonName?: string; | 29 | buttonName?: string; |
| 30 | modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; | 30 | modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; |
| 31 | transferProps?: ExtractPropTypes<TransferType['$props']>; | 31 | transferProps?: ExtractPropTypes<TransferType['$props']>; |
| 32 | - buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>; | 32 | + selectProps?: ExtractPropTypes<InstanceType<typeof Select>['$props']>; |
| 33 | disabled?: any; | 33 | disabled?: any; |
| 34 | }>(), | 34 | }>(), |
| 35 | { | 35 | { |
| @@ -52,24 +52,15 @@ | @@ -52,24 +52,15 @@ | ||
| 52 | return unref(getOptions).filter((item) => unref(targetKeys).includes(item[valueField])); | 52 | return unref(getOptions).filter((item) => unref(targetKeys).includes(item[valueField])); |
| 53 | }); | 53 | }); |
| 54 | 54 | ||
| 55 | - const getShowTagOptions = computed(() => { | ||
| 56 | - const { maxTagLength } = props; | ||
| 57 | - return unref(targetOptions).slice(0, maxTagLength); | 55 | + const getSelectOptions = computed(() => { |
| 56 | + return unref(targetOptions).map((item) => ({ ...item, label: item.title, value: item.key })); | ||
| 58 | }); | 57 | }); |
| 59 | 58 | ||
| 60 | - const getSurplusOptionsLength = computed(() => { | ||
| 61 | - const { maxTagLength } = props; | ||
| 62 | - const surplusValue = unref(targetKeys).length - maxTagLength; | ||
| 63 | - return surplusValue < 0 ? 0 : surplusValue; | ||
| 64 | - }); | ||
| 65 | - | ||
| 66 | - const count = computed(() => { | ||
| 67 | - return unref(targetKeys).length; | ||
| 68 | - }); | 59 | + const getSelectValue = computed(() => unref(getSelectOptions).map((item) => item.value)); |
| 69 | 60 | ||
| 70 | const targetKeys = ref<string[]>(props.value || []); | 61 | const targetKeys = ref<string[]>(props.value || []); |
| 71 | 62 | ||
| 72 | - const getOptions = computed<Recordable[]>(() => { | 63 | + const getOptions = computed<Record<'key' | 'title', string>[]>(() => { |
| 73 | const { labelField, valueField } = props; | 64 | const { labelField, valueField } = props; |
| 74 | return unref(options).map((item) => ({ | 65 | return unref(options).map((item) => ({ |
| 75 | ...item, | 66 | ...item, |
| @@ -99,13 +90,14 @@ | @@ -99,13 +90,14 @@ | ||
| 99 | }; | 90 | }; |
| 100 | }); | 91 | }); |
| 101 | 92 | ||
| 102 | - const getBindButtonProps = computed(() => { | ||
| 103 | - const { buttonProps = {} } = props; | 93 | + const getBindSelectProps = computed(() => { |
| 94 | + const { selectProps = {} } = props; | ||
| 104 | return { | 95 | return { |
| 105 | - ...buttonProps, | ||
| 106 | - type: 'link', | ||
| 107 | - onClick: handleOpenModal, | ||
| 108 | - } as ExtractPropTypes<InstanceType<typeof Button>['$props']>; | 96 | + maxTagCount: 3, |
| 97 | + ...selectProps, | ||
| 98 | + open: false, | ||
| 99 | + onDropdownVisibleChange: handleOpenModal, | ||
| 100 | + } as ExtractPropTypes<InstanceType<typeof Select>['$props']>; | ||
| 109 | }); | 101 | }); |
| 110 | 102 | ||
| 111 | const handleChange = (_targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => { | 103 | const handleChange = (_targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => { |
| @@ -165,22 +157,11 @@ | @@ -165,22 +157,11 @@ | ||
| 165 | <Transfer v-bind="getBindProps" /> | 157 | <Transfer v-bind="getBindProps" /> |
| 166 | </section> | 158 | </section> |
| 167 | </BasicModal> | 159 | </BasicModal> |
| 168 | - <Button v-bind="getBindButtonProps" class="!flex !justify-center !items-center min-h-8"> | ||
| 169 | - <span v-if="!count">{{ $props.buttonName }}</span> | ||
| 170 | - <div v-if="!!count"> | ||
| 171 | - <Tag | ||
| 172 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
| 173 | - v-for="item in getShowTagOptions" | ||
| 174 | - :key="item.key" | ||
| 175 | - > | ||
| 176 | - <span> | ||
| 177 | - {{ item.title }} | ||
| 178 | - </span> | ||
| 179 | - </Tag> | ||
| 180 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
| 181 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
| 182 | - </Tag> | ||
| 183 | - </div> | ||
| 184 | - </Button> | 160 | + <Select |
| 161 | + v-bind="getBindSelectProps" | ||
| 162 | + :options="getSelectOptions" | ||
| 163 | + :value="getSelectValue" | ||
| 164 | + mode="multiple" | ||
| 165 | + /> | ||
| 185 | </div> | 166 | </div> |
| 186 | </template> | 167 | </template> |
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 | - disabled?: any; | ||
| 46 | - }>(), | ||
| 47 | - { | ||
| 48 | - buttonName: '选择设备', | ||
| 49 | - primaryKey: 'id', | ||
| 50 | - maxTagLength: 2, | ||
| 51 | - labelField: 'label', | ||
| 52 | - valueField: 'value', | ||
| 53 | - disabled: false, | ||
| 54 | - } | ||
| 55 | - ); | ||
| 56 | - | ||
| 57 | - const { prefixCls } = useDesign('transfer-table-modal'); | ||
| 58 | - | ||
| 59 | - const activeKey = ref<Active>(Active.PENDING); | ||
| 60 | - | ||
| 61 | - const selectedRows = ref<Options[]>([]); | ||
| 62 | - | ||
| 63 | - const selectedRowKeys = ref<string[]>(props.value || []); | ||
| 64 | - | ||
| 65 | - const pendingOptions = ref<Options[]>([]); | ||
| 66 | - | ||
| 67 | - const selectedConfirmQueue = ref<Options[]>([]); | ||
| 68 | - | ||
| 69 | - const pendingConfirmQueue = ref<Options[]>([]); | ||
| 70 | - | ||
| 71 | - const selectedTotal = ref(0); | ||
| 72 | - | ||
| 73 | - const pendingTotal = ref(0); | ||
| 74 | - | ||
| 75 | - const getFetchSetting = computed(() => { | ||
| 76 | - const { pendingTableProps } = props; | ||
| 77 | - return pendingTableProps?.fetchSetting || FETCH_SETTING; | ||
| 78 | - }); | ||
| 79 | - | ||
| 80 | - const getPendingRowSelection = computed<TableRowSelection>(() => { | ||
| 81 | - const rowKeys = unref(selectedRowKeys); | ||
| 82 | - return { | ||
| 83 | - type: 'checkbox', | ||
| 84 | - getCheckboxProps: (record: Recordable) => { | ||
| 85 | - const { primaryKey } = props; | ||
| 86 | - return { | ||
| 87 | - ...record, | ||
| 88 | - disabled: rowKeys.includes(record[primaryKey]), | ||
| 89 | - }; | ||
| 90 | - }, | ||
| 91 | - onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => { | ||
| 92 | - pendingConfirmQueue.value = selectedRows; | ||
| 93 | - }, | ||
| 94 | - onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => { | ||
| 95 | - pendingConfirmQueue.value = selectedRows; | ||
| 96 | - }, | ||
| 97 | - }; | ||
| 98 | - }); | ||
| 99 | - | ||
| 100 | - const getPendingTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => { | ||
| 101 | - const { pendingTableProps, primaryKey } = props; | ||
| 102 | - return { | ||
| 103 | - ...pendingTableProps, | ||
| 104 | - rowKey: primaryKey, | ||
| 105 | - api: handlePendingApiIntercept, | ||
| 106 | - clickToRowSelect: false, | ||
| 107 | - rowSelection: getPendingRowSelection, | ||
| 108 | - }; | ||
| 109 | - }); | ||
| 110 | - | ||
| 111 | - const getSelectedTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => { | ||
| 112 | - const { selectedTableProps, primaryKey } = props; | ||
| 113 | - return { | ||
| 114 | - ...selectedTableProps, | ||
| 115 | - dataSource: selectedRows, | ||
| 116 | - clickToRowSelect: false, | ||
| 117 | - rowKey: primaryKey, | ||
| 118 | - api: selectedTableProps!.api ? handleSelectedApiIntercept : undefined, | ||
| 119 | - rowSelection: { | ||
| 120 | - type: 'checkbox', | ||
| 121 | - onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => { | ||
| 122 | - selectedConfirmQueue.value = selectedRows; | ||
| 123 | - }, | ||
| 124 | - onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => { | ||
| 125 | - selectedConfirmQueue.value = selectedRows; | ||
| 126 | - }, | ||
| 127 | - }, | ||
| 128 | - }; | ||
| 129 | - }); | ||
| 130 | - | ||
| 131 | - const getModalBindProps = computed(() => { | ||
| 132 | - const { modalProps = {} } = props; | ||
| 133 | - return { | ||
| 134 | - width: '60%', | ||
| 135 | - title: '穿梭表格', | ||
| 136 | - wrapClassName: prefixCls, | ||
| 137 | - ...modalProps, | ||
| 138 | - showOkBtn: false, | ||
| 139 | - }; | ||
| 140 | - }); | ||
| 141 | - | ||
| 142 | - const getBindButtonProps = computed<ButtonProps>(() => { | ||
| 143 | - const { buttonProps = {} } = props; | ||
| 144 | - return { | ||
| 145 | - type: 'link', | ||
| 146 | - ...buttonProps, | ||
| 147 | - }; | ||
| 148 | - }); | ||
| 149 | - | ||
| 150 | - const getShowTagOptions = computed(() => { | ||
| 151 | - const { maxTagLength } = props; | ||
| 152 | - return unref(selectedRows).slice(0, maxTagLength); | ||
| 153 | - }); | ||
| 154 | - | ||
| 155 | - const getSurplusOptionsLength = computed(() => { | ||
| 156 | - const { maxTagLength } = props; | ||
| 157 | - const surplusValue = unref(selectedRows).length - maxTagLength; | ||
| 158 | - return surplusValue < 0 ? 0 : surplusValue; | ||
| 159 | - }); | ||
| 160 | - | ||
| 161 | - const [registerModal, { openModal }] = useModal(); | ||
| 162 | - | ||
| 163 | - const [ | ||
| 164 | - regsterPendingTable, | ||
| 165 | - { | ||
| 166 | - getSelectRows: getPendingSelectRows, | ||
| 167 | - getSelectRowKeys: getPendingSelectRowKeys, | ||
| 168 | - reload: reloadPending, | ||
| 169 | - clearSelectedRowKeys: clearPendingSelectedRowKeys, | ||
| 170 | - }, | ||
| 171 | - ] = useTable(unref(getPendingTableBindProps)); | ||
| 172 | - | ||
| 173 | - const [ | ||
| 174 | - registerSelectedTable, | ||
| 175 | - { getSelectRowKeys, setProps, clearSelectedRowKeys, reload: reloadSelected }, | ||
| 176 | - ] = useTable(unref(getSelectedTableBindProps)); | ||
| 177 | - | ||
| 178 | - async function handlePendingApiIntercept(params?: Recordable) { | ||
| 179 | - try { | ||
| 180 | - const { api } = props.pendingTableProps || {}; | ||
| 181 | - if (api && isFunction(api)) { | ||
| 182 | - let options = await api(params); | ||
| 183 | - pendingOptions.value = options; | ||
| 184 | - const { totalField, listField } = unref(getFetchSetting); | ||
| 185 | - const total = get(options, totalField!); | ||
| 186 | - if (unref(selectedTotal) + unref(pendingTotal) !== total) { | ||
| 187 | - pendingTotal.value = total; | ||
| 188 | - } | ||
| 189 | - let list: Recordable[] = get(options, listField!); | ||
| 190 | - list = getSelectedRows(list); | ||
| 191 | - options = set(options, listField!, list); | ||
| 192 | - return options; | ||
| 193 | - } | ||
| 194 | - } catch (error) { | ||
| 195 | - console.error(error); | ||
| 196 | - return []; | ||
| 197 | - } | ||
| 198 | - return []; | ||
| 199 | - } | ||
| 200 | - | ||
| 201 | - async function handleSelectedApiIntercept(params?: Recordable) { | ||
| 202 | - try { | ||
| 203 | - const { api } = props.selectedTableProps || {}; | ||
| 204 | - if (api && isFunction(api)) { | ||
| 205 | - let options = await api(params); | ||
| 206 | - pendingOptions.value = options; | ||
| 207 | - const { totalField, listField } = unref(getFetchSetting); | ||
| 208 | - selectedTotal.value = get(options, totalField!); | ||
| 209 | - let list: Recordable[] = get(options, listField!); | ||
| 210 | - list = getSelectedRows(list); | ||
| 211 | - options = set(options, listField!, list); | ||
| 212 | - return options; | ||
| 213 | - } | ||
| 214 | - } catch (error) { | ||
| 215 | - console.error(error); | ||
| 216 | - return []; | ||
| 217 | - } | ||
| 218 | - return []; | ||
| 219 | - } | ||
| 220 | - | ||
| 221 | - const handleOpenModal = async () => { | ||
| 222 | - const { disabled } = props; | ||
| 223 | - if (disabled) return; | ||
| 224 | - openModal(true); | ||
| 225 | - await nextTick(); | ||
| 226 | - if (props.value && !props.value.length) { | ||
| 227 | - activeKey.value = Active.PENDING; | ||
| 228 | - reloadPending(); | ||
| 229 | - } | ||
| 230 | - }; | ||
| 231 | - | ||
| 232 | - const handleTriggerEmit = (selectedRowKeys: string[], selectedRows: Options[]) => { | ||
| 233 | - const { transformValue } = props; | ||
| 234 | - let value = selectedRowKeys; | ||
| 235 | - if (transformValue && isFunction(transformValue)) { | ||
| 236 | - value = transformValue(selectedRowKeys, selectedRows); | ||
| 237 | - } | ||
| 238 | - emit('change', unref(selectedRowKeys), unref(selectedRows)); | ||
| 239 | - emit('update:value', unref(value)); | ||
| 240 | - }; | ||
| 241 | - | ||
| 242 | - const handleSelected = async () => { | ||
| 243 | - const { onSelectedAfter } = props; | ||
| 244 | - const currentPageSelectRows = getPendingSelectRows(); | ||
| 245 | - const currentPageSelectRowKeys = getPendingSelectRowKeys(); | ||
| 246 | - const { primaryKey } = props; | ||
| 247 | - selectedRows.value = uniqBy([...unref(selectedRows), ...currentPageSelectRows], primaryKey); | ||
| 248 | - selectedRowKeys.value = [...new Set([...unref(selectedRowKeys), ...currentPageSelectRowKeys])]; | ||
| 249 | - pendingConfirmQueue.value = []; | ||
| 250 | - // selectedTotal.value = unref(selectedRowKeys).length; | ||
| 251 | - pendingTotal.value = unref(pendingTotal) - currentPageSelectRows.length; | ||
| 252 | - selectedTotal.value = unref(selectedTotal) + currentPageSelectRows.length; | ||
| 253 | - | ||
| 254 | - clearPendingSelectedRowKeys(); | ||
| 255 | - handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows)); | ||
| 256 | - | ||
| 257 | - if (onSelectedAfter && isFunction(onSelectedAfter)) { | ||
| 258 | - await onSelectedAfter(actionType); | ||
| 259 | - } | ||
| 260 | - reloadPending(); | ||
| 261 | - }; | ||
| 262 | - | ||
| 263 | - const handleRemoveSelected = async () => { | ||
| 264 | - const { onRemoveAfter } = props; | ||
| 265 | - const removeRowKeys = getSelectRowKeys(); | ||
| 266 | - selectedRowKeys.value = unref(selectedRowKeys).filter((key) => !removeRowKeys.includes(key)); | ||
| 267 | - selectedRows.value = unref(selectedRows).filter((item) => { | ||
| 268 | - const { primaryKey } = props; | ||
| 269 | - return unref(selectedRowKeys).includes(item[primaryKey]); | ||
| 270 | - }); | ||
| 271 | - pendingTotal.value = unref(pendingTotal) + removeRowKeys.length; | ||
| 272 | - selectedTotal.value = unref(selectedTotal) - removeRowKeys.length; | ||
| 273 | - | ||
| 274 | - clearSelectedRowKeys(); | ||
| 275 | - selectedConfirmQueue.value = []; | ||
| 276 | - setProps({ dataSource: unref(selectedRows) }); | ||
| 277 | - handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows)); | ||
| 278 | - | ||
| 279 | - if (onRemoveAfter && isFunction(onRemoveAfter)) { | ||
| 280 | - await onRemoveAfter(actionType); | ||
| 281 | - } | ||
| 282 | - }; | ||
| 283 | - | ||
| 284 | - const actionType = { | ||
| 285 | - setSelectedOptions, | ||
| 286 | - setSelectedTotal, | ||
| 287 | - reloadPending, | ||
| 288 | - reloadSelected, | ||
| 289 | - }; | ||
| 290 | - | ||
| 291 | - const getSelectedRows = (options: Recordable[]) => { | ||
| 292 | - const { labelField, valueField } = props; | ||
| 293 | - return options.map((item) => ({ ...item, label: item[labelField], value: item[valueField] })); | ||
| 294 | - }; | ||
| 295 | - | ||
| 296 | - const getSelectedKeys = (options: Recordable[]) => { | ||
| 297 | - const { primaryKey } = props; | ||
| 298 | - return options.map((item) => item[primaryKey]); | ||
| 299 | - }; | ||
| 300 | - | ||
| 301 | - function setSelectedOptions(options: Recordable[]) { | ||
| 302 | - selectedRows.value = getSelectedRows(options); | ||
| 303 | - selectedRowKeys.value = getSelectedKeys(options); | ||
| 304 | - } | ||
| 305 | - | ||
| 306 | - function setSelectedTotal(number: number) { | ||
| 307 | - selectedTotal.value = number; | ||
| 308 | - } | ||
| 309 | - | ||
| 310 | - const handleCheckoutPanel = async (keys: Active) => { | ||
| 311 | - await nextTick(); | ||
| 312 | - if (keys === Active.PENDING) { | ||
| 313 | - reloadPending(); | ||
| 314 | - } else { | ||
| 315 | - reloadSelected(); | ||
| 316 | - setProps({ | ||
| 317 | - dataSource: unref(selectedRows), | ||
| 318 | - }); | ||
| 319 | - } | ||
| 320 | - }; | ||
| 321 | - | ||
| 322 | - watch( | ||
| 323 | - () => props.value, | ||
| 324 | - () => { | ||
| 325 | - if (props.value && !props.value.length) { | ||
| 326 | - selectedRowKeys.value = []; | ||
| 327 | - selectedRows.value = []; | ||
| 328 | - // pendingTotal.value = 0; | ||
| 329 | - selectedTotal.value = 0; | ||
| 330 | - } | ||
| 331 | - } | ||
| 332 | - ); | ||
| 333 | - | ||
| 334 | - onMounted(async () => { | ||
| 335 | - const { initSelectedOptions } = props; | ||
| 336 | - if (initSelectedOptions && isFunction(initSelectedOptions)) { | ||
| 337 | - const options = await initSelectedOptions(actionType); | ||
| 338 | - setSelectedOptions(options); | ||
| 339 | - } | ||
| 340 | - }); | ||
| 341 | -</script> | ||
| 342 | - | ||
| 343 | -<template> | ||
| 344 | - <section> | ||
| 345 | - <BasicModal @register="registerModal" v-bind="getModalBindProps"> | ||
| 346 | - <section class="bg-gray-100"> | ||
| 347 | - <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel"> | ||
| 348 | - <Tabs.TabPane :key="Active.PENDING"> | ||
| 349 | - <template #tab> | ||
| 350 | - <div class="flex items-center justify-center"> | ||
| 351 | - <span>待选设备</span> | ||
| 352 | - <Badge show-zero :count="pendingTotal" /> | ||
| 353 | - </div> | ||
| 354 | - </template> | ||
| 355 | - <BasicTable @register="regsterPendingTable"> | ||
| 356 | - <template #toolbar> | ||
| 357 | - <section class="flex w-full justify-end items-center"> | ||
| 358 | - <!-- <Button type="primary">全选</Button> --> | ||
| 359 | - <div class="text-blue-400"> | ||
| 360 | - <span class="mr-2">选择设备:</span> | ||
| 361 | - <span>{{ pendingConfirmQueue.length }}</span> | ||
| 362 | - </div> | ||
| 363 | - </section> | ||
| 364 | - </template> | ||
| 365 | - </BasicTable> | ||
| 366 | - <section class="flex justify-end px-4 pb-4"> | ||
| 367 | - <Button | ||
| 368 | - type="primary" | ||
| 369 | - @click="handleSelected" | ||
| 370 | - :disabled="!pendingConfirmQueue.length" | ||
| 371 | - > | ||
| 372 | - <span>确定已选</span> | ||
| 373 | - </Button> | ||
| 374 | - </section> | ||
| 375 | - </Tabs.TabPane> | ||
| 376 | - <Tabs.TabPane :key="Active.SELECTED"> | ||
| 377 | - <template #tab> | ||
| 378 | - <div class="flex items-center justify-center"> | ||
| 379 | - <span>已选设备</span> | ||
| 380 | - <Badge show-zero :count="selectedTotal" /> | ||
| 381 | - </div> | ||
| 382 | - </template> | ||
| 383 | - <BasicTable @register="registerSelectedTable"> | ||
| 384 | - <template #toolbar> | ||
| 385 | - <section class="flex w-full justify-end items-center"> | ||
| 386 | - <!-- <Button type="primary">全选</Button> --> | ||
| 387 | - <div class="text-blue-400"> | ||
| 388 | - <span class="mr-2">选择设备:</span> | ||
| 389 | - <span>{{ selectedConfirmQueue.length }}</span> | ||
| 390 | - </div> | ||
| 391 | - </section> | ||
| 392 | - </template> | ||
| 393 | - </BasicTable> | ||
| 394 | - <section class="flex justify-end px-4 pb-4"> | ||
| 395 | - <Button | ||
| 396 | - type="primary" | ||
| 397 | - :disabled="!selectedConfirmQueue.length" | ||
| 398 | - @click="handleRemoveSelected" | ||
| 399 | - > | ||
| 400 | - <span>移除已选</span> | ||
| 401 | - </Button> | ||
| 402 | - </section> | ||
| 403 | - </Tabs.TabPane> | ||
| 404 | - </Tabs> | ||
| 405 | - </section> | ||
| 406 | - </BasicModal> | ||
| 407 | - <Button @click="handleOpenModal" v-bind="getBindButtonProps"> | ||
| 408 | - <span v-if="!selectedRowKeys.length">选择设备</span> | ||
| 409 | - <div v-if="selectedRowKeys.length"> | ||
| 410 | - <Tag | ||
| 411 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
| 412 | - v-for="item in getShowTagOptions" | ||
| 413 | - :key="item.value" | ||
| 414 | - > | ||
| 415 | - <span> | ||
| 416 | - {{ item.alias || item.name }} | ||
| 417 | - </span> | ||
| 418 | - </Tag> | ||
| 419 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
| 420 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
| 421 | - </Tag> | ||
| 422 | - </div> | ||
| 423 | - </Button> | ||
| 424 | - </section> | ||
| 425 | -</template> | ||
| 426 | - | ||
| 427 | -<style lang="less"> | ||
| 428 | - @prefix-cls: ~'@{namespace}-transfer-table-modal'; | ||
| 429 | - | ||
| 430 | - .@{prefix-cls} { | ||
| 431 | - .vben-basic-table { | ||
| 432 | - padding-top: 0; | ||
| 433 | - } | ||
| 434 | - | ||
| 435 | - .vben-basic-form > .ant-row { | ||
| 436 | - width: 100%; | ||
| 437 | - } | ||
| 438 | - | ||
| 439 | - .ant-tabs-top-bar { | ||
| 440 | - background-color: #fff; | ||
| 441 | - } | ||
| 442 | - } | ||
| 443 | -</style> |
| @@ -25,19 +25,18 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { | @@ -25,19 +25,18 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { | ||
| 25 | const { t } = useI18n(); | 25 | const { t } = useI18n(); |
| 26 | 26 | ||
| 27 | const configRef = ref<PaginationProps>({ | 27 | const configRef = ref<PaginationProps>({ |
| 28 | - hideOnSinglePage:true | 28 | + hideOnSinglePage: true, |
| 29 | }); | 29 | }); |
| 30 | const show = ref(true); | 30 | const show = ref(true); |
| 31 | 31 | ||
| 32 | watchEffect(() => { | 32 | watchEffect(() => { |
| 33 | const { pagination } = unref(refProps); | 33 | const { pagination } = unref(refProps); |
| 34 | - | 34 | + |
| 35 | if (!isBoolean(pagination) && pagination) { | 35 | if (!isBoolean(pagination) && pagination) { |
| 36 | configRef.value = { | 36 | configRef.value = { |
| 37 | ...unref(configRef), | 37 | ...unref(configRef), |
| 38 | ...(pagination ?? {}), | 38 | ...(pagination ?? {}), |
| 39 | - }; | ||
| 40 | - | 39 | + }; |
| 41 | } | 40 | } |
| 42 | }); | 41 | }); |
| 43 | 42 |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | - import { Button, Tabs, Tag } from 'ant-design-vue'; | 2 | + import { Button, Tabs, Select } from 'ant-design-vue'; |
| 3 | import { remove, uniqBy, cloneDeep } from 'lodash'; | 3 | import { remove, uniqBy, cloneDeep } from 'lodash'; |
| 4 | - import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue'; | 4 | + import { nextTick, onMounted, ref, unref, toRaw, watch } from 'vue'; |
| 5 | import { | 5 | import { |
| 6 | deviceTableColumn, | 6 | deviceTableColumn, |
| 7 | deviceTableFormSchema, | 7 | deviceTableFormSchema, |
| @@ -21,8 +21,7 @@ | @@ -21,8 +21,7 @@ | ||
| 21 | 21 | ||
| 22 | const props = withDefaults( | 22 | const props = withDefaults( |
| 23 | defineProps<{ | 23 | defineProps<{ |
| 24 | - getPendingTableParams: (params: Recordable) => any; | ||
| 25 | - getSelectedTableParams: (params: Recordable) => any; | 24 | + params?: Recordable; |
| 26 | value?: (Recordable & DeviceModel)[]; | 25 | value?: (Recordable & DeviceModel)[]; |
| 27 | maxTagLength?: number; | 26 | maxTagLength?: number; |
| 28 | openModalValidate?: () => boolean; | 27 | openModalValidate?: () => boolean; |
| @@ -34,6 +33,7 @@ | @@ -34,6 +33,7 @@ | ||
| 34 | value: () => [], | 33 | value: () => [], |
| 35 | maxTagLength: 2, | 34 | maxTagLength: 2, |
| 36 | primaryKey: 'tbDeviceId', | 35 | primaryKey: 'tbDeviceId', |
| 36 | + params: () => ({}), | ||
| 37 | } | 37 | } |
| 38 | ); | 38 | ); |
| 39 | 39 | ||
| @@ -56,23 +56,6 @@ | @@ -56,23 +56,6 @@ | ||
| 56 | 56 | ||
| 57 | const selectedConfirmQueue = ref<DeviceModel[]>([]); | 57 | const selectedConfirmQueue = ref<DeviceModel[]>([]); |
| 58 | 58 | ||
| 59 | - const getShowTagOptions = computed(() => { | ||
| 60 | - const { maxTagLength } = props; | ||
| 61 | - return unref(selectedTotalList).slice(0, maxTagLength); | ||
| 62 | - }); | ||
| 63 | - | ||
| 64 | - const getSurplusOptionsLength = computed(() => { | ||
| 65 | - const { maxTagLength } = props; | ||
| 66 | - const surplusValue = unref(selectedTotalList).length - maxTagLength; | ||
| 67 | - return surplusValue < 0 ? 0 : surplusValue; | ||
| 68 | - }); | ||
| 69 | - | ||
| 70 | - // const pendingListCount = computed(() => { | ||
| 71 | - // const { value } = props; | ||
| 72 | - // const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id)); | ||
| 73 | - // return unref(pendingTotalList).length - selectedList.length; | ||
| 74 | - // }); | ||
| 75 | - | ||
| 76 | const [registerModal, { openModal }] = useModal(); | 59 | const [registerModal, { openModal }] = useModal(); |
| 77 | 60 | ||
| 78 | const [regsterPendingTable, pendingTableActionType] = useTable({ | 61 | const [regsterPendingTable, pendingTableActionType] = useTable({ |
| @@ -82,9 +65,8 @@ | @@ -82,9 +65,8 @@ | ||
| 82 | immediate: false, | 65 | immediate: false, |
| 83 | clickToRowSelect: false, | 66 | clickToRowSelect: false, |
| 84 | beforeFetch: (params) => { | 67 | beforeFetch: (params) => { |
| 85 | - const { getPendingTableParams } = props; | ||
| 86 | - const data = getPendingTableParams?.(params) || {}; | ||
| 87 | - Object.assign(params, { ...data, selected: false }); | 68 | + const { params: otherParams } = props; |
| 69 | + Object.assign(params, { ...otherParams, selected: false }); | ||
| 88 | return params; | 70 | return params; |
| 89 | }, | 71 | }, |
| 90 | afterFetch: (list: DeviceModel[]) => { | 72 | afterFetch: (list: DeviceModel[]) => { |
| @@ -150,9 +132,8 @@ | @@ -150,9 +132,8 @@ | ||
| 150 | dataSource: selectedTotalList, | 132 | dataSource: selectedTotalList, |
| 151 | clickToRowSelect: false, | 133 | clickToRowSelect: false, |
| 152 | beforeFetch: (params) => { | 134 | beforeFetch: (params) => { |
| 153 | - const { getSelectedTableParams } = props; | ||
| 154 | - const data = getSelectedTableParams?.(params) || {}; | ||
| 155 | - Object.assign(params, { ...data, selected: false }); | 135 | + const { params: otherParams } = props; |
| 136 | + Object.assign(params, { ...otherParams, selected: false }); | ||
| 156 | return params; | 137 | return params; |
| 157 | }, | 138 | }, |
| 158 | rowSelection: { | 139 | rowSelection: { |
| @@ -219,18 +200,31 @@ | @@ -219,18 +200,31 @@ | ||
| 219 | 200 | ||
| 220 | if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return; | 201 | if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return; |
| 221 | 202 | ||
| 203 | + activeKey.value = Active.PENDING; | ||
| 222 | openModal(true); | 204 | openModal(true); |
| 223 | await nextTick(); | 205 | await nextTick(); |
| 224 | pendingTableActionType.reload(); | 206 | pendingTableActionType.reload(); |
| 225 | }; | 207 | }; |
| 226 | 208 | ||
| 209 | + watch( | ||
| 210 | + () => props.params, | ||
| 211 | + () => { | ||
| 212 | + selectedTotalList.value = []; | ||
| 213 | + selectedConfirmQueue.value = []; | ||
| 214 | + pendingConfirmQueue.value = []; | ||
| 215 | + try { | ||
| 216 | + pendingTableActionType.clearSelectedRowKeys(); | ||
| 217 | + selectedTableActionType.clearSelectedRowKeys(); | ||
| 218 | + } catch (error) {} | ||
| 219 | + } | ||
| 220 | + ); | ||
| 221 | + | ||
| 227 | onMounted(async () => { | 222 | onMounted(async () => { |
| 228 | - const { getSelectedTableParams } = props; | ||
| 229 | - const data = getSelectedTableParams?.({}) || {}; | ||
| 230 | - if (!data?.convertConfigId || !data?.deviceProfileIds) { | 223 | + const { params } = props; |
| 224 | + if (!params?.convertConfigId || !params?.deviceProfileIds) { | ||
| 231 | return; | 225 | return; |
| 232 | } | 226 | } |
| 233 | - const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true }); | 227 | + const { items } = await devicePage({ page: 1, pageSize: 10, ...params, selected: true }); |
| 234 | selectedTotalList.value = items; | 228 | selectedTotalList.value = items; |
| 235 | }); | 229 | }); |
| 236 | </script> | 230 | </script> |
| @@ -239,7 +233,7 @@ | @@ -239,7 +233,7 @@ | ||
| 239 | <section> | 233 | <section> |
| 240 | <BasicModal | 234 | <BasicModal |
| 241 | @register="registerModal" | 235 | @register="registerModal" |
| 242 | - title="穿梭表格" | 236 | + title="设备选择" |
| 243 | width="60%" | 237 | width="60%" |
| 244 | :wrapClassName="prefixCls" | 238 | :wrapClassName="prefixCls" |
| 245 | :showOkBtn="false" | 239 | :showOkBtn="false" |
| @@ -251,13 +245,11 @@ | @@ -251,13 +245,11 @@ | ||
| 251 | <template #tab> | 245 | <template #tab> |
| 252 | <div class="flex items-center justify-center"> | 246 | <div class="flex items-center justify-center"> |
| 253 | <span>待选设备</span> | 247 | <span>待选设备</span> |
| 254 | - <!-- <Badge show-zero :count="pendingListCount" /> --> | ||
| 255 | </div> | 248 | </div> |
| 256 | </template> | 249 | </template> |
| 257 | <BasicTable @register="regsterPendingTable"> | 250 | <BasicTable @register="regsterPendingTable"> |
| 258 | <template #toolbar> | 251 | <template #toolbar> |
| 259 | <section class="flex w-full justify-end items-center"> | 252 | <section class="flex w-full justify-end items-center"> |
| 260 | - <!-- <Button type="primary">全选</Button> --> | ||
| 261 | <div class="text-blue-400"> | 253 | <div class="text-blue-400"> |
| 262 | <span class="mr-2">选择设备:</span> | 254 | <span class="mr-2">选择设备:</span> |
| 263 | <span>{{ pendingConfirmQueue.length }}</span> | 255 | <span>{{ pendingConfirmQueue.length }}</span> |
| @@ -279,7 +271,6 @@ | @@ -279,7 +271,6 @@ | ||
| 279 | <template #tab> | 271 | <template #tab> |
| 280 | <div class="flex items-center justify-center"> | 272 | <div class="flex items-center justify-center"> |
| 281 | <span>已选设备</span> | 273 | <span>已选设备</span> |
| 282 | - <!-- <Badge show-zero :count="selectedTotalList.length" /> --> | ||
| 283 | </div> | 274 | </div> |
| 284 | </template> | 275 | </template> |
| 285 | <BasicTable @register="registerSelectedTable"> | 276 | <BasicTable @register="registerSelectedTable"> |
| @@ -305,23 +296,22 @@ | @@ -305,23 +296,22 @@ | ||
| 305 | </Tabs> | 296 | </Tabs> |
| 306 | </section> | 297 | </section> |
| 307 | </BasicModal> | 298 | </BasicModal> |
| 308 | - <Button @click="handleOpenModal" type="link" :disabled="disabled"> | ||
| 309 | - <span v-if="!selectedTotalList.length">选择设备</span> | ||
| 310 | - <div v-if="selectedTotalList.length"> | ||
| 311 | - <Tag | ||
| 312 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
| 313 | - v-for="item in getShowTagOptions" | ||
| 314 | - :key="item[primaryKey]" | ||
| 315 | - > | ||
| 316 | - <span> | ||
| 317 | - {{ item.alias || item.name }} | ||
| 318 | - </span> | ||
| 319 | - </Tag> | ||
| 320 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
| 321 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
| 322 | - </Tag> | ||
| 323 | - </div> | ||
| 324 | - </Button> | 299 | + <Select |
| 300 | + placeholder="请选择设备" | ||
| 301 | + :disabled="disabled" | ||
| 302 | + :value="selectedTotalList.map((item) => item[primaryKey])" | ||
| 303 | + mode="multiple" | ||
| 304 | + :maxTagCount="3" | ||
| 305 | + :open="false" | ||
| 306 | + @dropdownVisibleChange="handleOpenModal" | ||
| 307 | + :options=" | ||
| 308 | + selectedTotalList.map((item) => ({ | ||
| 309 | + ...item, | ||
| 310 | + label: item.alias || item.name, | ||
| 311 | + value: item[primaryKey], | ||
| 312 | + })) | ||
| 313 | + " | ||
| 314 | + /> | ||
| 325 | </section> | 315 | </section> |
| 326 | </template> | 316 | </template> |
| 327 | 317 |
| @@ -73,6 +73,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | @@ -73,6 +73,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | ||
| 73 | labelField: 'name', | 73 | labelField: 'name', |
| 74 | valueField: 'tbProfileId', | 74 | valueField: 'tbProfileId', |
| 75 | disabled, | 75 | disabled, |
| 76 | + selectProps: { | ||
| 77 | + disabled, | ||
| 78 | + placeholder: '请选择产品', | ||
| 79 | + }, | ||
| 76 | transferProps: { | 80 | transferProps: { |
| 77 | listStyle: { height: '400px' }, | 81 | listStyle: { height: '400px' }, |
| 78 | showSearch: true, | 82 | showSearch: true, |
| @@ -98,26 +102,14 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | @@ -98,26 +102,14 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | ||
| 98 | valueField: 'value', | 102 | valueField: 'value', |
| 99 | changeEvent: 'update:value', | 103 | changeEvent: 'update:value', |
| 100 | rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], | 104 | rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], |
| 101 | - componentProps: ({ formActionType }) => { | ||
| 102 | - const { getFieldsValue } = formActionType; | 105 | + componentProps: ({ formModel }) => { |
| 106 | + const convertConfigId = formModel[BasicInfoFormField.CONVERT_CONFIG_ID]; | ||
| 107 | + const deviceProfileIds = formModel[BasicInfoFormField.DATA_SOURCE_PRODUCT]; | ||
| 103 | return { | 108 | return { |
| 104 | disabled, | 109 | disabled, |
| 105 | - getPendingTableParams: () => { | ||
| 106 | - const values = getFieldsValue(); | ||
| 107 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 108 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 109 | - return { convertConfigId, deviceProfileIds }; | ||
| 110 | - }, | ||
| 111 | - getSelectedTableParams: () => { | ||
| 112 | - const values = getFieldsValue(); | ||
| 113 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
| 114 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 115 | - return { convertConfigId, deviceProfileIds }; | ||
| 116 | - }, | 110 | + params: { convertConfigId, deviceProfileIds }, |
| 117 | transformValue: handleGroupDevice, | 111 | transformValue: handleGroupDevice, |
| 118 | openModalValidate: () => { | 112 | openModalValidate: () => { |
| 119 | - const values = getFieldsValue(); | ||
| 120 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
| 121 | if (!deviceProfileIds || !deviceProfileIds?.length) { | 113 | if (!deviceProfileIds || !deviceProfileIds?.length) { |
| 122 | createMessage.warning('请选择数据源设备'); | 114 | createMessage.warning('请选择数据源设备'); |
| 123 | } | 115 | } |