Skip to content

Commit 2d84f02

Browse files
committed
feat:forge支持批量导出
1 parent ede3daf commit 2d84f02

6 files changed

Lines changed: 134 additions & 32 deletions

File tree

app/renderer/src/main/src/pages/ai-agent/aiChatWelcome/AIChatWelcome.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {YakitButton} from "@/components/yakitUI/YakitButton/YakitButton"
1616
import {
1717
OutlineArrowrightIcon,
1818
OutlineCloseIcon,
19+
OutlineExportIcon,
1920
OutlineImportIcon,
2021
OutlineInformationcircleIcon,
2122
OutlineOpenIcon,
@@ -246,6 +247,7 @@ const AIChatWelcome: React.FC<AIChatWelcomeProps> = React.memo(
246247
return randomAIMaterialsDataIsEmpty(randomAIMaterialsData)
247248
}, [randomAIMaterials])
248249

250+
const [isSelectForgeName, setIsSelectForgeName] = useState<boolean>(false)
249251
const knowledgeSidebarListRef = useRef<KnowledgeModalRef>(null)
250252
const forgeNameRef = useRef<ForgeNameRef>(null)
251253
const {installPlug, refresh: refreshPluginStatus, ThirdPartyBinaryRunAsync} = useCheckKnowledgePlugin()
@@ -289,8 +291,17 @@ const AIChatWelcome: React.FC<AIChatWelcomeProps> = React.memo(
289291
{
290292
label: t("AIChatWelcome.skillBase"),
291293
key: "skills",
292-
children: <ForgeName ref={forgeNameRef} />,
294+
children: <ForgeName ref={forgeNameRef} onSelectChange={setIsSelectForgeName} />,
293295
extra: [
296+
<YakitButton
297+
key='batch-import'
298+
onClick={() => {
299+
forgeNameRef.current?.onBatchExport()
300+
}}
301+
type='text2'
302+
icon={<OutlineExportIcon />}
303+
disabled={!isSelectForgeName}
304+
/>,
294305
<YakitButton
295306
key='import'
296307
onClick={() => {
@@ -325,7 +336,7 @@ const AIChatWelcome: React.FC<AIChatWelcomeProps> = React.memo(
325336
]
326337
}
327338
]
328-
}, [api, streams, installPlug, i18n.language])
339+
}, [api, streams, installPlug, i18n.language, isSelectForgeName])
329340

