Commit cf9ae590 by lijiabin

【需求 25171】fix: 实践查看权限保存字段改为 finalPermissions,并显式传各类型避免旧的公开权限残留

parent 3cf73794
...@@ -265,8 +265,7 @@ export interface ArticleItemDto { ...@@ -265,8 +265,7 @@ export interface ArticleItemDto {
publishTime: number publishTime: number
// 官方账号推送的文章才有这个字段,列表接口直接带出来,不用再单独查一次推送状态 // 官方账号推送的文章才有这个字段,列表接口直接带出来,不用再单独查一次推送状态
pushInfo?: ArticlePushInfoDto pushInfo?: ArticlePushInfoDto
// 实践的查看权限回显(详情接口才有)。跟保存时传的 valueList(纯 id 数组)不一样, // 实践的查看权限回显(详情接口才有),结构跟 addOrUpdatePractice 保存时传的一样
// 这里是 finalPermissions(带 valueName),方便直接拿来回显名字,不用再反查一次。
typeUpdates?: ArticleTypeUpdateEchoDto[] typeUpdates?: ArticleTypeUpdateEchoDto[]
// 视频分类,只有视频类型才有;categoryIdList/categoryNameList 按下标一一对应, // 视频分类,只有视频类型才有;categoryIdList/categoryNameList 按下标一一对应,
// 详情接口回显的分类里可能包含已被设为不公开的分类,前端需要把它们并进下拉选项里 // 详情接口回显的分类里可能包含已被设为不公开的分类,前端需要把它们并进下拉选项里
...@@ -275,11 +274,11 @@ export interface ArticleItemDto { ...@@ -275,11 +274,11 @@ export interface ArticleItemDto {
} }
/** /**
* 实践查看权限的回显(getArticleDetail 返回),跟保存时的 PracticeTypeUpdateDto 结构不同: * 实践查看权限的回显(getArticleDetail 返回),结构跟保存时的 PracticeTypeUpdateDto 一样,
* 保存传 valueList(纯 id 数组),这里返回 finalPermissions(带 valueName,方便直接回显)。 * 只是多了 valueType=0 的「公开」这一组(没选范围时后端存的默认值),回显时要跳过它
*/ */
export interface ArticleTypeUpdateEchoDto { export interface ArticleTypeUpdateEchoDto {
/** 1 部门 / 2 人员 */ /** 0 公开 / 1 部门 / 2 人员 */
valueType: number valueType: number
finalPermissions: { valueId: string; valueName: string }[] finalPermissions: { valueId: string; valueName: string }[]
} }
......
...@@ -2,12 +2,13 @@ import { ArticleTypeEnum, ReleaseStatusTypeEnum, BooleanFlag, SendTypeEnum } fro ...@@ -2,12 +2,13 @@ import { ArticleTypeEnum, ReleaseStatusTypeEnum, BooleanFlag, SendTypeEnum } fro
import type { PageSearchParams } from '@/utils/request/types' import type { PageSearchParams } from '@/utils/request/types'
/** /**
* 保存查看权限时传的一组:valueType 1 部门 / 2 人员,valueList 是纯 id 数组 * 保存查看权限时传的一组:valueType 0 公开 / 1 部门 / 2 人员(valueType=2 时 valueId 存的是 user_id,不是企微账号)。
* (valueType=2 时 valueId 存的是 user_id,不是企微账号)。 * 字段名必须是 finalPermissions,跟详情接口返回的结构完全一致——后端不认识的字段会被直接忽略,
* 传错了后端会当成「没选范围」,按默认规则存(官方账号就是公开)。finalPermissions 传空数组表示删掉这一类
*/ */
export interface PracticeTypeUpdateDto { export interface PracticeTypeUpdateDto {
valueType: 1 | 2 valueType: 0 | 1 | 2
valueList: string[] finalPermissions: { valueId: string; valueName: string }[]
} }
/** /**
......
...@@ -214,6 +214,7 @@ import { ...@@ -214,6 +214,7 @@ import {
import { useTagsStore } from '@/stores' import { useTagsStore } from '@/stores'
import { addOrUpdatePractice, addOrUpdateArticle } from '@/api' import { addOrUpdatePractice, addOrUpdateArticle } from '@/api'
import type { AddOrUpdatePracticeDto } from '@/api' import type { AddOrUpdatePracticeDto } from '@/api'
import { buildPracticeTypeUpdates } from '@/utils/practicePermission'
import type { ElButton } from 'element-plus' import type { ElButton } from 'element-plus'
import { useAnimate } from '@vueuse/core' import { useAnimate } from '@vueuse/core'
import RichTextarea from '../RichTextarea/index.vue' import RichTextarea from '../RichTextarea/index.vue'
...@@ -348,18 +349,9 @@ const validateForm = () => { ...@@ -348,18 +349,9 @@ const validateForm = () => {
return true return true
} }
// 把选中的部门/人员按 valueType 分组,一个都没选就不传 typeUpdates(不要传空数组硬凑)
const buildTypeUpdates = () => {
const depts = permissionSelection.value.filter((item) => item.valueType === 1)
const users = permissionSelection.value.filter((item) => item.valueType === 2)
const typeUpdates: { valueType: 1 | 2; valueList: string[] }[] = []
if (depts.length) typeUpdates.push({ valueType: 1, valueList: depts.map((item) => item.valueId) })
if (users.length) typeUpdates.push({ valueType: 2, valueList: users.map((item) => item.valueId) })
return typeUpdates
}
const transformForm = (releaseStatus: ReleaseStatusTypeEnum): AddOrUpdatePracticeDto => { const transformForm = (releaseStatus: ReleaseStatusTypeEnum): AddOrUpdatePracticeDto => {
const typeUpdates = buildTypeUpdates() // 一个都没选就不传 typeUpdates,由后端按默认规则处理
const typeUpdates = buildPracticeTypeUpdates(permissionSelection.value)
return { return {
...form.value, ...form.value,
releaseStatus, releaseStatus,
......
import type { DepartmentUserSelection } from '@/components/common/DepartmentUserModal/types'
import type { ArticleTypeUpdateEchoDto } from '@/api/article/types'
import type { PracticeTypeUpdateDto } from '@/api/practice/types'
/**
* 实践「查看权限」选中的部门/人员 → addOrUpdatePractice 的 typeUpdates。
*
* - 一个都没选:返回空数组,调用方不传 typeUpdates,由后端按默认规则处理(不要传空数组硬凑)。
* - 选了:0 公开 / 1 部门 / 2 人员 三组都显式传。后端是按类型逐个比对增删的,没传的类型
* 原样保留——比如原来是公开(有一条 valueType=0)、现在改成指定部门,不显式传一个空的公开组,
* 那条公开记录就删不掉,文章照样所有人可见。
*/
export const buildPracticeTypeUpdates = (
selection: DepartmentUserSelection[],
): PracticeTypeUpdateDto[] => {
if (!selection.some((item) => item.valueType === 1 || item.valueType === 2)) return []
const pick = (valueType: 1 | 2) =>
selection
.filter((item) => item.valueType === valueType)
.map((item) => ({ valueId: item.valueId, valueName: item.valueName }))
return [
{ valueType: 0, finalPermissions: [] },
{ valueType: 1, finalPermissions: pick(1) },
{ valueType: 2, finalPermissions: pick(2) },
]
}
/**
* 详情接口回显的 typeUpdates → DeptUserPicker 认的选中项。
* 没设过范围时后端返回的是 valueType=0 的「公开」,不是部门也不是人,要跳过
*/
export const toPracticeSelection = (
typeUpdates?: ArticleTypeUpdateEchoDto[],
): DepartmentUserSelection[] =>
(typeUpdates ?? [])
.filter((group) => group.valueType === 1 || group.valueType === 2)
.flatMap((group) =>
group.finalPermissions.map((item) => ({
valueType: group.valueType as 1 | 2,
valueId: item.valueId,
valueName: item.valueName,
})),
)
...@@ -7,7 +7,8 @@ import type { FormInstance, FormRules } from 'element-plus' ...@@ -7,7 +7,8 @@ import type { FormInstance, FormRules } from 'element-plus'
import type { BackendColumnListItemDto, AddOrUpdateColumnDto } from '@/api/backend' import type { BackendColumnListItemDto, AddOrUpdateColumnDto } from '@/api/backend'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { ArticleTypeEnum, BooleanFlag } from '@/constants' import { ArticleTypeEnum, BooleanFlag } from '@/constants'
import type { ArticleItemDto, ArticleTypeUpdateEchoDto } from '@/api/article/types' import type { ArticleItemDto } from '@/api/article/types'
import { buildPracticeTypeUpdates, toPracticeSelection } from '@/utils/practicePermission'
import { push } from 'notivue' import { push } from 'notivue'
import { useMessageBox } from '@/hooks' import { useMessageBox } from '@/hooks'
import ArticleContentDialog from '@/components/common/ArticleContentDialog/index.vue' import ArticleContentDialog from '@/components/common/ArticleContentDialog/index.vue'
...@@ -116,24 +117,10 @@ const permissionDeptIds = ref<string[]>([]) ...@@ -116,24 +117,10 @@ const permissionDeptIds = ref<string[]>([])
const permissionUserIds = ref<string[]>([]) const permissionUserIds = ref<string[]>([])
const permissionSelection = ref<DepartmentUserSelection[]>([]) const permissionSelection = ref<DepartmentUserSelection[]>([])
// 详情接口回显的查看权限是 finalPermissions(带名字),转成 DeptUserPicker 认的 DepartmentUserSelection
const toSelectionFromTypeUpdates = (
typeUpdates?: ArticleTypeUpdateEchoDto[],
): DepartmentUserSelection[] => {
if (!typeUpdates) return []
return typeUpdates.flatMap((group) =>
group.finalPermissions.map((item) => ({
valueType: group.valueType as 1 | 2,
valueId: item.valueId,
valueName: item.valueName,
})),
)
}
const handleEditPermission = async (row: ArticleItemDto) => { const handleEditPermission = async (row: ArticleItemDto) => {
const { data } = await getArticleDetail(row.id) const { data } = await getArticleDetail(row.id)
permissionDetail.value = data permissionDetail.value = data
const items = toSelectionFromTypeUpdates(data.typeUpdates) const items = toPracticeSelection(data.typeUpdates)
permissionDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId) permissionDeptIds.value = items.filter((item) => item.valueType === 1).map((item) => item.valueId)
permissionUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId) permissionUserIds.value = items.filter((item) => item.valueType === 2).map((item) => item.valueId)
permissionSelection.value = items permissionSelection.value = items
...@@ -143,11 +130,7 @@ const handleEditPermission = async (row: ArticleItemDto) => { ...@@ -143,11 +130,7 @@ const handleEditPermission = async (row: ArticleItemDto) => {
const handleSubmitPermission = async () => { const handleSubmitPermission = async () => {
if (!permissionDetail.value) return if (!permissionDetail.value) return
const depts = permissionSelection.value.filter((item) => item.valueType === 1) const typeUpdates = buildPracticeTypeUpdates(permissionSelection.value)
const users = permissionSelection.value.filter((item) => item.valueType === 2)
const typeUpdates: { valueType: 1 | 2; valueList: string[] }[] = []
if (depts.length) typeUpdates.push({ valueType: 1, valueList: depts.map((item) => item.valueId) })
if (users.length) typeUpdates.push({ valueType: 2, valueList: users.map((item) => item.valueId) })
const detail = permissionDetail.value const detail = permissionDetail.value
permissionSubmitting.value = true permissionSubmitting.value = true
......
...@@ -100,7 +100,8 @@ import UploadFile from '@/components/common/UploadFile/index.vue' ...@@ -100,7 +100,8 @@ import UploadFile from '@/components/common/UploadFile/index.vue'
import SelectTags from '@/components/common/SelectTags/index.vue' import SelectTags from '@/components/common/SelectTags/index.vue'
import DeptUserPicker from '@/components/common/DeptUserPicker/index.vue' import DeptUserPicker from '@/components/common/DeptUserPicker/index.vue'
import type { DepartmentUserSelection } from '@/components/common/DepartmentUserModal/types' import type { DepartmentUserSelection } from '@/components/common/DepartmentUserModal/types'
import type { ArticleItemDto, ArticleTypeUpdateEchoDto } from '@/api/article/types' import type { ArticleItemDto } from '@/api/article/types'
import { buildPracticeTypeUpdates, toPracticeSelection } from '@/utils/practicePermission'
import { useResetData } from '@/hooks' import { useResetData } from '@/hooks'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
...@@ -151,34 +152,11 @@ const rules: Record<string, FormItemRule[]> = { ...@@ -151,34 +152,11 @@ const rules: Record<string, FormItemRule[]> = {
regionHide: [{ required: true, message: '请选择是否隐藏IP信息', trigger: 'trigger' }], regionHide: [{ required: true, message: '请选择是否隐藏IP信息', trigger: 'trigger' }],
} }
// 把选中的部门/人员按 valueType 分组,一个都没选就不传 typeUpdates(不要传空数组硬凑)
const buildTypeUpdates = () => {
const depts = permissionSelection.value.filter((item) => item.valueType === 1)
const users = permissionSelection.value.filter((item) => item.valueType === 2)
const typeUpdates: { valueType: 1 | 2; valueList: string[] }[] = []
if (depts.length) typeUpdates.push({ valueType: 1, valueList: depts.map((item) => item.valueId) })
if (users.length) typeUpdates.push({ valueType: 2, valueList: users.map((item) => item.valueId) })
return typeUpdates
}
// 详情接口回显的查看权限是 finalPermissions(带名字),转成 DeptUserPicker 认的 DepartmentUserSelection
const toSelectionFromTypeUpdates = (
typeUpdates?: ArticleTypeUpdateEchoDto[],
): DepartmentUserSelection[] => {
if (!typeUpdates) return []
return typeUpdates.flatMap((group) =>
group.finalPermissions.map((item) => ({
valueType: group.valueType as 1 | 2,
valueId: item.valueId,
valueName: item.valueName,
})),
)
}
defineExpose({ defineExpose({
validate: () => formRef.value!.validate(), validate: () => formRef.value!.validate(),
getSubmitData: () => { getSubmitData: () => {
const typeUpdates = buildTypeUpdates() const typeUpdates = buildPracticeTypeUpdates(permissionSelection.value)
return { return {
imgUrl: form.value.imgUrl, imgUrl: form.value.imgUrl,
faceUrl: form.value.imgUrl ? form.value.imgUrl.split(',')[0] || '' : '', faceUrl: form.value.imgUrl ? form.value.imgUrl.split(',')[0] || '' : '',
...@@ -205,7 +183,7 @@ defineExpose({ ...@@ -205,7 +183,7 @@ defineExpose({
form.value.deptId = data.deptId === '0' ? '' : data.deptId form.value.deptId = data.deptId === '0' ? '' : data.deptId
} }
const items = toSelectionFromTypeUpdates(data.typeUpdates) const items = toPracticeSelection(data.typeUpdates)
permissionDeptIds.value = items permissionDeptIds.value = items
.filter((item) => item.valueType === 1) .filter((item) => item.valueType === 1)
.map((item) => item.valueId) .map((item) => item.valueId)
......
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