198 lines
4.9 KiB
Vue
198 lines
4.9 KiB
Vue
<template>
|
|
<Header class="sticky top-0 z-10 bg-white">
|
|
<div class="w-full sm:flex sm:items-center sm:justify-between">
|
|
<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="mt-1 flex items-center justify-between space-x-2 sm:mt-0"
|
|
v-if="$resources.document?.pg"
|
|
>
|
|
<div class="sm:hidden">
|
|
<Badge v-if="$resources.document?.pg && badge" v-bind="badge" />
|
|
</div>
|
|
<div class="space-x-2">
|
|
<ActionButton
|
|
v-for="button in actions"
|
|
v-bind="button"
|
|
:key="button.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 }">
|
|
<!-- 这个 div 是必需的 -->
|
|
<div></div>
|
|
<router-view
|
|
v-if="$resources.document?.pg"
|
|
:tab="tab"
|
|
:document="$resources.document"
|
|
/>
|
|
</template>
|
|
</TabsWithRouter>
|
|
<div
|
|
v-else-if="$resources.document.get.error"
|
|
class="mx-auto mt-60 w-fit rounded border border-dashed px-12 py-8 text-center text-gray-600"
|
|
>
|
|
<i-lucide-alert-triangle class="mx-auto mb-4 h-6 w-6 text-red-600" />
|
|
<ErrorMessage :message="$resources.document.get.error" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Header from '../components/Header.vue';
|
|
import ActionButton from '../components/ActionButton.vue';
|
|
import { Breadcrumbs } from 'jingrow-ui';
|
|
import { getObject } from '../objects';
|
|
import TabsWithRouter from '../components/TabsWithRouter.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} 详情`,
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 如果面包屑过长则添加省略号
|
|
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> |