{issue.source === 'youtrack' ? issue.externalId : `#${issue.number}`}
diff --git a/vscode/webview-ui/src/components/IssueDetailPanel.tsx b/vscode/webview-ui/src/components/IssueDetailPanel.tsx
index 073bde1..bf34eb3 100644
--- a/vscode/webview-ui/src/components/IssueDetailPanel.tsx
+++ b/vscode/webview-ui/src/components/IssueDetailPanel.tsx
@@ -4,6 +4,8 @@ import type { PropertyGroup, RowAction } from './property-grid'
import { ArrowRightLeft, CircleSlash, Download, ExternalLink, GitMerge, Loader2, Play, RotateCcw, Terminal, Trash2, X } from 'lucide-react'
import { useMemo, useRef } from 'react'
import { isIssueLocked } from '../lib/dependencies'
+import { isIssueMine, issueOwnerLogin } from '../lib/issueOwnership'
+import { toReadOnlySchema } from '../lib/readOnlySchema'
import { COLUMN_LABELS, COLUMN_ORDER } from '../types'
import { PropertyGrid } from './property-grid'
@@ -107,6 +109,8 @@ interface IssueDetailPanelProps {
onStartHandoff: (issueNumber: number) => void
onAcceptHandoff: (issueNumber: number) => void
isHandoffRunning: (issueNumber: number) => boolean
+ /** Gitea login → 头像 data URI;只读工单在标题旁贴归属人头像。 */
+ avatars?: Record
}
/**
@@ -149,6 +153,7 @@ export function IssueDetailPanel({
onStartHandoff,
onAcceptHandoff,
isHandoffRunning,
+ avatars,
}: IssueDetailPanelProps) {
// 前置工单未完成 → 锁定。锁定时禁用"实施"等启动新工作的动作按钮,
// 但已存在的会话恢复链接保持可用(属于用户已操作过的入口)。
@@ -520,6 +525,14 @@ export function IssueDetailPanel({
[onResumeSession, onResumeReviewSession, onResumeTestSession, onStartTestSession, onOpenFile, onImplement, onOpenPr, onGeneratePrDiffSummary, onOpenWorktree, onDeleteWorktree, onMergeBranch, onCloseSessionTab, onContinueBrainstorm, onOpenLogs, issue, locked, lockTitle, globalAutoReview, prDiffSummaryRunning, profiles],
)
+ // 同事的工单只读:属性表统一消毒,顶部只留查看类按钮。
+ const readOnly = issue ? !isIssueMine(issue, me) : false
+ const ownerLogin = issue ? issueOwnerLogin(issue) : undefined
+ const effectiveSchema = useMemo(
+ () => readOnly ? toReadOnlySchema(schema) : schema,
+ [schema, readOnly],
+ )
+
const data = useMemo | null>(() => {
if (!issue)
return null
@@ -589,6 +602,20 @@ export function IssueDetailPanel({
)
: null}
+ {readOnly && ownerLogin
+ ? (
+
+ {avatars?.[ownerLogin]
+ ?
+ : null}
+ {ownerLogin}
+ {' · 只读'}
+
+ )
+ : null}
{/* Close/delete write to gitea by number — hide for youtrack cards to
avoid acting on a gitea issue of the same number. */}
- {issue.source !== 'youtrack' && (
+ {issue.source !== 'youtrack' && !readOnly && (
<>
{issue.handoffAttachmentId && me !== undefined && (issue.assignees ?? []).includes(me) && (