feat(feedback): 增加反馈搜索状态筛选功能
- 在反馈搜索组件中引入状态字典选项 - 为反馈搜索区域增加状态选择器字段 - 设置反馈页面初始搜索参数中的状态值为1 fix(personal-center): 限制已完成项目的编辑操作 - 根据项目完成状态动态控制编辑按钮的显示 - 从个人中心我的项目详情对话框中移除已完成项目的操作权限 refactor(project): 优化任务操作权限控制逻辑 - 移除原有的任务权限钩子函数依赖 - 使用当前用户身份直接判断任务操作权限 - 只有任务负责人才能进行编辑和删除操作 - 已完成任务不可编辑 - 重构成更清晰的任务操作按钮显示逻辑 fix(project): 完成任务禁用工时记录删除功能 - 添加计算属性控制完成状态下的工时记录删除权限 - 在工时记录面板中根据任务状态控制删除操作的显示
This commit is contained in:
@@ -45,7 +45,7 @@ function canEditRow(row: Api.Feedback.FeedbackItem) {
|
||||
}
|
||||
|
||||
function getInitSearchParams(): Api.Feedback.FeedbackSearchParams {
|
||||
return { pageNo: 1, pageSize: 20, type: undefined, status: undefined, title: undefined, creator: undefined };
|
||||
return { pageNo: 1, pageSize: 20, type: undefined, status: 1, title: undefined, creator: undefined };
|
||||
}
|
||||
|
||||
function transformPageResult(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { FEEDBACK_STATUS_DICT_CODE } from '@/constants/dict';
|
||||
import { fetchGetUserSimpleList } from '@/service/api';
|
||||
import { useDict } from '@/hooks/business/dict';
|
||||
import TableSearchFields, { type SearchField } from '@/components/custom/table-search-fields.vue';
|
||||
|
||||
defineOptions({ name: 'FeedbackSearch' });
|
||||
@@ -14,8 +16,9 @@ const model = defineModel<Api.Feedback.FeedbackSearchParams>('model', { required
|
||||
|
||||
// 提交人下拉选项(全员简易列表;value 取用户 id,ID 铁律 string)
|
||||
const userOptions = ref<{ label: string; value: string }[]>([]);
|
||||
const { dictOptions: statusOptions } = useDict(FEEDBACK_STATUS_DICT_CODE);
|
||||
|
||||
// 分类 / 状态筛选已迁移至左侧分面面板,搜索区保留标题关键词 + 提交人
|
||||
// 分类保留在左侧分面;搜索区补充标题关键词、提交人、状态
|
||||
const fields = computed<SearchField[]>(() => [
|
||||
{ key: 'title', label: '标题', type: 'input', placeholder: '请输入标题关键词' },
|
||||
{
|
||||
@@ -25,6 +28,13 @@ const fields = computed<SearchField[]>(() => [
|
||||
placeholder: '请选择提交人',
|
||||
filterable: true,
|
||||
options: userOptions.value
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: '状态',
|
||||
type: 'select',
|
||||
placeholder: '请选择状态',
|
||||
options: statusOptions.value
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
@@ -281,6 +281,8 @@ function buildRowActions(row: Api.PersonalItem.PersonalItem): PersonalItemRowAct
|
||||
type: 'primary',
|
||||
onClick: async () => openDetail(row, 'worklog')
|
||||
},
|
||||
...(!isCompleted
|
||||
? ([
|
||||
{
|
||||
key: 'edit',
|
||||
tooltip: '编辑',
|
||||
@@ -291,7 +293,9 @@ function buildRowActions(row: Api.PersonalItem.PersonalItem): PersonalItemRowAct
|
||||
editingData.value = row;
|
||||
openOperateDialog();
|
||||
}
|
||||
},
|
||||
}
|
||||
] satisfies PersonalItemRowAction[])
|
||||
: []),
|
||||
...(!isCompleted
|
||||
? ([
|
||||
{
|
||||
|
||||
@@ -81,10 +81,7 @@ const totalHoursText = computed(() => {
|
||||
});
|
||||
const canSubmitWorklog = computed(() =>
|
||||
Boolean(
|
||||
detailData.value?.id &&
|
||||
(detailData.value.statusCode === 'pending' ||
|
||||
detailData.value.statusCode === 'active' ||
|
||||
detailData.value.statusCode === 'completed')
|
||||
detailData.value?.id && (detailData.value.statusCode === 'pending' || detailData.value.statusCode === 'active')
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { markRaw } from 'vue';
|
||||
import { useTaskPermissions } from './use-task-permissions';
|
||||
import IconMdiCheckCircleOutline from '~icons/mdi/check-circle-outline';
|
||||
import { computed, markRaw } from 'vue';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import IconMdiClipboardEditOutline from '~icons/mdi/clipboard-edit-outline';
|
||||
import IconMdiCloseCircleOutline from '~icons/mdi/close-circle-outline';
|
||||
import IconMdiDeleteOutline from '~icons/mdi/delete-outline';
|
||||
import IconMdiPause from '~icons/mdi/pause';
|
||||
import IconMdiPencilOutline from '~icons/mdi/pencil-outline';
|
||||
import IconMdiRestart from '~icons/mdi/restart';
|
||||
import IconMdiSync from '~icons/mdi/sync';
|
||||
|
||||
export interface TaskAction {
|
||||
key: string;
|
||||
@@ -27,43 +22,22 @@ export interface TaskActionEmits {
|
||||
) => void;
|
||||
}
|
||||
|
||||
const STATUS_ACTION_ICON_MAP: Record<string, object> = {
|
||||
pause: markRaw(IconMdiPause),
|
||||
complete: markRaw(IconMdiCheckCircleOutline),
|
||||
resume: markRaw(IconMdiRestart),
|
||||
cancel: markRaw(IconMdiCloseCircleOutline)
|
||||
};
|
||||
|
||||
// 状态推进按钮 type 映射(对齐执行 execution-section.vue 同源语义):
|
||||
// cancel 破坏性=红,pause 中断=橙,complete 完结=绿,resume 主动作=蓝
|
||||
const STATUS_ACTION_TYPE_MAP: Record<string, TaskAction['type']> = {
|
||||
cancel: 'danger',
|
||||
pause: 'warning',
|
||||
complete: 'success',
|
||||
resume: 'primary'
|
||||
};
|
||||
|
||||
// 同一状态下多个推进按钮的展示顺序,与执行侧保持一致:暂停 → 取消 → 完成 → 恢复
|
||||
const STATUS_ACTION_ORDER: Record<string, number> = {
|
||||
pause: 1,
|
||||
cancel: 2,
|
||||
complete: 3,
|
||||
resume: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务行操作按钮的集中装配。
|
||||
*
|
||||
* 表格操作列与看板卡片操作区共用同一份语义:填报 / 编辑 / 删除 / 状态推进按钮,
|
||||
* 含 auto_start 过滤、complete 进度 100% 兜底、按钮排序与 icon/type 映射。
|
||||
* 只在这里收口任务行按钮的最终显示口径,不改动底层子任务 / 父任务负责人 / 执行负责人那套权限体系:
|
||||
* - 当前用户是任务负责人(task.ownerId): 显示 填报 / 编辑 / 删除
|
||||
* - 当前用户是协办人(task.assignees[].userId): 只显示 填报
|
||||
* - 都不是: 也显示 填报
|
||||
*/
|
||||
export function useTaskActions(emits: TaskActionEmits) {
|
||||
const { canEditTask, canDeleteTask } = useTaskPermissions();
|
||||
const authStore = useAuthStore();
|
||||
const currentUserId = computed(() => authStore.userInfo.userId || '');
|
||||
|
||||
function createActions(row: Api.Project.ProjectTask): TaskAction[] {
|
||||
const actions: TaskAction[] = [];
|
||||
const isOwner = Boolean(currentUserId.value) && row.ownerId === currentUserId.value;
|
||||
|
||||
// 填报:行操作入口始终显示——查看人人可看;新增/编辑由弹层内 canSubmit 按身份与状态控制
|
||||
actions.push({
|
||||
key: 'report',
|
||||
tooltip: '填报',
|
||||
@@ -72,7 +46,11 @@ export function useTaskActions(emits: TaskActionEmits) {
|
||||
onClick: () => emits.report(row)
|
||||
});
|
||||
|
||||
if (canEditTask(row)) {
|
||||
if (!isOwner) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
if (row.statusCode !== 'completed') {
|
||||
actions.push({
|
||||
key: 'edit',
|
||||
tooltip: '编辑',
|
||||
@@ -82,7 +60,6 @@ export function useTaskActions(emits: TaskActionEmits) {
|
||||
});
|
||||
}
|
||||
|
||||
if (canDeleteTask(row)) {
|
||||
actions.push({
|
||||
key: 'delete',
|
||||
tooltip: '删除',
|
||||
@@ -90,34 +67,6 @@ export function useTaskActions(emits: TaskActionEmits) {
|
||||
type: 'danger',
|
||||
onClick: () => emits.remove(row)
|
||||
});
|
||||
}
|
||||
|
||||
if (!row.availableActions.length) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
// 排序:暂停 → 取消 → 完成 → 恢复(对齐执行)
|
||||
const sortedActions = [...row.availableActions].sort(
|
||||
(a, b) => (STATUS_ACTION_ORDER[a.actionCode] ?? 99) - (STATUS_ACTION_ORDER[b.actionCode] ?? 99)
|
||||
);
|
||||
|
||||
sortedActions.forEach(action => {
|
||||
// auto_start 由后端在填工时时自动触发,无手工开始通道;防御后端误下发,前端不渲染
|
||||
if (action.actionCode === 'auto_start') {
|
||||
return;
|
||||
}
|
||||
// 完成任务至少要求任务进度达到 100%;父级提交入口仍保留同样兜底校验
|
||||
if (action.actionCode === 'complete' && row.progressRate < 100) {
|
||||
return;
|
||||
}
|
||||
actions.push({
|
||||
key: `status-${action.actionCode}`,
|
||||
tooltip: action.actionName,
|
||||
icon: markRaw(STATUS_ACTION_ICON_MAP[action.actionCode] ?? IconMdiSync),
|
||||
type: STATUS_ACTION_TYPE_MAP[action.actionCode] ?? 'primary',
|
||||
onClick: () => emits.statusAction(row, action)
|
||||
});
|
||||
});
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
@@ -256,6 +256,7 @@ const canCreate = computed(() => Boolean(props.canSubmit && props.taskId));
|
||||
const isWorklogMutableStatus = computed(
|
||||
() => props.taskStatusCode === 'active' || props.taskStatusCode === 'completed'
|
||||
);
|
||||
const showDeleteAction = computed(() => props.taskStatusCode !== 'completed');
|
||||
|
||||
function canEditRow(row: Api.Project.TaskWorklog) {
|
||||
return Boolean(isWorklogMutableStatus.value && currentUserId.value && row.userId === currentUserId.value);
|
||||
@@ -706,6 +707,7 @@ watch(
|
||||
</ElButton>
|
||||
</span>
|
||||
</ElTooltip>
|
||||
<template v-if="showDeleteAction">
|
||||
<ElPopconfirm
|
||||
v-if="canDeleteRow(row)"
|
||||
title="确认删除该条工时记录?"
|
||||
@@ -731,6 +733,7 @@ watch(
|
||||
</ElButton>
|
||||
</span>
|
||||
</ElTooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
|
||||
Reference in New Issue
Block a user