lina/src/components/Table/CardTable/index.vue
2025-03-28 10:11:31 +08:00

290 lines
6.4 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 class="el-card-table">
<TableAction
:reload-table="reloadTable"
:search-table="search"
:table-url="tableUrl"
v-bind="headerActions"
/>
<el-row v-loading="loading" class="the-row">
<IBox v-if="totalData.length === 0" class="empty-box">
<el-empty :description="$t('NoData')" :image-size="200" class="no-data" style="padding: 20px" />
</IBox>
<div class="card-container">
<el-card
v-for="(d, index) in totalData"
:key="index"
:class="{'is-disabled': isDisabled(d)}"
class="the-card"
shadow="hover"
>
<keep-alive>
<slot :index="index" :item="d" :onView="onView">
<Panel :d="d" @click.native="onView(d)" />
</slot>
</keep-alive>
</el-card>
</div>
</el-row>
<Pagination
v-show="pagination && total > paginationSize"
ref="pagination"
class="pagination"
v-bind="$data"
@currentSizeChange="handleCurrentChange"
@sizeChange="handleSizeChange"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { Pagination } from '@/components'
import TableAction from '@/components/Table/ListTable/TableAction'
import IBox from '@/components/Common/IBox/index.vue'
import Panel from './Panel'
const defaultFirstPage = 1
export default {
name: 'CardTable',
components: {
IBox,
Panel,
TableAction,
Pagination
},
props: {
// 定义 table 的配置
columns: {
type: Number,
default: 3
},
tableConfig: {
type: Object,
default: () => ({})
},
headerActions: {
type: Object,
default: () => ({})
},
pagination: {
type: Boolean,
default: true
},
subComponent: {
type: Object,
default: () => null
},
subComponentProps: {
type: Object,
default: () => ({})
}
},
data() {
return {
total: 0,
totalData: [],
page: defaultFirstPage,
extraQuery: {},
paginationSize: 6,
paginationLayout: 'total, sizes, prev, pager, next',
paginationSizes: [6, 18, 27],
loading: true,
axiosConfig: {
raw: 1,
params: {
display: 1,
draw: 1
}
}
}
},
computed: {
...mapGetters(['hasValidLicense']),
tableUrl() {
return this.tableConfig.url || ''
}
},
async mounted() {
try {
await this.getList()
} finally {
this.loading = false
}
},
methods: {
isDisabled(item) {
return item.edition?.value === 'enterprise' && !this.hasValidLicense
},
getIcon(status) {
let iconClass = 'fa-check-circle'
if (status === false) {
iconClass = 'fa-times-circle'
}
return `<i class="fa ${iconClass}" />`
},
getPageQuery(currentPage, pageSize) {
return this.$refs.pagination.getPageQuery(currentPage, pageSize)
},
async getList() {
if (this.tableConfig.totalData) {
this.totalData = this.tableConfig.totalData
this.total = this.totalData.length
return
}
if (!this.tableUrl) {
return
}
const pageQuery = this.getPageQuery(this.page, this.paginationSize)
const query = Object.assign(this.extraQuery, pageQuery)
const queryString = Object.keys(query).map(key => key + '=' + query[key]).join('&')
const url = `${this.tableUrl}?${queryString}`
const resp = await this.$axios.get(url, this.axiosConfig)
const data = resp.data
this.total = data?.count || 0
this.totalData = data?.results || []
},
reloadTable() {
this.getList()
},
search(attrs) {
this.extraQuery = attrs
this.getList()
},
handleSizeChange(val) {
this.page = defaultFirstPage
this.paginationSize = val
this.getList()
},
handleCurrentChange(val) {
this.page = val
this.getList()
},
defaultPerformView(obj) {
const defaultRoute = this.$route.name.replace('List', 'Detail')
const route = this.headerActions.detailRoute || defaultRoute
let detailRoute = { replace: true }
if (typeof route === 'string') {
detailRoute.name = route
detailRoute.params = { id: obj.id }
} else {
detailRoute = route
}
this.$router.push(detailRoute)
},
defaultPerformDelete(obj) {
this.$axios.delete(`${this.tableConfig.url}${obj.id}/`)
},
onView(obj) {
if (this.isDisabled(obj)) {
return
}
const viewFunc = this.tableConfig.onView || this.defaultPerformView
viewFunc(obj)
},
onDelete(obj) {
const msg = `${this.$t('DeleteWarningMsg')} "${obj.name}" ?`
this.$confirm(msg, this.$tc('Info'), {
type: 'warning',
confirmButtonClass: 'el-button--danger',
beforeClose: async(action, instance, done) => {
if (action !== 'confirm') return done()
const deleteFunc = this.tableConfig.onDelete || this.defaultPerformDelete
await deleteFunc(obj)
done()
this.reloadTable()
this.$message.success(this.$tc('DeleteSuccessMsg'))
}
}).catch(() => {
/* 取消*/
})
}
}
}
</script>
<style lang="scss" scoped>
.the-row .empty-box {
display: block;
::v-deep {
.el-empty {
margin: 0 auto;
}
}
}
.the-row {
margin-top: 15px;
max-width: 1600px;
text-align: center;
.card-container {
display: flex;
justify-content: left;
flex-wrap: wrap;
}
.el-col, div {
gap: 20px;
.the-card {
min-width: 330px;
position: relative;
margin-bottom: 20px;
//height: 230px;
width: 380px;
padding: 15px;
::v-deep .el-card__body {
height: 100%;
width: 100%;
padding: 0;
}
&.is-disabled {
opacity: 0.6;
cursor: not-allowed;
}
&:hover {
.closeIcon {
visibility: visible;
}
}
.closeIcon {
float: right;
display: block;
visibility: hidden;
i {
font-size: 20px;
cursor: pointer;
}
}
}
}
}
.pagination {
padding-top: 10px;
border-top: 1px solid #e7eaec;
}
.el-col {
//min-width: 330px; 设置完后remote app 列表会有问题
}
.no-data {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 65vh;
width: 100%;
}
</style>