mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Support custom component action
This commit is contained in:
parent
010d2243ba
commit
46ee30204d
160
registry/lib/components/utils/dev-client/Action.vue
Normal file
160
registry/lib/components/utils/dev-client/Action.vue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="canStartDebug"
|
||||
class="component-action dev-client-action"
|
||||
@click="startDebug"
|
||||
>
|
||||
<VIcon v-if="busy" icon="mdi-network-outline" :size="16" />
|
||||
<VIcon v-else icon="mdi-play-network-outline" :size="16" />
|
||||
{{ busy ? '启动中' : '开始调试' }}
|
||||
</div>
|
||||
<div
|
||||
v-if="canStopDebug"
|
||||
class="component-action dev-client-action"
|
||||
@click="stopDebug"
|
||||
>
|
||||
<VIcon v-if="busy" icon="mdi-network-outline" :size="16" />
|
||||
<VIcon v-else icon="mdi-minus-network-outline" :size="16" />
|
||||
{{ busy ? '停止中' : '停止调试' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { OptionsOfMetadata } from '@/components/define'
|
||||
import { ComponentMetadata } from '@/components/types'
|
||||
import { getComponentSettings } from '@/core/settings'
|
||||
import { Toast } from '@/core/toast'
|
||||
import { VIcon } from '@/ui'
|
||||
import { autoUpdateOptions, devClientOptionsMetadata } from './options'
|
||||
|
||||
const { options } = getComponentSettings<OptionsOfMetadata<typeof devClientOptionsMetadata>>('devClient')
|
||||
const converter = {
|
||||
toDevUrl: (url: string) => {
|
||||
if (!url) {
|
||||
return null
|
||||
}
|
||||
const devUrlMatch = url.match(new RegExp(`localhost:${options.port}\\/registry\\/components\\/(.+)$`))
|
||||
if (devUrlMatch) {
|
||||
return url
|
||||
}
|
||||
const localhostMatch = url.match(/localhost:(\d+?)\/components\/(.+)$/)
|
||||
if (localhostMatch) {
|
||||
return `http://localhost:${options.port}/registry/dist/components/${localhostMatch[2]}`
|
||||
}
|
||||
const onlineMatch = url.match(/\/registry\/dist\/components\/(.+)$/)
|
||||
if (onlineMatch) {
|
||||
return `http://localhost:${options.port}/registry/dist/components/${onlineMatch[1]}`
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
VIcon,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
component: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
busy: false,
|
||||
autoUpdateComponents: autoUpdateOptions.urls.components,
|
||||
sessions: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
autoUpdateRecord() {
|
||||
const metadata = this.component as ComponentMetadata
|
||||
return this.autoUpdateComponents[metadata.name]
|
||||
},
|
||||
componentUpdateUrl() {
|
||||
return this.autoUpdateRecord?.url
|
||||
},
|
||||
isDebugging() {
|
||||
return this.componentUpdateUrl && this.sessions.some((path: string) => {
|
||||
const { pathname } = new URL(this.componentUpdateUrl)
|
||||
return path === pathname
|
||||
})
|
||||
},
|
||||
canStartDebug() {
|
||||
return !this.isDebugging && converter.toDevUrl(this.componentUpdateUrl) !== null
|
||||
},
|
||||
canStopDebug() {
|
||||
return Boolean(this.isDebugging && this.componentUpdateUrl)
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
const { devClient } = await import('./client')
|
||||
this.sessions = devClient.sessions
|
||||
devClient.addEventListener('sessionsUpdate', this.handleSessionsUpdate)
|
||||
},
|
||||
async beforeDestroy() {
|
||||
const { devClient } = await import('./client')
|
||||
devClient.removeEventListener('sessionsUpdate', this.handleSessionsUpdate)
|
||||
},
|
||||
methods: {
|
||||
handleSessionsUpdate(e: CustomEvent<string[]>) {
|
||||
this.sessions = e.detail
|
||||
},
|
||||
async handleClick(action: () => Promise<void>) {
|
||||
if (this.busy) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
this.busy = true
|
||||
await action()
|
||||
} finally {
|
||||
this.busy = false
|
||||
}
|
||||
},
|
||||
async startDebug() {
|
||||
await this.handleClick(async () => {
|
||||
const { devClient } = await import('./client')
|
||||
const metadata = this.component as ComponentMetadata
|
||||
const devUrl = converter.toDevUrl(this.componentUpdateUrl)
|
||||
console.log('devUrl:', devUrl, 'autoUpdateRecord.url:', this.autoUpdateRecord.url)
|
||||
if (this.autoUpdateRecord.url !== devUrl) {
|
||||
options.devRecords[metadata.name] = {
|
||||
name: metadata.name,
|
||||
originalUrl: this.componentUpdateUrl,
|
||||
}
|
||||
this.autoUpdateRecord.url = devUrl
|
||||
}
|
||||
const toast = Toast.info('启动调试中...', 'DevClient')
|
||||
try {
|
||||
await devClient.startDebug(this.autoUpdateRecord.url)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
toast.close()
|
||||
}
|
||||
})
|
||||
},
|
||||
async stopDebug() {
|
||||
await this.handleClick(async () => {
|
||||
const { devClient } = await import('./client')
|
||||
const metadata = this.component as ComponentMetadata
|
||||
const { pathname } = new URL(this.componentUpdateUrl)
|
||||
await devClient.stopDebug(pathname)
|
||||
if (options.devRecords[metadata.name]) {
|
||||
this.autoUpdateRecord.url = options.devRecords[metadata.name].originalUrl
|
||||
delete options.devRecords[metadata.name]
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.dev-client-action {
|
||||
}
|
||||
</style>
|
||||
@ -165,6 +165,7 @@ export class DevClient extends EventTarget {
|
||||
handleSocketMessage(e, payload => {
|
||||
if (payload.type === 'querySessionsResponse') {
|
||||
this.sessions = payload.sessions
|
||||
this.dispatchEvent(new CustomEvent('sessionsUpdate', { detail: payload.sessions }))
|
||||
resolve(payload.sessions)
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,106 +1,19 @@
|
||||
import { PluginSetupParameters } from '@/plugins/plugin'
|
||||
import { ComponentAction } from '@/components/settings-panel/component-actions/component-actions'
|
||||
import { isIframe } from '@/core/utils'
|
||||
import { Toast } from '@/core/toast'
|
||||
import { getComponentSettings } from '@/core/settings'
|
||||
import { ComponentMetadata } from '@/components/types'
|
||||
import { OptionsOfMetadata } from '@/components/define'
|
||||
import { autoUpdateOptions, devClientOptionsMetadata } from './options'
|
||||
|
||||
export const setupPlugin = async ({ addData }: PluginSetupParameters) => {
|
||||
if (isIframe()) {
|
||||
return
|
||||
}
|
||||
const { options } = getComponentSettings<OptionsOfMetadata<typeof devClientOptionsMetadata>>('devClient')
|
||||
const { devClient } = await import('./client')
|
||||
addData('settingsPanel.componentActions', (actions: ComponentAction[]) => {
|
||||
const converter = {
|
||||
toDevUrl: (url: string) => {
|
||||
if (!url) {
|
||||
return null
|
||||
}
|
||||
const devUrlMatch = url.match(new RegExp(`localhost:${options.port}\\/registry\\/components\\/(.+)$`))
|
||||
if (devUrlMatch) {
|
||||
return url
|
||||
}
|
||||
const localhostMatch = url.match(/localhost:(\d+?)\/components\/(.+)$/)
|
||||
if (localhostMatch) {
|
||||
return `http://localhost:${options.port}/registry/dist/components/${localhostMatch[2]}`
|
||||
}
|
||||
const onlineMatch = url.match(/\/registry\/dist\/components\/(.+)$/)
|
||||
if (onlineMatch) {
|
||||
return `http://localhost:${options.port}/registry/dist/components/${onlineMatch[1]}`
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
const getDebugInfo = (metadata: ComponentMetadata) => {
|
||||
const autoUpdateRecord = autoUpdateOptions.urls.components[metadata.name]
|
||||
const componentUpdateUrl = autoUpdateRecord?.url
|
||||
const isDebugging = () => componentUpdateUrl && devClient.sessions.some(path => {
|
||||
const { pathname } = new URL(componentUpdateUrl)
|
||||
return path === pathname
|
||||
})
|
||||
const canStartDebug = () => !isDebugging() && converter.toDevUrl(componentUpdateUrl) !== null
|
||||
const canStopDebug = () => Boolean(isDebugging() && componentUpdateUrl)
|
||||
return {
|
||||
autoUpdateRecord,
|
||||
componentUpdateUrl,
|
||||
canStartDebug,
|
||||
canStopDebug,
|
||||
}
|
||||
}
|
||||
actions.push(
|
||||
metadata => {
|
||||
const { componentUpdateUrl, autoUpdateRecord, canStartDebug } = getDebugInfo(metadata)
|
||||
const result = {
|
||||
name: 'startDebug',
|
||||
displayName: '开始调试',
|
||||
icon: 'mdi-play-network-outline',
|
||||
visible: canStartDebug(),
|
||||
action: async () => {
|
||||
const devUrl = converter.toDevUrl(componentUpdateUrl)
|
||||
console.log('devUrl:', devUrl, 'autoUpdateRecord.url:', autoUpdateRecord.url)
|
||||
if (autoUpdateRecord.url !== devUrl) {
|
||||
options.devRecords[metadata.name] = {
|
||||
name: metadata.name,
|
||||
originalUrl: componentUpdateUrl,
|
||||
}
|
||||
autoUpdateRecord.url = devUrl
|
||||
}
|
||||
const toast = Toast.info('启动调试中...', 'DevClient')
|
||||
try {
|
||||
await devClient.startDebug(autoUpdateRecord.url)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
toast.close()
|
||||
result.visible = canStartDebug()
|
||||
console.log('visible', result.visible)
|
||||
}
|
||||
},
|
||||
() => {
|
||||
const ActionModule = () => import('./Action.vue')
|
||||
return {
|
||||
name: 'devClient',
|
||||
component: ActionModule,
|
||||
}
|
||||
return result
|
||||
},
|
||||
metadata => {
|
||||
const { componentUpdateUrl, autoUpdateRecord, canStopDebug } = getDebugInfo(metadata)
|
||||
const result = {
|
||||
name: 'stopDebug',
|
||||
displayName: '停止调试',
|
||||
icon: 'mdi-minus-network-outline',
|
||||
visible: canStopDebug(),
|
||||
action: async () => {
|
||||
const { pathname } = new URL(componentUpdateUrl)
|
||||
await devClient.stopDebug(pathname)
|
||||
if (options.devRecords[metadata.name]) {
|
||||
autoUpdateRecord.url = options.devRecords[metadata.name].originalUrl
|
||||
delete options.devRecords[metadata.name]
|
||||
}
|
||||
result.visible = canStopDebug()
|
||||
console.log('visible', result.visible)
|
||||
},
|
||||
}
|
||||
return result
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
@ -68,14 +68,24 @@
|
||||
</div>
|
||||
<template #toast>
|
||||
<div class="extra-actions-list">
|
||||
<ComponentAction
|
||||
<div
|
||||
v-for="a of componentActions"
|
||||
v-show="a.visible !== false"
|
||||
:key="a.name"
|
||||
class="extra-action-item"
|
||||
:item="a"
|
||||
:component="componentData"
|
||||
/>
|
||||
>
|
||||
<component
|
||||
:is="a.component"
|
||||
v-if="a.component"
|
||||
:item="a"
|
||||
:component="componentData"
|
||||
/>
|
||||
<ComponentAction
|
||||
v-else
|
||||
v-show="a.visible !== false"
|
||||
class="extra-action-item"
|
||||
:item="a"
|
||||
:component="componentData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MiniToast>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { registerAndGetData } from '@/plugins/data'
|
||||
import { Executable } from '@/core/common-types'
|
||||
import { Executable, VueModule } from '@/core/common-types'
|
||||
import { getHook } from '@/plugins/hook'
|
||||
import { isUserComponent } from '@/core/settings'
|
||||
import { ComponentMetadata } from '../../types'
|
||||
@ -13,6 +13,9 @@ export type ComponentAction = (metadata: ComponentMetadata) => {
|
||||
visible?: boolean
|
||||
title?: string
|
||||
// condition?: () => boolean
|
||||
} | {
|
||||
name: string
|
||||
component: Executable<VueModule>
|
||||
}
|
||||
|
||||
const builtInActions: ComponentAction[] = [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user