最近项目组有个需求,需要下拉展示一个表格,表格的数据为异步获取,然后再选中表格中的某一项。回传id和name给后端保存。保存后可编辑,但是在回显数据保存的数据时,我遇到了问题,在此简单记录一下我的解决方案。
js
//环境
"vue": "^3.2.38",
"vxe-pc-ui": "^4.8.23",
"vxe-table": "^4.15.10"
问题复现:
js
<template>
<div>
<!-- <div>名称:{{ data.name }}</div> -->
<div>id:{{ data.id }}</div>
<vxe-table-select
v-model="data.id"
v-bind="vxeTableSelectProps"
size="mini"
></vxe-table-select>
<button @click="onSave">保存</button>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
const data = reactive({
name: "",
id: "",
});
const vxeTableSelectProps = reactive({
columns: [{ field: "label", title: "Name" }],
gridConfig: {
rowConfig: {
keyField: "id",
},
radioConfig: {},
loading: true,
proxyConfig: {
ajax: {
query: (params) => {
return getSelectOptions().then((res) => {
vxeTableSelectProps.options = res;
return res;
});
},
},
},
},
options: [],
optionProps: {
label: "label",
value: "id",
},
});
function onSave() {
localStorage.setItem("userInfo", JSON.stringify(data));
}
const getSelectOptions = () => {
return new Promise((resolve) => {
setTimeout(() => {
const selectOptions = [
{
id: 10001,
label: "哈吉蜂",
},
{
id: 10002,
label: "金露娜",
},
{
id: 10003,
label: "威龙",
},
{
id: 10004,
label: "鸟鲁鲁",
},
];
resolve(selectOptions);
vxeTableSelectProps.gridConfig.loading = false;
}, 1000);
});
};
onMounted(() => {
const saveData = JSON.parse(localStorage.getItem("userInfo") || "{}");
data.id = saveData.id;
});
</script>
页面效果

可以看到当我们选中某条数据时,选择框内能正确回显我们设定的label值,这是理所当然的! 但是,当我们点击保存按钮,将数据保存到localStorage之后再刷新页面之后,界面就将会有所不同。

此时,下拉框内,回显的将是绑定的value的值,为什么会这样啦?原因是因为vxeTableSelect只有在第一次focus,弹出下拉表格时才会发请求获取数据,所以第一次进入页面时,组件并不知道id对应的label是啥。只有在请求后界面才能回显label值。网上也没发现好的解决方法。


分析了原因之后,进行了各种尝试,发现组件始终无法在第一时间拿到该展示的label值。 冥思苦想很久之后,突然有了想法:'既然组件无法自动获取到label,不妨我们帮它一把,一开始告诉它该展示啥label'
思路:初次渲染时将id改为label的值,在加载下拉选项时,赋值回本来的id
js
<template>
...
<vxe-table-select
v-model="data.id"
v-bind="vxeTableSelectProps"
size="mini"
@change="onChange"
></vxe-table-select>
...
</template>
<script setup>
...
const originId = ref();
const onChange = ({ row }) => {
// row为下拉表格选中的行数据
data.name = row.label;
};
const vxeTableSelectProps = reactive({
columns: [{ field: "label", title: "Name" }],
gridConfig: {
rowConfig: {
keyField: "id",
},
radioConfig: {},
loading: true,
proxyConfig: {
ajax: {
query: (params) => {
return getSelectOptions().then((res) => {
vxeTableSelectProps.options = res;
// 恢复数据
data.id = originId.value;
return res;
});
},
},
},
},
options: [],
optionProps: {
label: "label",
value: "id",
},
});
onMounted(() => {
const saveData = JSON.parse(localStorage.getItem("userInfo") || "{}");
// 先赋值成name
originId.value = saveData.id;
data.id = saveData.name;
data.name = saveData.name;
});
...
</script>
来看看效果

一开始id被我们强行改成了name,实现了数据回显

异步加载完成后,恢复id且不影响数据回显。
完整代码如下:
js
<template>
<div>
<div>名称:{{ data.name }}</div>
<div>id:{{ data.id }}</div>
<vxe-table-select
v-model="data.id"
v-bind="vxeTableSelectProps"
size="mini"
@change="onChange"
@focus="onFocus"
></vxe-table-select>
<button @click="onSave">保存</button>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
const data = reactive({
name: "",
id: "",
});
const vxeTableSelectProps = reactive({
columns: [{ field: "label", title: "Name" }],
gridConfig: {
rowConfig: {
keyField: "id",
},
radioConfig: {},
loading: true,
proxyConfig: {
ajax: {
query: (params) => {
return getSelectOptions().then((res) => {
vxeTableSelectProps.options = res;
//****************************
// 恢复数据(关键点)
data.id = originId.value;
//****************************
return res;
});
},
},
},
},
options: [],
optionProps: {
label: "label",
value: "id",
},
});
function onSave() {
localStorage.setItem("userInfo", JSON.stringify(data));
}
const getSelectOptions = () => {
return new Promise((resolve) => {
setTimeout(() => {
const selectOptions = [
{
id: 10001,
label: "哈吉蜂",
},
{
id: 10002,
label: "金露娜",
},
{
id: 10003,
label: "威龙",
},
{
id: 10004,
label: "鸟鲁鲁",
},
];
resolve(selectOptions);
vxeTableSelectProps.gridConfig.loading = false;
}, 1000);
});
};
const originId = ref();
const onChange = ({ row }) => {
// row为下拉表格选中的行数据
data.name = row.label;
};
onMounted(() => {
const saveData = JSON.parse(localStorage.getItem("userInfo") || "{}");
// 先赋值成name
originId.value = saveData.id;
data.id = saveData.name;
data.name = saveData.name;
});
</script>
除了上述方案之外,我还尝试过将vxe-table-select的v-model绑定为data.name,此法亦能解决回显问题但是会带来新的问题------下拉列表的label可能重复,会引起组件异常,回显时无法判断应该选中哪条数据。故放弃此法。
上述方案成功解决了数据回显的需求,但是总给人一种是在邪修的感觉,有点投机取巧了,改变原始数据的方式存在一定的风险,但是管他啦,顺利完成了需求!!! Jym要是有更好的方式实现,可以分享一下,让我开开眼界😘