看了最新代码,我理解你的目的了。下面我来详细讲解这个下拉多选和回显的完整逻辑:

📋 整体数据结构设计
| 字段 | 类型 | 存储格式 | 用途 |
|---|---|---|---|
oaId |
数组(编辑)/字符串(提交) | fdId1,fdId2 |
选中的 OA 流程 ID 列表 |
oaName |
字符串 | 标题1$$链接1,标题2$$链接2 |
同时保存显示文本和跳转链接 |
oaProcessId |
字符串 | 用户手动输入 | OA 流程编码 |
🔧 编辑状态逻辑(isEdit = true)
1. 选择流程(getOaList)
javascript
getOaList(e) {
// e 是选中的 fdId 数组
const choseItems = this.oaList.filter(item => e.includes(item.fdId))
if (choseItems.length > 0) {
this.$set(this.balic, 'oaName',
choseItems.map(item => `${item.fdSubject}$$${item.fdFormUrl}`).join(',')
)
}
}
作用 :多选下拉变化时,将选中的项拼接成 标题$$链接 格式存入 oaName
2. 远程搜索(getBpmList)
javascript
getBpmList(keyword) {
bpmList({ subject: keyword, ... }).then(res => {
// 如果是编辑已有数据,需要把已选项合并到列表中
if (this.balic.oaId && this.balic.oaId.length > 0) {
const choseItems = this.oaList.filter(item => this.balic.oaId.includes(item.fdId))
// 把已选项 unshift 到搜索结果前面,确保下拉框能显示已选
choseItems.map(item => {
if (!res.data.data.content.find(s => s.fdId === item.fdId)) {
res.data.data.content.unshift(item)
}
})
}
this.oaList = res.data.data.content
})
}
作用:远程搜索时保留已选项,否则下拉框会显示空白
👁️ 回显状态逻辑(isEdit = false)
1. 详情回显(detailInit)
javascript
detailInit(detailId) {
meetingDetail({ meetingId: detailId }).then(res => {
if (res.data.oaId && res.data.oaId.length > 0) {
res.data.oaId = res.data.oaId.split(',') // 字符串转数组供 el-select 使用
const temp = res.data.oaName.split(',')
this.oaList = res.data.oaId.map((item, index) => {
return {
fdId: item,
fdSubject: temp[index].split('$$')[0], // 解析标题
fdFormUrl: temp[index].split('$$')[1] // 解析链接
}
})
}
this.balic = res.data
})
}
作用 :从后端获取数据后,将 oaName 字符串还原成对象数组,填充 oaList 供下拉框回显
2. 页面展示(模板部分)
html
<div v-else>
<span v-if="balic.oaName">
<a
v-for="(item, index) in parseOaName(balic.oaName)"
:key="index"
:href="item.fdFormUrl"
target="_blank"
style="color: blue; margin-right: 15px; text-decoration: underline;"
>
{{ item.fdSubject }}
</a>
</span>-
<span v-if="balic.oaProcessId">{{ balic.oaProcessId }}</span>
</div>
3. 解析方法(parseOaName)
javascript
parseOaName(oaName) {
if (!oaName) return []
return oaName.split(',').map(item => {
const parts = item.split('$$')
return {
fdSubject: parts[0] || '',
fdFormUrl: parts[1] || ''
}
})
}
作用 :将 标题$$链接,标题$$链接 解析成 [{fdSubject, fdFormUrl}, ...] 数组供模板遍历
📊 数据流转图
编辑时:
┌─────────────┐ 选择 ┌─────────────────────────────┐
│ el-select │ ───────────▶ │ oaName: "标题$$链接,标题$$" │
│ [fdId数组] │ │ oaId: [fdId1, fdId2] │
└─────────────┘ └─────────────────────────────┘
│
▼
提交时 join(',')
│
▼
后端存储字符串
回显时:
┌─────────────┐ split ┌─────────────────────────────┐
│ el-select │ ◀─────────── │ oaId: "fdId1,fdId2" │
│ [fdId数组] │ │ oaName: "标题$$链接,标题$$" │
└─────────────┘ └─────────────────────────────┘
│
▼ (split('$$'))
┌─────────────────┐
│ 只显示标题+链接 │
└─────────────────┘
✅ 你的目的总结
- 编辑时:多选下拉框可以选择多个 OA 流程,选择后自动将标题和链接拼接保存
- 回显时:从后端取到拼接字符串,解析后只显示标题,点击能打开链接
- 兼顾:编辑状态下拉框要能正确回显之前选中的项
这个设计巧妙地用一个字段 oaName 同时存储了显示文本和跳转链接,避免了额外存储映射关系。