|
|
@@ -6,6 +6,7 @@ import { Icon } from '@/components/Icon';
|
|
|
|
|
|
import './style/SmartPullownTable.less';
|
|
|
import { useRuleFormItem } from '@/hooks/component/useFormItem';
|
|
|
+import { Row, Col } from 'ant-design-vue';
|
|
|
|
|
|
/**
|
|
|
* 下拉表格
|
|
|
@@ -28,14 +29,74 @@ export default defineComponent({
|
|
|
api: {
|
|
|
type: Function as PropType<() => Promise<any>>,
|
|
|
},
|
|
|
+ showSearch: propTypes.bool.def(true),
|
|
|
+ allowClear: propTypes.bool.def(true),
|
|
|
+ filterOption: {
|
|
|
+ type: Function as PropType<(searchValue: string, row: Recordable) => boolean>,
|
|
|
+ },
|
|
|
+ searchIgnoreCase: propTypes.bool.def(true),
|
|
|
+ // 弹窗高度
|
|
|
+ height: propTypes.number,
|
|
|
},
|
|
|
- emits: ['select', 'change', 'update:value'],
|
|
|
+ emits: ['select', 'change', 'update:value', 'visible-change'],
|
|
|
setup(props, { emit, slots, attrs }) {
|
|
|
- const { showField, dropdownWidth, value: valueRef } = toRefs(props);
|
|
|
+ const {
|
|
|
+ showField,
|
|
|
+ dropdownWidth,
|
|
|
+ value: valueRef,
|
|
|
+ showSearch: showSearchRef,
|
|
|
+ allowClear: allowClearRef,
|
|
|
+ height: heightRef,
|
|
|
+ } = toRefs(props);
|
|
|
const pulldownRef = ref<VxePulldownInstance>();
|
|
|
const dataLoadingRef = ref<boolean>(false);
|
|
|
const tableDataRef = ref<Recordable[]>([]);
|
|
|
const tableRef = ref();
|
|
|
+ const searchValueRef = ref<string>('');
|
|
|
+
|
|
|
+ const filterOptionFunction = props.filterOption
|
|
|
+ ? props.filterOption
|
|
|
+ : (searchValue: string, row: Recordable) => {
|
|
|
+ if (!searchValue || searchValue.trim().length === 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ const searchValueTrim = searchValue.trim();
|
|
|
+ let showValue = '';
|
|
|
+ if (props.showFunction) {
|
|
|
+ showValue = props.showFunction(row);
|
|
|
+ } else {
|
|
|
+ showValue = row[props.showField];
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ (props.searchIgnoreCase ? showValue.toUpperCase() : showValue).includes(searchValueTrim)
|
|
|
+ ) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ const fieldValue: string = row[props.valueField].toString();
|
|
|
+ return (props.searchIgnoreCase ? fieldValue.toUpperCase() : fieldValue).includes(
|
|
|
+ searchValueTrim,
|
|
|
+ );
|
|
|
+ };
|
|
|
+ const computeTableData = computed(() => {
|
|
|
+ const tableData = unref(tableDataRef);
|
|
|
+ if (tableData.length === 0) {
|
|
|
+ return tableData;
|
|
|
+ }
|
|
|
+ const searchValue = unref(searchValueRef);
|
|
|
+ if (!searchValue) {
|
|
|
+ return tableData;
|
|
|
+ }
|
|
|
+ return tableData.filter((item) =>
|
|
|
+ filterOptionFunction(
|
|
|
+ props.searchIgnoreCase ? searchValue.toUpperCase() : searchValue,
|
|
|
+ item,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ });
|
|
|
+ /**
|
|
|
+ * 下拉框是否显示
|
|
|
+ */
|
|
|
+ const isVisibleRef = ref(false);
|
|
|
// 选中的行
|
|
|
const selectRowRef = ref<Recordable | null>(null);
|
|
|
const computedSelectRowValue = computed(() => {
|
|
|
@@ -85,46 +146,67 @@ export default defineComponent({
|
|
|
* 下拉容器样式
|
|
|
*/
|
|
|
const computedDropdownContainerStyle = computed(() => {
|
|
|
- return {
|
|
|
+ const style: Recordable = {
|
|
|
width: unref(dropdownWidth) + 'px',
|
|
|
};
|
|
|
+ const height = unref(heightRef);
|
|
|
+ if (height) {
|
|
|
+ style.height = `${height}px`;
|
|
|
+ }
|
|
|
+ return style;
|
|
|
});
|
|
|
|
|
|
watch([valueRef, tableDataRef], ([value, tableData]) => {
|
|
|
- const tableInstance: VxeGridInstance = unref(tableRef)?.tableAction.getTableInstance();
|
|
|
let row: Recordable | null = null;
|
|
|
- if (!value) {
|
|
|
- tableInstance && tableInstance.clearCurrentRow();
|
|
|
- selectRowRef.value = null;
|
|
|
- return;
|
|
|
- }
|
|
|
- const selectRows = tableData.filter(
|
|
|
- (item) => item[props.valueField].toString() === value.toString(),
|
|
|
- );
|
|
|
- if (selectRows.length === 0) {
|
|
|
- tableInstance && tableInstance.clearCurrentRow();
|
|
|
- } else {
|
|
|
- row = selectRows[0];
|
|
|
- tableInstance && tableInstance.setCurrentRow(selectRows[0]);
|
|
|
+ if (value) {
|
|
|
+ const selectRows = tableData.filter(
|
|
|
+ (item) => item[props.valueField].toString() === value.toString(),
|
|
|
+ );
|
|
|
+ if (selectRows.length > 0) {
|
|
|
+ row = selectRows[0];
|
|
|
+ }
|
|
|
}
|
|
|
selectRowRef.value = row;
|
|
|
+ setCurrentRow();
|
|
|
});
|
|
|
|
|
|
/**
|
|
|
* 显示弹窗
|
|
|
*/
|
|
|
const handleShow = async () => {
|
|
|
+ isVisibleRef.value = true;
|
|
|
unref(pulldownRef)?.showPanel();
|
|
|
if (props.alwaysLoad) {
|
|
|
await loadTableData();
|
|
|
}
|
|
|
+ emit('visible-change', true);
|
|
|
+ setCurrentRow();
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置当前行
|
|
|
+ */
|
|
|
+ const setCurrentRow = () => {
|
|
|
nextTick(() => {
|
|
|
const tableInstance: VxeGridInstance = unref(tableRef)?.tableAction.getTableInstance();
|
|
|
+ if (!tableInstance) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
const row = unref(selectRowRef);
|
|
|
- row && tableInstance && tableInstance.setCurrentRow(row);
|
|
|
+ if (!row) {
|
|
|
+ tableInstance.clearCurrentRow();
|
|
|
+ } else {
|
|
|
+ tableInstance.setCurrentRow(row);
|
|
|
+ }
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ const handleHide = () => {
|
|
|
+ isVisibleRef.value = false;
|
|
|
+ searchValueRef.value = '';
|
|
|
+ emit('visible-change', false);
|
|
|
+ };
|
|
|
+
|
|
|
const dropdownTableProps = {
|
|
|
size: 'mini',
|
|
|
...props.tableProps,
|
|
|
@@ -135,6 +217,7 @@ export default defineComponent({
|
|
|
onCellClick: ({ row }) => {
|
|
|
changeValue(row);
|
|
|
unref(pulldownRef)?.hidePanel();
|
|
|
+ handleHide();
|
|
|
},
|
|
|
};
|
|
|
|
|
|
@@ -143,20 +226,58 @@ export default defineComponent({
|
|
|
state.value = row?.[props.valueField];
|
|
|
};
|
|
|
|
|
|
- const inputIconSlots = {
|
|
|
- addonAfter: () => <Icon onClick={handleShow} icon="ant-design:table-outlined" />,
|
|
|
- clearIcon: () => <Icon onClick={() => changeValue()} icon="ant-design:close-circle-filled" />,
|
|
|
+ /**
|
|
|
+ * 搜索框变化触发
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ const handleSearchChange = (value: string) => {
|
|
|
+ searchValueRef.value = value;
|
|
|
+ setCurrentRow();
|
|
|
+ };
|
|
|
+
|
|
|
+ // const inputIconSlots = {
|
|
|
+ // addonAfter: () => <Icon onClick={handleShow} icon="ant-design:table-outlined" />,
|
|
|
+ // clearIcon: () => <Icon onClick={() => changeValue()} icon="ant-design:close-circle-filled" />,
|
|
|
+ // };
|
|
|
+ //
|
|
|
+ // const selectSlots = {
|
|
|
+ // suffixIcon: () => <Icon onClick={handleShow} icon="ant-design:table-outlined" />,
|
|
|
+ // };
|
|
|
+
|
|
|
+ const handleUpdateValue = () => {
|
|
|
+ changeValue();
|
|
|
+ handleShow();
|
|
|
};
|
|
|
|
|
|
const pulldownSlots = {
|
|
|
default: () => (
|
|
|
- <a-input onFocus={handleShow} value={unref(computedSelectRowValue)} {...attrs}>
|
|
|
- {inputIconSlots}
|
|
|
- </a-input>
|
|
|
+ // <div>
|
|
|
+ // <a-input onFocus={handleShow} value={unref(computedSelectRowValue)} {...attrs}>
|
|
|
+ // {inputIconSlots}
|
|
|
+ // </a-input>
|
|
|
+ // <span>abc</span>
|
|
|
+ // </div>
|
|
|
+ <Row type="flex" class={unref(isVisibleRef) ? 'smart-pulldown-open' : ''}>
|
|
|
+ <Col class="select">
|
|
|
+ <a-select
|
|
|
+ allowClear={unref(allowClearRef)}
|
|
|
+ showSearch={unref(showSearchRef)}
|
|
|
+ onFocus={handleShow}
|
|
|
+ open={false}
|
|
|
+ onUpdate:value={handleUpdateValue}
|
|
|
+ searchValue={unref(searchValueRef)}
|
|
|
+ onSearch={handleSearchChange}
|
|
|
+ value={unref(computedSelectRowValue)}
|
|
|
+ ></a-select>
|
|
|
+ </Col>
|
|
|
+ <Col class="suffix-icon">
|
|
|
+ <Icon onClick={handleShow} icon="ant-design:table-outlined" />
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
),
|
|
|
dropdown: () => (
|
|
|
<div style={unref(computedDropdownContainerStyle)}>
|
|
|
- <SmartTable data={unref(tableDataRef)} ref={tableRef} {...dropdownTableProps} />
|
|
|
+ <SmartTable data={unref(computeTableData)} ref={tableRef} {...dropdownTableProps} />
|
|
|
</div>
|
|
|
),
|
|
|
...slots,
|
|
|
@@ -164,6 +285,7 @@ export default defineComponent({
|
|
|
return () => (
|
|
|
<vxe-pulldown
|
|
|
ref={pulldownRef}
|
|
|
+ onHidePanel={handleHide}
|
|
|
{...attrs}
|
|
|
popup-class-name="smart-pullown-table"
|
|
|
class-name="smart-pullown-input"
|