mirror of
https://github.com/kuaifan/dootask.git
synced 2026-05-24 09:24:07 +00:00
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:
parent
8cd4669b90
commit
ef7293704b
@ -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;
|
||||||
|
|||||||
14
resources/assets/js/functions/common.js
vendored
14
resources/assets/js/functions/common.js
vendored
@ -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
|
||||||
|
|||||||
@ -678,7 +678,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
cacheDepartmentOwnerIds() {
|
cacheDepartmentOwnerIds() {
|
||||||
return (this.$store.state.cacheDepartmentOwnerIds || []).map(id => parseInt(id));
|
return this.$store.state.cacheDepartmentOwnerIds || [];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -169,7 +169,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ownerDepartmentIds() {
|
ownerDepartmentIds() {
|
||||||
return (this.cacheDepartmentOwnerIds || []).map(id => parseInt(id));
|
return this.cacheDepartmentOwnerIds || [];
|
||||||
},
|
},
|
||||||
|
|
||||||
projectBaseLists() {
|
projectBaseLists() {
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
49
resources/assets/js/store/actions.js
vendored
49
resources/assets/js/store/actions.js
vendored
@ -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(() => {});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
6
resources/assets/js/store/mutations.js
vendored
6
resources/assets/js/store/mutations.js
vendored
@ -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)
|
||||||
|
|||||||
16
resources/assets/js/store/state.js
vendored
16
resources/assets/js/store/state.js
vendored
@ -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: [],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user