@@ -8,24 +8,32 @@ import (
88
99 "github.com/docker/docker/api/types/image"
1010 "github.com/yaogh99123/dcli/pkg/commands"
11+ "github.com/yaogh99123/dcli/pkg/i18n"
1112 "github.com/yaogh99123/dcli/pkg/utils"
1213)
1314
1415// RunImageMenu handles image management CLI interactions
15- func RunImageMenu (dockerCmd * commands.DockerCommand , readInput func (string ) string , runInteractiveSubprocess func (* exec.Cmd ) error ) {
16+ func RunImageMenu (
17+ dockerCmd * commands.DockerCommand ,
18+ readInput func (string ) string ,
19+ runInteractiveSubprocess func (* exec.Cmd ) error ,
20+ runFzfSelect func (string , []string ) string ,
21+ parseFzfResult func (string ) string ,
22+ ) {
23+ tr := dockerCmd .Tr
1624 for {
1725 images , err := dockerCmd .RefreshImages ()
1826 if err != nil {
19- fmt .Printf ("%s错误 : %v%s\n " , utils .ColorRed , err , utils .ColorNC )
27+ fmt .Printf ("%s%s : %v%s\n " , utils .ColorRed , tr . ErrorTitle , err , utils .ColorNC )
2028 return
2129 }
2230
23- fmt .Printf ("\033 [H\033 [2J" ) // 清屏
31+ fmt .Printf ("\033 [H\033 [2J" ) // Clear screen
2432 fmt .Printf ("%s========================================%s\n " , utils .ColorBlue , utils .ColorNC )
25- fmt .Printf ("%s Docker 镜像管理%s \n " , utils .ColorBlue , utils .ColorNC )
33+ fmt .Printf ("%s %s%s \n " , utils .ColorBlue , tr . MenuImageManagement , utils .ColorNC )
2634 fmt .Printf ("%s========================================%s\n \n " , utils .ColorBlue , utils .ColorNC )
2735
28- // 打印表头
36+ // Print header
2937 fmt .Printf ("%-50s %-12s %-12s %-12s %-10s\n " , "IMAGE" , "ID" , "DISK USAGE" , "CONTENT SIZE" , "EXTRA" )
3038 for i , img := range images {
3139 name := img .Name
@@ -42,63 +50,122 @@ func RunImageMenu(dockerCmd *commands.DockerCommand, readInput func(string) stri
4250 i + 1 , name , img .ID [:12 ], img .GetDisplaySize (), img .GetDisplayContentSize (), extra )
4351 }
4452
45- fmt .Printf ("\n %s镜像操作%s \n " , utils .ColorGreen , utils .ColorNC )
53+ fmt .Printf ("\n %s%s%s \n " , utils .ColorGreen , tr . MenuFunction , utils .ColorNC )
4654 fmt .Println ("------------------------------" )
47- fmt .Println (" 1. 拉取镜像" )
48- fmt .Println (" 2. 删除指定镜像" )
49- fmt .Println (" 3. 删除所有镜像" )
55+ fmt .Printf (" 1. %s \n " , tr . PullImage )
56+ fmt .Printf (" 2. %s \n " , tr . DeleteSpecifiedImage )
57+ fmt .Printf (" 3. %s \n " , tr . DeleteAllImages )
5058 fmt .Println ("------------------------------" )
51- fmt .Println (" 0. 返回" )
59+ fmt .Printf (" s. %s \n " , tr . SearchImageTitle )
5260 fmt .Println ("------------------------------" )
53- fmt .Print ("请选择: " )
61+ fmt .Printf (" 0. %s\n " , tr .Return )
62+ fmt .Println ("------------------------------" )
63+ fmt .Print (tr .InputServiceNameIdx ) // Using common prompt
5464
5565 input := readInput ("" )
5666 input = strings .TrimSpace (input )
5767
58- if input == "0" || input == "" {
68+ if input == "0" || input == "" || input == "q" {
5969 break
6070 }
6171
72+ if input == "s" || input == "search" {
73+ var lines []string
74+ for i , img := range images {
75+ lines = append (lines , fmt .Sprintf ("%d. %s (%s)" , i + 1 , img .Name , img .ID [:12 ]))
76+ }
77+ result := runFzfSelect (tr .SearchImageTitle , lines )
78+ selected := parseFzfResult (result )
79+ if selected != "" {
80+ // Handle selected image
81+ var idx int
82+ _ , _ = fmt .Sscanf (selected , "%d" , & idx )
83+ if idx > 0 && idx <= len (images ) {
84+ handleImageAction (images [idx - 1 ], tr , readInput , runInteractiveSubprocess , runFzfSelect , parseFzfResult )
85+ }
86+ }
87+ continue
88+ }
89+
6290 switch input {
6391 case "1" :
64- fmt .Print ( "请输入要拉取的镜像名称 (如 nginx:latest): " )
92+ fmt .Printf ( "%s: " , tr . PullImage )
6593 name := readInput ("" )
6694 name = strings .TrimSpace (name )
6795 if name != "" {
68- fmt .Printf ("%s正在拉取镜像 %s...%s\n " , utils .ColorYellow , name , utils .ColorNC )
96+ fmt .Printf ("%s%s %s...%s\n " , utils .ColorYellow , tr . PullImage , name , utils .ColorNC )
6997 cmd := exec .Command ("docker" , "pull" , name )
7098 _ = runInteractiveSubprocess (cmd )
7199 }
72100 case "2" :
73- fmt .Print ("请输入要删除的镜像索引: " )
101+ fmt .Print (tr . InputServiceNameIdx )
74102 idxStr := readInput ("" )
75103 var idx int
76104 _ , _ = fmt .Sscanf (strings .TrimSpace (idxStr ), "%d" , & idx )
77105 if idx > 0 && idx <= len (images ) {
78- img := images [idx - 1 ]
79- fmt .Printf ("%s确定要删除镜像 %s (ID: %s) 吗? (y/n): %s" , utils .ColorYellow , img .Name , img .ID [:12 ], utils .ColorNC )
80- confirm := readInput ("" )
81- if strings .ToLower (strings .TrimSpace (confirm )) == "y" {
82- fmt .Printf ("%s正在删除镜像...%s\n " , utils .ColorYellow , utils .ColorNC )
83- if err := img .Remove (image.RemoveOptions {Force : true }); err != nil {
84- fmt .Printf ("%s失败: %v%s\n " , utils .ColorRed , err , utils .ColorNC )
85- } else {
86- fmt .Printf ("%s成功%s\n " , utils .ColorGreen , utils .ColorNC )
87- }
88- time .Sleep (1 * time .Second )
89- }
106+ handleImageAction (images [idx - 1 ], tr , readInput , runInteractiveSubprocess , runFzfSelect , parseFzfResult )
90107 } else {
91- fmt .Printf ("%s无效的索引%s \n " , utils .ColorRed , utils .ColorNC )
108+ fmt .Printf ("%s%s%s \n " , utils .ColorRed , tr . InvalidIndex , utils .ColorNC )
92109 time .Sleep (1 * time .Second )
93110 }
94111 case "3" :
95- fmt .Printf ("%s危险: 这将删除所有未被使用的镜像! 是否继续? (y/n): %s " , utils .ColorRed , utils .ColorNC )
112+ fmt .Printf ("%s%s%s " , utils .ColorRed , tr . DangerDeleteAllImages , utils .ColorNC )
96113 confirm := readInput ("" )
97114 if strings .ToLower (strings .TrimSpace (confirm )) == "y" {
98- fmt .Printf ("%s正在清理所有未使用镜像...%s \n " , utils .ColorYellow , utils .ColorNC )
115+ fmt .Printf ("%s%s%s \n " , utils .ColorYellow , tr . CleaningAllUnusedImages , utils .ColorNC )
99116 cmd := exec .Command ("docker" , "image" , "prune" , "-af" )
100117 _ = runInteractiveSubprocess (cmd )
101118 }
102119 }
103120 }
104121}
122+
123+ func handleImageAction (
124+ img * commands.Image ,
125+ tr * i18n.TranslationSet ,
126+ readInput func (string ) string ,
127+ runInteractiveSubprocess func (* exec.Cmd ) error ,
128+ runFzfSelect func (string , []string ) string ,
129+ parseFzfResult func (string ) string ,
130+ ) {
131+ lines := []string {
132+ fmt .Sprintf ("1: %s" , tr .RemoveImage ),
133+ fmt .Sprintf ("2: %s" , tr .RunImage ),
134+ }
135+
136+ result := runFzfSelect (fmt .Sprintf (tr .SelectActionForImage , img .Name ), lines )
137+ actionID := parseFzfResult (result )
138+
139+ switch actionID {
140+ case "1" : // Remove
141+ fmt .Printf ("%s%s %s (ID: %s) ? (y/n): %s" , utils .ColorYellow , tr .ConfirmDeleteImage , img .Name , img .ID [:12 ], utils .ColorNC )
142+ confirm := readInput ("" )
143+ if strings .ToLower (strings .TrimSpace (confirm )) == "y" {
144+ fmt .Printf ("%s%s%s\n " , utils .ColorYellow , tr .DeletingImage , utils .ColorNC )
145+ if err := img .Remove (image.RemoveOptions {Force : true }); err != nil {
146+ fmt .Printf ("%s%s: %v%s\n " , utils .ColorRed , tr .ActionFailed , err , utils .ColorNC )
147+ } else {
148+ fmt .Printf ("%s%s%s\n " , utils .ColorGreen , tr .ActionSuccess , utils .ColorNC )
149+ }
150+ time .Sleep (1 * time .Second )
151+ }
152+ case "2" : // Run (Interactive)
153+ fmt .Print (tr .InputContainerName )
154+ name := readInput ("" )
155+ name = strings .TrimSpace (name )
156+
157+ fmt .Printf ("%s%s%s\n " , utils .ColorBlue , tr .DetectingShell , utils .ColorNC )
158+ // 参考进入容器的做法,通过一个临时容器检测 shell
159+ checkCmd := exec .Command ("docker" , "run" , "--rm" , img .ID , "which" , "bash" )
160+ shell := "sh"
161+ if err := checkCmd .Run (); err == nil {
162+ shell = "bash"
163+ }
164+
165+ cmd := img .GetInteractiveRunCmd (name , shell )
166+ if err := runInteractiveSubprocess (cmd ); err != nil {
167+ fmt .Printf ("%s%s%s\n " , utils .ColorRed , fmt .Sprintf (tr .RunImageFailed , err ), utils .ColorNC )
168+ time .Sleep (1 * time .Second )
169+ }
170+ }
171+ }
0 commit comments