Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
dac2188a
Commit
dac2188a
authored
May 22, 2026
by
edy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(ui): filter douyin task artifacts
parent
782eb235
Pipeline
#18478
failed
Changes
4
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
122 additions
and
4 deletions
+122
-4
TaskPanelView.tsx
apps/ui/src/features/tasks/TaskPanelView.tsx
+2
-2
taskPanelData.ts
apps/ui/src/features/tasks/taskPanelData.ts
+41
-2
taskPanelData.test.ts
apps/ui/test/taskPanelData.test.ts
+73
-0
taskPanelViewSource.test.ts
apps/ui/test/taskPanelViewSource.test.ts
+6
-0
No files found.
apps/ui/src/features/tasks/TaskPanelView.tsx
View file @
dac2188a
...
@@ -4,7 +4,7 @@ import { renderExpertIcon } from "../../components/icons/AppIcons"
...
@@ -4,7 +4,7 @@ import { renderExpertIcon } from "../../components/icons/AppIcons"
import
{
Panel
}
from
"../../components/ui/Panel"
import
{
Panel
}
from
"../../components/ui/Panel"
import
{
ScrollArea
}
from
"../../components/ui/ScrollArea"
import
{
ScrollArea
}
from
"../../components/ui/ScrollArea"
import
type
{
ExpertVisualKey
}
from
"../shell/ExpertTree"
import
type
{
ExpertVisualKey
}
from
"../shell/ExpertTree"
import
{
getDefaultTaskPanelDate
,
loadTaskPanelItems
,
summarizeTaskPanelItems
}
from
"./taskPanelData"
import
{
getDefaultTaskPanelDate
,
getVisibleTaskPanelArtifacts
,
loadTaskPanelItems
,
summarizeTaskPanelItems
}
from
"./taskPanelData"
function
resolveTaskExpertIconKey
(
expertName
:
string
):
ExpertVisualKey
{
function
resolveTaskExpertIconKey
(
expertName
:
string
):
ExpertVisualKey
{
const
seed
=
expertName
.
toLowerCase
()
const
seed
=
expertName
.
toLowerCase
()
...
@@ -324,7 +324,7 @@ export function TaskPanelView() {
...
@@ -324,7 +324,7 @@ export function TaskPanelView() {
const displayDate = useMemo(() => selectedDate.replaceAll("-", "/"), [selectedDate])
const displayDate = useMemo(() => selectedDate.replaceAll("-", "/"), [selectedDate])
const displayMonth = useMemo(() => `
$
{
Number
(
selectedDate
.
slice
(
5
,
7
))}
月
`, [selectedDate])
const displayMonth = useMemo(() => `
$
{
Number
(
selectedDate
.
slice
(
5
,
7
))}
月
`, [selectedDate])
const outputItems = useMemo<TaskPanelOutputItem[]>(() => {
const outputItems = useMemo<TaskPanelOutputItem[]>(() => {
return items.flatMap((task) =>
task.artifacts
.map((artifact) => ({ artifact, task })))
return items.flatMap((task) =>
getVisibleTaskPanelArtifacts(task)
.map((artifact) => ({ artifact, task })))
}, [items])
}, [items])
return (
return (
...
...
apps/ui/src/features/tasks/taskPanelData.ts
View file @
dac2188a
import
type
{
TaskPanelItem
}
from
"@qjclaw/shared-types"
import
type
{
TaskPanel
Artifact
,
TaskPanel
Item
}
from
"@qjclaw/shared-types"
export
interface
TaskPanelSummary
{
export
interface
TaskPanelSummary
{
creditsUsed
:
number
creditsUsed
:
number
...
@@ -7,6 +7,27 @@ export interface TaskPanelSummary {
...
@@ -7,6 +7,27 @@ export interface TaskPanelSummary {
employeeCount
:
number
employeeCount
:
number
}
}
const
douyinVisibleArtifactExtensions
=
new
Set
([
".docx"
,
".mp4"
])
function
isDouyinTaskPanelExpert
(
expertName
:
string
)
{
return
/douyin|抖音/
.
test
(
expertName
.
toLowerCase
())
}
function
getTaskPanelArtifactExtension
(
artifact
:
TaskPanelArtifact
)
{
const
source
=
(
artifact
.
path
??
artifact
.
url
??
artifact
.
name
).
trim
()
const
cleanSource
=
source
.
split
(
/
[
?#
]
/
,
1
)[
0
]
??
""
const
match
=
cleanSource
.
match
(
/
\.([
a-z0-9
]
+
)
$/i
)
return
match
?
`.
${
match
[
1
].
toLowerCase
()}
`
:
""
}
export
function
getVisibleTaskPanelArtifacts
(
task
:
Pick
<
TaskPanelItem
,
"expertName"
|
"artifacts"
>
)
{
if
(
!
isDouyinTaskPanelExpert
(
task
.
expertName
))
{
return
task
.
artifacts
}
return
task
.
artifacts
.
filter
((
artifact
)
=>
douyinVisibleArtifactExtensions
.
has
(
getTaskPanelArtifactExtension
(
artifact
)))
}
function
toDateInputValue
(
date
:
Date
)
{
function
toDateInputValue
(
date
:
Date
)
{
const
year
=
date
.
getFullYear
()
const
year
=
date
.
getFullYear
()
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)
...
@@ -57,6 +78,24 @@ export const mockTaskPanelItems: TaskPanelItem[] = [
...
@@ -57,6 +78,24 @@ export const mockTaskPanelItems: TaskPanelItem[] = [
{
id
:
"artifact-content-review"
,
name
:
"昨日内容复盘与下轮优化建议.md"
,
kind
:
"文档"
,
summary
:
"互动、转化和评论反馈的复盘结论。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/content/昨日内容复盘与下轮优化建议.md"
}
{
id
:
"artifact-content-review"
,
name
:
"昨日内容复盘与下轮优化建议.md"
,
kind
:
"文档"
,
summary
:
"互动、转化和评论反馈的复盘结论。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/content/昨日内容复盘与下轮优化建议.md"
}
]
]
},
},
{
id
:
"mock-task-douyin-delivery"
,
date
:
getDefaultTaskPanelDate
(),
expertName
:
"抖音专家"
,
taskTitle
:
"生成短视频脚本与成片"
,
status
:
"completed"
,
statusDetail
:
"已完成脚本、成片和过程素材归档"
,
creditsUsed
:
1480
,
messageCount
:
32
,
updatedAt
:
"11:48"
,
artifacts
:
[
{
id
:
"artifact-douyin-script"
,
name
:
"抖音口播脚本.docx"
,
kind
:
"文档"
,
summary
:
"数字人口播脚本、分镜节奏和字幕提示。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/douyin/抖音口播脚本.docx"
},
{
id
:
"artifact-douyin-video"
,
name
:
"抖音成片.mp4"
,
kind
:
"视频"
,
summary
:
"可发布的短视频成片。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/douyin/抖音成片.mp4"
},
{
id
:
"artifact-douyin-notes"
,
name
:
"执行过程记录.md"
,
kind
:
"文档"
,
summary
:
"运行日志和中间步骤记录。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/douyin/执行过程记录.md"
},
{
id
:
"artifact-douyin-assets"
,
name
:
"素材清单.xlsx"
,
kind
:
"表格"
,
summary
:
"镜头素材、来源和处理状态。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/douyin/素材清单.xlsx"
},
{
id
:
"artifact-douyin-cover"
,
name
:
"封面预览.png"
,
kind
:
"图片"
,
summary
:
"封面预览图。"
,
url
:
"/Users/edy/Documents/qianjiangclaw/tasks/douyin/封面预览.png"
}
]
},
{
{
id
:
"mock-task-zhihu"
,
id
:
"mock-task-zhihu"
,
date
:
getDefaultTaskPanelDate
(),
date
:
getDefaultTaskPanelDate
(),
...
@@ -133,7 +172,7 @@ export function summarizeTaskPanelItems(items: TaskPanelItem[]): TaskPanelSummar
...
@@ -133,7 +172,7 @@ export function summarizeTaskPanelItems(items: TaskPanelItem[]): TaskPanelSummar
const
summary
=
items
.
reduce
<
TaskPanelSummary
>
((
nextSummary
,
item
)
=>
{
const
summary
=
items
.
reduce
<
TaskPanelSummary
>
((
nextSummary
,
item
)
=>
{
nextSummary
.
creditsUsed
+=
item
.
creditsUsed
??
0
nextSummary
.
creditsUsed
+=
item
.
creditsUsed
??
0
nextSummary
.
messageCount
+=
item
.
messageCount
??
0
nextSummary
.
messageCount
+=
item
.
messageCount
??
0
nextSummary
.
artifactCount
+=
item
.
artifacts
.
length
nextSummary
.
artifactCount
+=
getVisibleTaskPanelArtifacts
(
item
)
.
length
return
nextSummary
return
nextSummary
},
{
},
{
creditsUsed
:
0
,
creditsUsed
:
0
,
...
...
apps/ui/test/taskPanelData.test.ts
View file @
dac2188a
...
@@ -71,3 +71,76 @@ test("summarizes task panel items from real artifacts and messages", async () =>
...
@@ -71,3 +71,76 @@ test("summarizes task panel items from real artifacts and messages", async () =>
employeeCount
:
2
employeeCount
:
2
})
})
})
})
test
(
"filters task panel artifacts for Douyin experts only"
,
async
()
=>
{
const
{
getVisibleTaskPanelArtifacts
}
=
await
import
(
"../src/features/tasks/taskPanelData.ts"
)
const
douyinArtifacts
=
[
{
id
:
"script"
,
name
:
"口播脚本.docx"
,
path
:
"/tmp/口播脚本.docx"
},
{
id
:
"video"
,
name
:
"成片.MP4"
,
path
:
"/tmp/成片.MP4"
},
{
id
:
"notes"
,
name
:
"执行记录.md"
,
path
:
"/tmp/执行记录.md"
},
{
id
:
"sheet"
,
name
:
"素材清单.xlsx"
,
path
:
"/tmp/素材清单.xlsx"
},
{
id
:
"cover"
,
name
:
"封面.png"
,
path
:
"/tmp/封面.png"
}
]
const
xhsArtifacts
=
[
{
id
:
"xhs-note"
,
name
:
"小红书笔记.md"
,
path
:
"/tmp/小红书笔记.md"
},
{
id
:
"xhs-cover"
,
name
:
"封面.png"
,
path
:
"/tmp/封面.png"
}
]
assert
.
deepEqual
(
getVisibleTaskPanelArtifacts
({
expertName
:
"抖音专家"
,
artifacts
:
douyinArtifacts
}),
[
douyinArtifacts
[
0
],
douyinArtifacts
[
1
]]
)
assert
.
deepEqual
(
getVisibleTaskPanelArtifacts
({
expertName
:
"openclaw-douyin-skills-delivery"
,
artifacts
:
douyinArtifacts
}),
[
douyinArtifacts
[
0
],
douyinArtifacts
[
1
]]
)
assert
.
deepEqual
(
getVisibleTaskPanelArtifacts
({
expertName
:
"小红书专家"
,
artifacts
:
xhsArtifacts
}),
xhsArtifacts
)
})
test
(
"summarizes artifact count from visible task panel artifacts"
,
async
()
=>
{
const
{
summarizeTaskPanelItems
}
=
await
import
(
"../src/features/tasks/taskPanelData.ts"
)
assert
.
deepEqual
(
summarizeTaskPanelItems
([
{
id
:
"douyin"
,
date
:
"2026-05-15"
,
expertName
:
"抖音专家"
,
taskTitle
:
"douyin"
,
status
:
"completed"
,
statusDetail
:
"已完成"
,
creditsUsed
:
8
,
messageCount
:
3
,
updatedAt
:
"10:00:00"
,
artifacts
:
[
{
id
:
"docx"
,
name
:
"脚本.docx"
,
path
:
"/tmp/脚本.docx"
},
{
id
:
"mp4"
,
name
:
"成片.mp4"
,
path
:
"/tmp/成片.mp4"
},
{
id
:
"md"
,
name
:
"过程.md"
,
path
:
"/tmp/过程.md"
},
{
id
:
"png"
,
name
:
"封面.png"
,
path
:
"/tmp/封面.png"
}
]
},
{
id
:
"xhs"
,
date
:
"2026-05-15"
,
expertName
:
"小红书专家"
,
taskTitle
:
"xhs"
,
status
:
"completed"
,
statusDetail
:
"已完成"
,
creditsUsed
:
5
,
messageCount
:
2
,
updatedAt
:
"11:00:00"
,
artifacts
:
[
{
id
:
"xhs-md"
,
name
:
"笔记.md"
,
path
:
"/tmp/笔记.md"
},
{
id
:
"xhs-png"
,
name
:
"封面.png"
,
path
:
"/tmp/封面.png"
}
]
}
]),
{
creditsUsed
:
13
,
messageCount
:
5
,
artifactCount
:
4
,
employeeCount
:
2
})
})
apps/ui/test/taskPanelViewSource.test.ts
View file @
dac2188a
...
@@ -33,6 +33,12 @@ test("task panel output copy feedback is scoped to the clicked output row", () =
...
@@ -33,6 +33,12 @@ test("task panel output copy feedback is scoped to the clicked output row", () =
assert
.
doesNotMatch
(
source
,
/copiedArtifactId === artifact
\.
id/
)
assert
.
doesNotMatch
(
source
,
/copiedArtifactId === artifact
\.
id/
)
})
})
test
(
"task panel output list uses visible artifact filtering"
,
()
=>
{
assert
.
match
(
source
,
/getVisibleTaskPanelArtifacts/
)
assert
.
match
(
source
,
/getVisibleTaskPanelArtifacts
\(
task
\)\.
map/
)
assert
.
doesNotMatch
(
source
,
/task
\.
artifacts
\.
map
\(\(
artifact
\)
=>
\(\{
artifact, task
\}\)\)
/
)
})
test
(
"task panel output summaries use a single-line visual ellipsis"
,
()
=>
{
test
(
"task panel output summaries use a single-line visual ellipsis"
,
()
=>
{
const
summaryBlock
=
cssBlock
(
taskStylesSource
,
".task-panel-output-main p"
)
const
summaryBlock
=
cssBlock
(
taskStylesSource
,
".task-panel-output-main p"
)
assert
.
match
(
summaryBlock
,
/overflow:
\s
*hidden;/
)
assert
.
match
(
summaryBlock
,
/overflow:
\s
*hidden;/
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment