210 lines
4.7 KiB
JavaScript
210 lines
4.7 KiB
JavaScript
/**
|
|
* {}
|
|
* @author yutent<yutent.io@gmail.com>
|
|
* @date 2023/08/08 18:19:17
|
|
*/
|
|
import { html, css, Component, classMap, nextTick, outsideClick } from 'wkit'
|
|
|
|
import { docker } from '../utils/index.js'
|
|
|
|
class Volumes extends Component {
|
|
static props = {}
|
|
|
|
static styles = [
|
|
css`
|
|
:host {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
color: var(--color-dark-1);
|
|
background: #fff;
|
|
}
|
|
.visible {
|
|
display: block;
|
|
}
|
|
|
|
ul li {
|
|
list-style: none;
|
|
}
|
|
|
|
.noselect {
|
|
-webkit-touch-callout: none;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
`,
|
|
|
|
css`
|
|
.main {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
padding: 16px;
|
|
}
|
|
|
|
.main .toolbar {
|
|
height: 48px;
|
|
border-bottom: 1px solid var(--color-plain-3);
|
|
}
|
|
|
|
.main .list {
|
|
overflow: auto;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 12px 0;
|
|
}
|
|
.item,
|
|
.thead {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
width: 100%;
|
|
height: 48px;
|
|
padding: 0 12px;
|
|
line-height: 1.25;
|
|
border-radius: 24px;
|
|
|
|
--wc-icon-size: 16px;
|
|
}
|
|
.field {
|
|
overflow: hidden;
|
|
flex: 1;
|
|
|
|
&.flex-2 {
|
|
flex: 2;
|
|
}
|
|
|
|
&.name {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
&.center {
|
|
text-align: center;
|
|
}
|
|
|
|
.text-ell {
|
|
overflow: hidden;
|
|
display: block;
|
|
width: 100%;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
color: var(--color-blue-1);
|
|
}
|
|
|
|
.action {
|
|
&.red {
|
|
color: var(--color-red-1);
|
|
}
|
|
&.green {
|
|
color: var(--color-teal-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
.thead {
|
|
color: var(--color-dark-2);
|
|
}
|
|
.item {
|
|
&:hover {
|
|
background: var(--color-plain-1);
|
|
}
|
|
}
|
|
`
|
|
]
|
|
|
|
async mounted() {
|
|
this.#fetch()
|
|
}
|
|
|
|
async #fetch() {
|
|
let list = await docker.volumes()
|
|
this.$store.volumes = list
|
|
}
|
|
|
|
async #remove(item) {
|
|
try {
|
|
await docker.volume.rm(item.name)
|
|
this.#fetch()
|
|
} catch (err) {
|
|
layer.toast(err, 'error')
|
|
}
|
|
}
|
|
|
|
async #clearUnUse(ev) {
|
|
try {
|
|
await layer.confirm(
|
|
'是否要清除本地未使用的磁盘? <br>(此操作仅是清除docker的挂载, 不会删除本地文件)'
|
|
)
|
|
await docker.volume.prune()
|
|
layer.toast('清除成功', 'success')
|
|
this.#fetch()
|
|
} catch (e) {}
|
|
}
|
|
|
|
render() {
|
|
let list = this.$store.volumes
|
|
|
|
return html`
|
|
<main class="main noselect">
|
|
<wc-space class="toolbar">
|
|
<wc-button
|
|
size="small"
|
|
type="warning"
|
|
icon="trash"
|
|
:disabled=${list.length < 1}
|
|
@click=${this.#clearUnUse}
|
|
>
|
|
清除未使用磁盘
|
|
</wc-button>
|
|
</wc-space>
|
|
|
|
<ul class="list">
|
|
<li class="thead">
|
|
<span class="field">name</span>
|
|
<span class="field center">Scope</span>
|
|
<span class="field center">挂载点</span>
|
|
<span class="field center">大小</span>
|
|
<span class="field center">状态</span>
|
|
<span class="field center">操作</span>
|
|
</li>
|
|
${list.map(
|
|
it => html`
|
|
<li class="item">
|
|
<section class="field name">
|
|
<wc-tooltip title=${it.name}>
|
|
<span class="text-ell">${it.name}</span>
|
|
</wc-tooltip>
|
|
</section>
|
|
<section class="field center">${it.scope}</section>
|
|
<section class="field center">
|
|
<wc-tooltip title=${it.mountpoint}>
|
|
<span class="text-ell">${it.mountpoint}</span>
|
|
</wc-tooltip>
|
|
</section>
|
|
<section class="field center">${it.size}</section>
|
|
<section class="field center">${it.status}</section>
|
|
<section class="field center">
|
|
<wc-popconfirm
|
|
title="是否要删除此镜像?"
|
|
@confirm=${ev => this.#remove(it)}
|
|
>
|
|
<span class="action">🗑</span>
|
|
</wc-popconfirm>
|
|
</section>
|
|
</li>
|
|
`
|
|
)}
|
|
</ul>
|
|
</main>
|
|
`
|
|
}
|
|
}
|
|
|
|
Volumes.reg('volumes')
|