Improve task manager etc

This commit is contained in:
syuilo 2020-11-01 22:09:16 +09:00
parent 21b6e23e98
commit 7060625adf
7 changed files with 108 additions and 23 deletions

View File

@ -169,15 +169,15 @@ export default defineComponent({
font-size: 32px; font-size: 32px;
&.success { &.success {
color: var(--accent); color: var(--success);
} }
&.error { &.error {
color: #ec4137; color: var(--error);
} }
&.warning { &.warning {
color: #ecb637; color: var(--warn);
} }
> * { > * {

View File

@ -7,7 +7,7 @@
<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/> <MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
<div class="content"> <div class="content">
<div v-if="tab === 'windows'" class="windows"> <div v-if="tab === 'windows'" class="windows" v-follow>
<div class="header"> <div class="header">
<div>#ID</div> <div>#ID</div>
<div>Component</div> <div>Component</div>
@ -19,7 +19,7 @@
<div><button class="_textButton" @click="killPopup(p)">Kill</button></div> <div><button class="_textButton" @click="killPopup(p)">Kill</button></div>
</div> </div>
</div> </div>
<div v-if="tab === 'stream'" class="stream"> <div v-if="tab === 'stream'" class="stream" v-follow>
<div class="header"> <div class="header">
<div>#ID</div> <div>#ID</div>
<div>Ch</div> <div>Ch</div>
@ -36,7 +36,7 @@
<div>{{ c.out }}</div> <div>{{ c.out }}</div>
</div> </div>
</div> </div>
<div v-if="tab === 'streamPool'" class="streamPool"> <div v-if="tab === 'streamPool'" class="streamPool" v-follow>
<div class="header"> <div class="header">
<div>#ID</div> <div>#ID</div>
<div>Ch</div> <div>Ch</div>
@ -48,6 +48,18 @@
<div>{{ p.users }}</div> <div>{{ p.users }}</div>
</div> </div>
</div> </div>
<div v-if="tab === 'api'" class="api" v-follow>
<div class="header">
<div>#ID</div>
<div>Endpoint</div>
<div>State</div>
</div>
<div v-for="req in apiRequests">
<div>#{{ req.id }}</div>
<div>{{ req.endpoint }}</div>
<div class="state" :class="req.state">{{ req.state }}</div>
</div>
</div>
</div> </div>
<footer> <footer>
@ -65,6 +77,7 @@ import { faTerminal } from '@fortawesome/free-solid-svg-icons';
import XWindow from '@/components/ui/window.vue'; import XWindow from '@/components/ui/window.vue';
import MkTab from '@/components/tab.vue'; import MkTab from '@/components/tab.vue';
import MkButton from '@/components/ui/button.vue'; import MkButton from '@/components/ui/button.vue';
import follow from '@/directives/follow-append';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
@ -74,6 +87,10 @@ export default defineComponent({
MkButton, MkButton,
}, },
directives: {
follow
},
props: { props: {
}, },
@ -105,6 +122,7 @@ export default defineComponent({
return { return {
tab: ref('stream'), tab: ref('stream'),
popups: os.popups, popups: os.popups,
apiRequests: os.apiRequests,
connections, connections,
pools, pools,
killPopup, killPopup,
@ -125,9 +143,7 @@ export default defineComponent({
flex: 1; flex: 1;
overflow: auto; overflow: auto;
> .windows, > div {
> .stream,
> .streamPool {
display: table; display: table;
width: 100%; width: 100%;
padding: 16px; padding: 16px;
@ -140,8 +156,31 @@ export default defineComponent({
opacity: 0.7; opacity: 0.7;
} }
> * { > div {
display: table-cell; display: table-cell;
white-space: nowrap;
&:not(:last-child) {
padding-right: 8px;
}
}
}
&.api {
> div {
> .state {
&.pending {
color: var(--warn);
}
&.success {
color: var(--success);
}
&.failed {
color: var(--error);
}
}
} }
} }
} }

View File

@ -0,0 +1,25 @@
import { Directive } from 'vue';
import { getScrollContainer, getScrollPosition } from '@/scripts/scroll';
export default {
mounted(src, binding, vn) {
const ro = new ResizeObserver((entries, observer) => {
const pos = getScrollPosition(src);
const container = getScrollContainer(src);
const viewHeight = container.clientHeight;
const height = container.scrollHeight;
if (pos + viewHeight > height - 32) {
container.scrollTop = height;
}
});
ro.observe(src);
// TODO: 新たにプロパティを作るのをやめMapを使う
src._ro_ = ro;
},
unmounted(src, binding, vn) {
src._ro_.unobserve(src);
}
} as Directive;

View File

@ -2,7 +2,7 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vu
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import Stream from '@/scripts/stream'; import Stream from '@/scripts/stream';
import { store } from '@/store'; import { store } from '@/store';
import { apiUrl } from '@/config'; import { apiUrl, debug } from '@/config';
import MkPostFormDialog from '@/components/post-form-dialog.vue'; import MkPostFormDialog from '@/components/post-form-dialog.vue';
import MkWaitingDialog from '@/components/waiting-dialog.vue'; import MkWaitingDialog from '@/components/waiting-dialog.vue';
import { resolve } from '@/router'; import { resolve } from '@/router';
@ -13,28 +13,26 @@ export const isMobile = /mobile|iphone|ipad|android/.test(ua);
export const stream = markRaw(new Stream()); export const stream = markRaw(new Stream());
export const pendingApiRequestsCount = ref(0); export const pendingApiRequestsCount = ref(0);
export const apiRequests = ref([]); // for debug
export const windows = new Map(); export const windows = new Map();
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) { export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
pendingApiRequestsCount.value++; pendingApiRequestsCount.value++;
if (_DEV_) {
performance.mark(_PERF_PREFIX_ + 'api:begin');
}
const onFinally = () => { const onFinally = () => {
pendingApiRequestsCount.value--; pendingApiRequestsCount.value--;
if (_DEV_) {
performance.mark(_PERF_PREFIX_ + 'api:end');
performance.measure(_PERF_PREFIX_ + 'api',
_PERF_PREFIX_ + 'api:begin',
_PERF_PREFIX_ + 'api:end');
}
}; };
const log = debug ? reactive({
id: apiRequests.value.length,
endpoint,
state: 'pending'
}) : null;
if (debug) {
apiRequests.value.push(log);
}
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
// Append a credential // Append a credential
if (store.getters.isSignedIn) (data as any).i = store.state.i.token; if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
@ -51,10 +49,19 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
if (res.status === 200) { if (res.status === 200) {
resolve(body); resolve(body);
if (debug) {
log.state = 'success';
}
} else if (res.status === 204) { } else if (res.status === 204) {
resolve(); resolve();
if (debug) {
log.state = 'success';
}
} else { } else {
reject(body.error); reject(body.error);
if (debug) {
log.state = 'failed';
}
} }
}).catch(reject); }).catch(reject);
}); });

