Commit e8108f96 by lijiabin

【需求 25171】refactor: DeptUserPicker 人员回显改为按 userIdList 自动反查姓名,去掉 initial-selected

parent c0ef89a5
......@@ -29,4 +29,6 @@ export interface DepartmentListParams {
export interface DepartmentUserListParams {
deptId?: string | number
keyword?: string
/** 按 userId(或企微账号)批量查人,传了就忽略 deptId/keyword */
userIdList?: string[]
}
......@@ -28,24 +28,18 @@
* v-model:dept-value="deptIds"
* v-model:user-value="userIds"
* selection-mode="both"
* :initial-selected="initialPushDetail"
* @confirm="(items) => (pushList = items)"
* v-slot="{ echoText }"
* >
* <el-input readonly :model-value="echoText" placeholder="请选择部门/人员" />
* </DeptUserPicker>
*
* 回显文案(echoText)不用页面自己拼:组件内部会缓存选过/查过的每一项完整信息(名字、头像)。
* - 部门:id 缺名字时组件会自己调 getAllDept({ deptIdList }) 反查名字,不用调用方传,
* 跟另一个项目里 deptUserModal 的 getSpecifyDept 是一个思路。
* - 人员:后端 getDeptUsers 没有“按 userId 批量查”的接口,没法反查,
* 所以人员这边编辑回显时如果没在本次会话选过,就只能显示 id;
* 如果页面本来就有现成的详情(比如后端详情接口直接返回的 pushList,自带 valueName),
* 传 initial-selected 进来能让首次回显正确显示名字,不用等组件去查(其实也查不到)。
* 回显文案(echoText)不用页面自己拼:组件内部会缓存选过/查过的每一项完整信息(名字、头像),
* id 缺名字时自己反查——部门用 getAllDept({ deptIdList }),人员用 getDeptUsers({ userIdList })。
* 如果业务上还需要把选中的名字存到某个字段(比如 deptName),在 @confirm 里自己处理就行。
*/
import DepartmentUserModal from '@/components/common/DepartmentUserModal/index.vue'
import { getAllDept } from '@/api'
import { getAllDept, getDeptUsers } from '@/api'
import type { DepartmentUserSelection } from '@/components/common/DepartmentUserModal/types'
import type { DeptUserPickerProps } from './types'
......@@ -56,7 +50,6 @@ const {
disabled = false,
max,
valueType = 'array',
initialSelected,
} = defineProps<DeptUserPickerProps>()
const emit = defineEmits<{
......@@ -68,23 +61,11 @@ const userValue = defineModel<string[] | string>('userValue', { default: () => [
const modalRef = useTemplateRef<InstanceType<typeof DepartmentUserModal>>('modalRef')
// 记住选过/回显过/查过的每一项完整信息(名字、头像),key 是 `${valueType}-${valueId}`。
// 只增不减;用 reactive 包一层是因为要让 getAllDept 查完之后异步写回缓存也能触发 echoText 重新计算。
// 记住选过/查过的每一项完整信息(名字、头像),key 是 `${valueType}-${valueId}`。
// 只增不减;用 reactive 包一层是因为反查接口异步写回缓存后也要触发 echoText 重新计算。
const detailCache = reactive(new Map<string, DepartmentUserSelection>())
const cacheKey = (valueType: 1 | 2, valueId: string) => `${valueType}-${valueId}`
watch(
() => initialSelected,
(items) => {
items?.forEach((item) => {
const key = cacheKey(item.valueType, item.valueId)
// 不覆盖已有缓存:避免只有 id+名字的种子数据把本次会话里已经拿到的真实头像覆盖掉。
if (!detailCache.has(key)) detailCache.set(key, item)
})
},
{ immediate: true },
)
const toIdArray = (value: string[] | string | undefined): string[] => {
if (!value) return []
return Array.isArray(value) ? value : value.split(',').filter(Boolean)
......@@ -96,8 +77,7 @@ const serializeIds = (ids: string[]): string[] | string =>
const deptIds = computed(() => toIdArray(deptValue.value))
const userIds = computed(() => toIdArray(userValue.value))
// 部门缺名字时自己查一次(getAllDept 支持按 deptIdList 查),不用调用方传;
// 人员没有等价的批量查询接口,查不到,只能靠 confirm 时缓存的结果或 initial-selected。
// 部门缺名字时自己查一次(getAllDept 支持按 deptIdList 查)
watch(
deptIds,
async (ids) => {
......@@ -123,6 +103,40 @@ watch(
{ immediate: true },
)
// 人员缺名字时同理,getDeptUsers 传 userIdList 批量查(后端认 user_id 和企微账号,不认工号)
watch(
userIds,
async (ids) => {
if (userValueField === 'jobNumId') return
const missingIds = ids.filter((id) => !detailCache.has(cacheKey(2, id)))
if (!missingIds.length) return
try {
const { data } = await getDeptUsers({ userIdList: missingIds })
data?.forEach((user) => {
const valueId = String(userValueField === 'account' ? user.account : user.userId)
const key = cacheKey(2, valueId)
if (!detailCache.has(key)) {
detailCache.set(key, {
valueType: 2,
valueId,
valueName: user.name,
valueField: userValueField,
userId: String(user.userId),
account: user.account,
jobNumId: user.jobNumId,
avatar: user.avatar,
deptId: String(user.deptId),
})
}
})
} catch (error) {
console.error('查询人员名称失败:', error)
}
},
{ immediate: true },
)
const resolveSelection = (ids: string[], type: 1 | 2): DepartmentUserSelection[] =>
ids.map(
(valueId) =>
......
import type { SelectionMode, UserValueField } from '@/components/common/DepartmentUserModal/types'
import type { DepartmentUserSelection } from '@/components/common/DepartmentUserModal/types'
/** v-model 的值格式:array 是 id 数组,string 是逗号拼接的 id 字符串(跟后端字段直接对齐)。 */
export type DeptUserValueType = 'array' | 'string'
......@@ -16,10 +15,4 @@ export type DeptUserPickerProps = {
max?: number
/** v-model:dept-value / v-model:user-value 的值格式,默认 array */
valueType?: DeptUserValueType
/**
* 编辑回显用的初始详情(valueId/valueType/valueName,最好带上 avatar),
* 页面如果本来就有(比如详情接口直接返回的 pushList),传进来就行;
* 不传的话首次回显(还没点开过弹窗前)只能看到 id。
*/
initialSelected?: DepartmentUserSelection[]
}
......@@ -115,7 +115,6 @@ const permissionDetail = ref<ArticleItemDto>()
const permissionDeptIds = ref<string[]>([])
const permissionUserIds = ref<string[]>([])
const permissionSelection = ref<DepartmentUserSelection[]>([])
const permissionInitialSelected = ref<DepartmentUserSelection[]>([])
// 详情接口回显的查看权限是 finalPermissions(带名字),转成 DeptUserPicker 认的 DepartmentUserSelection
const toSelectionFromTypeUpdates = (
......@@ -138,7 +137,6 @@ const handleEditPermission = async (row: ArticleItemDto) => {
permissionDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
permissionUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
permissionSelection.value = items
permissionInitialSelected.value = items
permissionDialogVisible.value = true
}
......@@ -325,7 +323,6 @@ const handleSubmitPermission = async () => {
v-model:dept-value="permissionDeptIds"
v-model:user-value="permissionUserIds"
selection-mode="both"
:initial-selected="permissionInitialSelected"
@confirm="(items) => (permissionSelection = items)"
v-slot="{ echoText }"
>
......
......@@ -59,7 +59,6 @@
v-model:dept-value="pushDeptIds"
v-model:user-value="pushUserIds"
selection-mode="both"
:initial-selected="pushInitialSelected"
@confirm="(items) => (pushSelection = items)"
v-slot="{ echoText }"
>
......@@ -195,7 +194,6 @@ const [form, resetForm] = useResetData({
const pushDeptIds = ref<string[]>([])
const pushUserIds = ref<string[]>([])
const pushSelection = ref<DepartmentUserSelection[]>([])
const pushInitialSelected = ref<DepartmentUserSelection[]>([])
const disabledSecondsOnlyZero = () => Array.from({ length: 59 }, (_, i) => i + 1)
const disabledMinutesOnlyTens = () =>
......@@ -281,7 +279,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
} else {
form.value.isPushAll = BooleanFlag.NO
const items: DepartmentUserSelection[] = (data.pushList || []).map((item) => ({
......@@ -292,7 +289,6 @@ defineExpose({
pushDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
pushUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
pushSelection.value = items
pushInitialSelected.value = items
}
},
reset: () => {
......@@ -300,7 +296,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
},
})
</script>
......@@ -53,7 +53,6 @@
v-model:dept-value="pushDeptIds"
v-model:user-value="pushUserIds"
selection-mode="both"
:initial-selected="pushInitialSelected"
@confirm="(items) => (pushSelection = items)"
v-slot="{ echoText }"
>
......@@ -184,7 +183,6 @@ const [form, resetForm] = useResetData({
const pushDeptIds = ref<string[]>([])
const pushUserIds = ref<string[]>([])
const pushSelection = ref<DepartmentUserSelection[]>([])
const pushInitialSelected = ref<DepartmentUserSelection[]>([])
const disabledSecondsOnlyZero = () => Array.from({ length: 59 }, (_, i) => i + 1)
const disabledMinutesOnlyTens = () =>
......@@ -267,7 +265,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
} else {
form.value.isPushAll = BooleanFlag.NO
const items: DepartmentUserSelection[] = (data.pushList || []).map((item) => ({
......@@ -278,7 +275,6 @@ defineExpose({
pushDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
pushUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
pushSelection.value = items
pushInitialSelected.value = items
}
},
reset: () => {
......@@ -286,7 +282,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
},
})
</script>
......@@ -32,7 +32,6 @@
v-model:dept-value="pushDeptIds"
v-model:user-value="pushUserIds"
selection-mode="both"
:initial-selected="pushInitialSelected"
@confirm="(items) => (pushSelection = items)"
v-slot="{ echoText }"
>
......@@ -152,8 +151,6 @@ const [form, resetForm] = useResetData({
const pushDeptIds = ref<string[]>([])
const pushUserIds = ref<string[]>([])
const pushSelection = ref<DepartmentUserSelection[]>([])
// 编辑回显用,给 DeptUserPicker 做首次回显的名字种子。
const pushInitialSelected = ref<DepartmentUserSelection[]>([])
// 时间选择只允许整十分(:00、:10...:50),秒固定为 0
const disabledSecondsOnlyZero = () => Array.from({ length: 59 }, (_, i) => i + 1)
......@@ -235,7 +232,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
} else {
form.value.isPushAll = BooleanFlag.NO
const items: DepartmentUserSelection[] = (data.pushList || []).map((item) => ({
......@@ -246,7 +242,6 @@ defineExpose({
pushDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
pushUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
pushSelection.value = items
pushInitialSelected.value = items
}
},
reset: () => {
......@@ -254,7 +249,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
},
})
</script>
......@@ -24,7 +24,6 @@
v-model:dept-value="permissionDeptIds"
v-model:user-value="permissionUserIds"
selection-mode="both"
:initial-selected="permissionInitialSelected"
@confirm="(items) => (permissionSelection = items)"
v-slot="{ echoText }"
>
......@@ -124,8 +123,6 @@ const [form, resetForm] = useResetData({
const permissionDeptIds = ref<string[]>([])
const permissionUserIds = ref<string[]>([])
const permissionSelection = ref<DepartmentUserSelection[]>([])
// 编辑回显用,给 DeptUserPicker 做首次回显的名字种子
const permissionInitialSelected = ref<DepartmentUserSelection[]>([])
const filterTagsFn = (allTags: any[]) =>
allTags.filter((tag) => tag.id !== Number(form.value.mainTagId))
......@@ -216,14 +213,12 @@ defineExpose({
.filter((item) => item.valueType === 2)
.map((item) => item.valueId)
permissionSelection.value = items
permissionInitialSelected.value = items
},
reset: () => {
resetForm()
permissionDeptIds.value = []
permissionUserIds.value = []
permissionSelection.value = []
permissionInitialSelected.value = []
},
})
</script>
......@@ -32,7 +32,6 @@
v-model:dept-value="pushDeptIds"
v-model:user-value="pushUserIds"
selection-mode="both"
:initial-selected="pushInitialSelected"
@confirm="(items) => (pushSelection = items)"
v-slot="{ echoText }"
>
......@@ -148,7 +147,6 @@ const [form, resetForm] = useResetData({
const pushDeptIds = ref<string[]>([])
const pushUserIds = ref<string[]>([])
const pushSelection = ref<DepartmentUserSelection[]>([])
const pushInitialSelected = ref<DepartmentUserSelection[]>([])
const disabledSecondsOnlyZero = () => Array.from({ length: 59 }, (_, i) => i + 1)
const disabledMinutesOnlyTens = () =>
......@@ -225,7 +223,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
} else {
form.value.isPushAll = BooleanFlag.NO
const items: DepartmentUserSelection[] = (data.pushList || []).map((item) => ({
......@@ -236,7 +233,6 @@ defineExpose({
pushDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
pushUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
pushSelection.value = items
pushInitialSelected.value = items
}
},
reset: () => {
......@@ -244,7 +240,6 @@ defineExpose({
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
},
})
</script>
......@@ -192,7 +192,6 @@
v-model:dept-value="pushDeptIds"
v-model:user-value="pushUserIds"
selection-mode="both"
:initial-selected="pushInitialSelected"
@confirm="(items) => (pushSelection = items)"
v-slot="{ echoText }"
>
......@@ -479,8 +478,6 @@ const disabledMinutesOnlyTens = () =>
const pushDeptIds = ref<string[]>([])
const pushUserIds = ref<string[]>([])
const pushSelection = ref<DepartmentUserSelection[]>([])
// 编辑回显用,给 DeptUserPicker 做首次回显的名字种子(不传的话首次打开只能看到 id)。
const pushInitialSelected = ref<DepartmentUserSelection[]>([])
// 视频分类下拉:不管是普通用户还是管理员,统一只展示公开分类——管理员如果选了不公开的分类,
// 这个分类在前台分类栏不展示,用户会一脸懵(产品那边确认过,统一改成只看公开的)。
......@@ -640,7 +637,6 @@ const resetPageData = () => {
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
resetData()
locationVideoBlolUrl.value = ''
}
......@@ -713,7 +709,6 @@ onActivated(async () => {
pushDeptIds.value = []
pushUserIds.value = []
pushSelection.value = []
pushInitialSelected.value = []
} else {
form.value.isPushAll = BooleanFlag.NO
const items: DepartmentUserSelection[] = (data.pushList || []).map((item) => ({
......@@ -724,7 +719,6 @@ onActivated(async () => {
pushDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
pushUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
pushSelection.value = items
pushInitialSelected.value = items
}
}
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment