mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-25 14:34:46 +00:00
perf: 修改远程应用的列表界面
This commit is contained in:
115
src/components/Pagination/index.vue
Normal file
115
src/components/Pagination/index.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="el-page">
|
||||
<el-pagination
|
||||
v-if="hasPagination"
|
||||
:current-page="page"
|
||||
:page-sizes="paginationSizes"
|
||||
:page-size="size"
|
||||
:total="total"
|
||||
:background="paginationBackground"
|
||||
:layout="paginationLayout"
|
||||
v-bind="extraPaginationAttrs"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const defaultFirstPage = 1
|
||||
|
||||
export default {
|
||||
name: 'Pagination',
|
||||
components: {},
|
||||
props: {
|
||||
hasPagination: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
firstPage: {
|
||||
type: Number,
|
||||
default: defaultFirstPage
|
||||
},
|
||||
pageSizeKey: {
|
||||
type: String,
|
||||
default: 'limit'
|
||||
},
|
||||
pageKey: {
|
||||
type: String,
|
||||
default: 'offset'
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
noPaginationSize: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
paginationSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
paginationSizes: {
|
||||
type: Array,
|
||||
default: () => [10, 20, 30, 40, 50, 100]
|
||||
},
|
||||
paginationBackground: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
paginationLayout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
extraPaginationAttrs: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
transformQuery: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
size: this.paginationSize || this.paginationSizes[0]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(val) {
|
||||
this.$emit('sizeChange', val)
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.$emit('currentSizeChange', val)
|
||||
},
|
||||
getPageQuery(currentPage, pageSize) {
|
||||
// 构造query对象
|
||||
let query = {}
|
||||
query[this.pageSizeKey] = this.hasPagination
|
||||
? pageSize
|
||||
: this.noPaginationSize
|
||||
|
||||
const offset = (currentPage - 1) * pageSize
|
||||
query[this.pageKey] = offset
|
||||
if (this.transformQuery) {
|
||||
query = this.transformQuery(query)
|
||||
}
|
||||
return query
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
>>> .el-pagination {
|
||||
text-align: right;
|
||||
}
|
||||
>>> .el-pagination__total {
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
@@ -28,3 +28,4 @@ export { default as AssetRelationCard } from './AssetRelationCard'
|
||||
export { default as UserConfirmDialog } from './UserConfirmDialog'
|
||||
export { default as Announcement } from './Announcement'
|
||||
export { default as CronTab } from './CronTab'
|
||||
export { default as Pagination } from './Pagination'
|
||||
|
||||
@@ -802,6 +802,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"AppletDetail": "Remote apps",
|
||||
"CreateEndpoint": "Create endpoint",
|
||||
"UpdateEndpoint": "Update endpoint",
|
||||
"CreateEndpointRule": "Create endpoint rule",
|
||||
|
||||
@@ -806,6 +806,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"AppletDetail": "遠隔応用です",
|
||||
"AssignedTicketList": "割り当て済みワークオーダー",
|
||||
"CreateEndpoint": "エンドポイントを作成する",
|
||||
"UpdateEndpoint": "エンドポイントを更新",
|
||||
|
||||
@@ -995,6 +995,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"AppletDetail": "远程应用",
|
||||
"AppletHostDetail": "远程应用发布机详情",
|
||||
"AppletHostCreate": "添加远程应用发布机",
|
||||
"AppletHostUpdate": "更新远程应用发布机",
|
||||
|
||||
@@ -3,22 +3,16 @@
|
||||
<el-col :md="14" :sm="24">
|
||||
<AutoDetailCard :url="url" :fields="detailFields" :object="object" />
|
||||
</el-col>
|
||||
<el-col :md="10" :sm="24">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDetailCard from '@/components/DetailCard/auto'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AutoDetailCard,
|
||||
QuickActions
|
||||
AutoDetailCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -27,86 +21,26 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.IsActive'),
|
||||
type: 'switch',
|
||||
attrs: {
|
||||
label: this.$t('common.Test'),
|
||||
model: this.object.is_active,
|
||||
disabled: !vm.$hasPerm('assets.change_asset')
|
||||
},
|
||||
callbacks: {
|
||||
change: function(val) {
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/assets/${this.object.id}/`,
|
||||
{ is_active: val }
|
||||
).then(res => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$tc('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.RefreshHardware'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Refresh'),
|
||||
disabled: !vm.$hasPerm('assets.refresh_assethardwareinfo')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/assets/${this.object.id}/tasks/`,
|
||||
{ action: 'refresh' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Test'),
|
||||
disabled: !vm.$hasPerm('assets.test_assetconnectivity')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/assets/${this.object.id}/tasks/`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
],
|
||||
url: `/api/v1/terminal/applets/${this.object.id}`,
|
||||
detailFields: [
|
||||
'name', 'ip',
|
||||
'name', 'author', 'display_name',
|
||||
{
|
||||
key: this.$t('assets.Protocols'),
|
||||
value: this.object.protocols.map(i => i.name).join(',')
|
||||
formatter: () => {
|
||||
const data = this.object.protocols.map(p => <el-tag size='mini'>{p} </el-tag>)
|
||||
return <span> {data} </span>
|
||||
}
|
||||
},
|
||||
'public_ip', 'admin_user_display',
|
||||
{
|
||||
key: this.$t('assets.Domain'),
|
||||
value: this.object.domain?.name || ''
|
||||
key: this.$t('assets.Label'),
|
||||
value: this.object.tags.join(',')
|
||||
},
|
||||
'vendor', 'model', 'cpu_model', 'memory', 'disk_info',
|
||||
{
|
||||
key: this.$t('assets.Platform'),
|
||||
value: this.object.platform?.name || ''
|
||||
key: this.$t('assets.Type'),
|
||||
value: this.object.type.label
|
||||
},
|
||||
'os_arch', 'is_active', 'sn', 'number', 'date_created',
|
||||
'created_by', 'comment'
|
||||
'date_created', 'date_updated', 'comment'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<template>
|
||||
<GenericDetailPage
|
||||
:object.sync="asset"
|
||||
:object.sync="applet"
|
||||
:active-menu.sync="config.activeMenu"
|
||||
v-bind="config"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="asset" />
|
||||
<component :is="config.activeMenu" :object="applet" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import Account from '@/views/assets/Asset/AssetDetail/Account'
|
||||
import Detail from './Detail'
|
||||
|
||||
export default {
|
||||
@@ -21,46 +20,33 @@ export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
Detail,
|
||||
Account
|
||||
Detail
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
asset: {},
|
||||
applet: {},
|
||||
config: {
|
||||
url: '/api/v1/terminal/applet-hosts/',
|
||||
url: '/api/v1/terminal/applets',
|
||||
activeMenu: 'Detail',
|
||||
submenu: [
|
||||
{
|
||||
'title': this.$t('common.Detail'),
|
||||
'name': 'Detail'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.Account'),
|
||||
name: 'Account'
|
||||
},
|
||||
{
|
||||
'title': this.$t('terminal.Applets'),
|
||||
'name': 'Applets'
|
||||
}
|
||||
],
|
||||
hasRightSide: true,
|
||||
actions: {
|
||||
updateCallback: () => {
|
||||
const category = this.asset.category.value || 'host'
|
||||
const routerName = _.capitalize(category) + 'Update'
|
||||
this.$router.push({
|
||||
name: routerName,
|
||||
params: { id: this.$route.params.id },
|
||||
query: { platform: this.asset.platform.id }
|
||||
})
|
||||
}
|
||||
}
|
||||
hasUpdate: false,
|
||||
canDelete: () => {
|
||||
return this.$hasPerm('terminal.delete_applet')
|
||||
},
|
||||
deleteSuccessRoute: 'Applets'
|
||||
},
|
||||
titlePrefix: this.$tc('route.AppletDetail')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" v-bind="$data" />
|
||||
<CardTable ref="CardTable" v-bind="$data" />
|
||||
<UploadDialog :visible.sync="uploadDialogVisible" @upload-event="handleUpload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ListTable } from '@/components'
|
||||
import CardTable from './components/CardTable'
|
||||
import UploadDialog from './UploadDialog'
|
||||
export default {
|
||||
name: 'Applets',
|
||||
components: {
|
||||
ListTable,
|
||||
CardTable,
|
||||
UploadDialog
|
||||
},
|
||||
data() {
|
||||
@@ -19,55 +19,18 @@ export default {
|
||||
uploadDialogVisible: false,
|
||||
tableConfig: {
|
||||
url: '/api/v1/terminal/applets/',
|
||||
columnsShow: {
|
||||
min: ['icon', 'name', 'version', 'author', 'protocols', 'actions'],
|
||||
default: [
|
||||
'icon', 'name', 'version', 'author', 'protocols',
|
||||
'type', 'comment', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
icon: {
|
||||
align: 'center',
|
||||
width: '60px',
|
||||
formatter: (row) => {
|
||||
return <img src={row.icon} width='30' height='30' alt='icon'></img>
|
||||
}
|
||||
},
|
||||
name: {
|
||||
formatter: function(row) {
|
||||
return <span>{row.display_name}</span>
|
||||
},
|
||||
formatterArgs: {
|
||||
getTitle: ({ row }) => row['display_name'],
|
||||
getIcon: ({ row }) => row['icon']
|
||||
}
|
||||
},
|
||||
version: {
|
||||
width: '80px'
|
||||
},
|
||||
type: {
|
||||
width: '80px'
|
||||
},
|
||||
protocols: {
|
||||
formatter: (row) => {
|
||||
return row.protocols.map(tag => <el-tag size='mini'>{tag}</el-tag>)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
hasUpdate: false,
|
||||
hasClone: false
|
||||
}
|
||||
}
|
||||
}
|
||||
deletePerm: 'terminal.delete_applet'
|
||||
},
|
||||
headerActions: {
|
||||
onCreate: () => {
|
||||
this.uploadDialogVisible = true
|
||||
},
|
||||
detailRoute: 'AppletDetail',
|
||||
hasExport: false,
|
||||
hasImport: false
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false,
|
||||
hasColumnSetting: false
|
||||
// moreCreates: {
|
||||
// callback: (option) => {
|
||||
// this.uploadDialogVisible = true
|
||||
@@ -88,7 +51,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleUpload(res) {
|
||||
this.$refs.ListTable.reloadTable()
|
||||
this.$refs.CardTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
217
src/views/settings/Applet/Applet/components/CardTable.vue
Normal file
217
src/views/settings/Applet/Applet/components/CardTable.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="el-card-table">
|
||||
<TableAction
|
||||
:table-url="tableUrl"
|
||||
:search-table="search"
|
||||
:reload-table="reloadTable"
|
||||
v-bind="headerActions"
|
||||
/>
|
||||
<div style="padding-top: 15px">
|
||||
<el-row :gutter="40">
|
||||
<el-col v-for="(d, index) in totalData" :key="index" :span="4">
|
||||
<el-card
|
||||
shadow="hover"
|
||||
:body-style="{ 'text-align': 'center', 'padding': '10px' }"
|
||||
class="my-card"
|
||||
@click.native="onView(d)"
|
||||
>
|
||||
<span class="closeIcon">
|
||||
<i class="el-icon-close" @click.stop="onDelete(d)" />
|
||||
</span>
|
||||
<!-- <div style="padding-top: 15px">-->
|
||||
<!-- <el-button v-if="$hasPerm(tableConfig.deletePerm)" type="danger" size="mini" @click="onDelete(d)">{{ $tc('common.Delete') }}</el-button>-->
|
||||
<!-- </div>-->
|
||||
<div>
|
||||
<img :src="d.icon" class="image">
|
||||
</div>
|
||||
<div>{{ d.display_name }}</div>
|
||||
<div style="margin: 10px 0" />
|
||||
<el-tag size="mini">{{ d.author }}</el-tag>
|
||||
<el-divider class="my-divider" />
|
||||
<el-tooltip placement="top">
|
||||
<div slot="content">{{ d.comment_i18n }}</div>
|
||||
<div class="line-limit">{{ d.comment_i18n }}</div>
|
||||
</el-tooltip>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<Pagination
|
||||
ref="pagination"
|
||||
v-bind="$data"
|
||||
@sizeChange="handleSizeChange"
|
||||
@currentSizeChange="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableAction from '@/components/ListTable/TableAction'
|
||||
import { Pagination } from '@/components'
|
||||
const defaultFirstPage = 1
|
||||
|
||||
export default {
|
||||
name: 'CardTable',
|
||||
components: {
|
||||
TableAction,
|
||||
Pagination
|
||||
},
|
||||
props: {
|
||||
// 定义 table 的配置
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
headerActions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
total: 0,
|
||||
totalData: [],
|
||||
page: defaultFirstPage,
|
||||
extraQuery: {},
|
||||
paginationSize: 12,
|
||||
paginationLayout: 'total, sizes, prev, pager, next',
|
||||
paginationSizes: [12, 24, 36, 48, 60, 120],
|
||||
axiosConfig: {
|
||||
raw: 1,
|
||||
params: {
|
||||
display: 1,
|
||||
draw: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableUrl() {
|
||||
return this.tableConfig.url || ''
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
mounted() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
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
|
||||
}
|
||||
console.log(detailRoute)
|
||||
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('common.deleteWarningMsg')} "${obj.name}" ?`
|
||||
this.$confirm(msg, this.$t('common.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('common.deleteSuccessMsg'))
|
||||
}
|
||||
}).catch(() => {
|
||||
/* 取消*/
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-card {
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.my-divider {
|
||||
margin: 10px 0
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.line-limit {
|
||||
line-height: 14px;
|
||||
height: 34px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
text-align: right;
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
i {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.my-card:hover .closeIcon {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user