feat(execution): 实现执行模块视角切换和快捷过滤功能
- 添加执行视角切换功能(my/all),支持不同身份维度查看 - 实现逾期/本周到期快捷过滤功能,提升执行管理效率 - 重构执行区域UI布局,优化用户体验和界面结构 - 集成Element Plus表单验证,在用户选择器组件中使用 - 优化执行状态筛选和计数逻辑,提升数据展示准确性 - 实现执行视角切换时的数据同步刷新机制 - 添加执行完成操作的二次确认对话框 - 重构权限码检查逻辑,统一使用query权限码进行控制 - 移除auth store依赖,精简代码结构 - 优化执行状态看板和任务计数的加载机制 - 实现执行创建和编辑流程的状态同步更新 - 统一任务工作区的执行范围传递方式,提高性能 - 添加执行详情面板的操作按钮权限控制 - 优化执行删除后的数据刷新逻辑,确保视图一致性
This commit is contained in:
@@ -22,6 +22,11 @@ export interface ProductActivityTextPart {
|
||||
strong?: boolean;
|
||||
}
|
||||
|
||||
interface ActivitySummaryResult {
|
||||
text: string;
|
||||
parts: ProductActivityTextPart[];
|
||||
}
|
||||
|
||||
export interface ProductActivityDisplayItem extends Api.Product.ProductActivityTimelineItem {
|
||||
tagLabel: string;
|
||||
timeText: string;
|
||||
@@ -265,34 +270,46 @@ function buildMemberChangeSummary(
|
||||
item: Api.Product.ProductActivityTimelineItem,
|
||||
detailsRecord: ActivityDetailRecord | null,
|
||||
operatorText: string
|
||||
) {
|
||||
): ActivitySummaryResult | null {
|
||||
const memberName = getActivityTargetUserName(item, detailsRecord);
|
||||
const roleName = getActivityTargetRoleName(item, detailsRecord);
|
||||
|
||||
if (!memberName) {
|
||||
return '';
|
||||
return null;
|
||||
}
|
||||
|
||||
const memberDetail = roleName ? `${memberName}(${roleName})` : memberName;
|
||||
const prefix =
|
||||
operatorText === '--' ? `执行了【${item.actionName}】:` : `${operatorText}执行了【${item.actionName}】:`;
|
||||
const roleSuffix = roleName ? `(${roleName})` : '';
|
||||
const text = `${prefix}${memberName}${roleSuffix}`;
|
||||
const parts: ProductActivityTextPart[] = [{ text: prefix }, { text: memberName, strong: true }];
|
||||
|
||||
return operatorText === '--'
|
||||
? `执行了【${item.actionName}】:${memberDetail}`
|
||||
: `${operatorText}执行了【${item.actionName}】:${memberDetail}`;
|
||||
if (roleSuffix) {
|
||||
parts.push({ text: roleSuffix });
|
||||
}
|
||||
|
||||
return { text, parts };
|
||||
}
|
||||
|
||||
function buildMemberUpdateSummary(
|
||||
item: Api.Product.ProductActivityTimelineItem,
|
||||
detailsRecord: ActivityDetailRecord | null,
|
||||
operatorText: string
|
||||
) {
|
||||
): ActivitySummaryResult {
|
||||
const memberName = getActivityTargetUserName(item, detailsRecord);
|
||||
const roleTransitionText = getRoleTransitionText(detailsRecord);
|
||||
const memberText = memberName || '成员';
|
||||
const roleText = roleTransitionText ? `,角色:${roleTransitionText}` : '';
|
||||
const prefix =
|
||||
operatorText === '--' ? `执行了【${item.actionName}】:` : `${operatorText}执行了【${item.actionName}】:`;
|
||||
const text = `${prefix}${memberText}${roleText}`;
|
||||
const parts: ProductActivityTextPart[] = [{ text: prefix }, { text: memberText, strong: Boolean(memberName) }];
|
||||
|
||||
return operatorText === '--'
|
||||
? `执行了【${item.actionName}】:${memberText}${roleText}`
|
||||
: `${operatorText}执行了【${item.actionName}】:${memberText}${roleText}`;
|
||||
if (roleText) {
|
||||
parts.push({ text: roleText });
|
||||
}
|
||||
|
||||
return { text, parts };
|
||||
}
|
||||
|
||||
function buildManagerChangeSummary(detailsRecord: ActivityDetailRecord | null, operatorText: string) {
|
||||
@@ -319,16 +336,20 @@ function buildManagerChangeSummary(detailsRecord: ActivityDetailRecord | null, o
|
||||
return operatorText === '--' ? `变更产品经理:${transitionText}` : `${operatorText}变更产品经理:${transitionText}`;
|
||||
}
|
||||
|
||||
function plainSummary(text: string): ActivitySummaryResult {
|
||||
return { text, parts: [{ text }] };
|
||||
}
|
||||
|
||||
function resolveDetailedSummary(
|
||||
item: Api.Product.ProductActivityTimelineItem,
|
||||
detailsRecord: ActivityDetailRecord | null,
|
||||
texts: { operatorText: string; actionText: string }
|
||||
) {
|
||||
): ActivitySummaryResult {
|
||||
const { operatorText, actionText } = texts;
|
||||
const summaryText = item.summary?.trim() || '';
|
||||
|
||||
if (item.actionType === 'add_member' || item.actionType === 'remove_member') {
|
||||
return buildMemberChangeSummary(item, detailsRecord, operatorText) || summaryText || actionText;
|
||||
return buildMemberChangeSummary(item, detailsRecord, operatorText) || plainSummary(summaryText || actionText);
|
||||
}
|
||||
|
||||
if (item.actionType === 'update_member') {
|
||||
@@ -336,29 +357,16 @@ function resolveDetailedSummary(
|
||||
}
|
||||
|
||||
if (!isGenericActivitySummary(summaryText, actionText)) {
|
||||
return summaryText;
|
||||
return plainSummary(summaryText);
|
||||
}
|
||||
|
||||
if (item.actionType === 'change_manager') {
|
||||
return buildManagerChangeSummary(detailsRecord, operatorText) || summaryText || actionText;
|
||||
const managerSummary = buildManagerChangeSummary(detailsRecord, operatorText);
|
||||
|
||||
return plainSummary(managerSummary || summaryText || actionText);
|
||||
}
|
||||
|
||||
return summaryText || actionText;
|
||||
}
|
||||
|
||||
function buildProductActivityTextParts(text: string, subjectText: string): ProductActivityTextPart[] {
|
||||
const normalizedSubject = subjectText.trim();
|
||||
const subjectIndex = normalizedSubject ? text.indexOf(normalizedSubject) : -1;
|
||||
|
||||
if (subjectIndex < 0) {
|
||||
return [{ text }];
|
||||
}
|
||||
|
||||
return [
|
||||
{ text: text.slice(0, subjectIndex) },
|
||||
{ text: normalizedSubject, strong: true },
|
||||
{ text: text.slice(subjectIndex + normalizedSubject.length) }
|
||||
].filter(part => part.text);
|
||||
return plainSummary(summaryText || actionText);
|
||||
}
|
||||
|
||||
export function buildProductActivityDisplayItem(
|
||||
@@ -369,18 +377,19 @@ export function buildProductActivityDisplayItem(
|
||||
operatorText === '--' ? `执行了【${item.actionName}】` : `${operatorText}执行了【${item.actionName}】`;
|
||||
const detailsRecord = parseActivityDetails(item.details);
|
||||
const subjectText = isMemberActivityAction(item.actionType) ? getActivityTargetUserName(item, detailsRecord) : '';
|
||||
const displaySummary =
|
||||
item.type === 'status' ? actionText : resolveDetailedSummary(item, detailsRecord, { operatorText, actionText });
|
||||
const compactText = displaySummary;
|
||||
const summary =
|
||||
item.type === 'status'
|
||||
? plainSummary(actionText)
|
||||
: resolveDetailedSummary(item, detailsRecord, { operatorText, actionText });
|
||||
|
||||
return {
|
||||
...item,
|
||||
tagLabel: activityTypeLabelMap[item.type],
|
||||
timeText: formatProductActivityTime(item.occurredAt) || '--',
|
||||
actionText,
|
||||
displaySummary,
|
||||
compactText,
|
||||
compactTextParts: buildProductActivityTextParts(compactText, subjectText),
|
||||
displaySummary: summary.text,
|
||||
compactText: summary.text,
|
||||
compactTextParts: summary.parts.filter(part => part.text),
|
||||
operatorText,
|
||||
subjectText,
|
||||
reasonText: item.reason?.trim() || '',
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
fetchUpdateProductMember,
|
||||
fetchUpdateProductSettingBaseInfo
|
||||
} from '@/service/api';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import { useObjectContextStore } from '@/store/modules/object-context';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
@@ -46,7 +45,6 @@ import {
|
||||
|
||||
defineOptions({ name: 'ProductSetting' });
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const objectContextStore = useObjectContextStore();
|
||||
const themeStore = useThemeStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
@@ -97,9 +95,7 @@ const baseInfo = computed(() => settings.value?.baseInfo || null);
|
||||
const lifecycle = computed(() => settings.value?.lifecycle || null);
|
||||
const canManageTeam = computed(() =>
|
||||
canManageProductTeam({
|
||||
buttonCodes: objectContextStore.buttonCodes,
|
||||
loginUserId: authStore.userInfo.userId,
|
||||
currentManagerUserId: currentManager.value?.userId
|
||||
buttonCodes: objectContextStore.buttonCodes
|
||||
})
|
||||
);
|
||||
const visibleSectionKeys = computed(() =>
|
||||
|
||||
@@ -6,8 +6,6 @@ export interface ProductManagerMemberLike {
|
||||
|
||||
interface ProductTeamManageContext {
|
||||
buttonCodes: readonly string[];
|
||||
loginUserId: string | null | undefined;
|
||||
currentManagerUserId: string | null | undefined;
|
||||
}
|
||||
|
||||
interface ProductLifecycleStatusSummary {
|
||||
@@ -203,13 +201,5 @@ export function getProductLifecycleActionCardMeta(actionCode: Api.Product.Produc
|
||||
}
|
||||
|
||||
export function canManageProductTeam(context: ProductTeamManageContext) {
|
||||
const hasUpdateAuth = context.buttonCodes.includes('project:product:update');
|
||||
const loginUserId = String(context.loginUserId || '');
|
||||
const currentManagerUserId = String(context.currentManagerUserId || '');
|
||||
|
||||
if (!hasUpdateAuth || !loginUserId || !currentManagerUserId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return loginUserId === currentManagerUserId;
|
||||
return context.buttonCodes.includes('project:product:update');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user