Files
lina/src/components/Table/ListTable/index.vue
fit2bot 4ed529bbfb perf: remove sub mod (#5254)
* perf: remove sub mod

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-10-30 15:23:58 +08:00

399 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<QuickFilter
:expand.sync="filterExpand"
:filters="quickFilters"
:summary="quickSummary"
:table-url="tableUrl"
@filter="filter"
/>
<TableAction
v-if="hasActions"
:class="{ 'filter-expand': filterExpand }"
:date-pick="handleDateChange"
:has-quick-filter="iHasQuickFilter"
:quick-filter-expand.sync="filterExpand"
:reload-table="reloadTable"
:search-table="search"
:selected-rows="selectedRows"
:table-url="tableUrl"
v-bind="iHeaderActions"
@done="handleActionInitialDone"
/>
<IBox v-loading="!actionInit" class="table-content">
<AutoDataTable
v-if="actionInit"
ref="dataTable"
:config="iTableConfig"
:filter-table="filter"
v-on="$listeners"
@selection-change="handleSelectionChange"
/>
</IBox>
</div>
</template>
<script>
import { getResourceFromApiUrl } from '@/utils/jms/index'
import deepmerge from 'deepmerge'
import { mapGetters } from 'vuex'
import IBox from '@/components/Common/IBox/index.vue'
import TableAction from './TableAction/index.vue'
import Emitter from '@/mixins/emitter'
import AutoDataTable from '../AutoDataTable/index.vue'
import QuickFilter from './TableAction/QuickFilter.vue'
import { getDayEnd, getDaysAgo } from '@/utils/common/time'
import { ObjectLocalStorage } from '@/utils/common/index'
export default {
name: 'ListTable',
components: {
QuickFilter,
AutoDataTable,
TableAction,
IBox
},
mixins: [Emitter],
props: {
// 定义 table 的配置
tableConfig: {
type: Object,
default: () => ({})
},
// 是否显示table左侧的action
headerActions: {
type: Object,
default: () => ({})
},
quickFilters: {
type: Array,
default: () => null
},
quickSummary: {
type: Array,
default: () => null
}
},
data() {
const order = this.$route?.params?.order
let extraQuery = {
...(order && { order })
}
if (this.headerActions.hasDatePicker) {
extraQuery = {
...extraQuery,
date_from: getDaysAgo(7).toISOString(),
date_to: this.$moment(getDayEnd()).add(1, 'day').toISOString()
}
this.headerActions.datePicker = Object.assign(
{
dateStart: extraQuery.date_from,
dateEnd: extraQuery.date_to
},
this.headerActions.datePicker
)
}
if (this.$route.query.order) {
extraQuery['order'] = this.$route.query.order
}
return {
selectedRows: [],
init: false,
urlUpdated: {},
isDeactivated: false,
extraQuery: extraQuery,
actionInit: this.headerActions.has === false,
initQuery: {},
tablePath: new URL(this.tableConfig.url || '', 'http://127.0.0.1').pathname,
objStorage: new ObjectLocalStorage('filterExpand'),
iFilterExpand: null,
reloadTable: _.debounce(this._reloadTable, 300),
searchQuery: {},
filterQuery: {}
}
},
computed: {
...mapGetters(['currentOrgIsRoot']),
filterExpand: {
get() {
if (this.iFilterExpand !== null) {
return this.iFilterExpand
}
return this.objStorage.get(this.tablePath)
},
set(val) {
this.iFilterExpand = val
this.objStorage.set(this.tablePath, val)
}
},
iHasQuickFilter() {
const has =
(this.quickFilters && this.quickFilters.length > 0) ||
(this.quickSummary && this.quickSummary.length > 0)
return !!has
},
dataTable() {
return this.$refs.dataTable?.$refs.dataTable
},
iHeaderActions() {
// 如果路由中锁定了 root 组织,就不在检查 root 组织下是否可以创建等
const checkRoot = !(this.$route.meta?.disableOrgsChange === true)
const actions = {
canCreate: { action: 'add', checkRoot: checkRoot },
canBulkDelete: { action: 'delete', checkRoot: false },
canBulkUpdate: { action: 'change', checkRoot: checkRoot },
hasImport: { action: 'add|change', checkRoot: checkRoot },
hasExport: { action: 'view', checkRoot: false }
}
const defaults = {}
for (const [k, v] of Object.entries(actions)) {
const hasPerm = v.action.split('|').some(i => this.hasActionPerm(i.trim()))
if (!hasPerm) {
defaults[k] = this.$t('NoPermission')
continue
}
if (v.checkRoot && this.currentOrgIsRoot) {
defaults[k] = this.$t('NoPermissionInGlobal')
continue
}
defaults[k] = true
}
return Object.assign(defaults, this.headerActions)
},
hasActions() {
return this.iHeaderActions.has === undefined ? true : this.iHeaderActions.has
},
iTableConfig() {
if (this.isDeactivated) {
return
}
const config = deepmerge(this.tableConfig, {
extraQuery: this.extraQuery
})
const checkRoot = !(this.$route.meta?.disableOrgsChange === true)
const checkPermAndRoot = action => {
if (!this.hasActionPerm(action)) {
return this.$t('NoPermission')
}
if (checkRoot && this.currentOrgIsRoot) {
return this.$t('NoPermissionInGlobal')
}
return true
}
const formatterArgs = {
'columnsMeta.actions.formatterArgs.canUpdate': () => {
return checkPermAndRoot('change')
},
'columnsMeta.actions.formatterArgs.canDelete': 'delete',
'columnsMeta.actions.formatterArgs.canClone': () => {
return checkPermAndRoot('add')
},
'columnsMeta.name.formatterArgs.can': 'view'
}
for (const [arg, action] of Object.entries(formatterArgs)) {
const notSet = _.get(config, arg) === undefined
const isFunction = typeof action === 'function'
if (notSet) {
const hasActionPerm = isFunction ? action() : this.hasActionPerm(action)
_.set(config, arg, hasActionPerm)
}
}
this.$log.debug('Header actions', this.headerActions)
this.$log.debug('ListTable: iTableConfig change', config)
return config
},
tableUrl() {
return this.tableConfig.url || ''
},
permissions() {
// 获取 permissions获取不到通过 url 解析
const permissions = this.tableConfig.permissions || {}
const { app: apiApp, resource: apiResource } = getResourceFromApiUrl(this.tableUrl)
const app = permissions.app || apiApp
const resource = permissions.resource || apiResource
const actions = ['add', 'change', 'delete', 'view']
const defaultPermissions = actions.reduce((result, action) => {
result[action] = `${app}.${action}_${resource}`
return result
}, {})
const perms = Object.assign(defaultPermissions, permissions)
// this.$log.debug('Permissions: ', perms)
return perms
}
},
watch: {
extraQuery: {
handler() {
this.$log.debug('ListTable: found extraQuery change')
},
deep: true
},
tableColConfig: {
handler() {
this.$log.debug('ListTable: found colConfig change')
},
deep: true
}
},
mounted() {
this.$set(this.urlUpdated, this.tableUrl, location.href)
},
deactivated() {
this.isDeactivated = true
},
activated() {
this.$nextTick(() => {
this.isDeactivated = false
const cleanUrl = this.tableUrl.split('?')[0]
const preURL = this.urlUpdated[cleanUrl]
if (!preURL || preURL === location.href) return
this.$set(this.urlUpdated, this.tableUrl, location.href)
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
this.reloadTable()
})
},
methods: {
handleFilterExpandChanged(expand) {
this.filterExpand = expand
},
handleQuickFilter(option) {
if (option.route) {
this.$router.push(option.route)
return
}
if (option.filter) {
const filter = { ...option.filter }
if (option.active) {
for (const key in filter) {
filter[key] = ''
}
}
this.filter(option.filter)
return
}
if (option.callback) {
option.callback(option.active)
}
},
handleActionInitialDone() {
setTimeout(() => {
this.actionInit = true
}, 100)
},
handleSelectionChange(val) {
this.selectedRows = val
},
_reloadTable() {
this.dataTable?.getList()
},
updateInitQuery(attrs) {
if (!this.actionInit) {
this.initQuery = attrs
for (const key in attrs) {
this.$set(this.extraQuery, key, attrs[key])
}
return true
}
const removeKeys = Object.keys(this.initQuery).filter(key => !attrs[key])
for (const key of removeKeys) {
this.$delete(this.extraQuery, key)
}
},
getMergedQuery() {
return { ...this.searchQuery, ...this.filterQuery }
},
search(attrs) {
const init = this.updateInitQuery(attrs)
if (init) {
return
}
this.searchQuery = attrs
const merged = this.getMergedQuery()
this.$log.debug('ListTable: search table', attrs)
this.$emit('TagSearch', attrs)
this.$refs.dataTable?.$refs.dataTable?.search(merged, true)
},
filter(attrs) {
this.filterQuery = attrs
const merged = this.getMergedQuery()
this.$emit('TagFilter', attrs)
this.$log.debug('ListTable: found filter change', attrs)
this.$refs.dataTable?.$refs.dataTable?.search(merged, true)
},
hasActionPerm(action) {
const permRequired = this.permissions[action]
return this.$hasPerm(permRequired)
},
handleDateChange(attrs) {
let dateFrom = ''
let dateTo = ''
try {
dateFrom = attrs[0].toISOString()
dateTo = attrs[1].toISOString()
} catch (e) {
this.$log.error('Handle date change error: ', attrs)
dateFrom = new Date()
dateFrom.setDate(dateFrom.getDate() - 5)
dateFrom = dateFrom.toISOString()
dateTo = new Date()
dateTo.setDate(dateTo.getDate() + 1)
dateTo = dateTo.toISOString()
}
this.$set(this.extraQuery, 'date_from', dateFrom)
this.$set(this.extraQuery, 'date_to', dateTo)
const query = {
date_from: dateFrom,
date_to: dateTo
}
this.$emit('TagDateChange', attrs)
return this.dataTable.searchDate(query)
},
toggleRowSelection(row, isSelected) {
return this.dataTable.toggleRowSelection(row, isSelected)
}
}
}
</script>
<style lang="scss" scoped>
.filter-expand {
&::v-deep button.actionFilter {
background-color: rgb(0, 0, 0, 0.08) !important;
}
}
.table-content {
margin-top: 10px;
::v-deep {
.el-card__body {
padding: 0;
}
.el-table__row .cell {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.el-table__expanded-cell pre {
max-height: 500px;
overflow-y: scroll;
}
.el-button-ungroup .el-dropdown > .more-action {
//height: 24.6px;
}
}
}
//修改颜色
.el-button--text {
color: #409eff;
}
</style>