feat(projects): 新增项目、执行、任务等功能
This commit is contained in:
@@ -247,7 +247,12 @@ watch([() => visible.value, () => props.productId], ([currentVisible, productId]
|
||||
</div>
|
||||
|
||||
<p class="product-activity-dialog__sentence">
|
||||
<span class="product-activity-dialog__sentence-main">{{ item.compactText }}</span>
|
||||
<span class="product-activity-dialog__sentence-main">
|
||||
<template v-for="(part, index) in item.compactTextParts" :key="`${item.id}-${index}`">
|
||||
<strong v-if="part.strong" class="product-activity-dialog__subject">{{ part.text }}</strong>
|
||||
<span v-else>{{ part.text }}</span>
|
||||
</template>
|
||||
</span>
|
||||
<span v-if="item.statusTransition">,状态:{{ item.statusTransition }}</span>
|
||||
<span v-if="item.reasonText">,原因:{{ item.reasonText }}</span>
|
||||
</p>
|
||||
@@ -497,6 +502,11 @@ watch([() => visible.value, () => props.productId], ([currentVisible, productId]
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.product-activity-dialog__subject {
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.product-activity-dialog__footer-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -112,7 +112,12 @@ watch(
|
||||
</div>
|
||||
|
||||
<p class="product-activity-panel__sentence">
|
||||
<span class="product-activity-panel__sentence-main">{{ item.compactText }}</span>
|
||||
<span class="product-activity-panel__sentence-main">
|
||||
<template v-for="(part, index) in item.compactTextParts" :key="`${item.id}-${index}`">
|
||||
<strong v-if="part.strong" class="product-activity-panel__subject">{{ part.text }}</strong>
|
||||
<span v-else>{{ part.text }}</span>
|
||||
</template>
|
||||
</span>
|
||||
<span v-if="item.statusTransition">,状态:{{ item.statusTransition }}</span>
|
||||
<span v-if="item.reasonText">,原因:{{ item.reasonText }}</span>
|
||||
</p>
|
||||
@@ -262,6 +267,11 @@ watch(
|
||||
color: rgb(15 23 42 / 98%);
|
||||
}
|
||||
|
||||
.product-activity-panel__subject {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.product-activity-panel__body {
|
||||
min-height: auto;
|
||||
|
||||
@@ -17,13 +17,20 @@ export type ProductActivityFilterType = 'all' | Api.Product.ProductActivityType;
|
||||
|
||||
export type ProductActivityTone = 'sky' | 'emerald' | 'amber' | 'rose' | 'slate';
|
||||
|
||||
export interface ProductActivityTextPart {
|
||||
text: string;
|
||||
strong?: boolean;
|
||||
}
|
||||
|
||||
export interface ProductActivityDisplayItem extends Api.Product.ProductActivityTimelineItem {
|
||||
tagLabel: string;
|
||||
timeText: string;
|
||||
actionText: string;
|
||||
displaySummary: string;
|
||||
compactText: string;
|
||||
compactTextParts: ProductActivityTextPart[];
|
||||
operatorText: string;
|
||||
subjectText: string;
|
||||
reasonText: string;
|
||||
statusTransition: string;
|
||||
tone: ProductActivityTone;
|
||||
@@ -250,6 +257,10 @@ function isGenericActivitySummary(summaryText: string, actionText: string) {
|
||||
return summaryText === actionText || summaryText === actionText.replace('执行了', '执行了');
|
||||
}
|
||||
|
||||
function isMemberActivityAction(actionType: Api.Product.ProductActivityActionType) {
|
||||
return actionType === 'add_member' || actionType === 'remove_member' || actionType === 'update_member';
|
||||
}
|
||||
|
||||
function buildMemberChangeSummary(
|
||||
item: Api.Product.ProductActivityTimelineItem,
|
||||
detailsRecord: ActivityDetailRecord | null,
|
||||
@@ -263,9 +274,10 @@ function buildMemberChangeSummary(
|
||||
}
|
||||
|
||||
const memberDetail = roleName ? `${memberName}(${roleName})` : memberName;
|
||||
const actionLabel = item.actionType === 'add_member' ? '将成员加入产品' : '将成员移出产品';
|
||||
|
||||
return operatorText === '--' ? `${actionLabel}:${memberDetail}` : `${operatorText}${actionLabel}:${memberDetail}`;
|
||||
return operatorText === '--'
|
||||
? `执行了【${item.actionName}】:${memberDetail}`
|
||||
: `${operatorText}执行了【${item.actionName}】:${memberDetail}`;
|
||||
}
|
||||
|
||||
function buildMemberUpdateSummary(
|
||||
@@ -279,8 +291,8 @@ function buildMemberUpdateSummary(
|
||||
const roleText = roleTransitionText ? `,角色:${roleTransitionText}` : '';
|
||||
|
||||
return operatorText === '--'
|
||||
? `调整成员:${memberText}${roleText}`
|
||||
: `${operatorText}调整成员:${memberText}${roleText}`;
|
||||
? `执行了【${item.actionName}】:${memberText}${roleText}`
|
||||
: `${operatorText}执行了【${item.actionName}】:${memberText}${roleText}`;
|
||||
}
|
||||
|
||||
function buildManagerChangeSummary(detailsRecord: ActivityDetailRecord | null, operatorText: string) {
|
||||
@@ -309,15 +321,11 @@ function buildManagerChangeSummary(detailsRecord: ActivityDetailRecord | null, o
|
||||
|
||||
function resolveDetailedSummary(
|
||||
item: Api.Product.ProductActivityTimelineItem,
|
||||
operatorText: string,
|
||||
actionText: string
|
||||
detailsRecord: ActivityDetailRecord | null,
|
||||
texts: { operatorText: string; actionText: string }
|
||||
) {
|
||||
const { operatorText, actionText } = texts;
|
||||
const summaryText = item.summary?.trim() || '';
|
||||
const detailsRecord = parseActivityDetails(item.details);
|
||||
|
||||
if (!isGenericActivitySummary(summaryText, actionText)) {
|
||||
return summaryText;
|
||||
}
|
||||
|
||||
if (item.actionType === 'add_member' || item.actionType === 'remove_member') {
|
||||
return buildMemberChangeSummary(item, detailsRecord, operatorText) || summaryText || actionText;
|
||||
@@ -327,6 +335,10 @@ function resolveDetailedSummary(
|
||||
return buildMemberUpdateSummary(item, detailsRecord, operatorText);
|
||||
}
|
||||
|
||||
if (!isGenericActivitySummary(summaryText, actionText)) {
|
||||
return summaryText;
|
||||
}
|
||||
|
||||
if (item.actionType === 'change_manager') {
|
||||
return buildManagerChangeSummary(detailsRecord, operatorText) || summaryText || actionText;
|
||||
}
|
||||
@@ -334,13 +346,31 @@ function resolveDetailedSummary(
|
||||
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);
|
||||
}
|
||||
|
||||
export function buildProductActivityDisplayItem(
|
||||
item: Api.Product.ProductActivityTimelineItem
|
||||
): ProductActivityDisplayItem {
|
||||
const operatorText = item.operatorName?.trim() || '--';
|
||||
const actionText =
|
||||
operatorText === '--' ? `执行了【${item.actionName}】` : `${operatorText}执行了【${item.actionName}】`;
|
||||
const displaySummary = item.type === 'status' ? actionText : resolveDetailedSummary(item, operatorText, actionText);
|
||||
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;
|
||||
|
||||
return {
|
||||
@@ -350,7 +380,9 @@ export function buildProductActivityDisplayItem(
|
||||
actionText,
|
||||
displaySummary,
|
||||
compactText,
|
||||
compactTextParts: buildProductActivityTextParts(compactText, subjectText),
|
||||
operatorText,
|
||||
subjectText,
|
||||
reasonText: item.reason?.trim() || '',
|
||||
statusTransition:
|
||||
item.type === 'status' && item.fromStatus && item.toStatus
|
||||
|
||||
Reference in New Issue
Block a user