330341
return (
331342
<div className={styles["ai-chat-welcome-wrapper"]} ref={welcomeRef}>

app/renderer/src/main/src/pages/ai-agent/defaultConstant.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {DefaultMemoryList, DefaultPlanHistoryList} from "../ai-re-act/hooks/defa
4545
import {ColorsAIIcon} from "@/assets/icon/colors"
4646
import {AIGlobalConfig, AIModelTypeFileName} from "./aiModelList/utils"
4747
import {cloneDeep} from "lodash"
48+
import {ExportAIForgeRequest} from "./forgeName/type"
4849

4950
/** AI-Agent 页面的唯一 id */
5051
export const YakitAIAgentPageID = "yakit-ai-agent"
@@ -375,3 +376,12 @@ export const defaultAIGlobalConfig: AIGlobalConfig = {
375376
LightweightModels: [],
376377
VisionModels: []
377378
}
379+
380+
export const defaultExportAIForgeRequest: ExportAIForgeRequest = {
381+
ForgeNames: [],
382+
TargetPath: "",
383+
OutputName: "",
384+
ToolNames: [],
385+
Password: "",
386+
Filter: {}
387+
}

app/renderer/src/main/src/pages/ai-agent/forgeName/ForgeName.module.scss

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
padding: 8px 12px 0 12px;
1111
display: flex;
1212
flex-direction: column;
13-
gap: 12px;
13+
gap: 8px;
1414
.haeder-first {
1515
display: flex;
1616
justify-content: space-between;
@@ -31,13 +31,24 @@
3131
}
3232

3333
.header-second {
34+
display: flex;
35+
flex-direction: column;
36+
gap: 8px;
3437
.search-icon {
3538
svg {
3639
width: 16px;
3740
height: 16px;
3841
color: var(--Colors-Use-Neutral-Text-3-Secondary);
3942
}
4043
}
44+
.select-all {
45+
display: flex;
46+
align-items: center;
47+
gap: 4px;
48+
color: var(--Colors-Use-Neutral-Text-1-Title);
49+
font-size: 12px;
50+
font-weight: 400;
51+
}
4152
}
4253
}
4354
}
@@ -84,6 +95,10 @@
8495
font-size: 12px;
8596
font-weight: 400;
8697
line-height: 16px;
98+
99+
display: flex;
100+
align-items: center;
101+
gap: 4px;
87102
}
88103
.item-extra {
89104
display: none;
@@ -109,7 +124,7 @@
109124
.forge-opt-popover {
110125
padding-left: 4px;
111126
:global {
112-
.ant-popover-inner{
127+
.ant-popover-inner {
113128
border: none;
114129
}
115130
.ant-popover-inner-content {

app/renderer/src/main/src/pages/ai-agent/forgeName/ForgeName.tsx

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
useDebounceFn,
2323
useInViewport,
2424
useMemoizedFn,
25+
useSelections,
2526
useThrottleFn,
2627
useUpdateEffect,
2728
useVirtualList
@@ -37,7 +38,7 @@ import {YakitButton} from "@/components/yakitUI/YakitButton/YakitButton"
3738
import {YakitRoute} from "@/enums/yakitRoute"
3839
import {Form, Tooltip} from "antd"
3940
import {yakitNotify} from "@/utils/notification"
40-
import {AIForgeListDefaultPagination, ReActChatEventEnum} from "../defaultConstant"
41+
import {AIForgeListDefaultPagination, defaultExportAIForgeRequest, ReActChatEventEnum} from "../defaultConstant"
4142
import {YakitPopconfirm} from "@/components/yakitUI/YakitPopconfirm/YakitPopconfirm"
4243
import {AIForge, QueryAIForgeRequest, QueryAIForgeResponse} from "../type/forge"
4344
import ImportExportModal, {ImportExportModalExtra} from "@/components/ImportExportModal/ImportExportModal"
@@ -51,14 +52,18 @@ import {LogListInfo} from "@/components/YakitUploadModal/YakitUploadModal"
5152

5253
import classNames from "classnames"
5354
import styles from "./ForgeName.module.scss"
55+
import {YakitProtoCheckbox} from "@/components/TableVirtualResize/YakitProtoCheckbox/YakitProtoCheckbox"
56+
import {YakitCheckbox} from "@/components/yakitUI/YakitCheckbox/YakitCheckbox"
57+
import {cloneDeep} from "lodash"
5458
const {ipcRenderer} = window.require("electron")
5559

5660
export interface ForgeNameRef {
5761
openAdd: () => void
5862
openImport: () => void
63+
onBatchExport: () => void
5964
}
6065

61-
const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
66+
const ForwardForgeName = forwardRef((props: ForgeNameProps, ref: Ref<ForgeNameRef>) => {
6267
// const {} = props
6368

6469
// #region AIForge 模板增删改功能 使用功能
@@ -288,16 +293,15 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
288293
apiKey: "ExportAIForge"
289294
})
290295
const logListRef = useRef<LogListInfo[]>([])
291-
const forgeNamesRef = useRef<string[]>([])
292-
const toolNames = useRef<string[]>([])
293-
const outputNameRef = useRef<string>("")
296+
const forgeExtraParams = useRef<ExportAIForgeRequest>(cloneDeep(defaultExportAIForgeRequest))
294297
const handleOpenImportExportHint = useMemoizedFn((extra: Omit<ImportExportModalExtra, "hint">) => {
295298
if (importExportExtra.hint) return
296299
setImportExportExtra({...extra, hint: true})
297300
})
298301

299302
const exportPath = useRef<string>("")
300303
const handleFinishedImportExportHint = useMemoizedFn((result: boolean) => {
304+
onResetExportExtraParams()
301305
if (result) {
302306
const type = importExportExtra.type
303307
if (type === "import") {
@@ -397,16 +401,56 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
397401
}
398402
}
399403
// #endregion
404+
// #region AIForge 批量导出
405+
const {selected, allSelected, isSelected, toggle, toggleAll, partiallySelected} = useSelections(data.Data)
406+
useEffect(() => {
407+
props.onSelectChange(selected.length > 0)
408+
}, [selected.length])
409+
const onBatchExport = useMemoizedFn(() => {
410+
const query: ExportAIForgeRequest = {
411+
ForgeNames: [],
412+
OutputName: "",
413+
Filter: {
414+
Keyword: ""
415+
}
416+
}
417+
if (allSelected) {
418+
query.Filter = {
419+
Keyword: search
420+
}
421+
} else {
422+
query.ForgeNames = selected.map((item) => item.ForgeName) as any
423+
}
424+
forgeExtraParams.current = {
425+
...forgeExtraParams.current,
426+
...query
427+
}
428+
handleOpenImportExportHint({
429+
title: "导出技能",
430+
type: "export",
431+
apiKey: "ExportAIForge"
432+
})
433+
})
400434

401-
useImperativeHandle(ref, () => ({
402-
openAdd: handleNewAIForge,
403-
openImport: () =>
404-
handleOpenImportExportHint({
405-
title: "导入技能",
406-
type: "import",
407-
apiKey: "ImportAIForge"
408-
})
409-
}))
435+
const onResetExportExtraParams = useMemoizedFn(() => {
436+
forgeExtraParams.current = cloneDeep(defaultExportAIForgeRequest)
437+
})
438+
//#endregion
439+
440+
useImperativeHandle(
441+
ref,
442+
() => ({
443+
openAdd: handleNewAIForge,
444+
openImport: () =>
445+
handleOpenImportExportHint({
446+
title: "导入技能",
447+
type: "import",
448+
apiKey: "ImportAIForge"
449+
}),
450+
onBatchExport: () => onBatchExport()
451+
}),
452+
[]
453+
)
410454

411455
return (
412456
<div ref={wrapper} className={styles["forge-name"]}>
@@ -439,6 +483,14 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
439483
value={search}
440484
onChange={(e) => setSearch(e.target.value)}
441485
/>
486+
<div className={styles["select-all"]}>
487+
<YakitCheckbox
488+
checked={allSelected}
489+
onChange={() => toggleAll()}
490+
indeterminate={partiallySelected}
491+
/>
492+
<span>全选</span>
493+
</div>
442494
</div>
443495
</div>
444496

@@ -500,13 +552,17 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
500552
>
501553
<div className={styles["forge-list-opt"]} onClick={() => handleOnClick(data)}>
502554
<div
503-
className={classNames(
504-
styles["opt-title"],
505-
"yakit-content-single-ellipsis"
506-
)}
555+
className={classNames(styles["opt-title"])}
507556
title={ForgeVerboseName || ForgeName}
508557
>
509-
{ForgeVerboseName || ForgeName}
558+
<YakitProtoCheckbox
559+
checked={isSelected(data)}
560+
onChange={() => toggle(data)}
561+
onClick={(e) => e.stopPropagation()}
562+
/>
563+
<div className='yakit-content-single-ellipsis'>
564+
{ForgeVerboseName || ForgeName}
565+
</div>
510566
</div>
511567

512568
<div className={styles["item-extra"]}>
@@ -520,9 +576,12 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
520576
icon={<OutlineExportIcon />}
521577
onClick={(e) => {
522578
e.stopPropagation()
523-
forgeNamesRef.current = [ForgeName]
524-
toolNames.current = tools
525-
outputNameRef.current = ForgeVerboseName || ForgeName || ""
579+
forgeExtraParams.current = {
580+
...forgeExtraParams.current,
581+
ForgeNames: [ForgeName],
582+
ToolNames: tools,
583+
OutputName: ForgeVerboseName || ForgeName || ""
584+
}
526585
handleOpenImportExportHint({
527586
title: "导出技能",
528587
type: "export",
@@ -594,7 +653,10 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
594653
<ImportExportModal<ExportAIForgeFormValues, ExportAIForgeRequest, ExportImportAIForgeProgress>
595654
{...commonImportExportProps}
596655
formProps={{
597-
initialValues: {OutputName: outputNameRef.current || "", ToolNames: toolNames.current}
656+
initialValues: {
657+
OutputName: forgeExtraParams.current?.OutputName || "",
658+
ToolNames: forgeExtraParams.current?.ToolNames || []
659+
}
598660
}}
599661
renderForm={() => (
600662
<>
@@ -650,7 +712,7 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
650712
} catch (error) {}
651713
}}
652714
onSubmitForm={(values) => ({
653-
ForgeNames: forgeNamesRef.current,
715+
...forgeExtraParams.current,
654716
...values
655717
})}
656718
isProgressFinished={(p: ExportImportAIForgeProgress) =>
@@ -687,8 +749,6 @@ const ForgeName = (props, ref: Ref<ForgeNameRef>) => {
687749
)}
688750
</div>
689751
)
690-
}
691-
692-
const ForwardForgeName = forwardRef<ForgeNameRef, {}>(ForgeName)
752+
})
693753

694754
export default memo(ForwardForgeName)

app/renderer/src/main/src/pages/ai-agent/forgeName/type.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export interface ForgeNameProps {}
1+
import {QueryAIForgeRequest} from "../type/forge"
2+
3+
export interface ForgeNameProps {
4+
onSelectChange: (b:boolean) => void
5+
}
26
export interface ExportAIForgeFormValues {
37
OutputName: string
48
ToolNames?: string[]
@@ -14,6 +18,7 @@ export interface ExportAIForgeRequest {
1418
OutputName: string
1519
ToolNames?: string[]
1620
Password?: string
21+
Filter: QueryAIForgeRequest["Filter"]
1722
}
1823
export interface ImportAIForgeRequest {
1924
InputPath: string

app/renderer/src/main/src/yakitLib.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ html {
1111
white-space: nowrap;
1212
word-wrap: normal;
1313
text-overflow: ellipsis;
14+
overflow: hidden;
1415
}
1516
// 多行内容省略
1617
// 行数可通过设置 变量 --yakit-ellipsis-row 控制

0 commit comments

Comments
 (0)