Docker.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <el-container>
  3. <el-header height="220">
  4. <h3>Docker 容器列表</h3>
  5. <el-row style="margin-top: 10px">
  6. <span>
  7. <span v-if="wsConnectStatus" style="color: green; margin: 5px">WebSocket 已连接</span>
  8. <span v-if="!wsConnectStatus" style="color: red; margin: 5px">WebSocket 未连接</span>
  9. </span>
  10. <el-select
  11. v-model="env"
  12. size="mini"
  13. placeholder="环境"
  14. style="margin-left: 5px"
  15. @change="onSelectChange"
  16. >
  17. <el-option
  18. v-for="(item, index) in envList"
  19. :key="index"
  20. :label="item.label"
  21. :value="item.value"
  22. />
  23. </el-select>
  24. <el-select
  25. v-model="machine"
  26. size="mini"
  27. placeholder="机器"
  28. style="margin-left: 5px"
  29. @change="onSelectChange1"
  30. >
  31. <el-option
  32. v-for="(item, index) in machineList"
  33. :key="index"
  34. :label="item.label"
  35. :value="item.value"
  36. />
  37. </el-select>
  38. <el-button size="mini" type="warning" icon="el-icon-refresh" style="margin-left: 5px" @click="onRefresh">刷新</el-button>
  39. <el-button size="mini" type="warning" icon="el-icon-files" style="margin-left: 5px" @click="onGetImages">镜像列表</el-button>
  40. </el-row>
  41. </el-header>
  42. <el-main>
  43. <el-table
  44. :data="dataList"
  45. border
  46. height="480"
  47. style="width: 100%"
  48. >
  49. <el-table-column
  50. prop="name"
  51. label="容器名"
  52. />
  53. <el-table-column
  54. prop="status"
  55. label="状态"
  56. />
  57. <el-table-column
  58. prop="createdAt"
  59. label="创建时间"
  60. />
  61. <el-table-column
  62. prop="repoTag"
  63. label="依赖镜像"
  64. />
  65. <el-table-column
  66. fixed="right"
  67. label="操作"
  68. width="210"
  69. >
  70. <template slot-scope="scope">
  71. <el-button
  72. size="mini"
  73. type="danger"
  74. @click="handleDelete(scope.$index, scope.row)"
  75. >删除</el-button>
  76. </template>
  77. </el-table-column>
  78. </el-table>
  79. </el-main>
  80. <el-dialog
  81. title="镜像列表"
  82. append-to-body
  83. :visible.sync="showImageDialog"
  84. width="70%"
  85. center
  86. >
  87. <template>
  88. <el-select
  89. v-model="queryInfo.type"
  90. size="mini"
  91. style="margin-left: 5px"
  92. >
  93. <el-option label="关键字匹配" value="1" />
  94. <el-option label="前缀匹配" value="2" />
  95. </el-select>
  96. <el-input
  97. v-model="queryInfo.keyword"
  98. size="mini"
  99. style="width: 20%; padding: 3px"
  100. clearable
  101. placeholder="标题"
  102. />
  103. <el-button size="mini" type="success" icon="el-icon-search" style="margin-left: 5px" @click="onGetImages">查询</el-button>
  104. <el-table
  105. ref="multipleTable"
  106. :data="dataList1"
  107. border
  108. height="480"
  109. style="margin-left: 5px; width: 100%"
  110. @selection-change="handleSelectionChange"
  111. >
  112. <el-table-column
  113. type="selection"
  114. />
  115. <el-table-column
  116. prop="repoTag"
  117. label="镜像标签"
  118. />
  119. <el-table-column
  120. prop="createdAt"
  121. label="创建时间"
  122. />
  123. <el-table-column
  124. prop="totalContainers"
  125. label="使用的容器数量"
  126. />
  127. </el-table>
  128. <el-button size="mini" type="success" icon="el-icon-minus" style="margin: 5px" @click="toggleSelection()">取消已选择</el-button>
  129. <el-button size="mini" type="success" icon="el-icon-delete" style="margin: 5px" @click="deleteSelection()">删除已选择</el-button>
  130. </template>
  131. </el-dialog>
  132. </el-container>
  133. </template>
  134. <script>
  135. import { getEnvList, getMachineSessions } from '@/api/devops'
  136. export default {
  137. name: 'Docker',
  138. data() {
  139. return {
  140. env: 'test',
  141. envList: [],
  142. machine: '',
  143. machineList: [],
  144. queryInfo: {
  145. type: '1',
  146. keyword: ''
  147. },
  148. multipleSelection: [],
  149. dataList: [],
  150. dockerContainerList: [],
  151. showImageDialog: false,
  152. dataList1: [],
  153. wsClient: null,
  154. wsConnectStatus: false,
  155. wsReconnectLock: false
  156. }
  157. },
  158. created() {
  159. document.title = 'Docker 列表'
  160. getEnvList().then(resp => {
  161. if (resp.code === 0) {
  162. this.env = resp.data.userEnv
  163. this.envList = resp.data.envList
  164. this.getMachineList(this.env)
  165. } else {
  166. this.$message.error(resp.msg)
  167. }
  168. }).catch(error => {
  169. this.$message.error(error.message)
  170. })
  171. this.initWebSocket()
  172. },
  173. methods: {
  174. getMachineList(env) {
  175. getMachineSessions(env).then(resp => {
  176. if (resp.code === 0) {
  177. this.machineList = resp.data
  178. } else {
  179. this.$message.error(resp.msg)
  180. }
  181. }).catch(error => {
  182. this.$message.error(error.message)
  183. })
  184. },
  185. handleSelectionChange(val) {
  186. this.multipleSelection = val
  187. },
  188. toggleSelection(rows) {
  189. if (rows) {
  190. rows.forEach(row => {
  191. this.$refs.multipleTable.toggleRowSelection(row)
  192. })
  193. } else {
  194. this.$refs.multipleTable.clearSelection()
  195. }
  196. },
  197. deleteSelection() {
  198. if (this.multipleSelection.length === 0) {
  199. this.$message.warning('请先选择要删除的项')
  200. return
  201. }
  202. const imageIds = []
  203. for (const item of this.multipleSelection) {
  204. imageIds.push(item.imageId)
  205. }
  206. this.$confirm('确定要删除选择的镜像?', '提示', {
  207. confirmButtonText: '确定',
  208. cancelButtonText: '取消',
  209. type: 'warning'
  210. }).then(() => {
  211. const formData = new FormData()
  212. formData.append('imageIds', imageIds)
  213. const jsonPayload = {}
  214. jsonPayload.machineId = this.machine
  215. jsonPayload.ops = 'imageRm'
  216. jsonPayload.payload = imageIds
  217. this.sendMessage(JSON.stringify(jsonPayload))
  218. }).catch(() => {
  219. this.$message({
  220. type: 'info',
  221. message: '已取消'
  222. })
  223. })
  224. },
  225. handleDelete(index, row) {
  226. this.$confirm('确定要删除选择的容器?', '提示', {
  227. confirmButtonText: '确定',
  228. cancelButtonText: '取消',
  229. type: 'warning'
  230. }).then(() => {
  231. const formData = new FormData()
  232. formData.append('opsType', 3)
  233. formData.append('containerId', row.containerId)
  234. const containerIds = []
  235. containerIds.push(row.containerId)
  236. const jsonPayload = {}
  237. jsonPayload.machineId = this.machine
  238. jsonPayload.ops = 'containerRm'
  239. jsonPayload.payload = containerIds
  240. this.sendMessage(JSON.stringify(jsonPayload))
  241. }).catch(() => {
  242. this.$message({
  243. type: 'info',
  244. message: '已取消'
  245. })
  246. })
  247. },
  248. onRefresh() {
  249. const jsonPayload = {}
  250. jsonPayload.machineId = this.machine
  251. jsonPayload.ops = 'containerList'
  252. this.sendMessage(JSON.stringify(jsonPayload))
  253. },
  254. onGetImages() {
  255. const jsonPayload = {}
  256. jsonPayload.machineId = this.machine
  257. jsonPayload.ops = 'imageList'
  258. jsonPayload.dockerQuery = this.queryInfo
  259. this.sendMessage(JSON.stringify(jsonPayload))
  260. this.showImageDialog = true
  261. },
  262. onSelectChange() {
  263. this.machine = ''
  264. this.getMachineList(this.env)
  265. },
  266. onSelectChange1() {
  267. if (!this.wsConnectStatus) {
  268. this.$message.warning('websocket 未连接')
  269. return
  270. }
  271. const jsonPayload = {}
  272. jsonPayload.machineId = this.machine
  273. jsonPayload.ops = 'containerList'
  274. this.sendMessage(JSON.stringify(jsonPayload))
  275. },
  276. // ****************************************************************************************************************
  277. // WebSocket相关
  278. // ****************************************************************************************************************
  279. getUrl() {
  280. const protocol = location.protocol
  281. const hostname = location.hostname
  282. const port = location.port
  283. let prefix
  284. if (protocol === 'https:') {
  285. if (port === '' || port === 443) {
  286. prefix = 'wss://' + hostname
  287. } else {
  288. prefix = 'wss://' + hostname + ':' + port
  289. }
  290. } else {
  291. if (port === '' || port === 80) {
  292. prefix = 'ws://' + hostname
  293. } else {
  294. prefix = 'ws://' + hostname + ':' + port
  295. }
  296. }
  297. var token = '0123456789'
  298. var params = 'token=' + token
  299. return prefix + '/bgws/frontend?' + params
  300. },
  301. initWebSocket() {
  302. if ('WebSocket' in window) {
  303. const wsUrl = this.getUrl()
  304. console.log(wsUrl)
  305. this.wsClient = new WebSocket(wsUrl)
  306. const that = this
  307. this.wsClient.onopen = function() {
  308. that.setOnline()
  309. }
  310. this.wsClient.onclose = function() {
  311. that.setOffline()
  312. that.reconnect()
  313. }
  314. this.wsClient.onerror = function() {
  315. that.setOffline()
  316. console.log('websocket connection error...')
  317. that.reconnect()
  318. }
  319. this.wsClient.onmessage = function(evt) {
  320. const message = JSON.parse(evt.data)
  321. that.processMessage(message)
  322. }
  323. } else {
  324. // 浏览器不支持 WebSocket
  325. this.$message.error('您的浏览器不支持 WebSocket!')
  326. }
  327. },
  328. setOnline() {
  329. this.wsConnectStatus = true
  330. },
  331. setOffline() {
  332. this.wsConnectStatus = false
  333. },
  334. reconnect() {
  335. if (this.wsReconnectLock) return
  336. this.wsReconnectLock = true
  337. const that = this
  338. setTimeout(function() {
  339. console.log('websocket reconnecting...')
  340. that.initWebSocket()
  341. that.wsReconnectLock = false
  342. }, 5000)
  343. },
  344. sendMessage(message) {
  345. this.wsClient.send(message)
  346. },
  347. processMessage(message) {
  348. const ops = message.ops
  349. if (ops === 'containerList') {
  350. this.dataList = []
  351. this.dataList = message.resultList
  352. } else if (ops === 'imageList') {
  353. this.dataList1 = []
  354. this.dataList1 = message.resultList
  355. } else {
  356. console.log(message)
  357. }
  358. }
  359. }
  360. }
  361. </script>
  362. <style>
  363. </style>