mirror of
https://github.com/jumpserver/lina.git
synced 2025-04-27 11:10:51 +00:00
290 lines
6.4 KiB
Vue
290 lines
6.4 KiB
Vue
<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>
|