效果图:
1.声明 Props类型
export type comboGridPropType = {
modelValue: any;
url: string;
keyField?: string;
labelField?: string;
filterOptions?: Array<ISearchOption>;
tableColumns?: Array<TableColumns>;
enableField?: string;
multiple?: boolean;
width?: number | string;
panelWidth?: number;
defaultPageSize?: number;
autoSelectFirst?: boolean; //自动选择第一项
sort?: string;
order?: "desc" | "asc";
queryOp: "Contains" | "BeginsWith" | "Equal";
disabled?: boolean; //禁用
searchable?: boolean; // 搜索
isTreeData?: boolean; // 树结构数据
treeProps?: { hasChildren?: string; children?: string };
};
2.后端数据结构
后端返回的数据结构:
using System;
using System.Collections.Generic;
using System.Text;
namespace WebiMes.Models.ViewModels
{
/// <summary>
/// 翻页返回集合
/// </summary>
public sealed class PagenationResult
{
public PagenationResult()
{
}
/// <summary>
/// 当前页
/// </summary>
public int pageIndex { get; set; }
/// <summary>
/// 每页显示
/// </summary>
public int pageSize { get; set; }
/// <summary>
/// 页码token(弃用)
/// </summary>
public string PageNationToken { get; set; }
/// <summary>
/// 数据集
/// </summary>
public IEnumerable<Object> List { get; set; }
/// <summary>
/// 行数
/// </summary>
public long Total { get; set; }
/// <summary>
/// 额外数据
/// </summary>
public Object? ExtenalData { get; set; }
}
}
3.注意事项
1.如果不指定 keyfield和labelfield,默认将返回数据的第一列作为 keyfield和labelfield
2.filterOptions搜索字段类型
id: number;
label: string;
value: string;
type: "string" | "boolean" | "date" | "number";
3.tableColumns表格列数据
{
/** 是否隐藏 */
hide?: boolean | CallableFunction;
/** 自定义列的内容插槽 */
slot?: string;
/** 自定义表头的内容插槽 */
headerSlot?: string;
/** 多级表头,内部实现原理:嵌套 `el-table-column` */
children?: Array<TableColumns>;
/** 自定义单元格渲染器(`jsx`语法) */
cellRenderer?: (data: TableColumnRenderer) => VNode;
/** 自定义头部渲染器(`jsx`语法) */
headerRenderer?: (data: TableColumnRenderer) => VNode;
/** 显示的标题 */
label?: string;
/** 字段名称,对应列内容的字段名,也可以使用 `property` 属性 */
prop?: string | ((index: number) => string);
/** 对应列的类型,如果设置了 `selection` 则显示多选框;如果设置了 `index` 则显示该行的索引(从 `1` 开始计算);如果设置了 `expand` 则显示为一个可展开的按钮 */
type?: TableColumnType;
/** 如果设置了 `type=index`,可以通过传递 `index` 属性来自定义索引 */
index?: number | ((index: number) => number);
/** `column` 的 `key`, 如果需要使用 `filter-change` 事件,则需要此属性标识是哪个 `column` 的筛选条件 */
columnKey?: string;
/** 对应列的宽度 */
width?: string | number;
/** 对应列的最小宽度,对应列的最小宽度,与 `width` 的区别是 `width` 是固定的,`min-width` 会把剩余宽度按比例分配给设置了 `min-width` 的列 */
minWidth?: string | number;
/** 列是否固定在左侧或者右侧。`true` 表示固定在左侧 */
fixed?: TableColumnFixed;
/** 列标题 `Label` 区域渲染使用的 `Function` */
renderHeader?: (data: RH) => VNode;
/** 对应列是否可以排序, 如果设置为 `'custom'`,则代表用户希望远程排序,需要监听 `Table` 的 `sort-change `事件,默认值为 `false` */
sortable?: TableColumnSortable;
/** 指定数据按照哪个属性进行排序,仅当 `sortable` 设置为 `true` 的时候有效。应该如同 `Array.sort` 那样返回一个 `Number` */
sortMethod?: (a: any, b: any) => number;
/** 指定数据按照哪个属性进行排序,仅当 `sortable` 设置为 `true` 且没有设置 `sort-method` 的时候有效。如果 `sort-by` 为数组,则先按照第 `1` 个属性排序,如果第 `1` 个相等,再按照第 `2` 个排序,以此类推 */
sortBy?: string | ((row: any, index: number) => string) | string[];
/** 数据在排序时所使用排序策略的轮转顺序,仅当 `sortable` 为 `true` 时有效。需传入一个数组,随着用户点击表头,该列依次按照数组中元素的顺序进行排序,默认值为 `['ascending', 'descending', null]` */
sortOrders?: Array<TableColumnSortOrders>;
/** 对应列是否可以通过拖动改变宽度(需要在 `el-table` 上设置 `border` 属性为真),默认值为 `true` */
resizable?: boolean;
/** 用来格式化内容 */
formatter?: (row: any, column: TableColumnCtx<any>, cellValue: any, index: number) => VNode | string;
/** 当内容过长被隐藏时显示 `tooltip`,默认值为 `false` */
showOverflowTooltip?: boolean;
/** 对齐方式,默认值为 `left` */
align?: Align;
/** 表头对齐方式,若不设置该项,则使用表格的对齐方式 */
headerAlign?: Align;
/** 列的 `className` */
className?: string;
/** 当前列标题的自定义类名 */
labelClassName?: string;
/** 仅对 `type=selection` 的列有效,类型为 `Function`,`Function` 的返回值用来决定这一行的 `CheckBox` 是否可以勾选 */
selectable?: (row: any, index: number) => boolean;
/** 仅对 `type=selection` 的列有效,请注意,需指定 `row-key` 来让这个功能生效,默认值为 `false` */
reserveSelection?: boolean;
/** 数据过滤的选项,数组格式,数组中的元素需要有 `text` 和 `value` 属性。数组中的每个元素都需要有 `text` 和 `value` 属性 */
filters?: Array<{
text: string;
value: string;
}>;
/** 过滤弹出框的定位 */
filterPlacement?: TableColumnFilterPlacement;
/** 数据过滤的选项是否多选,默认值为 `true` */
filterMultiple?: boolean;
/** 数据过滤使用的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 `true` 就会显示 */
filterMethod?: FilterMethods;
/** 选中的数据过滤项,如果需要自定义表头过滤的渲染方式,可能会需要此属性 */
filteredValue?: Array<any>;
}
4.完整代码
combogrid.vue
<script setup lang="ts">
import { ref, onMounted, watch, toRefs } from "vue";
import iSearch from "@iconify-icons/ep/search";
import dayjs from "dayjs";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import _ from "lodash";
import { http } from "@/utils/http";
import {
IResultTable,
IFilterRule,
FilterOp,
FilterValueType
} from "@/api/baseTypes";
import { message } from "@/utils/message";
import { comboGridPropType, ISearchOption } from "./types";
// import { useUserStoreHook } from "@/store/modules/user";
defineOptions({
name: "ComboGrid"
});
// statting in Vue 3.4
// const modelValue = defineModel<any>({ default: "" });
/** 配置默认值 */
const props = withDefaults(defineProps<comboGridPropType>(), {
keyField: "", //主键Id
labelField: "", //主键Id
tableColumns: () => [],
filterOptions: () => [],
defaultPageSize: 10, //默认每页展示数量
multiple: false, //多选
width: 180, //宽度
panelWidth: 700, //高度
autoSelectFirst: false, //自动选中第一条
sort: "",
order: "asc",
queryOp: "Contains",
disabled: false,
searchable: true,
isTreeData: false,
treeProps: () => ({ hasChildren: "hasChildren", children: "children" })
});
/** emits */
const emits = defineEmits<{
(e: "update:modelValue", value: any): void;
(e: "change", value: any): void;
}>();
/** 已初始化 */
const inited = ref(false);
//解构出来 ref
const {
url, //搜索数据链接
keyField: _keyField, //主键Id
labelField: _labelField, //选中展示Label
enableField,
tableColumns,
defaultPageSize, //默认每页展示数量
filterOptions: _filterOptions, //搜索字段 数据集
queryOp,
disabled
} = toRefs(props);
const keyField = ref(""); //主键Id2
keyField.value = _keyField.value;
const labelField = ref(""); //选中展示Label
labelField.value = _labelField.value;
const filterOptions = ref<ISearchOption[]>([]); //搜索字段 数据集
filterOptions.value = _filterOptions.value;
/**初始化字段 */
const isInitColumn = ref(false);
isInitColumn.value = tableColumns.value?.length <= 0;
if (!tableColumns.value?.some(x => x.type === "index")) {
tableColumns.value.splice(0, 0, {
type: "index",
label: "#",
align: "center",
index: (index: number) => {
return (currentPage.value - 1) * defaultPageSize.value + index + 1;
}
});
}
const loadingList = ref(false); //加载数据列表
const tableData = ref([]); //表格数据
const selectRef = ref(); //选择框Ref
const tabRef = ref(); //数据展示Ref
const fieldSelectRef = ref(); //筛选字段Ref
//搜索字段
const filterField = ref<ISearchOption>();
/**初始化搜索字段 */
const isInitfilterOptions = ref(false);
//搜索字段 数组
if (_.isArray(filterOptions.value) && filterOptions.value.length > 0) {
filterField.value = filterOptions.value[0];
} else isInitfilterOptions.value = true;
// 当前账户
// const useUserStore = useUserStoreHook();
// 已选数据的Label
const selectLabel = ref<string[]>([]);
// 搜索值
const searchQuery = ref();
/** 搜索 */
function onSearch() {
let searchKey;
let op: FilterOp = FilterOp.BeginsWith;
if (queryOp.value === "Contains" || queryOp.value === "Equal") {
const FilterOpStr: keyof typeof FilterOp = queryOp.value;
op = FilterOp[FilterOpStr];
}
let valType: FilterValueType = FilterValueType.String;
if (filterField.value?.type === "boolean") {
const lowerVal = searchQuery.value.toLowerCase();
if (lowerVal === "true") searchKey = true;
if (lowerVal === "false") searchKey = false;
if (_.isNumber(lowerVal)) searchKey = Number(lowerVal) > 0;
if (_.isEmpty(searchKey)) {
message("搜索数据类型不匹配" + filterField.value.type, {
type: "warning",
showClose: true,
duration: 3000
});
return false;
}
op = FilterOp.Equal;
valType = FilterValueType.Boolean;
} else if (filterField.value?.type === "date") {
const dateVal = new Date(searchQuery.value);
if (!_.isDate(dateVal)) {
message("搜索数据类型不匹配" + filterField.value.type, {
type: "warning",
showClose: true,
duration: 3000
});
return false;
} else {
searchKey = dateVal;
op = FilterOp.GreaterThanOrEqual;
valType = FilterValueType.Datetime;
}
} else {
searchKey = searchQuery.value;
}
if (_searhFilters.length > 1)
_searhFilters.splice(1, _searhFilters.length - 1);
if (_.isEmpty(searchKey) || _.isEmpty(filterField.value)) {
// message(`搜索 类型或值,为空,将搜索所有数据`, {
// type: "warning",
// showClose: true,
// duration: 3000
// });
const sfilter = _searhFilters.find(
x => x.field == filterField.value?.value
);
if (_.isEmpty(sfilter)) {
_searhFilters.push({
field: filterField.value?.value, // 字段
op: op, // 比较符号
value: searchKey, // 值
valType: valType // 值类型
});
} else {
_searhFilters[0].op = op;
_searhFilters[0].valType = valType;
_searhFilters[0].value = "";
}
} else {
const sfilter = _searhFilters.find(x => x.field == filterField.value.value);
if (_.isEmpty(sfilter)) {
_searhFilters.push({
field: filterField.value.value, // 字段
op: op, // 比较符号
value: searchKey, // 值
valType: valType // 值类型
});
} else {
_searhFilters[0].op = op;
_searhFilters[0].valType = valType;
_searhFilters[0].value = searchKey;
}
}
currentPage.value = 1;
getTabelData();
}
/** 选中的 行 */
const selectedRows = ref([]);
/** 更新v-Model */
function updateVModel() {
if (!props.multiple) {
if (!_.isEmpty(selectedRows.value)) {
const newValue = selectedRows.value[0][keyField.value];
emits("update:modelValue", newValue);
emits("change", selectedRows.value[0]);
} else {
emits("update:modelValue", null);
emits("change", null);
}
} else {
const newValues = selectedRows.value.map(x => x[keyField.value]);
emits("update:modelValue", newValues);
emits("change", selectedRows.value);
}
}
/** 行选中 */
function handleRowClick(currentRow, _column, _event) {
if (!_.isEmpty(currentRow)) {
if (
!_.isEmpty(enableField.value) &&
!_.isEmpty(currentRow.value) &&
_.isBoolean(currentRow[enableField.value]) &&
!currentRow[enableField.value]
) {
message("没有权限选择本条数据", { type: "warning" });
return false;
}
let removeIndex = -1;
let some = false;
selectedRows.value.forEach((x, index) => {
if (x[keyField.value] === currentRow[keyField.value]) {
some = true;
removeIndex = index;
return false;
}
});
const _label = currentRow[labelField.value];
if (!props.multiple && selectedRows.value.length > 0) {
//清空
selectedRows.value.splice(0, selectedRows.value.length);
selectLabel.value.splice(0, selectLabel.value.length);
}
if (!some) {
selectedRows.value.push(currentRow);
} else {
if (removeIndex >= 0) selectedRows.value.splice(removeIndex, 1);
}
selectLabel.value.forEach((x, index) => {
if (x === _label) {
some = true;
removeIndex = index;
return false;
}
});
if (!some) {
selectLabel.value.push(_label);
} else {
if (removeIndex >= 0) selectLabel.value.splice(removeIndex, 1);
}
/** 更新v-Model */
updateVModel();
//单选自动关闭
if (!props.multiple) selectRef.value.blur();
set_ElSelectTagsText();
}
}
/** 暂存搜索数据 */
const _searhFilters: IFilterRule[] = [];
if (!_.isEmpty(filterField.value?.value)) {
_searhFilters.push({
field: filterField.value?.value, // 字段
op: FilterOp.Equal, // 比较符号
value: "", // 值
valType: FilterValueType.String // 值类型
});
}
/**翻页配置 */
const currentPage = ref(1);
// const defaultPageSize = ref(10);
const totalRecord = ref(0);
/** 翻页 */
async function handlePageChange(_currentPage) {
currentPage.value = _currentPage;
await getTabelData();
}
/** 获取数据信息 */
async function getTabelData(init = false) {
try {
loadingList.value = true;
// // 数据为空时,强制设定头
// if (!init) init = !_.isEmpty(tableData.value);
let res: IResultTable;
if (!_.isEmpty(url.value)) {
res = await http.request<IResultTable>("get", url.value, {
params: {
searhFilters: JSON.stringify(
_searhFilters.filter(x => !_.isEmpty(x))
),
page: currentPage.value,
limit: defaultPageSize.value,
sort: props.sort,
order: props.order
}
});
}
if (res && res.isSuccess) {
tableData.value = res.data.list;
totalRecord.value = res.data.total;
if (init || !inited.value) {
if (!_.isEmpty(tableData.value)) {
const top = tableData.value[0];
const arrEntry = Object.entries(top);
let i = 0;
// tableColumns没有配置的字段,新增进去
for (const [key, val] of arrEntry) {
i++;
if (i == 1) {
// 配置值字段,和显示字段
if (_.isEmpty(keyField.value)) {
keyField.value = _.cloneDeep(key);
}
if (_.isEmpty(labelField.value)) {
labelField.value = keyField.value;
}
}
if (isInitColumn.value) {
if (!tableColumns.value.some(x => x.prop == key))
tableColumns.value.push({ label: key, prop: key });
}
if (isInitfilterOptions.value) {
if (!filterOptions.value?.some(x => x.label == key)) {
let _type: "date" | "boolean" | "string" = "string";
const dateObj = dayjs(val?.toString());
if (dateObj.isValid() && dateObj > dayjs("2020-01-01")) {
_type = "date";
}
if (_.isBoolean(val)) {
_type = "boolean";
}
const filterOption: ISearchOption = {
id: i,
label: key,
value: key,
type: _type
};
filterOptions.value.push(filterOption);
}
}
}
//选中第一行
if (props.autoSelectFirst && _.isEmpty(props.modelValue)) {
const firstRow = tableData.value[0];
selectLabel.value.push(firstRow[labelField.value]);
selectedRows.value.push(firstRow);
} else {
if (!_.isEmpty(props.modelValue)) {
// 首次加载选中项
if (!props.multiple) {
// if (!_.isArray(props.modelValue)) {
// const seltRows = tableData.value.filter(
// x => x[keyField.value] == props.modelValue
// );
// if (!_.isEmpty(seltRows)) selectedRows.value = [seltRows[0]];
// }
} else {
selectedRows.value = tableData.value.filter(x =>
props.modelValue.some(n => n == x[keyField.value])
);
}
selectedRows.value.forEach(item => {
selectLabel.value.push(item[labelField.value]);
});
}
}
// 已初始化
inited.value = true;
}
} else {
//清空
_searhFilters.splice(0, _searhFilters.length);
}
} else {
if (!_.isEmpty(url.value))
message(`获取数据出错:${res.errMessage}`, { type: "error" });
}
} catch (error) {
message(`获取数据出错:${error.message}`, { type: "error" });
} finally {
loadingList.value = false;
}
}
/**行class */
const tableRowClassName = ({
row,
_rowIndex
}: {
row: any;
_rowIndex: number;
}) => {
if (selectedRows.value.some(x => x[keyField.value] === row[keyField.value])) {
return "success-row";
}
if (_.isBoolean(row?._Enable) && !row._Enable) return "warning-row";
return "";
};
/** 设置el-select_Tags-text Title */
function set_ElSelectTagsText() {
if (!_.isEmpty(selectedRows.value)) {
const maxTimes = 100;
let timeNUm = 0;
const _$el = selectRef.value.$el;
const len = selectedRows.value.length;
const interval = setInterval(() => {
timeNUm++;
if (timeNUm > maxTimes) {
clearInterval(interval);
}
const elems = _$el.querySelectorAll("span.el-select__tags-text");
if (elems.length === len) {
console.log(
"selectRef",
selectRef.value,
_$el.querySelectorAll("span.el-select__tags-text")
);
elems.forEach(elem => {
if (elem.innerText.indexOf("+ ") < 0) {
elem.setAttribute("title", elem.innerText);
}
});
clearInterval(interval);
}
}, 300);
}
}
/**设置主Select-panel不关闭 */
function visibleChange(_visible) {
if (!_visible) {
selectRef.value.visible = true;
selectRef.value.expanded = true;
// selectRef.value.focus();
// setTimeout(() => {
// }, 100);
console.log("_visible, selectRef.value");
}
console.log(_visible, selectRef.value);
}
/**设置主Select-panel不关闭 */
function mainVisibleChange(_visible) {
selectRef.value.visible = !_visible;
console.log(selectRef.value);
}
/** 监听 选中数据的修改 */
watch(selectLabel, (newval, oldval) => {
if (newval.length > 0) {
//清除未选择的数据
oldval.forEach((x, removeIndex) => {
if (!newval.some(n => n === x)) {
selectedRows.value.splice(removeIndex, 1);
}
});
} else {
//清空选择的数据
selectedRows.value.splice(0, selectedRows.value.length);
}
/** 更新v-Model */
updateVModel();
// if (!props.multiple) {
// if (!_.isEmpty(selectedRows.value)) {
// const newValue = selectedRows.value[0][keyField.value];
// emits("update:modelValue", newValue);
// emits("change", selectedRows.value[0]);
// } else {
// emits("update:modelValue", null);
// emits("change", null);
// }
// } else {
// const newValues = selectedRows.value.map(x => x[keyField.value]);
// emits("update:modelValue", newValues);
// emits("change", selectedRows.value);
// }
});
watch(
() => props.modelValue,
newval => {
console.log("watch-props", newval, typeof newval);
/** _.isEmpty 如果是int boolean,将返回true */
if (
(_.isNumber(newval) && (_.isNaN(newval) || newval <= 0)) ||
(!_.isNumber(newval) && _.isEmpty(newval))
) {
selectLabel.value = [];
}
}
);
watch(url, (newval, oldval) => {
//清空
_searhFilters.splice(0, _searhFilters.length);
if (_.isEmpty(newval)) {
tableData.value = [];
emits("update:modelValue", null);
// modelValue.value = null;
emits("change", null);
} else if (newval != oldval) {
tableData.value = [];
emits("update:modelValue", null);
// modelValue.value = null;
emits("change", null);
//链接修改重新初始化
getTabelData(true).then(() => {
set_ElSelectTagsText();
});
}
});
onMounted(async () => {
await getTabelData(true);
if (!_.isEmpty(props.modelValue)) {
let filter = _searhFilters.find(x => x.field == keyField.value);
let isNew = false;
if (_.isEmpty(filter)) {
isNew = true;
filter = {
field: keyField.value, // 字段
op: props.multiple ? FilterOp.In : FilterOp.BeginsWith, // 比较符号
value: "", // 值
valType: FilterValueType.String // 值类型
};
}
let filterValStr = props.modelValue;
if (_.isArray(props.modelValue)) filterValStr = props.modelValue.join(",");
Object.assign(filter, {
field: keyField.value, // 字段
op: props.multiple ? FilterOp.In : FilterOp.BeginsWith, // 比较符号
value: filterValStr, // 值
valType: FilterValueType.String // 值类型
});
if (isNew) _searhFilters.push(filter);
// 没有filter 无需重新搜索
if (!_.isEmpty(_searhFilters)) {
// getTabelData().then(() => {
// if (_.isEmpty(selectedRows.value)) {
// const seletData = tableData.value.find(
// x => x[keyField.value] == filterValStr
// );
// if (!_.isEmpty(seletData)) {
// selectedRows.value.push(seletData);
// selectLabel.value.push(seletData[labelField.value]);
// }
// }
// set_ElSelectTagsText();
// });
await getTabelData();
if (_.isEmpty(selectedRows.value)) {
const seletData = tableData.value.find(
x => x[keyField.value] == filterValStr
);
if (!_.isEmpty(seletData)) {
selectedRows.value.push(seletData);
selectLabel.value.push(seletData[labelField.value]);
emits("change", selectedRows.value[0]);
}
}
}
}
console.log("ComboGrid - onMounted", keyField, labelField);
set_ElSelectTagsText();
});
</script>
<template>
<el-select
ref="selectRef"
clearable
collapse-tags
collapse-tags-tooltip
placeholder="Please select Data"
value-key="_Id"
v-model="selectLabel"
:multiple="true"
:loading="loadingList"
:style="{
width: _.isNumber(props.width || 180)
? parseInt(props.width || 180) > 1000
? '100%'
: `${props.width || 180}px`
: props.width
}"
@visible-change="mainVisibleChange"
:disabled="disabled"
class="combo-grid"
>
<template #empty>
<div :class="`w-[${props.panelWidth}px] m-4`">
<el-card height="500px">
<template #header v-if="props.searchable">
<el-row :gutter="1">
<el-col :span="9" :sm="7" :md="8" :lg="9" :xl="10">
<el-select
ref="fieldSelectRef"
filterable
v-model="filterField"
placeholder="Select"
style="width: 100%"
value-key="id"
@visibleChange="visibleChange"
>
<el-option
v-for="item in filterOptions"
:key="item.id"
:label="item.label"
:value="item"
/>
</el-select>
</el-col>
<el-col :span="15" :sm="17" :md="16" :lg="15" :xl="14">
<el-input
v-model="searchQuery"
clearable
@keyup.enter="onSearch"
>
<template #append>
<el-button
type="primary"
:icon="useRenderIcon(iSearch)"
:loading="loadingList"
v-auth="'view'"
@click="onSearch"
/>
</template>
</el-input>
</el-col>
</el-row>
</template>
<el-row>
<el-col :span="24">
<pure-table
ref="tabRef"
style="width: 100%"
size="small"
border
fit
show-overflow-tooltip
:max-height="350"
:data="tableData"
:row-key="keyField"
:columns="tableColumns"
:loading="loadingList"
:row-class-name="tableRowClassName"
@row-click="handleRowClick"
:tree-props="(props.isTreeData ? props.treeProps : {}) as any"
:default-expand-all="props.isTreeData"
/>
<el-pagination
v-if="!props.isTreeData"
size="small"
layout="prev, pager, next, jumper"
:default-page-size="defaultPageSize"
:total="totalRecord"
@current-change="handlePageChange"
/>
</el-col>
</el-row>
</el-card>
</div>
</template>
</el-select>
</template>
<style lang="scss" scoped>
:deep(.el-card) {
--el-card-padding: 6px;
}
:deep(.el-table__row) {
cursor: pointer;
}
</style>
<style>
.el-table .warning-row {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
.el-table .success-row {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
</style>
types.ts
import { TableColumns } from "@pureadmin/table";
export type comboGridPropType = {
modelValue: any;
url: string;
keyField?: string;
labelField?: string;
filterOptions?: Array<ISearchOption>;
tableColumns?: Array<TableColumns>;
enableField?: string;
multiple?: boolean;
width?: number | string;
panelWidth?: number;
defaultPageSize?: number;
autoSelectFirst?: boolean; //自动选择第一项
sort?: string;
order?: "desc" | "asc";
queryOp: "Contains" | "BeginsWith" | "Equal";
disabled?: boolean; //禁用
searchable?: boolean; // 搜索
isTreeData?: boolean; // 树结构数据
treeProps?: { hasChildren?: string; children?: string };
};
export interface ISearchOption {
id: number;
label: string;
value: string;
type: "string" | "boolean" | "date" | "number";
}