jcloude/dashboard/src/pages/DetailPage.vue
2025-12-23 21:34:08 +08:00

204 lines
4.9 KiB
Vue

<template>
<Header class="sticky top-0 z-10 bg-white">
<div class="w-full sm:flex sm:justify-between sm:items-center">
<div class="flex items-center space-x-2">
<FBreadcrumbs :items="breadcrumbs" />
<Badge
class="hidden sm:inline-flex"
v-if="$resources.document?.pg && badge"
v-bind="badge"
/>
</div>
<div class="flex justify-between items-center mt-1 space-x-2 sm:mt-0">
<div class="sm:hidden">
<Badge v-if="$resources.document?.pg && badge" v-bind="badge" />
</div>
<AccessRequestButton
:pagetype="object.pagetype"
:docname="name"
:pg="$resources.document?.pg"
:error="$resources.document.get.error"
/>
<div class="space-x-2" v-if="$resources.document?.pg">
<ActionButton
v-for="action in actions"
v-bind="action"
:actionsAccess="$resources.document?.pg?.actions_access"
:key="action.label"
/>
</div>
</div>
</div>
</Header>
<div>
<TabsWithRouter
v-if="!$resources.document.get.error && $resources.document.get.fetched"
:document="$resources.document?.pg"
:tabs="tabs"
>
<template #tab-content="{ tab }">
<!-- this div is required for some reason -->
<div></div>
<router-view
v-if="$resources.document?.pg"
:tab="tab"
:document="$resources.document"
/>
</template>
</TabsWithRouter>
<DetailPageError
class="mt-60"
:pagetype="object.pagetype"
:docname="name"
:error="$resources.document.get.error"
/>
</div>
</template>
<script>
import Header from '../components/Header.vue';
import ActionButton from '../components/ActionButton.vue';
import DetailPageError from '../components/DetailPageError.vue';
import { Breadcrumbs } from 'jingrow-ui';
import { getObject } from '../objects';
import TabsWithRouter from '../components/TabsWithRouter.vue';
import AccessRequestButton from '../components/AccessRequestButton.vue';
let subscribed = {};
export default {
name: 'DetailPage',
props: {
id: String,
objectType: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
},
components: {
Header,
ActionButton,
TabsWithRouter,
FBreadcrumbs: Breadcrumbs,
},
resources: {
document() {
return {
type: 'document',
pagetype: this.object.pagetype,
name: this.name,
whitelistedMethods: this.object.whitelistedMethods || {},
onError(error) {
for (let message of error?.messages || []) {
if (message.redirect) {
window.location.href = message.redirect;
return;
}
}
},
};
},
},
mounted() {
if (!subscribed[`${this.object.pagetype}:${this.name}`]) {
this.$socket.emit('pg_subscribe', this.object.pagetype, this.name);
subscribed[`${this.object.pagetype}:${this.name}`] = true;
}
this.$socket.on('pg_update', (data) => {
if (data.pagetype === this.object.pagetype && data.name === this.name) {
this.$resources.document.reload();
}
});
},
beforeUnmount() {
let pagetype = this.object.pagetype;
if (subscribed[`${pagetype}:${this.name}`]) {
this.$socket.emit('pg_unsubscribe', pagetype, this.name);
subscribed[`${pagetype}:${this.name}`] = false;
}
},
computed: {
object() {
return getObject(this.objectType);
},
tabs() {
return this.object.detail.tabs.filter((tab) => {
if (tab.condition) {
return tab.condition({
documentResource: this.$resources.document,
});
}
return true;
});
},
title() {
let pg = this.$resources.document?.pg;
return pg ? pg[this.object.detail.titleField || 'name'] : this.name;
},
badge() {
if (this.object.detail.statusBadge) {
return this.object.detail.statusBadge({
documentResource: this.$resources.document,
});
}
return null;
},
actions() {
if (this.object.detail.actions && this.$resources.document?.pg) {
let actions = this.object.detail.actions({
documentResource: this.$resources.document,
});
return actions.filter((action) => {
if (action.condition) {
return action.condition({
documentResource: this.$resources.document,
});
}
return true;
});
}
return [];
},
breadcrumbs() {
let items = [
{ label: this.object.list.title, route: this.object.list.route },
{
label: this.title,
route: {
name: `${this.object.pagetype} Detail`,
params: { name: this.name },
},
},
];
if (this.object.detail.breadcrumbs && this.$resources.document?.pg) {
let result = this.object.detail.breadcrumbs({
documentResource: this.$resources.document,
items,
});
if (Array.isArray(result)) {
items = result;
}
}
// add ellipsis if breadcrumbs too long
for (let i = 0; i < items.length; i++) {
if (items[i].label.length > 30 && i !== items.length - 1) {
items[i].label = items[i].label.slice(0, 30) + '...';
}
}
return items;
},
},
};
</script>
<style scoped>
:deep(button[role='tab']) {
white-space: nowrap;
}
</style>