Skip to content

Commit

Permalink
online: support card view for instance
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jul 17, 2023
1 parent 4b4c245 commit 9908015
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 51 deletions.
89 changes: 89 additions & 0 deletions packages/online/app/components/instance.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<template>
<k-card class="instance">
<template #header>
<input v-model="model"/>
</template>
<template #footer>
<template v-if="data.current === id">
<el-button disabled>
运行中
</el-button>
</template>
<template v-else>
<el-button @click="activate(id, $event)">
<k-icon name="start"></k-icon>
切换
</el-button>
<el-button @click="remove(id)">
<k-icon name="delete"></k-icon>
删除
</el-button>
</template>
<el-button>
<k-icon name="share"></k-icon>
分享
</el-button>
</template>
</k-card>
</template>

<script lang="ts" setup>
import { activate, data, instances, remove, Instance, flush } from '../utils'
import { computed } from 'vue'
const props = defineProps<{ id: string } & Instance>()
const model = computed({
get: () => props.name,
set: (value) => {
instances.value[props.id].name = value
flush()
},
})
</script>

<style lang="scss" scoped>
.k-card.instance {
input {
outline: none;
background-color: transparent;
font-size: 1.25rem;
font-weight: 500;
border: none;
color: inherit;
}
:deep(.k-card-body) {
flex: 1 1 auto;
}
:deep(footer) {
margin: 0;
padding: 0;
height: 3rem;
display: flex;
.el-button {
flex: 1 1 auto;
margin: 0;
height: 100%;
border-radius: 0;
&:first-child {
border-bottom-left-radius: 8px;
}
&:last-child {
border-bottom-right-radius: 8px;
}
}
.k-icon {
margin-right: 0.5rem;
}
}
}
</style>
108 changes: 61 additions & 47 deletions packages/online/app/components/instances.vue
Original file line number Diff line number Diff line change
@@ -1,71 +1,85 @@
<template>
<k-layout>
<k-content>
<k-card class="instances">
<template #header>
<span>实例管理</span>
<k-button class="right" @click="activate()">添加</k-button>
<k-layout class="page-instances">
<el-scrollbar>
<div class="container first">
<instance v-bind="instances[data.current]" :id="data.current"/>
<el-button class="k-card create" @click="activate(null, $event)">
<k-icon name="plus"></k-icon>
</el-button>
</div>
<div class="container second" v-if="inactive.length">
<template v-for="([key, value]) in inactive" :key="key">
<instance v-if="data.current !== key" v-bind="value" :id="key"/>
</template>
<table>
<tr class="instance" v-for="({ name }, key) in instances" :key="key">
<td class="name">
{{ name }}
<template v-if="data.current === key">(运行中)</template>
</td>
<td class="actions">
<span>
<k-icon v-if="data.current !== key" name="start" @click="activate(key)"></k-icon>
<k-icon v-if="data.current !== key" name="delete" @click="remove(key)"></k-icon>
</span>
</td>
</tr>
</table>
</k-card>
</k-content>
</div>
</el-scrollbar>
</k-layout>
</template>

<script lang="ts" setup>
import { activate, data, instances, remove } from '../utils'
import Instance from './instance.vue'
import { computed } from 'vue'
import { activate, data, instances } from '../utils'
const inactive = computed(() => {
return Object.entries(instances.value)
.filter(([id]) => id !== data.value.current)
.sort(([, a], [, b]) => b.lastVisit - a.lastVisit)
})
</script>