View File

@ -10,6 +10,10 @@
<MkInput v-model:value="dialogBody"> <MkInput v-model:value="dialogBody">
<span>Body</span> <span>Body</span>
</MkInput> </MkInput>
<MkRadio v-model="dialogType" value="info">Info</MkRadio>
<MkRadio v-model="dialogType" value="success">Success</MkRadio>
<MkRadio v-model="dialogType" value="warning">Warn</MkRadio>
<MkRadio v-model="dialogType" value="error">Error</MkRadio>
<MkSwitch v-model:value="dialogCancel"> <MkSwitch v-model:value="dialogCancel">
<span>With cancel button</span> <span>With cancel button</span>
</MkSwitch> </MkSwitch>
@ -133,6 +137,7 @@ import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/ui/input.vue'; import MkInput from '@/components/ui/input.vue';
import MkSwitch from '@/components/ui/switch.vue'; import MkSwitch from '@/components/ui/switch.vue';
import MkTextarea from '@/components/ui/textarea.vue'; import MkTextarea from '@/components/ui/textarea.vue';
import MkRadio from '@/components/ui/radio.vue';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
@ -141,6 +146,7 @@ export default defineComponent({
MkInput, MkInput,
MkSwitch, MkSwitch,
MkTextarea, MkTextarea,
MkRadio,
}, },
data() { data() {
@ -153,6 +159,7 @@ export default defineComponent({
}, },
dialogTitle: 'Hello', dialogTitle: 'Hello',
dialogBody: 'World!', dialogBody: 'World!',
dialogType: 'info',
dialogCancel: false, dialogCancel: false,
dialogCancelByBgClick: true, dialogCancelByBgClick: true,
dialogInput: false, dialogInput: false,
@ -192,6 +199,7 @@ export default defineComponent({
async showDialog() { async showDialog() {
this.dialogResult = null; this.dialogResult = null;
this.dialogResult = await os.dialog({ this.dialogResult = await os.dialog({
type: this.dialogType,
title: this.dialogTitle, title: this.dialogTitle,
text: this.dialogBody, text: this.dialogBody,
showCancelButton: this.dialogCancel, showCancelButton: this.dialogCancel,

View File

@ -56,6 +56,9 @@
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
badge: '#31b1ce', badge: '#31b1ce',
messageBg: ':lighten<5<@bg', messageBg: ':lighten<5<@bg',
success: '#86b300',
error: '#ec4137',
warn: '#ecb637',
htmlThemeColor: '@bg', htmlThemeColor: '@bg',
X1: ':alpha<0<@bg', X1: ':alpha<0<@bg',
X2: ':darken<2<@panel', X2: ':darken<2<@panel',

View File

@ -56,6 +56,9 @@
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)', wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
badge: '#31b1ce', badge: '#31b1ce',
messageBg: '@panel', messageBg: '@panel',
success: '#86b300',
error: '#ec4137',
warn: '#ecb637',
htmlThemeColor: '@bg', htmlThemeColor: '@bg',
X1: ':alpha<0<@bg', X1: ':alpha<0<@bg',
X2: ':darken<2<@panel', X2: ':darken<2<@panel',