perf: update detail drawer show

This commit is contained in:
ibuler 2025-03-19 21:00:38 +08:00
parent ac6a0f49da
commit 2f21818517
8 changed files with 364 additions and 126 deletions

View File

@ -81,6 +81,7 @@ export default {
return this.visible
},
set(val) {
this.$log.debug('>>> Drawer iVisible set: ', val)
this.$emit('update:visible', val)
}
}

View File

@ -75,14 +75,12 @@ export default {
title: '',
action: '',
drawerVisible: false,
drawerComponent: ''
drawerComponent: '',
drawerTitle: ''
}
},
computed: {
...mapGetters(['inDrawer']),
drawerTitle() {
return this.getDefaultTitle()
},
iHeaderActions() {
const actions = this.headerActions
if (!actions.onCreate) {
@ -95,8 +93,7 @@ export default {
const actionMap = {
'columnsMeta.actions.formatterArgs.onUpdate': this.onUpdate,
'columnsMeta.actions.formatterArgs.onClone': this.onClone,
'columnsMeta.name.formatterArgs.drawer': true,
'columnsMeta.name.formatterArgs.drawerComponent': this.detailDrawer
'columnsMeta.name.formatterArgs.onClick': this.onDetail
}
for (const [key, value] of Object.entries(actionMap)) {
if (_.get(config, key)) {
@ -108,9 +105,9 @@ export default {
for (const value of Object.values(columnsMeta)) {
if (
value.formatter && value.formatter.name === 'AmountFormatter' &&
value.formatterArgs && !value.formatterArgs.drawer
value.formatterArgs && value.formatterArgs.drawer !== false
) {
value.formatterArgs.drawer = this.detailDrawer
value.formatterArgs.onClick = this.onDetail
}
}
return config
@ -122,11 +119,11 @@ export default {
return
}
if (!val) {
this.drawerVisible = false
this.reloadTable()
// this.drawerVisible = false
}
},
drawerVisible: {
immediate: true,
handler(val, oldVal) {
this.$log.debug('>>> drawerVisible changed: ', oldVal, '->', val)
if (!val && oldVal) {
@ -165,9 +162,18 @@ export default {
}
this.drawerComponent = ''
console.log('>>> afterCloseDrawer 2', this.$route)
},
getDefaultTitle() {
getDetailDrawerTitle({ col, row, cellValue, route }) {
let title = cellValue || row.name
const resource = route?.meta?.title || route?.name
if (resource) {
title = `${resource}: ${title}`
}
return title
},
getDefaultTitle({ row, col, cellValue, detailRoute }) {
let title = this.title
let dispatchAction = ''
if (!title && this.resource) {
@ -186,8 +192,13 @@ export default {
dispatchAction = this.$t('Create')
} else if (action === 'update') {
dispatchAction = this.$t('Update')
} else if (action === 'detail') {
dispatchAction = this.$t('Detail')
}
title = dispatchAction + this.$t('WordSep') + toLowerCaseExcludeAbbr(title)
if (this.action === 'detail') {
title = this.getDetailDrawerTitle({ row, col, cellValue, route: detailRoute })
}
return title
},
getDefaultDrawer(action) {
@ -214,7 +225,27 @@ export default {
return component
}
},
async showDrawer(action) {
resolveRoute(route) {
const routes = this.$router.resolve(route)
if (!routes) {
return
}
const matched = routes.resolved.matched.filter(item => item.name === route.name && item.components)
if (matched.length === 0) {
return
}
if (matched[0] && matched[0].components?.default) {
return matched[0]
}
},
getDetailComponent(r) {
const route = this.resolveRoute(r)
if (route) {
return route.components.default
}
},
async showDrawer(action, { row, col, cellValue, detailRoute }) {
this.drawerTitle = this.getDefaultTitle({ row, col, cellValue, detailRoute })
try {
// 1.
this.drawerVisible = false
@ -229,7 +260,7 @@ export default {
} else if (action === 'update') {
this.drawerComponent = this.updateDrawer || this.createDrawer
} else if (action === 'detail') {
this.drawerComponent = this.detailDrawer
this.drawerComponent = this.detailDrawer || this.getDetailComponent(detailRoute)
} else if (action === 'clone') {
this.drawerComponent = this.createDrawer || this.getDefaultDrawer('create')
} else {
@ -278,6 +309,13 @@ export default {
}
this.$refs.ListTable.reloadTable()
},
async onDetail({ row, col, cellValue, detailRoute }) {
this.$route.params.id = row.id
await this.$store.dispatch('common/setDrawerActionMeta', {
action: 'detail', row: row, col: col, id: row.id
})
await this.showDrawer('detail', { row, col, cellValue, detailRoute })
},
async onCreate(meta) {
if (!meta) {
meta = {}
@ -291,7 +329,7 @@ export default {
await this.$store.dispatch('common/setDrawerActionMeta', {
action: 'clone', row: row, col: col, id: row.id
})
await this.showDrawer('clone')
await this.showDrawer('clone', { row, col })
},
async onUpdate({ row, col }) {
this.$route.params.id = row.id
@ -299,7 +337,7 @@ export default {
await this.$store.dispatch('common/setDrawerActionMeta', {
action: 'update', row: row, col: col, id: row.id
})
await this.showDrawer('update')
await this.showDrawer('update', { row, col })
}
}
}

View File

@ -43,7 +43,8 @@ export default {
async: false,
ajax: {},
title: '',
preventClick: false
preventClick: false,
onClick: null
}
}
}
@ -53,6 +54,7 @@ export default {
return {
formatterArgs: formatterArgs,
listData: formatterArgs.async ? [] : (this.cellValue || []),
onClick: formatterArgs.onClick,
amount: '',
asyncGetDone: false
}

View File

@ -12,24 +12,14 @@
{{ iTitle }}
</slot>
</el-link>
<Drawer
v-if="formatterArgs.drawer && drawerComponent && drawerVisible"
:component="drawerComponent"
:has-footer="false"
:title="drawerTitle"
:visible.sync="drawerVisible"
class="detail-drawer"
/>
</div>
</template>
<script>
import BaseFormatter from './base.vue'
import Drawer from '@/components/Drawer/index.vue'
export default {
name: 'DetailFormatter',
components: { Drawer },
extends: BaseFormatter,
props: {
formatterArgsDefault: {
@ -49,7 +39,6 @@ export default {
getTitle({ row, cellValue }) {
return cellValue != null ? cellValue : row.name
},
getDrawerTitle: null,
getIcon({ col, row, cellValue }) {
return null
}
@ -62,15 +51,11 @@ export default {
}
},
data() {
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
const formatterArgs = _.cloneDeep(_.merge(this.formatterArgsDefault, this.col.formatterArgs))
return {
drawerTitle: '',
linkClicked: false,
drawerComponent: '',
showTableDetailDrawer: false,
currentTemplate: null,
formatterArgs: formatterArgs,
drawerVisible: false
formatterArgs: formatterArgs
}
},
computed: {
@ -104,95 +89,14 @@ export default {
}
}
},
watch: {
drawerVisible(val) {
this.$log.debug('>>> DetailFormatter drawerVisible: ', val)
if (!val) {
this.drawerComponent = ''
}
}
},
methods: {
getResource() {
const route = this.resolveRoute()
if (!route) {
return
}
const resource = route.meta.title || route.name
return resource.replace(' details', '').replace('详情', '')
},
getDrawerTitle() {
let title = this.cellValue || this.row.name
if (this.formatterArgs?.getDrawerTitle && typeof this.formatterArgs.getDrawerTitle === 'function') {
title = this.formatterArgs.getDrawerTitle({
col: this.col,
row: this.row,
cellValue: this.cellValue
})
}
const resource = this.getResource()
if (resource) {
title = `${resource}: ${title}`
}
return title
},
resolveRoute() {
const route = this.getDetailRoute()
const routes = this.$router.resolve(route)
if (!routes) {
return
}
const matched = routes.resolved.matched.filter(item => item.name === route.name && item.components)
if (matched.length === 0) {
return
}
if (matched[0] && matched[0].components?.default) {
return matched[0]
}
},
getRouteComponent() {
const route = this.resolveRoute()
if (route) {
return route.components.default
}
},
showDrawer() {
if (this.formatterArgs.drawerComponent) {
this.drawerComponent = this.formatterArgs.drawerComponent
} else {
this.drawerComponent = this.getRouteComponent()
}
const route = this.getDetailRoute()
if (route?.query?.tab) {
this.$cookie.set(route.name, route.query.tab, 1)
this.$route.query.tab = route.query.tab
}
const payload = {
action: 'detail',
row: this.row,
col: this.col,
id: route.params.id || this.row.id
}
this.$store.dispatch('common/setDrawerActionMeta', payload).then(() => {
this.drawerTitle = this.getDrawerTitle(payload)
this.drawerVisible = true
})
},
handleClick() {
if (this.formatterArgs.beforeClick) {
this.formatterArgs.beforeClick(this.callbackArgs)
}
if (this.formatterArgs.onClick) {
return this.formatterArgs.onClick(this.callbackArgs)
}
if (this.formatterArgs.drawer) {
return this.showDrawer()
return this.formatterArgs.onClick({ ...this.callbackArgs, detailRoute: this.getDetailRoute() })
}
if (this.preventClick) {

View File

@ -0,0 +1,293 @@
<template>
<div>
<img v-if="icon" :src="icon" alt="icon" class="icon">
<el-link
:class="{ 'clicked': linkClicked }"
:disabled="disabled"
:type="col.type || 'info'"
class="detail"
@click="handleClick"
>
<slot>
{{ iTitle }}
</slot>
</el-link>
<Drawer
v-if="ddrawerVisible"
:component="drawerComponent"
:has-footer="false"
:title="drawerTitle"
:visible.sync="ddrawerVisible"
class="detail-drawer"
/>
</div>
</template>
<script>
import BaseFormatter from './base.vue'
import Drawer from '@/components/Drawer/index.vue'
export default {
name: 'DetailFormatter',
components: { Drawer },
extends: BaseFormatter,
props: {
formatterArgsDefault: {
type: Object,
default() {
return {
route: this.$route.name.replace('List', 'Detail'),
can: true,
getRoute: null,
routeQuery: null,
drawer: false,
onClick: null,
openInNewPage: false,
removeColorOnClick: false,
beforeClick: () => {
},
getTitle({ row, cellValue }) {
return cellValue != null ? cellValue : row.name
},
getDrawerTitle: null,
getIcon({ col, row, cellValue }) {
return null
}
}
}
},
preventClick: {
type: Boolean,
default: false
}
},
data() {
const formatterArgs = _.cloneDeep(_.merge(this.formatterArgsDefault, this.col.formatterArgs))
return {
drawerTitle: '',
linkClicked: false,
drawerComponent: '',
showTableDetailDrawer: false,
currentTemplate: null,
formatterArgs: formatterArgs,
ddrawerVisible: false
}
},
computed: {
iTitle() {
return this.formatterArgs.getTitle({
col: this.col,
row: this.row,
cellValue: this.cellValue,
index: this.index
})
},
disabled() {
let can = this.formatterArgs.can
if (typeof can === 'function') {
can = can({ col: this.col, row: this.row })
}
return !can
},
icon() {
return this.formatterArgs.getIcon({
col: this.col,
row: this.row,
cellValue: this.cellValue
})
},
callbackArgs() {
return {
col: this.col,
row: this.row,
cellValue: this.cellValue
}
}
},
watch: {
ddrawerVisible: {
immediate: true,
handler(val, oldVal) {
this.$log.debug('>>> DetailFormatter ddrawerVisible: ', val, oldVal, this)
if (!val) {
setTimeout(() => {
this.drawerComponent = ''
}, 300)
}
}
}
},
mounted() {
this.$log.debug('>>> DetailFormatter isVisible mounted: ', this.iTitle, this)
},
destroyed() {
this.$log.debug('>>> DetailFormatter isVisible destroyed: ', this.iTitle, this)
},
methods: {
getResource() {
const route = this.resolveRoute()
if (!route) {
return
}
const resource = route.meta.title || route.name
return resource.replace(' details', '').replace('详情', '')
},
getDrawerTitle() {
let title = this.cellValue || this.row.name
if (this.formatterArgs?.getDrawerTitle && typeof this.formatterArgs.getDrawerTitle === 'function') {
title = this.formatterArgs.getDrawerTitle({
col: this.col,
row: this.row,
cellValue: this.cellValue
})
}
const resource = this.getResource()
if (resource) {
title = `${resource}: ${title}`
}
return title
},
resolveRoute() {
const route = this.getDetailRoute()
const routes = this.$router.resolve(route)
if (!routes) {
return
}
const matched = routes.resolved.matched.filter(item => item.name === route.name && item.components)
if (matched.length === 0) {
return
}
if (matched[0] && matched[0].components?.default) {
return matched[0]
}
},
getRouteComponent() {
const route = this.resolveRoute()
if (route) {
return route.components.default
}
},
showDrawer() {
if (this.formatterArgs.drawerComponent) {
this.drawerComponent = this.formatterArgs.drawerComponent
} else {
this.drawerComponent = this.getRouteComponent()
}
const route = this.getDetailRoute()
if (route?.query?.tab) {
this.$cookie.set(route.name, route.query.tab, 1)
this.$route.query.tab = route.query.tab
}
const payload = {
action: 'detail',
row: this.row,
col: this.col,
id: route.params.id || this.row.id
}
this.$store.dispatch('common/setDrawerActionMeta', payload).then(() => {
this.drawerTitle = this.getDrawerTitle(payload)
this.ddrawerVisible = true
})
},
handleClick() {
if (this.formatterArgs.beforeClick) {
this.formatterArgs.beforeClick(this.callbackArgs)
}
if (this.formatterArgs.onClick) {
return this.formatterArgs.onClick(this.callbackArgs)
}
if (this.formatterArgs.drawer) {
return this.showDrawer()
}
if (this.preventClick) {
return
}
this.goDetail()
},
getDetailRoute() {
// const defaultRoute = this.$route.name.replace('List', 'Detail')
let route = this.formatterArgs.route
if (this.formatterArgs.getRoute && typeof this.formatterArgs.getRoute === 'function') {
route = this.formatterArgs.getRoute(this.callbackArgs)
}
if (!route) {
console.error('No route found')
return
}
let detailRoute = { replace: true }
if (typeof route === 'string') {
detailRoute.name = route
detailRoute.params = { id: this.row.id }
} else {
detailRoute = route
}
const routeQuery = this.formatterArgs.routeQuery
if (routeQuery && typeof routeQuery === 'object') {
detailRoute.query = this.formatterArgs.routeQuery
}
return detailRoute
},
goDetail() {
const detailRoute = this.getDetailRoute()
if (this.formatterArgs.openInNewPage) {
const { href } = this.$router.resolve(detailRoute)
this.linkClicked = this.formatterArgs.removeColorOnClick
return window.open(href, '_blank')
}
this.$router.push(detailRoute)
}
}
}
</script>
<style lang="scss" scoped>
.detail {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 400;
}
.detail {
line-height: 25px;
font-size: 13px;
}
.clicked,
.el-link.el-link--info.clicked {
color: inherit !important;
}
.icon {
width: 28px;
height: 28px;
}
::v-deep .go-back {
display: none;
}
.detail-drawer {
::v-deep {
.el-drawer__header {
border-bottom: none;
padding-bottom: 1px;
}
}
}
</style>

View File

@ -10,7 +10,7 @@
</template>
<script>
import { DrawerListTable as ListTable } from '@/components'
import ListTable from '@/components/Table/ListTable/index.vue'
export default {
components: {
@ -68,12 +68,8 @@ export default {
createRoute: 'UserLoginACLCreate'
}
}
},
activated() {
setTimeout(() => {
this.$refs.listTable.reloadTable()
}, 300)
}
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<TwoCol>
<UserLoginACLTable :url="url" />
<UserLoginACLTable />
</TwoCol>
</template>

View File

@ -5,9 +5,7 @@
v-bind="config"
v-on="$listeners"
>
<keep-alive>
<component :is="config.activeMenu" :object="user" />
</keep-alive>
<component :is="config.activeMenu" :object="user" />
</GenericDetailPage>
</template>
@ -85,6 +83,12 @@ export default {
'currentUserIsSuperAdmin'
])
},
mounted() {
this.$log.debug('>>> UserDetail mounted: visible ', this)
},
destroyed() {
this.$log.debug('>>> UserDetail destroyed: visible ', this)
},
methods: {
handleTabClick(tab) {
this.$log.debug('Current nav is: ', this.config.activeMenu)