mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-24 05:50:06 +00:00
perf: add loading page
This commit is contained in:
@@ -268,6 +268,7 @@ export default {
|
||||
}
|
||||
|
||||
.drawer__content, .tab-page-content {
|
||||
height: 100%;
|
||||
background: #f3f3f3;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
<template>
|
||||
<DataForm
|
||||
v-if="!loading"
|
||||
ref="dataForm"
|
||||
:fields="totalFields"
|
||||
:form="iForm"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template
|
||||
v-for="(group, i) in groups"
|
||||
:slot="'id:'+group.name"
|
||||
<div v-loading="loading">
|
||||
<DataForm
|
||||
v-if="!loading"
|
||||
ref="dataForm"
|
||||
:fields="totalFields"
|
||||
:form="iForm"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<FormGroupHeader
|
||||
v-if="!groupHidden(group, i)"
|
||||
:key="'group-' + group.name"
|
||||
:group="group"
|
||||
:index="i"
|
||||
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
||||
/>
|
||||
</template>
|
||||
</DataForm>
|
||||
<template
|
||||
v-for="(group, i) in groups"
|
||||
:slot="'id:'+group.name"
|
||||
>
|
||||
<FormGroupHeader
|
||||
v-if="!groupHidden(group, i)"
|
||||
:key="'group-' + group.name"
|
||||
:group="group"
|
||||
:index="i"
|
||||
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
||||
/>
|
||||
</template>
|
||||
</DataForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-loading="loading">
|
||||
<DataTable
|
||||
v-if="!loading"
|
||||
ref="dataTable"
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
<IBox v-if="totalData.length === 0">
|
||||
<el-empty :description="$t('NoData')" :image-size="200" class="no-data" style="padding: 20px" />
|
||||
</IBox>
|
||||
<el-col v-for="(d, index) in totalData" :key="index" :lg="8" :md="12" :sm="24" class="el-col">
|
||||
<div class="card-container">
|
||||
<el-card
|
||||
v-for="(d, index) in totalData"
|
||||
:key="index"
|
||||
:body-style="{ 'text-align': 'center', 'padding': '15px' }"
|
||||
:class="{'is-disabled': isDisabled(d)}"
|
||||
class="my-card"
|
||||
@@ -52,7 +54,7 @@
|
||||
</slot>
|
||||
</keep-alive>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-row>
|
||||
<Pagination
|
||||
v-show="pagination && total > paginationSize"
|
||||
@@ -207,9 +209,7 @@ export default {
|
||||
this.$router.push(detailRoute)
|
||||
},
|
||||
defaultPerformDelete(obj) {
|
||||
this.$axios.delete(
|
||||
`${this.tableConfig.url}${obj.id}/`
|
||||
)
|
||||
this.$axios.delete(`${this.tableConfig.url}${obj.id}/`)
|
||||
},
|
||||
onView(obj) {
|
||||
if (this.isDisabled(obj)) {
|
||||
@@ -246,11 +246,15 @@ export default {
|
||||
text-align: center;
|
||||
|
||||
.el-col, div {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
||||
.my-card {
|
||||
min-width: 330px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
height: 230px;
|
||||
width: 380px;
|
||||
|
||||
::v-deep .el-card__body {
|
||||
height: 100%;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
v-bind="iHeaderActions"
|
||||
@done="handleActionInitialDone"
|
||||
/>
|
||||
<IBox class="table-content">
|
||||
<IBox v-loading="!actionInit" class="table-content">
|
||||
<AutoDataTable
|
||||
v-if="actionInit"
|
||||
ref="dataTable"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span v-if="loading" v-loading="loading" class="loading" />
|
||||
<span v-if="loading" v-loading="loading" :style="{ height: loadingHeight + 'px'}" class="loading" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -9,11 +9,14 @@ export default {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loadingHeight: {
|
||||
type: Number,
|
||||
default: 200
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<template>
|
||||
<AutoDataForm
|
||||
v-if="!loading"
|
||||
ref="form"
|
||||
:form="form"
|
||||
:has-reset="iHasReset"
|
||||
:has-save-continue="iHasSaveContinue"
|
||||
:is-submitting="isSubmitting"
|
||||
:method="method"
|
||||
:url="iUrl"
|
||||
v-bind="$attrs"
|
||||
@afterRemoteMeta="handleAfterRemoteMeta"
|
||||
@submit="handleSubmit"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<div v-loading="loading">
|
||||
<AutoDataForm
|
||||
v-if="!loading"
|
||||
ref="form"
|
||||
:form="form"
|
||||
:has-reset="iHasReset"
|
||||
:has-save-continue="iHasSaveContinue"
|
||||
:is-submitting="isSubmitting"
|
||||
:method="method"
|
||||
:url="iUrl"
|
||||
v-bind="$attrs"
|
||||
@afterRemoteMeta="handleAfterRemoteMeta"
|
||||
@submit="handleSubmit"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import AutoDataForm from '@/components/Form/AutoDataForm'
|
||||
@@ -297,6 +299,7 @@ export default {
|
||||
this.$log.debug('Object init is: ', this.object, this.method)
|
||||
await this.setDrawerMeta()
|
||||
this.setMethod()
|
||||
|
||||
try {
|
||||
const values = await this.getFormValue()
|
||||
this.$log.debug('Final object is: ', values)
|
||||
|
||||
@@ -694,3 +694,7 @@ li.rmenu i.fa {
|
||||
//background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
div.el-loading-parent--relative {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<SmallCard ref="table" class="account-table" v-bind="$data" />
|
||||
<CreateDialog v-if="visible" :visible.sync="visible" v-bind="providerConfig" />
|
||||
<Dialog
|
||||
<Drawer
|
||||
v-if="updateVisible"
|
||||
:destroy-on-close="true"
|
||||
:show-buttons="false"
|
||||
@@ -17,7 +17,7 @@
|
||||
origin="update"
|
||||
@submitSuccess="onSubmitSuccess"
|
||||
/>
|
||||
</Dialog>
|
||||
</Drawer>
|
||||
<Dialog
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
@@ -65,6 +65,7 @@ import CreateDialog from './components/CreateDialog.vue'
|
||||
import SmallCard from '@/components/Table/CardTable/DataCardTable/index.vue'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP } from '@/views/assets/Cloud/const'
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
import AssetPanel from './components/AssetPanel.vue'
|
||||
import AuthPanel from './components/AuthPanel.vue'
|
||||
import { toSafeLocalDateStr } from '@/utils/time'
|
||||
@@ -72,6 +73,7 @@ import { toSafeLocalDateStr } from '@/utils/time'
|
||||
export default {
|
||||
name: 'CloudAccountList',
|
||||
components: {
|
||||
Drawer,
|
||||
AuthPanel,
|
||||
AssetPanel,
|
||||
Dialog,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<Dialog
|
||||
<Drawer
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:has-footer="false"
|
||||
:show-buttons="false"
|
||||
:show-close="false"
|
||||
:title="$tc('CloudSyncConfig')"
|
||||
@@ -12,7 +13,7 @@
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-row :gutter="5" style="padding: 10px">
|
||||
<el-col :span="4" class="left-step-zone">
|
||||
<el-col :span="6" class="left-step-zone">
|
||||
<el-steps :active="active" direction="vertical">
|
||||
<el-step :description="firstStepDesc" />
|
||||
<el-step :description="$tc('Authentication')" />
|
||||
@@ -20,7 +21,7 @@
|
||||
<el-step :description="$tc('Result')" />
|
||||
</el-steps>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-col :span="18">
|
||||
<component
|
||||
:is="activeMenu"
|
||||
:active.sync="active"
|
||||
@@ -32,11 +33,11 @@
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</Drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import Drawer from '@/components/Drawer'
|
||||
import ProviderPanel from '@/views/assets/Cloud/Account/components/ProviderPanel'
|
||||
import AuthPanel from '@/views/assets/Cloud/Account/components/AuthPanel'
|
||||
import AssetPanel from '@/views/assets/Cloud/Account/components/AssetPanel'
|
||||
@@ -46,7 +47,7 @@ import { ACCOUNT_PROVIDER_ATTRS_MAP } from '@/views/assets/Cloud/const'
|
||||
export default {
|
||||
name: 'CreateDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
Drawer,
|
||||
AuthPanel,
|
||||
AssetPanel,
|
||||
ResultPanel,
|
||||
@@ -111,6 +112,10 @@ export default {
|
||||
.left-step-zone {
|
||||
border-right: solid 1px var(--color-border);
|
||||
height: 350px;
|
||||
|
||||
.el-steps {
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-step {
|
||||
|
||||
@@ -18,16 +18,17 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-button size="small" style="float: right;" @click="handleCancel">{{ $tc('Cancel') }}</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
style="float: right; margin-right: 10px;"
|
||||
type="primary"
|
||||
@click="handleNext"
|
||||
>
|
||||
{{ $tc('Next') }}
|
||||
</el-button>
|
||||
<el-row class="buttons">
|
||||
<el-col>
|
||||
<el-button size="small" @click="handleCancel">{{ $tc('Cancel') }}</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleNext"
|
||||
>
|
||||
{{ $tc('Next') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
@@ -77,6 +78,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.buttons {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.cloud-select-wrap {
|
||||
height: 300px;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CardTable from './components/CardTable'
|
||||
import CardTable from '@/components/Table/CardTable'
|
||||
import UploadDialog from './UploadDialog'
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
<template>
|
||||
<div class="el-card-table">
|
||||
<TableAction
|
||||
:reload-table="reloadTable"
|
||||
:search-table="search"
|
||||
:table-url="tableUrl"
|
||||
v-bind="headerActions"
|
||||
/>
|
||||
<div style="padding-top: 15px">
|
||||
<el-row :gutter="20">
|
||||
<IBox v-if="totalData.length === 0" class="empty-box">
|
||||
<el-empty />
|
||||
</IBox>
|
||||
<el-col v-for="(d, index) in totalData" :key="index" :span="6">
|
||||
<el-card
|
||||
:body-style="{ 'text-align': 'center', 'padding': '20px' }"
|
||||
class="my-card"
|
||||
shadow="hover"
|
||||
@click.native="onView(d)"
|
||||
>
|
||||
<span v-if="d.edition === 'enterprise'" class="enterprise">
|
||||
{{ $t('Enterprise') }}
|
||||
</span>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<img :src="d.icon" class="image">
|
||||
</el-col>
|
||||
<el-col :span="16" style="text-align: left; padding: 5px 0">
|
||||
<div class="one-line">
|
||||
<b>{{ d.display_name }}</b>
|
||||
<el-tag size="mini" style="margin-left: 5px">
|
||||
{{ d.version }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-divider class="my-divider" />
|
||||
<div class="comment">
|
||||
{{ d.comment }}
|
||||
</div>
|
||||
<el-tag v-for="tag of d.tags" :key="tag" size="mini">
|
||||
{{ capitalize(tag) }}
|
||||
</el-tag>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<Pagination
|
||||
ref="pagination"
|
||||
v-bind="$data"
|
||||
@currentSizeChange="handleCurrentChange"
|
||||
@sizeChange="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableAction from '@/components/Table/ListTable/TableAction'
|
||||
import { Pagination } from '@/components'
|
||||
import { toSafeLocalDateStr } from '@/utils/time'
|
||||
import IBox from '@/components/IBox/index.vue'
|
||||
|
||||
const defaultFirstPage = 1
|
||||
|
||||
export default {
|
||||
name: 'CardTable',
|
||||
components: {
|
||||
IBox,
|
||||
TableAction,
|
||||
Pagination
|
||||
},
|
||||
props: {
|
||||
// 定义 table 的配置
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
headerActions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
total: 0,
|
||||
totalData: [],
|
||||
page: defaultFirstPage,
|
||||
extraQuery: {},
|
||||
paginationSize: 8,
|
||||
paginationLayout: 'total, sizes, prev, pager, next',
|
||||
paginationSizes: [8, 12, 20, 36, 52, 120],
|
||||
axiosConfig: {
|
||||
raw: 1,
|
||||
params: {
|
||||
display: 1,
|
||||
draw: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableUrl() {
|
||||
return this.tableConfig.url || ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
getIcon(status) {
|
||||
let iconClass = 'fa-check-circle'
|
||||
if (status === false) {
|
||||
iconClass = 'fa-times-circle'
|
||||
}
|
||||
return `<i class="fa ${iconClass}" />`
|
||||
},
|
||||
convertData(data) {
|
||||
return toSafeLocalDateStr(data)
|
||||
},
|
||||
getPageQuery(currentPage, pageSize) {
|
||||
return this.$refs.pagination.getPageQuery(currentPage, pageSize)
|
||||
},
|
||||
getList() {
|
||||
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}`
|
||||
|
||||
this.$axios
|
||||
.get(url, this.axiosConfig)
|
||||
.then(({ data: resp }) => {
|
||||
this.total = resp?.count || 0
|
||||
this.totalData = resp?.results || []
|
||||
})
|
||||
.catch(err => {
|
||||
this.$log.error('Error occur: ', err)
|
||||
this.total = 0
|
||||
})
|
||||
},
|
||||
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) {
|
||||
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>
|
||||
.my-card {
|
||||
margin: 0 0 20px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.my-divider {
|
||||
margin: 10px 0
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: block;
|
||||
margin: 50% auto;
|
||||
}
|
||||
|
||||
.one-line {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.comment {
|
||||
font-size: 12px;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
float: right;
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
|
||||
i {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.my-card:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.my-card:hover .closeIcon {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.empty-box ::v-deep .el-empty {
|
||||
max-width: 200px;
|
||||
margin: 0 auto;
|
||||
|
||||
.el-empty__description {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.enterprise {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: -1px;
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
padding: 3px 8px 4px 9px;
|
||||
font-size: 13px;
|
||||
border-radius: 3px 3px 3px 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,21 +1,23 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
v-if="!loading"
|
||||
class="user-create-update"
|
||||
v-bind="$data"
|
||||
@getObjectDone="afterGetUser"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<div v-loading="loading">
|
||||
<GenericCreateUpdatePage
|
||||
v-if="!loading"
|
||||
class="user-create-update"
|
||||
v-bind="$data"
|
||||
@getObjectDone="afterGetUser"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { Select2 } from '@/components'
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { PhoneInput, UserPassword } from '@/components/Form/FormFields'
|
||||
import rules from '@/components/Form/DataForm/rules'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { Select2 } from '@/components'
|
||||
import store from '@/store'
|
||||
import { MFASystemSetting, MFALevel } from '../const'
|
||||
import { MFALevel, MFASystemSetting } from '../const'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
Reference in New Issue
Block a user