✨ feat(vscode): 看板默认显示团队工单,同事的卡只读并贴头像
看板范围默认从「只看我的」改为「团队全部」,同事移交或推进的工单不用 手动切开关就能看见。同事的卡片按 Gitea 归属(assignee 优先,无 assignee 退回创建者)判定为只读:禁止拖列、开会话、实施、合并分支、删 worktree、 删改工单,卡片右上角贴归属人头像,详情面板属性表整体消毒并显示只读徽章。 看 PR 改动、勾已查看、生成文件摘要属于本机审查动作,照常放行。 扩展侧在消息分发前统一拦截需要归属人身份的消息,兜住前端快照过期或工单 刚被重新指派的竞态;webhook 单卡上板时同步归属,避免增量卡漏进门禁。 头像由扩展侧带 token 抓回转 data URI 下发,绕开 webview CSP 对外链图片 的限制,按 URL 进程内缓存。 Claude-Session: https://claude.ai/code/session_01KSgcQwf1mWrb8qeTKFvJbV
This commit is contained in:
1 parent
e8963ac2a2
commit
9167b86708
26 files changed
+550
-34
No files matched your search
@@ -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<string, string>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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<Record<string, unknown> | null>(() => {
|
||||
if (!issue)
|
||||
return null
|
||||
@@ -589,6 +602,20 @@ export function IssueDetailPanel({
|
||||
)
|
||||
: null}
|
||||
</h3>
|
||||
{readOnly && ownerLogin
|
||||
? (
|
||||
<span
|
||||
className="inline-flex shrink-0 items-center gap-1.5 rounded border border-[var(--vscode-panel-border)] px-2 py-1 text-[11px] opacity-80"
|
||||
title={`${ownerLogin} 的工单,只能查看`}
|
||||
>
|
||||
{avatars?.[ownerLogin]
|
||||
? <img src={avatars[ownerLogin]} alt={ownerLogin} className="size-4 rounded-full" />
|
||||
: null}
|
||||
{ownerLogin}
|
||||
{' · 只读'}
|
||||
</span>
|
||||
)
|
||||
: null}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenInBrowser(issue.htmlUrl)}
|
||||
@@ -599,7 +626,7 @@ export function IssueDetailPanel({
|
||||
</button>
|
||||
{/* 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) && (
|
||||
<button
|
||||
@@ -691,11 +718,13 @@ export function IssueDetailPanel({
|
||||
<div className="min-h-0 flex-1">
|
||||
<PropertyGrid
|
||||
key={issue.number}
|
||||
schema={schema}
|
||||
schema={effectiveSchema}
|
||||
data={data}
|
||||
hideToolbar
|
||||
fillHeight
|
||||
onChange={(key, value) => {
|
||||
if (readOnly)
|
||||
return
|
||||
if (key === 'autoReview' && issue && typeof value === 'boolean')
|
||||
onUpdateAutoReview(issue.number, value)
|
||||
if (key === 'brainstormProfilePath' && issue && typeof value === 'string')
|
||||
|
||||
Reference in new issue
Block a user