refactor(manage): 收口部门负责人ID规范化逻辑并简化后端对话可见性校验

- 后端:任务群/项目群统一按项目级共享判断,不再区分任务可见性
- 前端:新增 department/owner/ids/save mutation 及 normalizeIntArray 工具函数
- 前端:departmentOwnerReadonlyUrls 从 action 局部变量提升至 state
- 前端:修复 TaskDetail 提示文本多余空格

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kuaifan 2026-05-21 06:06:39 +00:00
parent 8cd4669b90
commit ef7293704b
9 changed files with 59 additions and 43 deletions

View File

@ -904,19 +904,16 @@ class WebSocketDialog extends AbstractModel
case 'project': case 'project':
case 'task': case 'task':
// 项目群、任务群对话校验是否在项目内 // 项目群、任务群对话校验是否在项目内
$taskVisibility = 1; // 项目群不涉及任务可见性,按可见处理
if ($dialog->group_type === 'project') { if ($dialog->group_type === 'project') {
$projectId = intval(Project::whereDialogId($dialog->id)->value('id')); $projectId = intval(Project::whereDialogId($dialog->id)->value('id'));
} else { } else {
$taskRow = ProjectTask::select(['project_id', 'visibility'])->whereDialogId($dialog->id)->first(); $projectId = intval(ProjectTask::whereDialogId($dialog->id)->value('project_id'));
$projectId = intval($taskRow?->project_id);
$taskVisibility = intval($taskRow?->visibility);
} }
if ($projectId > 0 && ProjectUser::whereProjectId($projectId)->whereUserid($userid)->exists()) { if ($projectId > 0 && ProjectUser::whereProjectId($projectId)->whereUserid($userid)->exists()) {
return $dialog; return $dialog;
} }
// 部门负责人只读视角:项目群放行;任务群仅"全员可见"任务放行,指定成员可见任务不放行 // 部门负责人只读视角:项目/任务群按项目级共享放行(任务数据另按可见性校验,与普通成员一致)
if ($projectId > 0 && $checkOwner === false && ($dialog->group_type === 'project' || $taskVisibility === 1)) { if ($projectId > 0 && $checkOwner === false) {
$departmentView = UserDepartment::ownerViewContext(User::auth(), true); $departmentView = UserDepartment::ownerViewContext(User::auth(), true);
if (UserDepartment::isDepartmentReadonlyProject($departmentView, $projectId)) { if (UserDepartment::isDepartmentReadonlyProject($departmentView, $projectId)) {
return $dialog; return $dialog;

View File

@ -26,6 +26,20 @@ const timezone = require("dayjs/plugin/timezone");
return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == '[object array]' && typeof obj.length == "number"; return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == '[object array]' && typeof obj.length == "number";
}, },
/**
* 规范化为整型数组
* @param data
* @returns {number[]}
*/
normalizeIntArray(data) {
if (!this.isArray(data)) {
return [];
}
return [...new Set(data
.map(id => parseInt(id))
.filter(id => id > 0))]
},
/** /**
* 是否数组对象 * 是否数组对象
* @param obj * @param obj

View File

@ -678,7 +678,7 @@ export default {
}, },
cacheDepartmentOwnerIds() { cacheDepartmentOwnerIds() {
return (this.$store.state.cacheDepartmentOwnerIds || []).map(id => parseInt(id)); return this.$store.state.cacheDepartmentOwnerIds || [];
}, },
/** /**

View File

@ -63,7 +63,7 @@ export default {
immediate: true, immediate: true,
handler(show) { handler(show) {
if (show) { if (show) {
this.draftIds = (this.cacheDepartmentOwnerIds || []).map(id => parseInt(id)); this.draftIds = (this.cacheDepartmentOwnerIds || []).slice();
} else { } else {
this.applyLoading = false; this.applyLoading = false;
} }

View File

@ -169,7 +169,7 @@ export default {
}, },
ownerDepartmentIds() { ownerDepartmentIds() {
return (this.cacheDepartmentOwnerIds || []).map(id => parseInt(id)); return this.cacheDepartmentOwnerIds || [];
}, },
projectBaseLists() { projectBaseLists() {

View File

@ -113,7 +113,7 @@
</div> </div>
<Scrollbar ref="scroller" class="scroller" :touch-content-blur="false"> <Scrollbar ref="scroller" class="scroller" :touch-content-blur="false">
<Alert v-if="taskDetail.department_readonly" class="task-readonly-alert" type="info" show-icon> <Alert v-if="taskDetail.department_readonly" class="task-readonly-alert" type="info" show-icon>
{{$L('当前为负责人 ,并参与讨论,但不能编辑任务。')}} {{$L('当前为负责人,并参与讨论,但不能编辑任务。')}}
</Alert> </Alert>
<Alert v-if="!isDepartmentReadonly && taskDetail.task_user !== undefined && getOwner.length === 0" class="receive-box" type="warning"> <Alert v-if="!isDepartmentReadonly && taskDetail.task_user !== undefined && getOwner.length === 0" class="receive-box" type="warning">
<span class="receive-text">{{$L('该任务尚未被领取,点击这里')}}</span> <span class="receive-text">{{$L('该任务尚未被领取,点击这里')}}</span>

View File

@ -232,25 +232,9 @@ export default {
], true)) { ], true)) {
params.encrypt = true params.encrypt = true
} }
const departmentOwnerReadonlyUrls = [
'project/lists',
'project/one',
'project/column/lists',
'project/task/lists',
'project/task/one',
'project/task/content',
'project/task/content_history',
'project/task/files',
'project/task/fileinfo',
'project/task/subdata',
'project/task/related',
'project/flow/list',
'project/log/lists',
'project/tag/list',
]
if (params.departmentOwner !== false if (params.departmentOwner !== false
&& state.systemConfig.department_owner_project_view === 'open' && state.systemConfig.department_owner_project_view === 'open'
&& departmentOwnerReadonlyUrls.includes(params.url) && state.departmentOwnerReadonlyUrls.includes(params.url)
&& (state.cacheDepartmentOwnerIds || []).length > 0) { && (state.cacheDepartmentOwnerIds || []).length > 0) {
if (!$A.isJson(params.data)) params.data = {} if (!$A.isJson(params.data)) params.data = {}
if (params.data.department_owner_ids === undefined) { if (params.data.department_owner_ids === undefined) {
@ -1193,7 +1177,7 @@ export default {
* @param dispatch * @param dispatch
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
handleReadCache({state}) { handleReadCache({state, commit}) {
return new Promise(async resolve => { return new Promise(async resolve => {
// 定义需要获取的数据映射 // 定义需要获取的数据映射
const dataMap = { const dataMap = {
@ -1238,7 +1222,11 @@ export default {
// 更新 state // 更新 state
[...dataMap.string, ...dataMap.array, ...dataMap.json].forEach((key, index) => { [...dataMap.string, ...dataMap.array, ...dataMap.json].forEach((key, index) => {
state[key] = data[index]; if (key === 'cacheDepartmentOwnerIds') {
commit('department/owner/ids/save', data[index]);
} else {
state[key] = data[index];
}
}); });
// 特殊处理 cacheDialogs // 特殊处理 cacheDialogs
@ -1597,11 +1585,10 @@ export default {
* @param dispatch * @param dispatch
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async getProjectsForDepartmentOwnerView({state, dispatch}) { async getProjectsForDepartmentOwnerView({state, dispatch, commit}) {
await dispatch("systemSetting").catch(() => {}); await dispatch("systemSetting").catch(() => {});
if (state.systemConfig.department_owner_project_view !== 'open') { if (state.systemConfig.department_owner_project_view !== 'open') {
state.cacheDepartmentOwnerIds = []; commit('department/owner/ids/save', []);
await $A.IDBSet("cacheDepartmentOwnerIds", []).catch(() => {});
dispatch("getProjectByQueue"); dispatch("getProjectByQueue");
return; return;
} }
@ -1611,8 +1598,7 @@ export default {
__replace: true, __replace: true,
department_owner_ids: restoredDepartmentOwnerIds.join(',') department_owner_ids: restoredDepartmentOwnerIds.join(',')
}); });
state.cacheDepartmentOwnerIds = restoredDepartmentOwnerIds; commit('department/owner/ids/save', restoredDepartmentOwnerIds);
await $A.IDBSet("cacheDepartmentOwnerIds", restoredDepartmentOwnerIds).catch(() => {});
return; return;
} }
dispatch("getProjectByQueue"); dispatch("getProjectByQueue");
@ -1624,13 +1610,12 @@ export default {
* @param dispatch * @param dispatch
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async restoreDepartmentOwnerView({state, dispatch}) { async restoreDepartmentOwnerView({state, dispatch, commit}) {
if (state.departmentOwnerViewRestored) { if (state.departmentOwnerViewRestored) {
return []; return [];
} }
if (state.systemConfig.department_owner_project_view !== 'open') { if (state.systemConfig.department_owner_project_view !== 'open') {
state.cacheDepartmentOwnerIds = []; commit('department/owner/ids/save', []);
await $A.IDBSet("cacheDepartmentOwnerIds", []).catch(() => {});
return []; return [];
} }
state.departmentOwnerViewRestored = true; state.departmentOwnerViewRestored = true;
@ -1642,8 +1627,7 @@ export default {
if (restored.length > 0) { if (restored.length > 0) {
state.departmentOwnerProjectsRefreshing = true; state.departmentOwnerProjectsRefreshing = true;
} }
state.cacheDepartmentOwnerIds = restored; commit('department/owner/ids/save', restored);
await $A.IDBSet("cacheDepartmentOwnerIds", restored).catch(() => {});
return restored; return restored;
}, },
@ -1654,20 +1638,19 @@ export default {
* @param ids * @param ids
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async setDepartmentOwnerIds({state, dispatch}, ids) { async setDepartmentOwnerIds({state, dispatch, commit}, ids) {
if (state.systemConfig.department_owner_project_view !== 'open') { if (state.systemConfig.department_owner_project_view !== 'open') {
ids = []; ids = [];
} }
const normalized = await dispatch("normalizeDepartmentOwnerIds", ids); const normalized = await dispatch("normalizeDepartmentOwnerIds", ids);
const oldValue = (state.cacheDepartmentOwnerIds || []).map(id => parseInt(id)).sort().join(','); const oldValue = (state.cacheDepartmentOwnerIds || []).slice().sort().join(',');
const newValue = normalized.slice().sort().join(','); const newValue = normalized.slice().sort().join(',');
if (oldValue === newValue) { if (oldValue === newValue) {
return; return;
} }
state.departmentOwnerProjectsRefreshing = true; state.departmentOwnerProjectsRefreshing = true;
await dispatch("refreshDepartmentOwnerProjects", normalized); await dispatch("refreshDepartmentOwnerProjects", normalized);
state.cacheDepartmentOwnerIds = normalized; commit('department/owner/ids/save', normalized);
await $A.IDBSet("cacheDepartmentOwnerIds", normalized).catch(() => {});
}, },
/** /**

View File

@ -248,6 +248,12 @@ export default {
$A.IDBSave("cacheProjectParameter", state.cacheProjectParameter); $A.IDBSave("cacheProjectParameter", state.cacheProjectParameter);
}, },
// 部门负责人视角
'department/owner/ids/save': function(state, data) {
state.cacheDepartmentOwnerIds = $A.normalizeIntArray(data)
$A.IDBSet("cacheDepartmentOwnerIds", state.cacheDepartmentOwnerIds).catch(() => {});
},
// 文件管理 // 文件管理
'file/push': function(state, data) { 'file/push': function(state, data) {
state.fileLists.push(data) state.fileLists.push(data)

View File

@ -106,6 +106,22 @@ export default {
cacheDepartmentOwnerIds: [], cacheDepartmentOwnerIds: [],
departmentOwnerViewRestored: false, departmentOwnerViewRestored: false,
departmentOwnerProjectsRefreshing: false, departmentOwnerProjectsRefreshing: false,
departmentOwnerReadonlyUrls: [
'project/lists',
'project/one',
'project/column/lists',
'project/task/lists',
'project/task/one',
'project/task/content',
'project/task/content_history',
'project/task/files',
'project/task/fileinfo',
'project/task/subdata',
'project/task/related',
'project/flow/list',
'project/log/lists',
'project/tag/list',
],
// Emoji // Emoji
cacheEmojis: [], cacheEmojis: [],