<style lang="scss">
.k-card.instances {
margin: var(--card-margin);
.page-instances .el-scrollbar__view {
padding: 3rem 0;
max-width: 64rem;
min-height: 100%;
margin: auto;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
.right {
float: right;
.container {
display: flex;
flex-wrap: wrap;
gap: 2rem;
align-items: center;
}
.instance {
transition: var(--color-transition);
&:hover {
background-color: var(--k-hover-bg);
}
.container.first {
justify-content: center;
}
.name {
text-align: left;
}
.container.second {
margin-top: 2rem;
border-top: 1px solid var(--k-color-border);
padding-top: 2rem;
}
.actions {
text-align: right;
.k-card.create {
border-radius: 8px;
span {
display: flex;
gap: 1rem;
align-items: center;
justify-content: flex-end;
}
.k-icon {
font-size: 4rem;
color: var(--k-color-border);
transition: 0.3s ease;
}
.k-icon {
cursor: pointer;
}
&:hover .k-icon {
color: var(--k-color-primary);
}
}
.k-card {
width: 20rem;
height: 200px;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
}
</style>
5 changes: 5 additions & 0 deletions packages/online/app/icons/plus.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<svg class="k-icon" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/>
</svg>
</template>
5 changes: 5 additions & 0 deletions packages/online/app/icons/share.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<svg class="k-icon" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M568.5 142.6l-144-135.1c-9.625-9.156-24.81-8.656-33.91 .9687c-9.125 9.625-8.688 24.81 .9687 33.91l100.1 94.56h-163.4C287.5 134.2 249.7 151 221 179.4C192 208.2 176 246.7 176 288v87.1c0 13.25 10.75 23.1 24 23.1S224 389.3 224 376V288c0-28.37 10.94-54.84 30.78-74.5C274.3 194.2 298.9 183 328 184h163.6l-100.1 94.56c-9.656 9.094-10.09 24.28-.9687 33.91c4.719 4.1 11.06 7.531 17.44 7.531c5.906 0 11.84-2.156 16.47-6.562l144-135.1C573.3 172.9 576 166.6 576 160S573.3 147.1 568.5 142.6zM360 384c-13.25 0-24 10.75-24 23.1v47.1c0 4.406-3.594 7.1-8 7.1h-272c-4.406 0-8-3.594-8-7.1V184c0-4.406 3.594-7.1 8-7.1H112c13.25 0 24-10.75 24-23.1s-10.75-23.1-24-23.1H56c-30.88 0-56 25.12-56 55.1v271.1C0 486.9 25.13 512 56 512h272c30.88 0 56-25.12 56-55.1v-47.1C384 394.8 373.3 384 360 384z"/>
</svg>
</template>
4 changes: 4 additions & 0 deletions packages/online/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { connect, global, icons, root } from '@koishijs/client'
import IconDocs from './icons/docs.vue'
import IconForum from './icons/forum.vue'
import IconInstances from './icons/instances.vue'
import IconPlus from './icons/plus.vue'
import IconShare from './icons/share.vue'
import Home from './components/home.vue'
import Instances from './components/instances.vue'
import ClientWebSocket from './socket'
Expand All @@ -10,6 +12,8 @@ import '@koishijs/client/app'
icons.register('activity:docs', IconDocs)
icons.register('activity:forum', IconForum)
icons.register('activity:instances', IconInstances)
icons.register('plus', IconPlus)
icons.register('share', IconShare)

root.page({
id: 'home',
Expand Down
15 changes: 11 additions & 4 deletions packages/online/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,26 @@ provideStorage((key, version, fallback) => {
return result
})

interface Instance {
export interface Instance {
name: string
lastVisit: number
}

export const root = '/koishi/play/v1/instances'
export const instances = ref<Dict<Instance>>({})

export async function flush() {
await fs.writeFile(`${root}/index.json`, JSON.stringify(instances.value))
}

export async function remove(key: string) {
await fs.rm(`${root}/${key}`, { recursive: true })
delete instances.value[key]
await fs.writeFile(`${root}/index.json`, JSON.stringify(instances.value))
await flush()
}

export async function activate(id?: string) {
export async function activate(id?: string, event?: Event) {
(event?.target as HTMLElement)?.blur()
await loader.app?.stop()
id ||= Math.random().toString(36).slice(2, 10)
data.value.current = id
Expand All @@ -75,6 +80,8 @@ export async function activate(id?: string) {
try {
await loader.init(`${root}/${id}`)
await loader.readConfig()
instances.value[id].lastVisit = Date.now()
await flush()
} catch {
loader.config = {
plugins: {
Expand All @@ -96,7 +103,7 @@ export async function activate(id?: string) {
}
await fs.writeFile(filename, dump(loader.config))
instances.value[id] = { name: id, lastVisit: Date.now() }
await fs.writeFile(`${root}/index.json`, JSON.stringify(instances.value))
await flush()
await loader.init(`${root}/${id}`)
}
const files = await fs.readdir(`${root}/${id}/data/storage`)
Expand Down

0 comments on commit 9908015

Please sign in to comment.