| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- <script lang="ts">
- import { tableSelectProps, tableSelectEmits } from './props'
- import { ElPopover } from 'element-plus'
- import type { VxeTableInstance } from 'vxe-table'
- export default defineComponent({
- name: 'FsTableSelect',
- props: tableSelectProps,
- emits: tableSelectEmits,
- setup(props, { emit }) {
- // popover 实例
- const popoverRef = ref<InstanceType<typeof ElPopover>>()
- // 表格实例
- const tableRef = ref<VxeTableInstance<any>>()
- // 是否显示
- const visible = ref(false)
- // 选中展示的数据
- const selectLabel = ref<string | Array<any>>('')
- // 加载状态
- const loading = ref(false)
- // 表格数据
- const tableData = ref<any[]>([])
- // 页码
- const pageIndex = ref<number>(1)
- // 是否未选中
- const isEmpty = computed<boolean>(() => {
- if (!props.multiple) {
- return props.modelValue == null || props.modelValue === ''
- }
- return !Array.isArray(props.modelValue) || !props.modelValue.length
- })
- // 是否需要清空图标
- const closeEnable = computed<boolean>(() => {
- return props.clearable && !props.disabled && !isEmpty.value
- })
- onMounted(() => {
- if (props.initValue) {
- initValueChange(props.initValue)
- }
- })
- /* 打开弹窗 */
- const onFocus = (e: FocusEvent) => {
- if (props.automaticDropdown && !visible.value) {
- visible.value = true
- }
- emit('focus', e)
- }
- /* 关闭弹窗 */
- const onBlur = (e: FocusEvent) => {
- emit('blur', e)
- }
- /* 清除事件 */
- const onClear = () => {
- updateModelValue(props.multiple ? [] : null)
- selectLabel.value = ''
- // 取消表格全部选中
- tableRef.value?.clearCheckboxRow()
- emit('clear')
- }
- /* 单个清除事件 */
- const onItemClear = (item: any) => {
- const list = [...(selectLabel.value as Array<any>)]
- const index = list.findIndex(x => x[props.labelKey] === item[props.labelKey])
- list.splice(index, 1)
- selectLabel.value = list
- // 取消表格选中数据
- tableRef.value?.toggleCheckboxRow(item)
- updateModelValue(list.map(x => x[props.valueKey]))
- emit('item-clear', { item, list })
- }
- /* 表格单选事件 */
- const tableRadioChange = (data: any) => {
- selectLabel.value = data.row[props.labelKey]
- visible.value = false
- // 发出选择事件
- updateModelValue(data.row[props.valueKey])
- emit('change', data.row)
- }
- /* 表格多选择事件 */
- const tableCheckboxChange = (data: any) => {
- let result = []
- if (data.checked) {
- // 使用 Set 去重
- const uniqueArray = Array.from(
- new Set([...selectLabel.value, ...data.records].map((x: any) => JSON.stringify(x)))
- ).map((str: any) => JSON.parse(str))
- selectLabel.value = uniqueArray
- result = selectLabel.value.map(x => x[props.valueKey])
- } else {
- const selects = selectLabel.value as Array<any>
- const index = selects.findIndex(x => x[props.valueKey] === data.row[props.valueKey])
- selects?.splice(index, 1)
- result = selects.map(x => x[props.valueKey])
- }
- // 发出选择事件
- updateModelValue(result)
- emit('change', selectLabel.value)
- }
- /* initValue 改变 */
- const initValueChange = (value: any | Array<any>) => {
- if (props.initValue) {
- // 处理回显数据
- if (props.multiple) {
- selectLabel.value = value as Array<any>
- } else {
- selectLabel.value = value[props.labelKey] as any
- }
- }
- }
- /* 分页改变事件 */
- const paginationChange = (data: number) => {
- pageIndex.value = data
- request()
- }
- /* 表格请求完成 */
- const tableDone = () => {
- nextTick(() => {
- const newTableData = tableRef.value?.getTableData().tableData
- newTableData?.forEach(item => {
- if (props.multiple) {
- const temp =
- Array.isArray(selectLabel.value) &&
- selectLabel.value.find(x => x[props.valueKey] === item[props.valueKey])
- temp && tableRef.value?.setCheckboxRow(item, true)
- } else {
- const temp = item[props.valueKey] === props.modelValue
- temp && tableRef.value?.setRadioRow(item)
- }
- })
- })
- }
- /* 请求数据 */
- const request = () => {
- if (typeof props.tableConfig?.datasource === 'function') {
- loading.value = true
- props.tableConfig
- .datasource({
- pageIndex: pageIndex.value,
- pageSize: props.tableConfig.pageSize
- })
- .then((res: any) => {
- tableData.value = res
- tableDone()
- })
- .catch((e: any) => {
- console.warn(e)
- })
- .finally(() => {
- loading.value = false
- })
- } else {
- console.warn('tableConfig.datasource 必须为 Promise')
- }
- }
- request()
- /* 更新选中值 */
- const updateModelValue = (value: any) => {
- emit('update:modelValue', value)
- }
- watch(
- () => props.initValue,
- () => {
- initValueChange(props.initValue as object | Array<any>)
- }
- )
- return {
- popoverRef,
- tableRef,
- selectLabel,
- visible,
- isEmpty,
- loading,
- tableData,
- closeEnable,
- onFocus,
- onBlur,
- onClear,
- onItemClear,
- tableRadioChange,
- tableCheckboxChange,
- paginationChange
- }
- }
- })
- </script>
- <template>
- <el-popover
- ref="popoverRef"
- v-model:visible="visible"
- :placement="placement"
- :width="popperWidth"
- :popper-class="popperClass"
- :popper-options="popperOptions"
- trigger="click"
- transition="el-zoom-in-top"
- teleported
- >
- <template #reference>
- <div class="table-select-container">
- <el-input
- :size="size"
- :disabled="disabled"
- :placeholder="multiple && !isEmpty ? '' : placeholder"
- :readonly="true"
- :validateEvent="false"
- :autocomplete="autocomplete"
- :modelValue="multiple ? '' : selectLabel"
- @focus="onFocus"
- @blur="onBlur"
- >
- <template v-if="$slots.prefix" #prefix>
- <slot name="prefix"></slot>
- </template>
- <template #suffix>
- <div class="select-suffix">
- <ElIcon v-if="closeEnable" class="select-clear" @click.stop="onClear">
- <slot name="clearIcon">
- <CircleClose />
- </slot>
- </ElIcon>
- <ElIcon class="select-down" :class="{ 'select-down-rotate': visible }">
- <slot name="suffixIcon">
- <ArrowDown />
- </slot>
- </ElIcon>
- </div>
- </template>
- </el-input>
- <div class="table-select-multiple" v-if="multiple">
- <el-tag
- type="info"
- size="small"
- disable-transitions
- closable
- style="margin-right: 5px"
- v-for="item in selectLabel"
- :key="item[valueKey]"
- @close="onItemClear(item)"
- >
- {{ item[labelKey] }}
- </el-tag>
- </div>
- </div>
- </template>
- <vxe-table
- :data="tableData"
- :row-config="{ isCurrent: true, isHover: true }"
- :radio-config="{ trigger: multiple ? '' : 'row' }"
- :checkbox-config="{ trigger: multiple ? 'row' : '' }"
- ref="tableRef"
- @radio-change="tableRadioChange"
- @checkbox-all="tableCheckboxChange"
- @checkbox-change="tableCheckboxChange"
- v-loading="loading"
- >
- <vxe-column type="checkbox" width="60" v-if="multiple"></vxe-column>
- <vxe-column type="radio" width="40" v-else> </vxe-column>
- <vxe-column v-for="(item, index) in tableConfig?.column" :key="index" v-bind="item">
- <template #default="slotProps" v-if="item.slot">
- <slot :name="item.slot" v-bind="slotProps || {}"></slot>
- </template>
- </vxe-column>
- </vxe-table>
- <div class="table-select-pagination" v-if="tableConfig.total">
- <el-pagination
- background
- layout="total, prev, pager, next, jumper"
- size="small"
- :pager-count="5"
- :page-size="tableConfig.pageSize"
- :total="tableConfig.total"
- @current-change="paginationChange"
- />
- </div>
- </el-popover>
- </template>
- <style scoped lang="scss">
- .table-select-container {
- width: 100%;
- position: relative;
- .select-clear {
- position: absolute;
- right: 10px;
- top: 49%;
- transform: translateY(-50%);
- cursor: pointer;
- border-radius: 50%;
- overflow: hidden;
- z-index: 2;
- color: #fff;
- background-color: var(--el-color-info-light-3);
- opacity: 0;
- &:hover {
- background-color: var(--el-color-info);
- }
- }
- .select-down {
- transition: transform 0.2s;
- }
- .select-down-rotate {
- transform: rotate(180deg);
- }
- &:hover {
- .select-clear {
- opacity: 1;
- }
- }
- }
- .table-select-multiple {
- position: absolute;
- top: 0px;
- width: calc(100% - 24px);
- height: 100%;
- z-index: 2;
- padding: 4px 10px;
- box-sizing: border-box;
- }
- .table-select-pagination {
- display: flex;
- justify-content: center;
- margin-top: 12px;
- }
- </style>
|