mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-13 19:35:24 +00:00
Compare commits
28 Commits
pr@pam@sty
...
pam_new_dr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dfca604c2 | ||
|
|
b4abcd4c90 | ||
|
|
d62c87b858 | ||
|
|
743187b4b3 | ||
|
|
5c1f24af6a | ||
|
|
9477bfa2c1 | ||
|
|
1ba58f476f | ||
|
|
02600d7a1b | ||
|
|
2126c92e07 | ||
|
|
af774a8835 | ||
|
|
b93fad1f91 | ||
|
|
ef43be0cb7 | ||
|
|
8b6eea0267 | ||
|
|
d0da22738f | ||
|
|
18eda16851 | ||
|
|
610e9b9efa | ||
|
|
a0c7d60719 | ||
|
|
60ba0d8f02 | ||
|
|
b9afb05f1b | ||
|
|
fcf9ea2b79 | ||
|
|
430b1117c9 | ||
|
|
a65023c8f7 | ||
|
|
d6de85ffdd | ||
|
|
5f11d8b54f | ||
|
|
c7ce602d4c | ||
|
|
d0988da277 | ||
|
|
7f13ef35a7 | ||
|
|
44348de4ab |
@@ -21,8 +21,8 @@
|
||||
<AccountCreateUpdate
|
||||
v-if="showAddDialog"
|
||||
:account="account"
|
||||
:origin="origin"
|
||||
:asset="iAsset"
|
||||
:origin="origin"
|
||||
:title="accountCreateUpdateTitle"
|
||||
:visible.sync="showAddDialog"
|
||||
@add="addAccountSuccess"
|
||||
@@ -31,9 +31,9 @@
|
||||
<AccountCreateUpdate
|
||||
v-if="showAddTemplateDialog"
|
||||
:account="account"
|
||||
:origin="origin"
|
||||
:add-template="true"
|
||||
:asset="iAsset"
|
||||
:origin="origin"
|
||||
:title="accountCreateByTemplateTitle"
|
||||
:visible.sync="showAddTemplateDialog"
|
||||
@add="addAccountSuccess"
|
||||
@@ -200,13 +200,13 @@ export default {
|
||||
{
|
||||
label: '僵尸账号',
|
||||
filter: {
|
||||
risk: 'zombie'
|
||||
risk: 'long_time_no_login'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '幽灵账号',
|
||||
filter: {
|
||||
risk: 'ghost'
|
||||
risk: 'new_found'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -299,7 +299,7 @@ export default {
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '120px',
|
||||
formatter: function(row) {
|
||||
formatter: (row) => {
|
||||
const to = {
|
||||
name: 'AssetAccountDetail',
|
||||
params: { id: row.id }
|
||||
@@ -333,7 +333,7 @@ export default {
|
||||
}
|
||||
},
|
||||
asset: {
|
||||
formatter: function(row) {
|
||||
formatter: (row) => {
|
||||
const to = {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset.id }
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
</div>
|
||||
<slot>
|
||||
<h3 class="no-margins ">
|
||||
<span class="num" @click="handleClick">
|
||||
{{ iCount }}
|
||||
<span v-async="iCount" class="num" @click="handleClick">
|
||||
-
|
||||
</span>
|
||||
</h3>
|
||||
</slot>
|
||||
@@ -27,7 +27,7 @@ export default {
|
||||
default: () => ({})
|
||||
},
|
||||
count: {
|
||||
type: [Number, String],
|
||||
type: [Number, String, Promise],
|
||||
default: 0
|
||||
},
|
||||
route: {
|
||||
@@ -49,7 +49,8 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
iCount() {
|
||||
return this.body.count || this.count
|
||||
const count = this.body.count || this.count
|
||||
return count
|
||||
},
|
||||
iRoute() {
|
||||
return this.body.route || this.route
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
trigger="click"
|
||||
@command="handleDropdownCallback"
|
||||
>
|
||||
<el-button :size="size" class="more-action" v-bind="cleanButtonAction(action)">
|
||||
<span v-if="action.icon && !action.icon.startsWith('el-')" class="pre-icon">
|
||||
<i v-if="action.icon.startsWith('fa')" :class="'fa fa-fw ' + action.icon" />
|
||||
<svg-icon v-else :icon-class="action.icon" />
|
||||
<el-button :class="action.name" :size="size" class="more-action" v-bind="{...cleanButtonAction(action), icon: ''}">
|
||||
<span v-if="action.icon" class="pre-icon">
|
||||
<Icon :icon="action.icon" />
|
||||
</span>
|
||||
<span v-if="action.title">
|
||||
{{ action.title }}<i class="el-icon-arrow-down el-icon--right" />
|
||||
@@ -44,8 +43,7 @@
|
||||
v-bind="{...option, icon: ''}"
|
||||
>
|
||||
<span v-if="option.icon" class="pre-icon">
|
||||
<i v-if="option.icon.startsWith('fa')" :class="'fa fa-fw ' + option.icon" />
|
||||
<svg-icon v-else :icon-class="option.icon" />
|
||||
<Icon :icon="option.icon" />
|
||||
</span>
|
||||
{{ option.title }}
|
||||
</el-dropdown-item>
|
||||
@@ -57,16 +55,16 @@
|
||||
<el-button
|
||||
v-else
|
||||
:key="action.name"
|
||||
:class="action.name"
|
||||
:size="size"
|
||||
class="action-item"
|
||||
v-bind="{...cleanButtonAction(action), icon: action.icon && action.icon.startsWith('el-') ? action.icon : ''}"
|
||||
v-bind="{...cleanButtonAction(action), icon: ''}"
|
||||
@click="handleClick(action)"
|
||||
>
|
||||
<el-tooltip :content="action.tip" :disabled="!action.tip" :open-delay="500" placement="top">
|
||||
<span>
|
||||
<span v-if="action.icon && !action.icon.startsWith('el-')" style="vertical-align: initial">
|
||||
<i v-if="action.icon.startsWith('fa')" :class="'fa ' + action.icon" />
|
||||
<svg-icon v-else :icon-class="action.icon" />
|
||||
<span v-if="action.icon" style="vertical-align: initial">
|
||||
<Icon :icon="action.icon" />
|
||||
</span>
|
||||
{{ action.title }}
|
||||
</span>
|
||||
@@ -78,9 +76,13 @@
|
||||
|
||||
<script>
|
||||
import { toSentenceCase } from '@/utils/common'
|
||||
import Icon from '@/components/Widgets/Icon/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'DataActions',
|
||||
components: {
|
||||
Icon
|
||||
},
|
||||
props: {
|
||||
grouped: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:before-close="handleClose"
|
||||
:visible.sync="dialog"
|
||||
:wrapper-closable="true"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<div class="drawer__content">
|
||||
<slot name="default" />
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
<el-row :gutter="20" class="panel-content">
|
||||
<el-col :span="6" class="panel-image">
|
||||
<el-image :src="imageUrl" fit="contain" />
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="panel-item">
|
||||
<div class="item-label">{{ title }}</div>
|
||||
<div class="text-info" :title="content">{{ content }}</div>
|
||||
<span class="item-label">{{ title }} </span>
|
||||
<span :title="content" class="text-info">{{ content || '' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
44
src/components/Table/CardTable/DataCardTable/index.vue
Normal file
44
src/components/Table/CardTable/DataCardTable/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<CardTable
|
||||
ref="table"
|
||||
:columns="3"
|
||||
:table-config="tableConfig"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template v-slot:default="slotProps">
|
||||
<CardPanel :object="slotProps.item" :table-config="tableConfig" v-bind="subComponentProps" />
|
||||
</template>
|
||||
</CardTable>
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import CardTable from '@/components/Table/CardTable/index.vue'
|
||||
import CardPanel from './CardPanel.vue'
|
||||
|
||||
export default {
|
||||
name: 'SmallCard',
|
||||
components: {
|
||||
CardPanel,
|
||||
CardTable
|
||||
},
|
||||
props: {
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
subComponentProps: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reloadTable() {
|
||||
this.$refs.table.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<CardTable
|
||||
ref="table"
|
||||
:columns="3"
|
||||
:sub-component="subComponent"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import CardTable from '@/components/Table/CardTable'
|
||||
import CardPanel from './components/CardPanel.vue'
|
||||
|
||||
export default {
|
||||
name: 'SmallCard',
|
||||
components: {
|
||||
CardTable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
subComponent: CardPanel
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reloadTable() {
|
||||
this.$refs.table.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -10,7 +10,7 @@
|
||||
<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="24 / columns" :md="12" :sm="24" style="min-width: 335px;">
|
||||
<el-col v-for="(d, index) in totalData" :key="index" :lg="8" :md="12" :sm="24" class="el-col">
|
||||
<el-card
|
||||
:body-style="{ 'text-align': 'center', 'padding': '15px' }"
|
||||
:class="{'is-disabled': isDisabled(d)}"
|
||||
@@ -19,15 +19,7 @@
|
||||
@click.native="onView(d)"
|
||||
>
|
||||
<keep-alive>
|
||||
<component
|
||||
:is="subComponent"
|
||||
v-if="subComponent"
|
||||
:object="d"
|
||||
:table-config="tableConfig"
|
||||
v-bind="subComponentProps"
|
||||
@refresh="getList"
|
||||
/>
|
||||
<slot v-else :index="index" :item="d">
|
||||
<slot :index="index" :item="d">
|
||||
<span v-if="d.edition === 'enterprise'" class="enterprise">
|
||||
{{ $t('Enterprise') }}
|
||||
</span>
|
||||
@@ -386,6 +378,10 @@ export default {
|
||||
border-top: 1px solid #e7eaec;
|
||||
}
|
||||
|
||||
.el-col {
|
||||
min-width: 330px;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
},
|
||||
moreActionsTitle: {
|
||||
type: String,
|
||||
default: null
|
||||
default: ''
|
||||
},
|
||||
moreCreates: {
|
||||
type: Object,
|
||||
|
||||
@@ -1,47 +1,53 @@
|
||||
<template>
|
||||
<div v-if="filters || summary" :class="isExpand ? 'expand': 'shrink' " class="quick-filter">
|
||||
<div v-show="isExpand" class="quick-filter-wrap">
|
||||
<div v-if="filters" class="quick-filter-zone">
|
||||
<div v-for="category in iFilters" :key="category.label" class="item-zone">
|
||||
<div>
|
||||
<h5>{{ category.label }}</h5>
|
||||
<div class="filter-options">
|
||||
<span
|
||||
v-for="option in category.options"
|
||||
:key="option.label"
|
||||
:class="option.active ? 'active' : ''"
|
||||
class="item"
|
||||
@click="handleFilterClick(option)"
|
||||
>
|
||||
{{ option.label }}
|
||||
<i class="el-icon-circle-check" />
|
||||
</span>
|
||||
<div v-show="isExpand">
|
||||
<div v-if="filters || summary" :class="isExpand ? 'expand': 'shrink' " class="quick-filter">
|
||||
<div v-show="isExpand" class="quick-filter-wrap">
|
||||
<div v-if="filters" class="quick-filter-zone">
|
||||
<div v-for="category in iFilters" :key="category.label" class="item-zone">
|
||||
<div>
|
||||
<h5>{{ category.label }}</h5>
|
||||
<div class="filter-options">
|
||||
<span
|
||||
v-for="option in category.options"
|
||||
:key="option.label"
|
||||
:class="option.active ? 'active' : ''"
|
||||
class="item"
|
||||
@click="handleFilterClick(option)"
|
||||
>
|
||||
{{ option.label }}
|
||||
<span v-if="option.hasCount">
|
||||
(<span v-async="getCount(option)">-</span>)
|
||||
</span>
|
||||
<!-- <i class="el-icon-circle-check" />-->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="summary" class="summary-zone">
|
||||
<span v-for="item of iSummary" :key="item.title" class="summary-block">
|
||||
<SummaryCard
|
||||
:class="item.active ? 'active' : ''"
|
||||
:count="getCount(item)"
|
||||
:title="item.title"
|
||||
@click="handleFilterClick(item)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="summary" class="summary-zone">
|
||||
<span v-for="item of iSummary" :key="item.title" class="summary-block">
|
||||
<SummaryCard
|
||||
:class="item.active ? 'active' : ''"
|
||||
:count="item.count"
|
||||
:title="item.title"
|
||||
@click="handleFilterClick(item)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="expand-bar-wrap">
|
||||
<div class="expand-bar" @click="toggle">
|
||||
<i :class="isExpand ? 'expand': 'shrink' " class="fa fa-angle-double-up" />
|
||||
<span v-show="!isExpand"> 展开过滤器 </span>
|
||||
<div class="expand-bar-wrap">
|
||||
<div class="expand-bar" @click="toggle">
|
||||
<i :class="isExpand ? 'expand': 'shrink' " class="fa fa-angle-double-up" />
|
||||
<span v-show="!isExpand"> 展开过滤器 </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SummaryCard from '@/components/Cards/SummaryCard'
|
||||
import SummaryCard from '@/components/Cards/SummaryCard/index.vue'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'QuickFilter',
|
||||
@@ -58,26 +64,49 @@ export default {
|
||||
expand: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
tableUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpand: this.expand,
|
||||
iFilters: this.cleanFilters(),
|
||||
iSummary: this.cleanSummary(),
|
||||
filtered: {},
|
||||
activeFilters: []
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
isExpand(val) {
|
||||
this.$emit('expand', val)
|
||||
computed: {
|
||||
isExpand: {
|
||||
set(val) {
|
||||
this.$emit('update:expand', val)
|
||||
},
|
||||
get() {
|
||||
return this.expand
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
async getCount(item) {
|
||||
if (item.count) {
|
||||
return item.count
|
||||
}
|
||||
if (!item.filter) {
|
||||
return '-'
|
||||
}
|
||||
let url = this.tableUrl
|
||||
for (const [k, v] of Object.entries({ ...item.filter, limit: 1 })) {
|
||||
url = setUrlParam(url, k, v)
|
||||
}
|
||||
const res = await this.$axios.get(url, { raw: 1 })
|
||||
item.count = res.data.count
|
||||
console.log('............get count: ', item.count)
|
||||
return item.count
|
||||
},
|
||||
cleanSummary() {
|
||||
if (!this.summary) {
|
||||
return []
|
||||
@@ -98,11 +98,23 @@ export default {
|
||||
canBulkUpdate: {
|
||||
type: [Boolean, Function, String],
|
||||
default: false
|
||||
},
|
||||
hasQuickFilter: defaultTrue,
|
||||
quickFilterExpand: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultRightSideActions: [
|
||||
{
|
||||
name: 'actionFilter',
|
||||
icon: 'filter',
|
||||
tip: this.$t('Filter'),
|
||||
has: this.hasQuickFilter,
|
||||
callback: this.handleFilterClick.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'actionSetting',
|
||||
icon: 'system-setting',
|
||||
@@ -159,6 +171,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFilterClick() {
|
||||
this.$emit('update:quick-filter-expand', !this.quickFilterExpand)
|
||||
},
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
},
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<QuickFilter :filters="quickFilters" :summary="quickSummary" @filter="filter" />
|
||||
<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"
|
||||
@@ -32,7 +41,7 @@ import IBox from '../../IBox/index.vue'
|
||||
import TableAction from './TableAction/index.vue'
|
||||
import Emitter from '@/mixins/emitter'
|
||||
import AutoDataTable from '../AutoDataTable/index.vue'
|
||||
import QuickFilter from './QuickFilter.vue'
|
||||
import QuickFilter from './TableAction/QuickFilter.vue'
|
||||
import { getDayEnd, getDaysAgo } from '@/utils/time'
|
||||
|
||||
export default {
|
||||
@@ -90,13 +99,18 @@ export default {
|
||||
isDeactivated: false,
|
||||
extraQuery: extraQuery,
|
||||
actionInit: this.headerActions.has === false,
|
||||
initQuery: {}
|
||||
initQuery: {},
|
||||
filterExpand: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot']),
|
||||
iHasQuickFilter() {
|
||||
const has = this.quickFilters && this.quickFilters.length > 0
|
||||
return !!has
|
||||
},
|
||||
dataTable() {
|
||||
return this.$refs.dataTable.$refs.dataTable
|
||||
return this.$refs.dataTable?.$refs.dataTable
|
||||
},
|
||||
iHeaderActions() {
|
||||
// 如果路由中锁定了 root 组织,就不在检查 root 组织下是否可以创建等
|
||||
@@ -219,6 +233,9 @@ export default {
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleFilterExpandChanged(expand) {
|
||||
this.filterExpand = expand
|
||||
},
|
||||
handleQuickFilter(option) {
|
||||
if (option.route) {
|
||||
this.$router.push(option.route)
|
||||
@@ -312,6 +329,11 @@ export default {
|
||||
</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;
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup/index.vue'
|
||||
import BaseFormatter from './base.vue'
|
||||
import ActionsGroup from '@/components/ActionsGroup/index.vue'
|
||||
|
||||
const defaultPerformDelete = function({ row, col }) {
|
||||
const id = row.id
|
||||
@@ -23,7 +23,9 @@ const defaultPerformDelete = function({ row, col }) {
|
||||
|
||||
const defaultUpdateCallback = function({ row, col }) {
|
||||
const id = row.id
|
||||
|
||||
let route = { params: { id: id }}
|
||||
|
||||
const updateRoute = this.colActions.updateRoute
|
||||
|
||||
if (typeof updateRoute === 'object') {
|
||||
@@ -33,6 +35,7 @@ const defaultUpdateCallback = function({ row, col }) {
|
||||
} else {
|
||||
route.name = updateRoute
|
||||
}
|
||||
|
||||
this.$router.push(route)
|
||||
}
|
||||
|
||||
@@ -106,7 +109,7 @@ export default {
|
||||
onUpdate: defaultUpdateCallback,
|
||||
onDelete: defaultDeleteCallback,
|
||||
onClone: defaultCloneCallback,
|
||||
extraActions: [] // format see defaultActions
|
||||
extraActions: []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +149,8 @@ export default {
|
||||
colActions: colActions,
|
||||
defaultActions: defaultActions,
|
||||
extraActions: colActions.extraActions,
|
||||
moreActionsTitle: ''
|
||||
// moreActionsTitle: colActions.moreActionsTitle || null
|
||||
moreActionsTitle: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
56
src/components/Table/TableFormatters/CopyableFormatter.vue
Normal file
56
src/components/Table/TableFormatters/CopyableFormatter.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script>
|
||||
import BaseFormatter from './base.vue'
|
||||
import { copy } from '@/utils/common'
|
||||
export default {
|
||||
name: 'CopyableFormatter',
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
shadow: false,
|
||||
getText: ({ cellValue }) => cellValue
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iCellValue() {
|
||||
if (this.formatterArgs.shadow) {
|
||||
return '*'.repeat(6)
|
||||
} else {
|
||||
return this.cellValue
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async copy() {
|
||||
const text = await this.formatterArgs.getText({ cellValue: this.cellValue, row: this.row })
|
||||
copy(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="copyable">
|
||||
{{ iCellValue }} <i class="el-icon-copy-document copy" @click="copy()" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.copy {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -6,7 +6,7 @@
|
||||
:disabled="disabled"
|
||||
:type="col.type || 'info'"
|
||||
class="detail"
|
||||
@click="goDetail"
|
||||
@click="handleClick"
|
||||
>
|
||||
<slot>
|
||||
{{ iTitle }}
|
||||
@@ -30,6 +30,7 @@ export default {
|
||||
getRoute: null,
|
||||
routeQuery: null,
|
||||
can: true,
|
||||
onClick: null,
|
||||
openInNewPage: false,
|
||||
removeColorOnClick: false,
|
||||
getTitle({ col, row, cellValue }) {
|
||||
@@ -46,6 +47,9 @@ export default {
|
||||
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
return {
|
||||
linkClicked: false,
|
||||
showTableDetailDrawer: false,
|
||||
drawerTitle: '',
|
||||
currentTemplate: null,
|
||||
formatterArgs: formatterArgs
|
||||
}
|
||||
},
|
||||
@@ -73,6 +77,17 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
if (this.formatterArgs.onClick) {
|
||||
this.formatterArgs.onClick({
|
||||
col: this.col,
|
||||
row: this.row,
|
||||
cellValue: this.cellValue
|
||||
})
|
||||
} else {
|
||||
this.goDetail()
|
||||
}
|
||||
},
|
||||
getDetailRoute() {
|
||||
// const defaultRoute = this.$route.name.replace('List', 'Detail')
|
||||
let route = this.formatterArgs.route
|
||||
@@ -107,12 +122,12 @@ export default {
|
||||
const detailRoute = this.getDetailRoute()
|
||||
|
||||
if (this.formatterArgs.openInNewPage) {
|
||||
this.linkClicked = this.formatterArgs.removeColorOnClick
|
||||
const { href } = this.$router.resolve(detailRoute)
|
||||
window.open(href, '_blank')
|
||||
} else {
|
||||
this.$router.push(detailRoute)
|
||||
this.linkClicked = this.formatterArgs.removeColorOnClick
|
||||
return window.open(href, '_blank')
|
||||
}
|
||||
|
||||
this.$router.push(detailRoute)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,4 +157,8 @@ export default {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
::v-deep .go-back {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<span class="conform-td">
|
||||
<span v-if="iValue === '0'">
|
||||
<span v-if="iValue === statusMap.pending">
|
||||
<el-dropdown trigger="click" @command="handleRisk">
|
||||
<el-button class="confirm action" size="mini">
|
||||
<i class="fa fa-check" />
|
||||
@@ -13,12 +13,12 @@
|
||||
</el-dropdown>
|
||||
<el-tooltip :content="$tc('Ignore')" :open-delay="400">
|
||||
<el-button class="ignore action" size="mini">
|
||||
<svg-icon icon-class="ignore" />
|
||||
<svg-icon icon-class="ignore" @click="handleRisk('ignore')" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip v-else :content="iLabel" :open-delay="400" class="platform-status">
|
||||
<span v-if="iValue === '1' ">
|
||||
<span v-if="iValue === statusMap.confirmed ">
|
||||
<i class="fa fa-check color-primary" />
|
||||
</span>
|
||||
<span v-else>
|
||||
@@ -33,6 +33,11 @@
|
||||
import BaseFormatter from './base.vue'
|
||||
import ProcessingDialog from '@/components/Dialog/ProcessingDialog.vue'
|
||||
|
||||
const statusMap = {
|
||||
pending: '0',
|
||||
confirmed: '1',
|
||||
ignored: '2'
|
||||
}
|
||||
export default {
|
||||
name: 'ConfirmOrIgnoreFormatter',
|
||||
components: { ProcessingDialog },
|
||||
@@ -56,7 +61,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs),
|
||||
processing: false
|
||||
processing: false,
|
||||
statusMap: statusMap
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -94,7 +100,10 @@ export default {
|
||||
if (cmd === 'add_account') {
|
||||
this.row.present = true
|
||||
}
|
||||
this.row.status = { 'value': '1' }
|
||||
if (cmd === 'ignore') {
|
||||
this.row.status = { 'value': statusMap.ignored }
|
||||
}
|
||||
this.row.status = { 'value': statusMap.confirmed }
|
||||
}).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.processing = false
|
||||
@@ -117,6 +126,11 @@ export default {
|
||||
name: 'add_account',
|
||||
label: this.$t('Add account'),
|
||||
has: !this.row.present
|
||||
},
|
||||
{
|
||||
name: 'add_account_after_change_password',
|
||||
label: this.$t('Add account after changing password'),
|
||||
has: !this.row.present
|
||||
}
|
||||
]
|
||||
return actions.filter(action => {
|
||||
|
||||
26
src/components/Widgets/Loading/index.vue
Normal file
26
src/components/Widgets/Loading/index.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<span v-if="loading" v-loading="loading" class="loading" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Index',
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loading {
|
||||
margin-top: 20px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
23
src/directive/async.js
Normal file
23
src/directive/async.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
Vue.directive('async', {
|
||||
async bind(el, binding) {
|
||||
const { value: asyncFn, arg } = binding
|
||||
if (typeof asyncFn === 'function') {
|
||||
const result = await asyncFn(arg)
|
||||
el.innerText = result
|
||||
}
|
||||
},
|
||||
async update(el, binding) {
|
||||
const { value: asyncFn, arg } = binding
|
||||
if (typeof asyncFn === 'function') {
|
||||
const result = await asyncFn(arg)
|
||||
el.innerText = result
|
||||
} else if (typeof asyncFn === 'object' && asyncFn.then) {
|
||||
const result = await asyncFn
|
||||
el.innerText = result
|
||||
} else {
|
||||
el.innerText = asyncFn
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1 +1,2 @@
|
||||
import './permission'
|
||||
import './async'
|
||||
|
||||
@@ -5,3 +5,12 @@ Vue.filter('date', function(value) {
|
||||
return toSafeLocalDateStr(value)
|
||||
})
|
||||
|
||||
Vue.filter('async', async(asyncFn, args) => {
|
||||
if (typeof asyncFn === 'function') {
|
||||
return await asyncFn(args)
|
||||
} else if (typeof asyncFn === 'object' && asyncFn.then) {
|
||||
return await asyncFn
|
||||
} else {
|
||||
return asyncFn
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,6 +14,7 @@ router.beforeEach(async(to, from, next) => {
|
||||
// start progress bar
|
||||
// NProgress.start()
|
||||
try {
|
||||
await store.dispatch('common/cleanDrawerActionMeta')
|
||||
await startup({ to, from, next })
|
||||
next()
|
||||
} catch (e) {
|
||||
|
||||
@@ -133,8 +133,13 @@ export default {
|
||||
submitMethod: {
|
||||
type: [Function, String],
|
||||
default: function() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
const cloneFrom = this.getCloneId()
|
||||
console.log('Clone from: ', cloneFrom)
|
||||
if (cloneFrom) {
|
||||
return 'post'
|
||||
}
|
||||
const objectId = this.getUpdateId()
|
||||
if (objectId) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
@@ -145,13 +150,13 @@ export default {
|
||||
getUrl: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
const params = this.$route.params
|
||||
const objectId = this.getUpdateId()
|
||||
let url = this.url
|
||||
if (params.id) {
|
||||
url = getUpdateObjURL(url, params.id)
|
||||
if (objectId) {
|
||||
url = getUpdateObjURL(url, objectId)
|
||||
}
|
||||
|
||||
const clone_from = this.$route.query['clone_from']
|
||||
const clone_from = this.getCloneId()
|
||||
const query = clone_from ? `clone_from=${clone_from}` : ''
|
||||
if (query) {
|
||||
if (url.indexOf('?') === -1) {
|
||||
@@ -204,7 +209,6 @@ export default {
|
||||
type: Function,
|
||||
default(res, method, vm, addContinue) {
|
||||
const route = this.getNextRoute(res, method)
|
||||
|
||||
if (!(route.params && route.params.id)) {
|
||||
route['params'] = deepmerge(route['params'] || {}, { 'id': res.id })
|
||||
}
|
||||
@@ -213,10 +217,18 @@ export default {
|
||||
this.$emit('submitSuccess', res)
|
||||
|
||||
this.emitPerformSuccessMsg(method, res, addContinue)
|
||||
if (!addContinue) {
|
||||
if (addContinue) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!vm.drawer) {
|
||||
if (this.$router.currentRoute.name !== route?.name) {
|
||||
setTimeout(() => this.$router.push(route), 100)
|
||||
}
|
||||
} else {
|
||||
console.log('Reload table clsonse dr')
|
||||
this.$emit('close-drawer')
|
||||
this.$emit('reload-table')
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -268,7 +280,11 @@ export default {
|
||||
form: {},
|
||||
loading: true,
|
||||
isSubmitting: false,
|
||||
clone: false
|
||||
clone: false,
|
||||
drawer: false,
|
||||
action: '',
|
||||
actionId: '',
|
||||
row: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -297,19 +313,47 @@ export default {
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$log.debug('Object init is: ', this.object)
|
||||
const drawActionMeta = await this.$store.dispatch('common/getDrawerActionMeta')
|
||||
if (drawActionMeta) {
|
||||
this.drawer = true
|
||||
this.action = drawActionMeta.action
|
||||
this.row = drawActionMeta.row
|
||||
this.actionId = this.row?.id
|
||||
}
|
||||
this.$log.debug('Object init is: ', this.object, this.method)
|
||||
console.log('Action: ', this.action, this.actionId)
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const values = await this.getFormValue()
|
||||
this.$log.debug('Final object is: ', values)
|
||||
const formValue = Object.assign(this.form, values)
|
||||
this.form = this.afterGetFormValue(formValue)
|
||||
console.log('Form: ', this.form)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUpdateId() {
|
||||
if (this.actionId && this.action === 'update') {
|
||||
return this.actionId
|
||||
} else {
|
||||
return this.$route.params.id
|
||||
}
|
||||
},
|
||||
getAction() {
|
||||
return this.action
|
||||
},
|
||||
getCloneId() {
|
||||
if (this.actionId && this.action === 'clone') {
|
||||
return this.actionId
|
||||
} else {
|
||||
return this.$route.query['clone_from']
|
||||
}
|
||||
},
|
||||
isUpdateMethod() {
|
||||
console.log('This method: ', this.method)
|
||||
return ['put', 'patch'].indexOf(this.method.toLowerCase()) > -1
|
||||
},
|
||||
encryptFields(values) {
|
||||
@@ -352,27 +396,34 @@ export default {
|
||||
}, 200)
|
||||
})
|
||||
},
|
||||
async getUpdateForm() {
|
||||
},
|
||||
async getCloneForm(cloneFrom) {
|
||||
const [curUrl, query] = this.url.split('?')
|
||||
const url = `${curUrl}${cloneFrom}/${query ? ('?' + query) : ''}`
|
||||
const object = await this.getObjectDetail(url)
|
||||
let name = ''
|
||||
let attr = ''
|
||||
if (object['name']) {
|
||||
name = object['name']
|
||||
attr = 'name'
|
||||
} else if (object['hostname']) {
|
||||
name = object['hostname']
|
||||
attr = 'hostname'
|
||||
}
|
||||
object[attr] = name + '-' + this.cloneNameSuffix.toString()
|
||||
return object
|
||||
},
|
||||
async getFormValue() {
|
||||
const cloneFrom = this.$route.query['clone_from']
|
||||
if ((!this.isUpdateMethod() && !cloneFrom) || !this.needGetObjectDetail) {
|
||||
const cloneFrom = this.getCloneId()
|
||||
const objectId = this.getUpdateId()
|
||||
if ((!objectId && !cloneFrom) || !this.needGetObjectDetail) {
|
||||
return Object.assign(this.form, this.initial)
|
||||
}
|
||||
let object = this.object
|
||||
if (!object || Object.keys(object).length === 0) {
|
||||
if (cloneFrom) {
|
||||
const [curUrl, query] = this.url.split('?')
|
||||
const url = `${curUrl}${cloneFrom}/${query ? ('?' + query) : ''}`
|
||||
object = await this.getObjectDetail(url)
|
||||
let name = ''
|
||||
let attr = ''
|
||||
if (object['name']) {
|
||||
name = object['name']
|
||||
attr = 'name'
|
||||
} else if (object['hostname']) {
|
||||
name = object['hostname']
|
||||
attr = 'hostname'
|
||||
}
|
||||
object[attr] = name + '-' + this.cloneNameSuffix.toString()
|
||||
object = await this.getCloneForm(cloneFrom)
|
||||
} else {
|
||||
object = await this.getObjectDetail(this.iUrl)
|
||||
}
|
||||
@@ -393,7 +444,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ibox ::v-deep .el-card__body {
|
||||
padding-top: 30px;
|
||||
}
|
||||
.ibox ::v-deep .el-card__body {
|
||||
padding-top: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Page v-bind="$attrs">
|
||||
<Page :class="{drawer: drawer}" v-bind="$attrs">
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm ref="createUpdateForm" v-bind="$attrs" v-on="$listeners" />
|
||||
</IBox>
|
||||
@@ -14,6 +14,18 @@ export default {
|
||||
name: 'GenericCreateUpdatePage',
|
||||
components: {
|
||||
Page, IBox, GenericCreateUpdateForm
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawer: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('common/getDrawerActionMeta').then((res) => {
|
||||
if (res.action) {
|
||||
this.drawer = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -27,4 +39,8 @@ export default {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer ::v-deep .page-heading {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,7 +35,6 @@ export default {
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
object: {
|
||||
@@ -98,37 +97,34 @@ export default {
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const detailApiUrl = (function() {
|
||||
if (vm.url) {
|
||||
return `${vm.url}/${vm.$route.params.id}/`
|
||||
} else {
|
||||
return getApiPath(vm)
|
||||
}
|
||||
}())
|
||||
const defaultActions = {
|
||||
// Delete button
|
||||
canDelete: vm.$hasCurrentResAction('delete'),
|
||||
hasDelete: true,
|
||||
deleteCallback: function(item) {
|
||||
vm.defaultDelete(item)
|
||||
},
|
||||
deleteApiUrl: detailApiUrl,
|
||||
deleteSuccessRoute: this.$route.name.replace('Detail', 'List'),
|
||||
// Update button
|
||||
canUpdate: () => {
|
||||
return !vm.currentOrgIsRoot && vm.$hasCurrentResAction('change')
|
||||
},
|
||||
hasUpdate: true,
|
||||
updateCallback: function(item) {
|
||||
this.defaultUpdate(item)
|
||||
},
|
||||
updateRoute: this.$route.name.replace('Detail', 'Update')
|
||||
}
|
||||
return {
|
||||
detailApiUrl,
|
||||
defaultActions,
|
||||
loading: true,
|
||||
drawer: '',
|
||||
action: '',
|
||||
actionId: '',
|
||||
validActions: Object.assign(defaultActions, this.actions)
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot']),
|
||||
pageActions() {
|
||||
@@ -139,7 +135,7 @@ export default {
|
||||
icon: 'el-icon-edit-outline',
|
||||
size: 'small',
|
||||
can: this.validActions.canUpdate,
|
||||
has: this.validActions.hasUpdate,
|
||||
has: this.validActions.hasUpdate && !this.drawer,
|
||||
callback: this.validActions.updateCallback.bind(this)
|
||||
},
|
||||
{
|
||||
@@ -178,20 +174,53 @@ export default {
|
||||
return [...this.submenu, activity]
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
async created() {
|
||||
try {
|
||||
this.loading = true
|
||||
await this.checkDrawer()
|
||||
await this.getObject()
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async checkDrawer() {
|
||||
const drawActionMeta = await this.$store.dispatch('common/getDrawerActionMeta')
|
||||
if (drawActionMeta) {
|
||||
this.drawer = true
|
||||
this.row = drawActionMeta.row
|
||||
this.actionId = this.row?.id
|
||||
}
|
||||
},
|
||||
getDetailUrl() {
|
||||
const vm = this
|
||||
let objectId = ''
|
||||
if (this.actionId) {
|
||||
objectId = this.actionId
|
||||
} else {
|
||||
objectId = vm.$route.params.id
|
||||
}
|
||||
if (vm.url) {
|
||||
return `${vm.url}/${objectId}/`
|
||||
} else {
|
||||
return getApiPath(vm, objectId)
|
||||
}
|
||||
},
|
||||
afterDelete() {
|
||||
if (this.drawer) {
|
||||
this.$emit('close-drawer')
|
||||
this.$emit('detail-delete-success')
|
||||
this.$emit('reload-table')
|
||||
} else {
|
||||
this.$message.success(this.$tc('DeleteSuccessMsg'))
|
||||
this.$router.push({ name: this.validActions.deleteSuccessRoute })
|
||||
}
|
||||
},
|
||||
defaultDelete() {
|
||||
const msg = this.$t('DeleteWarningMsg') + ' ' + this.iTitle + ' ?'
|
||||
const title = this.$t('Info')
|
||||
const performDelete = () => {
|
||||
const url = this.validActions.deleteApiUrl
|
||||
const url = this.getDetailUrl()
|
||||
this.$log.debug('Start perform delete: ', url)
|
||||
return this.$axios.delete(url)
|
||||
}
|
||||
@@ -206,8 +235,7 @@ export default {
|
||||
try {
|
||||
await performDelete.bind(this)()
|
||||
done()
|
||||
this.$message.success(this.$tc('DeleteSuccessMsg'))
|
||||
this.$router.push({ name: this.validActions.deleteSuccessRoute })
|
||||
this.afterDelete()
|
||||
} catch (error) {
|
||||
const errorDetail = error?.response?.data?.detail || ''
|
||||
if (errorDetail) {
|
||||
@@ -238,13 +266,13 @@ export default {
|
||||
},
|
||||
getObject() {
|
||||
// 兼容之前的 detailApiUrl
|
||||
const url = this.validActions.detailApiUrl || this.detailApiUrl
|
||||
const url = this.getDetailUrl()
|
||||
return this.$axios.get(url, { disableFlashErrorMsg: true }).then(data => {
|
||||
this.$emit('update:object', data)
|
||||
this.$emit('getObjectDone', data)
|
||||
}).catch(error => {
|
||||
if (error.response && error.response.status === 404) {
|
||||
const msg = this.$t('ObjectNotFoundOrDeletedMsg')
|
||||
const msg = this.$tc('ObjectNotFoundOrDeletedMsg')
|
||||
this.$message.error(msg)
|
||||
} else {
|
||||
flashErrorMsg({ error, response: error.response })
|
||||
@@ -261,7 +289,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header-buttons {
|
||||
z-index: 999;
|
||||
}
|
||||
.header-buttons {
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
|
||||
86
src/layout/components/GenericListDrawerPage/Drawer.vue
Normal file
86
src/layout/components/GenericListDrawerPage/Drawer.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:size="drawerSize"
|
||||
:title="title"
|
||||
:visible.sync="iVisible"
|
||||
append-to-body
|
||||
class="form-drawer"
|
||||
destroy-on-close
|
||||
>
|
||||
<component
|
||||
:is="component"
|
||||
v-bind="props"
|
||||
@close="closeDrawer"
|
||||
v-on="$listeners"
|
||||
@close-drawer="iVisible=false"
|
||||
/>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
component: {
|
||||
type: [String, Function],
|
||||
required: true
|
||||
},
|
||||
props: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
},
|
||||
drawerSize() {
|
||||
const width = window.innerWidth
|
||||
if (width >= 768) return '800px'
|
||||
return '90%'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeDrawer() {
|
||||
this.iVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-drawer {
|
||||
/* 可自定义样式 */
|
||||
|
||||
::v-deep {
|
||||
.el-card.ibox {
|
||||
//border: none;
|
||||
}
|
||||
|
||||
.el-drawer__header {
|
||||
//border-bottom: 1px solid #EBEEF5;
|
||||
margin-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
183
src/layout/components/GenericListDrawerPage/index.vue
Normal file
183
src/layout/components/GenericListDrawerPage/index.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div>
|
||||
<Page v-bind="$attrs">
|
||||
<GenericListTable
|
||||
ref="ListTable"
|
||||
:header-actions="iHeaderActions"
|
||||
:table-config="iTableConfig"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</Page>
|
||||
<Drawer
|
||||
v-if="drawerVisible"
|
||||
:action="action"
|
||||
:component="drawerComponent"
|
||||
:props="drawerProps"
|
||||
:title="drawerTitle"
|
||||
:visible.sync="drawerVisible"
|
||||
@reload-table="reloadTable"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Page from '@/layout/components/Page'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import Drawer from './Drawer'
|
||||
import { toSentenceCase } from '@/utils/common'
|
||||
|
||||
const drawerType = [String, Function]
|
||||
|
||||
export default {
|
||||
name: 'GenericListPage',
|
||||
components: {
|
||||
Page, GenericListTable, Drawer
|
||||
},
|
||||
props: {
|
||||
detailDrawer: {
|
||||
type: drawerType,
|
||||
default: ''
|
||||
},
|
||||
createDrawer: {
|
||||
type: drawerType,
|
||||
default: ''
|
||||
},
|
||||
updateDrawer: {
|
||||
type: drawerType,
|
||||
default: ''
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
headerActions: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
drawerVisible: false,
|
||||
drawerComponent: '',
|
||||
drawerProps: {},
|
||||
iHeaderActions: {},
|
||||
iTableConfig: {},
|
||||
action: '',
|
||||
iCreateDrawer: this.createDrawer,
|
||||
iUpdateDrawer: this.updateDrawer,
|
||||
iDetailDrawer: this.detailDrawer
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
drawerTitle() {
|
||||
let title = this.title || this.$route.meta.title
|
||||
if (!title) {
|
||||
title = this.$t('NoTitle')
|
||||
}
|
||||
title = toSentenceCase(this.action) + ' ' + title.toLowerCase()
|
||||
return title
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
drawerVisible(val) {
|
||||
if (!val) {
|
||||
this.$store.dispatch('common/cleanDrawerActionMeta')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.createDrawer) {
|
||||
this.iCreateDrawer = this.getDefaultDrawer('create')
|
||||
}
|
||||
if (!this.updateDrawer) {
|
||||
this.iUpdateDrawer = this.getDefaultDrawer('update')
|
||||
}
|
||||
if (!this.detailDrawer) {
|
||||
this.iDetailDrawer = this.getDefaultDrawer('detail')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.iHeaderActions = {
|
||||
...this.headerActions,
|
||||
onCreate: this.onCreate
|
||||
}
|
||||
this.iTableConfig = {
|
||||
...this.tableConfig
|
||||
}
|
||||
_.set(this.iTableConfig, 'columnsMeta.actions.formatterArgs.onUpdate', this.onUpdate)
|
||||
_.set(this.iTableConfig, 'columnsMeta.actions.formatterArgs.onClone', this.onClone)
|
||||
_.set(this.iTableConfig, 'columnsMeta.name.formatterArgs.onClick', this.onDetail)
|
||||
},
|
||||
methods: {
|
||||
getDefaultDrawer(action) {
|
||||
const route = this.$route.name
|
||||
const actionRouteName = route.replace('List', toSentenceCase(action))
|
||||
return this.getRouteNameComponent(actionRouteName)
|
||||
},
|
||||
getRouteNameComponent(name) {
|
||||
const routes = this.$router.resolve({ name: name })
|
||||
if (!routes) {
|
||||
return
|
||||
}
|
||||
const matched = routes.resolved.matched.filter(item => item.name === name && item.components)
|
||||
if (matched.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (matched[0] && matched[0].components?.default) {
|
||||
return matched[0].components.default
|
||||
}
|
||||
},
|
||||
onCreate() {
|
||||
this.action = 'create'
|
||||
this.drawerComponent = this.iCreateDrawer
|
||||
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||
action: 'create'
|
||||
}).then(() => {
|
||||
this.drawerVisible = true
|
||||
})
|
||||
},
|
||||
reloadTable() {
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
onClone({ row, col }) {
|
||||
this.drawerComponent = this.iCreateDrawer
|
||||
this.action = 'clone'
|
||||
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||
action: 'clone',
|
||||
row: row,
|
||||
col: col
|
||||
}).then(() => {
|
||||
this.drawerVisible = true
|
||||
})
|
||||
},
|
||||
onUpdate({ row, col }) {
|
||||
this.action = 'update'
|
||||
let updateDrawer = this.iUpdateDrawer
|
||||
if (!updateDrawer) {
|
||||
updateDrawer = this.iCreateDrawer
|
||||
}
|
||||
this.drawerComponent = updateDrawer
|
||||
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||
action: 'update',
|
||||
row: row,
|
||||
col: col
|
||||
}).then(() => {
|
||||
this.drawerVisible = true
|
||||
})
|
||||
},
|
||||
onDetail({ row, cellValue }) {
|
||||
this.action = 'detail'
|
||||
this.drawerComponent = this.iDetailDrawer
|
||||
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||
action: 'detail',
|
||||
row: row,
|
||||
cellValue: cellValue
|
||||
}).then(() => {
|
||||
this.drawerVisible = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot name="globalNotification">
|
||||
<SqlQueryTip v-if="debug" />
|
||||
<SqlQueryTip v-if="debug && !inDrawer" />
|
||||
<LicenseRelatedTip v-else />
|
||||
<PasswordExpireTip />
|
||||
</slot>
|
||||
@@ -22,6 +22,7 @@
|
||||
import LicenseRelatedTip from './LicenseRelatedTip'
|
||||
import PasswordExpireTip from './PasswordExpireTip'
|
||||
import SqlQueryTip from './SqlQueryTip'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'PageHeading',
|
||||
@@ -40,6 +41,9 @@ export default {
|
||||
return {
|
||||
debug: process.env.NODE_ENV === 'development'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['inDrawer'])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<TagsView />
|
||||
<PageHeading v-if="iTitle || helpMessage" :help-msg="helpMessage" class="disabled-when-print">
|
||||
<el-button
|
||||
v-if="!inDrawer"
|
||||
:disabled="gobackDisabled"
|
||||
class="go-back"
|
||||
icon="el-icon-back"
|
||||
@@ -46,6 +47,7 @@ import PageContent from './PageContent'
|
||||
import UserConfirmDialog from '@/components/Apps/UserConfirmDialog/index.vue'
|
||||
import TagsView from '../TagsView/index.vue'
|
||||
import { toSentenceCase } from '@/utils/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Page',
|
||||
@@ -81,6 +83,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['inDrawer']),
|
||||
iTitle() {
|
||||
let title = this.title || this.$route.meta.title
|
||||
if (!title) {
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
<el-tab-pane
|
||||
:key="item.name"
|
||||
:disabled="item.disabled"
|
||||
:label-content="item.labelContent"
|
||||
:name="item.name"
|
||||
>
|
||||
<span slot="label">
|
||||
<i v-if="item.icon" :class="item.icon" class="fa pre-icon " />
|
||||
<Icon v-if="item.icon" :icon="item.icon" class="pre-icon" />
|
||||
{{ toSentenceCase(item.title) }}
|
||||
<slot :tab="item.name" name="badge" />
|
||||
<el-tooltip
|
||||
@@ -60,12 +59,14 @@
|
||||
|
||||
<script>
|
||||
import Page from '../Page/'
|
||||
import Icon from '@/components/Widgets/Icon'
|
||||
import { toSentenceCase } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'TabPage',
|
||||
components: {
|
||||
Page
|
||||
Page,
|
||||
Icon
|
||||
},
|
||||
props: {
|
||||
submenu: {
|
||||
|
||||
@@ -67,57 +67,57 @@ export default [
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'account-template',
|
||||
component: empty,
|
||||
redirect: {
|
||||
name: 'AccountTemplateList'
|
||||
},
|
||||
meta: {
|
||||
title: i18n.t('AccountTemplate'),
|
||||
app: 'accounts',
|
||||
icon: 'template',
|
||||
permissions: ['accounts.view_accounttemplate']
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'AccountTemplateList',
|
||||
component: () => import('@/views/accounts/AccountTemplate/AccountTemplateList'),
|
||||
meta: {
|
||||
menuTitle: i18n.t('MenuAccountTemplates'),
|
||||
title: i18n.t('AccountTemplateList'),
|
||||
permissions: ['accounts.view_accounttemplate']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountTemplate/AccountTemplateCreateUpdate.vue'),
|
||||
name: 'AccountTemplateCreate',
|
||||
meta: {
|
||||
title: i18n.t('CreateAccountTemplate'),
|
||||
action: 'create'
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/accounts/AccountTemplate/AccountTemplateCreateUpdate.vue'),
|
||||
name: 'AccountTemplateUpdate',
|
||||
meta: {
|
||||
title: i18n.t('UpdateAccountTemplate'),
|
||||
action: 'update'
|
||||
},
|
||||
hidden: true
|
||||
}
|
||||
// {
|
||||
// path: ':id',
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateDetail/Application.vue'),
|
||||
// name: 'AccountTemplateDetail',
|
||||
// meta: { title: i18n.t('AccountTemplate') },
|
||||
// hidden: true
|
||||
// }
|
||||
]
|
||||
}
|
||||
// {
|
||||
// path: 'account-template',
|
||||
// component: empty,
|
||||
// redirect: {
|
||||
// name: 'AccountTemplateList'
|
||||
// },
|
||||
// meta: {
|
||||
// title: i18n.t('AccountTemplate'),
|
||||
// app: 'accounts',
|
||||
// icon: 'template',
|
||||
// permissions: ['accounts.view_accounttemplate']
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// name: 'AccountTemplateList',
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateList'),
|
||||
// meta: {
|
||||
// menuTitle: i18n.t('MenuAccountTemplates'),
|
||||
// title: i18n.t('AccountTemplateList'),
|
||||
// permissions: ['accounts.view_accounttemplate']
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'create',
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateCreateUpdate.vue'),
|
||||
// name: 'AccountTemplateCreate',
|
||||
// meta: {
|
||||
// title: i18n.t('CreateAccountTemplate'),
|
||||
// action: 'create'
|
||||
// },
|
||||
// hidden: true
|
||||
// },
|
||||
// {
|
||||
// path: ':id/update',
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateCreateUpdate.vue'),
|
||||
// name: 'AccountTemplateUpdate',
|
||||
// meta: {
|
||||
// title: i18n.t('UpdateAccountTemplate'),
|
||||
// action: 'update'
|
||||
// },
|
||||
// hidden: true
|
||||
// },
|
||||
// {
|
||||
// path: ':id',
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateDetail/Application.vue'),
|
||||
// name: 'AccountTemplateDetail',
|
||||
// meta: { title: i18n.t('AccountTemplate') },
|
||||
// hidden: true
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
]
|
||||
|
||||
@@ -159,6 +159,82 @@ export default [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'account-push',
|
||||
name: 'AccountPush',
|
||||
component: empty,
|
||||
redirect: {
|
||||
name: 'AccountPushList'
|
||||
},
|
||||
meta: {
|
||||
app: 'accounts',
|
||||
name: 'BaseAccountPushList',
|
||||
resource: 'pushaccountautomation',
|
||||
icon: 'change-password'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/AccountPush/index.vue'),
|
||||
name: 'AccountPushList',
|
||||
meta: {
|
||||
title: i18n.t('AccountPushList'),
|
||||
menuTitle: i18n.t('AccountPushList'),
|
||||
permissions: ['accounts.view_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushCreateUpdate.vue'),
|
||||
name: 'AccountPushCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('AccountPushCreate'),
|
||||
permissions: ['accounts.add_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushCreateUpdate.vue'),
|
||||
name: 'AccountPushUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('AccountPushUpdate'),
|
||||
permissions: ['accounts.change_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushDetail/index.vue'),
|
||||
name: 'AccountPushDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('AccountPushList'),
|
||||
permissions: ['accounts.view_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushExecutionList.vue'),
|
||||
name: 'AccountPushExecutionList',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ExecutionList'),
|
||||
permissions: ['accounts.view_pushaccountexecution']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions/:id',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushExecutionDetail/index.vue'),
|
||||
name: 'AccountPushExecutionDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ExecutionDetail'),
|
||||
permissions: ['accounts.view_pushaccountexecution']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'account-backup',
|
||||
component: empty,
|
||||
|
||||
@@ -48,7 +48,7 @@ export default [
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/pam/Integration/ApplicationDetail/index.vue'),
|
||||
name: 'ApplicationDetail',
|
||||
name: 'IntegrationApplicationDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ApplicationDetail'),
|
||||
|
||||
@@ -18,7 +18,8 @@ export default [
|
||||
component: () => import('@/views/pam/RiskDetect/index.vue'),
|
||||
name: 'AccountCheckList',
|
||||
meta: {
|
||||
title: i18n.t('RiskDetection')
|
||||
title: i18n.t('RiskDetection'),
|
||||
permissions: ['accounts.view_accountrisk']
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -27,7 +28,8 @@ export default [
|
||||
name: 'AccountCheckCreateUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('AccountCheckCreate')
|
||||
title: i18n.t('AccountCheckCreate'),
|
||||
permissions: ['accounts.view_accountrisk']
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -31,6 +31,7 @@ const getters = {
|
||||
hasValidLicense: state => state.settings.hasValidLicense,
|
||||
isSystemAdmin: state => state.users.profile.system_roles.some(i => (i?.id === '00000000-0000-0000-0000-000000000001')),
|
||||
sqlQueryCounter: state => state.common.sqlQueryCounter,
|
||||
showSqlQueryCounter: state => state.common.showSqlQueryCounter
|
||||
showSqlQueryCounter: state => state.common.showSqlQueryCounter,
|
||||
inDrawer: state => state.common.inDrawer
|
||||
}
|
||||
export default getters
|
||||
|
||||
@@ -7,7 +7,9 @@ const getDefaultState = () => {
|
||||
isRouterAlive: true,
|
||||
sqlQueryCounter: [],
|
||||
showSqlQueryCounter: true,
|
||||
confirmDialogVisible: false
|
||||
confirmDialogVisible: false,
|
||||
drawerActionMeta: {},
|
||||
inDrawer: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +87,17 @@ const actions = {
|
||||
},
|
||||
showSqlQueryCounter({ commit, state }, show) {
|
||||
state.showSqlQueryCounter = show
|
||||
},
|
||||
setDrawerActionMeta({ commit, state }, meta) {
|
||||
state.drawerActionMeta = meta
|
||||
state.inDrawer = true
|
||||
},
|
||||
getDrawerActionMeta({ commit, state }) {
|
||||
return state.drawerActionMeta
|
||||
},
|
||||
cleanDrawerActionMeta({ commit, state }) {
|
||||
state.drawerActionMeta = {}
|
||||
state.inDrawer = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { message } from '@/utils/message'
|
||||
|
||||
const _ = require('lodash')
|
||||
|
||||
export function getApiPath(that) {
|
||||
export function getApiPath(that, objectId) {
|
||||
let pagePath = that.$route.path
|
||||
const pagePathArray = pagePath.split('/')
|
||||
if (pagePathArray.indexOf('orgs') !== -1) {
|
||||
@@ -11,6 +11,9 @@ export function getApiPath(that) {
|
||||
} else if (pagePathArray.indexOf('gathered-user') !== -1 || pagePathArray.indexOf('change-auth-plan') !== -1) {
|
||||
pagePathArray[pagePathArray.indexOf('accounts')] = 'xpack'
|
||||
}
|
||||
if (pagePathArray.indexOf(objectId) === -1) {
|
||||
pagePathArray.push(objectId)
|
||||
}
|
||||
if (pagePathArray.indexOf('tickets') !== -1) {
|
||||
// ticket ...
|
||||
pagePath = pagePathArray.slice(1, pagePathArray.length).join('/')
|
||||
@@ -310,4 +313,5 @@ export function toSentenceCase(string) {
|
||||
}).join(' ')
|
||||
return s[0].toUpperCase() + s.slice(1)
|
||||
}
|
||||
|
||||
export { BASE_URL }
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="15" :sm="24" class="auto-detail-card">
|
||||
<AutoDetailCard :object="object" v-bind="detail" />
|
||||
</el-col>
|
||||
<el-col :md="9" :sm="24" class="quick-actions">
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:account="object"
|
||||
:url="secretUrl"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
<AutomationParamsForm
|
||||
:has-button="false"
|
||||
:method="pushAccountMethod"
|
||||
:visible.sync="autoPushVisible"
|
||||
@canSetting="onCanSetting"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="15" :sm="24" class="auto-detail-card">
|
||||
<AutoDetailCard :object="object" v-bind="detail" />
|
||||
</el-col>
|
||||
<el-col :md="9" :sm="24" class="quick-actions">
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:account="object"
|
||||
:url="secretUrl"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
<AutomationParamsForm
|
||||
:has-button="false"
|
||||
:method="pushAccountMethod"
|
||||
:visible.sync="autoPushVisible"
|
||||
@canSetting="onCanSetting"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-drawer
|
||||
size="50%"
|
||||
:with-header="false"
|
||||
:append-to-body="true"
|
||||
:visible.sync="pamDrawerShow"
|
||||
>
|
||||
<component :is="drawerRefName" />
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -28,12 +39,14 @@ import QuickActions from '@/components/QuickActions/index.vue'
|
||||
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret.vue'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
|
||||
import AssetDetail from '@/views/assets/Asset/AssetDetail'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AutoDetailCard,
|
||||
QuickActions,
|
||||
AssetDetail,
|
||||
AutomationParamsForm,
|
||||
ViewSecret
|
||||
},
|
||||
@@ -47,6 +60,8 @@ export default {
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
pamDrawerShow: false,
|
||||
drawerRefName: null,
|
||||
needSetAutoPushParams: false,
|
||||
autoPushVisible: false,
|
||||
secretUrl: `/api/v1/accounts/account-secrets/${this.object.id}/`,
|
||||
@@ -213,6 +228,22 @@ export default {
|
||||
name: 'AssetDetail',
|
||||
params: { id: this.object.asset.id }
|
||||
}
|
||||
|
||||
console.log(this.$route)
|
||||
|
||||
if (this.$route.params.type === 'pam') {
|
||||
return (
|
||||
<span style={{ color: '#1c84c6', cursor: 'pointer' }} onClick={() => {
|
||||
this.pamDrawerShow = true
|
||||
this.$route.params.id = this.object.asset.id
|
||||
|
||||
this.drawerRefName = 'AssetDetail'
|
||||
}}>
|
||||
{value.name}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return <router-link to={route}>{value?.name}</router-link>
|
||||
},
|
||||
su_from: (item, value) => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import AccountBackupExecutionList
|
||||
from '@/views/accounts/AccountBackup/AccountBackupExecution/AccountBackupExecutionList.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupDetail',
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
AccountBackupInfo,
|
||||
@@ -39,7 +40,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
<template>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
|
||||
<Drawer v-if="showTableUpdateDrawer" :title="drawerTitle" @close-drawer="showTableUpdateDrawer = !showTableUpdateDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { ArrayFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupList',
|
||||
components: {
|
||||
GenericListTable
|
||||
Drawer,
|
||||
GenericListTable,
|
||||
AccountBackupUpdate: () => import('@/views/accounts/AccountBackup/AccountBackupCreateUpdate.vue'),
|
||||
AccountBackupCreate: () => import('@/views/accounts/AccountBackup/AccountBackupCreateUpdate.vue')
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
drawerTitle: '',
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/account-backup-plans/',
|
||||
permissions: {
|
||||
@@ -36,7 +49,13 @@ export default {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'AccountBackupDetail'
|
||||
isPam: true,
|
||||
route: 'AccountBackupDetail',
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AccountBackupDetail',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
}
|
||||
},
|
||||
types: {
|
||||
@@ -68,7 +87,14 @@ export default {
|
||||
vm.$router.push({ name: 'AccountBackupCreate', query: { clone_from: row.id }})
|
||||
},
|
||||
onUpdate: ({ row }) => {
|
||||
vm.$router.push({ name: 'AccountBackupUpdate', params: { id: row.id }})
|
||||
this.$route.params.id = row.id
|
||||
|
||||
// 解决表单详情中的跳转
|
||||
this.$route.query.type = 'pam'
|
||||
|
||||
this.currentTemplate = 'AccountBackupUpdate'
|
||||
this.drawerTitle = this.$t('AccountBackupUpdate')
|
||||
this.showTableUpdateDrawer = true
|
||||
},
|
||||
extraActions: [
|
||||
{
|
||||
@@ -101,6 +127,11 @@ export default {
|
||||
return {
|
||||
name: 'AccountBackupCreate'
|
||||
}
|
||||
},
|
||||
onCreate: () => {
|
||||
this.currentTemplate = 'AccountBackupCreate'
|
||||
this.drawerTitle = this.$t('AccountBackupCreate')
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,12 +58,10 @@ export default {
|
||||
{
|
||||
id: 'discover',
|
||||
icon: 'discovery',
|
||||
name: '发现账号',
|
||||
name: this.$t('DiscoverAccounts'),
|
||||
callback: (node) => {
|
||||
console.log('Discovery it: ', node)
|
||||
this.discoveryDialog.asset = node.id
|
||||
this.discoveryDialog.visible = true
|
||||
// this.discoveryDialog.asset = node.data
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -71,58 +69,25 @@ export default {
|
||||
quickSummary: [
|
||||
{
|
||||
title: '最近一周发现',
|
||||
count: 10,
|
||||
hasCount: true,
|
||||
filter: {
|
||||
name: 'admin'
|
||||
'days': '7'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '最近一月发现',
|
||||
count: 321,
|
||||
filter: {
|
||||
username: 'admin'
|
||||
'days': '30'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '待确认',
|
||||
count: 544,
|
||||
filter: {
|
||||
username: 'admin'
|
||||
status: '0'
|
||||
}
|
||||
}
|
||||
],
|
||||
quickFilters: [
|
||||
{
|
||||
label: '快速过滤',
|
||||
options: [
|
||||
{
|
||||
label: '未同步到资产',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: this.$t('最近一个月'),
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('最近发现'),
|
||||
options: [
|
||||
{
|
||||
label: '最近一天 (20)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: '最近一周 (300)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: '最近一个月 (600)',
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
quickFilters: [],
|
||||
tableConfig: gatherAccountTableConfig(this),
|
||||
headerActions: gatherAccountHeaderActions(this)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
<template>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
|
||||
<Drawer v-if="showTableUpdateDrawer" :title="drawerTitle" @close-drawer="showTableUpdateDrawer = !showTableUpdateDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountDiscoverTaskList',
|
||||
components: {
|
||||
GenericListTable
|
||||
Drawer,
|
||||
GenericListTable,
|
||||
AccountDiscoverTaskCreate: () => import('@/views/accounts/AccountDiscover/TaskCreateUpdate'),
|
||||
AccountDiscoverTaskUpdate: () => import('@/views/accounts/AccountDiscover/TaskCreateUpdate')
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
drawerTitle: '',
|
||||
tableConfig: {
|
||||
name: 'AccountDiscoverTaskList',
|
||||
url: '/api/v1/accounts/gather-account-automations/',
|
||||
@@ -37,10 +51,11 @@ export default {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'AccountDiscoverTaskDetail',
|
||||
routeQuery: {
|
||||
tab: 'AccountDiscoverTaskDetail'
|
||||
}
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AccountDiscoverTaskDetail',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
@@ -94,7 +109,15 @@ export default {
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
onUpdate: ({ row }) => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
this.$route.query.type = 'pam'
|
||||
|
||||
this.currentTemplate = 'AccountDiscoverTaskUpdate'
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,14 +130,15 @@ export default {
|
||||
createRoute: 'AccountDiscoverTaskCreate',
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
},
|
||||
onCreate: ({ row }) => {
|
||||
this.$route.query.type = 'pam'
|
||||
|
||||
this.currentTemplate = 'AccountDiscoverTaskCreate'
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// $route(to, from) {
|
||||
// this.$router.go(0)
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -119,7 +119,15 @@ export default {
|
||||
})
|
||||
},
|
||||
handleConfirm() {
|
||||
|
||||
this.$axios.delete('/api/v1/accounts/gathered-accounts/', {
|
||||
params: {
|
||||
username: this.account.username,
|
||||
asset: this.account.asset.id
|
||||
}
|
||||
}).then(res => {
|
||||
this.$message.success('删除成功')
|
||||
this.iVisible = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import i18n from '@/i18n/i18n'
|
||||
import { periodicMeta } from '@/components/const'
|
||||
|
||||
export default {
|
||||
name: 'AccountDiscoverTaskCreate',
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<template>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
|
||||
<Drawer v-if="showTableUpdateDrawer" :title="drawerTitle" @close-drawer="showTableUpdateDrawer = !showTableUpdateDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
|
||||
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AccountDiscoverTaskExecutionList',
|
||||
components: {
|
||||
GenericListTable
|
||||
Drawer,
|
||||
GenericListTable,
|
||||
AccountDiscoverExecutionDetail: () => import('@/views/accounts/AccountDiscover/ExecutionDetail/index.vue')
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -21,6 +31,9 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
drawerTitle: '',
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/gather-account-executions/',
|
||||
columns: [
|
||||
@@ -41,7 +54,8 @@ export default {
|
||||
getTitle: ({ row }) => row.snapshot.name,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AccountDiscoverTaskDetail',
|
||||
params: { id: row.automation }
|
||||
params: { id: row.automation },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
},
|
||||
id: ({ row }) => row.automation
|
||||
@@ -74,8 +88,10 @@ export default {
|
||||
name: 'detail',
|
||||
title: this.$t('Detail'),
|
||||
type: 'info',
|
||||
callback: function({ row }) {
|
||||
return this.$router.push({ name: 'AccountDiscoverExecutionDetail', params: { id: row.id }})
|
||||
callback: ({ row }) => {
|
||||
this.handleDetailCallback(row)
|
||||
|
||||
// return this.$router.push({ name: 'AccountDiscoverExecutionDetail', params: { id: row.id }})
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -118,6 +134,16 @@ export default {
|
||||
if (automation_id !== undefined) {
|
||||
this.tableConfig.url = `${this.tableConfig.url}?automation_id=${automation_id}`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDetailCallback(row) {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
this.$route.query.type = 'pam'
|
||||
|
||||
this.currentTemplate = 'AccountDiscoverExecutionDetail'
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { getChangeSecretFields } from '@/views/accounts/AccountChangeSecret/fields'
|
||||
import { AssetSelect, AutomationParams } from '@/components'
|
||||
import { periodicMeta } from '@/components/const'
|
||||
import { TagInput } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'AccountPushCreateUpdate',
|
||||
@@ -92,6 +93,10 @@ export default {
|
||||
readonly: true
|
||||
}
|
||||
},
|
||||
accounts: {
|
||||
component: TagInput,
|
||||
helpText: this.$t('If the account already exists, the ciphertext of the account will be used for push; if the account does not exist, the account will be created according to the filled-in ciphertext and then pushed.')
|
||||
},
|
||||
params: {
|
||||
component: AutomationParams,
|
||||
label: this.$t('PushParams'),
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<RecordViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:url="secretUrl"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import RecordViewSecret from '@/components/Apps/ChangeSecret/RecordViewSecret.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountPushExecutionTaskList',
|
||||
components: {
|
||||
RecordViewSecret,
|
||||
GenericListTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
secretUrl: '',
|
||||
showViewSecretDialog: false,
|
||||
tableConfig: {
|
||||
url: `/api/v1/accounts/push-account-records/?execution_id=${this.object.id}`,
|
||||
columns: [
|
||||
'asset', 'account', 'date_finished', 'is_success', 'error', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
asset: {
|
||||
label: this.$t('Asset'),
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: this.$hasPerm('assets.view_asset'),
|
||||
getTitle({ row }) {
|
||||
return row.asset.name
|
||||
},
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
account: {
|
||||
label: this.$t('Username'),
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: this.$hasPerm('accounts.view_account'),
|
||||
getTitle({ row }) {
|
||||
return row.account.username
|
||||
},
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'AssetAccountDetail',
|
||||
params: { id: row.account.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
is_success: {
|
||||
label: this.$t('Success'),
|
||||
formatter: (row) => {
|
||||
if (row.status === 'pending') {
|
||||
return <i Class='fa fa fa-spinner fa-spin'/>
|
||||
}
|
||||
if (row.is_success) {
|
||||
return <i Class='fa fa-check text-primary'/>
|
||||
}
|
||||
return <i Class='fa fa-times text-danger'/>
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false,
|
||||
hasDelete: false,
|
||||
hasClone: false,
|
||||
moreActionsTitle: this.$t('More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('View'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
// debugger
|
||||
vm.secretUrl = `/api/v1/accounts/change-secret-records/${row.id}/secret/`
|
||||
vm.showViewSecretDialog = false
|
||||
setTimeout(() => {
|
||||
vm.showViewSecretDialog = true
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Retry',
|
||||
title: this.$t('Retry'),
|
||||
can: this.$hasPerm('accounts.add_changesecretexecution'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$axios.post(
|
||||
'/api/v1/accounts/push-account-records/execute/',
|
||||
{ record_ids: [row.id] }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasSearch: true,
|
||||
hasRefresh: true,
|
||||
hasLeftActions: true,
|
||||
hasRightActions: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasCreate: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false,
|
||||
searchConfig: {
|
||||
exclude: ['id', 'status', 'execution'],
|
||||
options: [
|
||||
{
|
||||
label: this.$t('Asset'),
|
||||
value: 'asset_name'
|
||||
},
|
||||
{
|
||||
label: this.$t('Accounts'),
|
||||
value: 'account_username'
|
||||
},
|
||||
{
|
||||
value: 'status',
|
||||
label: this.$t('Status'),
|
||||
type: 'choice',
|
||||
children: [
|
||||
{
|
||||
default: true,
|
||||
value: 'success',
|
||||
label: this.$t('Success')
|
||||
},
|
||||
{
|
||||
value: 'failed',
|
||||
label: this.$t('Failed')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'RetrySelected',
|
||||
title: this.$t('RetrySelected'),
|
||||
type: 'primary',
|
||||
fa: 'fa-retweet',
|
||||
can: ({ selectedRows }) => {
|
||||
return selectedRows.length > 0 && vm.$hasPerm('accounts.add_changesecretexecution')
|
||||
},
|
||||
callback: function({ selectedRows }) {
|
||||
const ids = selectedRows.map(v => {
|
||||
return v.id
|
||||
})
|
||||
this.$axios.post(
|
||||
'/api/v1/accounts/change-secret-records/execute/',
|
||||
{ record_ids: ids }).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -9,13 +9,11 @@
|
||||
<script>
|
||||
import { GenericDetailPage } from '@/layout/components'
|
||||
import AccountPushExecutionInfo from './AccountPushExecutionInfo.vue'
|
||||
import AccountPushExecutionTaskList from './AccountPushExecutionTaskList.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
AccountPushExecutionInfo,
|
||||
AccountPushExecutionTaskList
|
||||
AccountPushExecutionInfo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -32,11 +30,6 @@ export default {
|
||||
title: this.$t('Basic'),
|
||||
name: 'AccountPushExecutionInfo',
|
||||
hidden: () => !this.$hasPerm('accounts.view_pushaccountexecution')
|
||||
},
|
||||
{
|
||||
title: this.$t('TaskList'),
|
||||
name: 'AccountPushExecutionTaskList',
|
||||
hidden: () => !this.$hasPerm('accounts.view_changesecretrecord')
|
||||
}
|
||||
],
|
||||
getTitle: this.getExecutionTitle
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/push-account-executions/?' + `${this.object.id ? 'automation_id=' + this.object.id : ''}`,
|
||||
url: '/api/v1/accounts/push-account-executions',
|
||||
columns: [
|
||||
'automation', 'push_user_name', 'asset_amount', 'node_amount', 'status',
|
||||
'trigger', 'date_start', 'date_finished', 'actions'
|
||||
@@ -94,7 +94,16 @@ export default {
|
||||
type: 'info',
|
||||
can: this.$hasPerm('accounts.view_pushaccountexecution'),
|
||||
callback: function({ row }) {
|
||||
return this.$router.push({ name: 'AccountCheckExecutionDetail', params: { id: row.id }})
|
||||
return this.$router.push({ name: 'AccountPushExecutionDetail', params: { id: row.id }})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'report',
|
||||
title: this.$t('Report'),
|
||||
type: 'success',
|
||||
can: this.$hasPerm('accounts.view_pushaccountexecution'),
|
||||
callback: function({ row }) {
|
||||
window.open(`/api/v1/accounts/push-account-executions/${row.id}/report/`)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
<template>
|
||||
<GenericListTable ref="listTable" :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<div>
|
||||
<GenericListTable ref="listTable" :header-actions="headerActions" :table-config="tableConfig" />
|
||||
|
||||
<Drawer v-if="showTableUpdateDrawer" :title="drawerTitle" @close-drawer="showTableUpdateDrawer = !showTableUpdateDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountPushList',
|
||||
components: {
|
||||
GenericListTable
|
||||
Drawer,
|
||||
GenericListTable,
|
||||
AccountPushUpdate: () => import('@/views/accounts/AccountPush/AccountPushCreateUpdate.vue'),
|
||||
AccountPushCreate: () => import('@/views/accounts/AccountPush/AccountPushCreateUpdate.vue')
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
drawerTitle: '',
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/push-account-automations/',
|
||||
columns: [
|
||||
@@ -32,7 +45,12 @@ export default {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'AccountCheckDetail'
|
||||
isPam: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AccountPushDetail',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
}
|
||||
},
|
||||
accounts: {
|
||||
@@ -81,7 +99,20 @@ export default {
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
isPam: true,
|
||||
updateRoute: 'AccountPushUpdate',
|
||||
onUpdate: ({ row }) => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
// 解决表单详情中的跳转
|
||||
this.$route.query.type = 'pam'
|
||||
|
||||
this.currentTemplate = 'AccountPushUpdate'
|
||||
this.drawerTitle = this.$t('AccountPushUpdate')
|
||||
this.showTableUpdateDrawer = true
|
||||
},
|
||||
extraActions: [
|
||||
{
|
||||
title: vm.$t('Execute'),
|
||||
@@ -107,10 +138,16 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
helpMsg: this.$t('WebHelpMessage'),
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false
|
||||
hasImport: false,
|
||||
onCreate: () => {
|
||||
this.currentTemplate = 'AccountPushCreate'
|
||||
this.drawerTitle = this.$t('AccountPushCreate')
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListPage :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:account="account"
|
||||
:show-password-record="false"
|
||||
:url="secretUrl"
|
||||
type="template"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
</div>
|
||||
<Page>
|
||||
<AccountTemplateTable />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { ActionsFormatter } from '@/components/Table/TableFormatters'
|
||||
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret'
|
||||
import AccountTemplateTable from './AccountTemplateTable.vue'
|
||||
import { Page } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
name: 'AccountTemplateList',
|
||||
components: {
|
||||
GenericListPage,
|
||||
ViewSecret
|
||||
Page, AccountTemplateTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
account: {},
|
||||
secretUrl: '',
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
columns: null,
|
||||
columnsExclude: ['spec_info', 'password_rules', 'push_params'],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
privileged: {
|
||||
width: '120px',
|
||||
formatterArgs: {
|
||||
showText: false,
|
||||
showFalse: false
|
||||
}
|
||||
},
|
||||
has_secret: {
|
||||
formatterArgs: {
|
||||
showFalse: false,
|
||||
showText: false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
hasDelete: true,
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('View'),
|
||||
can: this.$hasPerm('accounts.view_accounttemplatesecret'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
vm.secretUrl = `/api/v1/accounts/account-template-secrets/${row.id}/`
|
||||
vm.account = row
|
||||
vm.showViewSecretDialog = false
|
||||
setTimeout(() => {
|
||||
vm.showViewSecretDialog = true
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: this.$hasPerm('accounts.view_accounttemplatesecret'),
|
||||
hasMoreActions: false,
|
||||
hasLabelSearch: true,
|
||||
exportOptions: {
|
||||
url: '/api/v1/accounts/account-template-secrets/',
|
||||
mfaVerifyRequired: true,
|
||||
tips: this.$t('AccountExportTips')
|
||||
},
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AccountTemplateCreate'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
100
src/views/accounts/AccountTemplate/AccountTemplateTable.vue
Normal file
100
src/views/accounts/AccountTemplate/AccountTemplateTable.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:account="account"
|
||||
:show-password-record="false"
|
||||
:url="secretUrl"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
type="template"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ActionsFormatter } from '@/components/Table/TableFormatters'
|
||||
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret'
|
||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountTemplateTable',
|
||||
components: {
|
||||
ListTable,
|
||||
ViewSecret
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
account: {},
|
||||
secretUrl: '',
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
columns: null,
|
||||
columnsExclude: ['spec_info', 'password_rules', 'push_params'],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
privileged: {
|
||||
width: '120px',
|
||||
formatterArgs: {
|
||||
showText: false,
|
||||
showFalse: false
|
||||
}
|
||||
},
|
||||
has_secret: {
|
||||
formatterArgs: {
|
||||
showFalse: false,
|
||||
showText: false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
hasDelete: true,
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('View'),
|
||||
can: this.$hasPerm('accounts.view_accounttemplatesecret'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
vm.secretUrl = `/api/v1/accounts/account-template-secrets/${row.id}/`
|
||||
vm.account = row
|
||||
vm.showViewSecretDialog = false
|
||||
setTimeout(() => {
|
||||
vm.showViewSecretDialog = true
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: this.$hasPerm('accounts.view_accounttemplatesecret'),
|
||||
hasMoreActions: false,
|
||||
hasLabelSearch: true,
|
||||
exportOptions: {
|
||||
url: '/api/v1/accounts/account-template-secrets/',
|
||||
mfaVerifyRequired: true,
|
||||
tips: this.$t('AccountExportTips')
|
||||
},
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AccountTemplateCreate'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -59,7 +59,6 @@
|
||||
</div>
|
||||
|
||||
<el-drawer
|
||||
direction="btt"
|
||||
size="50%"
|
||||
:with-header="false"
|
||||
:append-to-body="true"
|
||||
|
||||
@@ -54,7 +54,9 @@ export default {
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: vm.$hasPerm('users.view_user') ? DetailFormatter : '',
|
||||
formatter: (row) => {
|
||||
return vm.$hasPerm('users.view_user') ? DetailFormatter : ''
|
||||
},
|
||||
formatterArgs: {
|
||||
route: 'UserDetail'
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
},
|
||||
url: {
|
||||
get() {
|
||||
return `/api/v1/accounts/gathered-accounts/discover/?asset_id=${this.asset}`
|
||||
return `/api/v1/accounts/gather-account-executions/adhoc/?asset_id=${this.asset}`
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -84,7 +84,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onVisibleChange() {
|
||||
|
||||
},
|
||||
onIframeLoad() {
|
||||
this.loading = false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<SmallCard ref="table" v-bind="$data" />
|
||||
<CreateDialog v-if="visible" v-bind="providerConfig" :visible.sync="visible" />
|
||||
<CreateDialog v-if="visible" :visible.sync="visible" v-bind="providerConfig" />
|
||||
<Dialog
|
||||
v-if="updateVisible"
|
||||
:destroy-on-close="true"
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
qcloud, qcloud_lighthouse, qingcloud_private, scp, ucloud, vmware, volcengine, zstack
|
||||
} from '../const'
|
||||
import CreateDialog from './components/CreateDialog.vue'
|
||||
import SmallCard from '@/components/Table/CardTable/SmallCard.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 AssetPanel from './components/AssetPanel.vue'
|
||||
|
||||
@@ -1,58 +1,49 @@
|
||||
<template>
|
||||
<Page>
|
||||
<div v-if="this.$hasPerm('accounts.view_changesecretautomation')">
|
||||
|
||||
<div>
|
||||
<SwitchDate class="switch-date" @change="onChange" />
|
||||
</div>
|
||||
|
||||
<el-row type="flex">
|
||||
|
||||
<el-col :span="18">
|
||||
<CardSummary class="card-summary" :days="days" />
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<DataSummary class="data-summary" :days="days" style="margin-left: 1rem" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row type="flex" :gutter="24">
|
||||
|
||||
<el-col :span="14">
|
||||
<FailedAccountSummary :days="days" class="failed-account-summary" />
|
||||
</el-col>
|
||||
|
||||
<el-col :span="10">
|
||||
<AccountSummary class="account-summary" :days="days" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<div v-if="this.$hasPerm('accounts.view_changesecretautomation')">
|
||||
<div>
|
||||
<SwitchDate class="switch-date" @change="onChange" />
|
||||
</div>
|
||||
<Page403 v-else />
|
||||
</Page>
|
||||
|
||||
<el-row type="flex">
|
||||
|
||||
<el-col :span="18">
|
||||
<CardSummary :days="days" class="card-summary" />
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<DataSummary :days="days" class="data-summary" style="margin-left: 1rem" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24" type="flex">
|
||||
|
||||
<el-col :span="14">
|
||||
<FailedAccountSummary :days="days" class="failed-account-summary" />
|
||||
</el-col>
|
||||
|
||||
<el-col :span="10">
|
||||
<AccountSummary :days="days" class="account-summary" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Page } from '@/layout/components'
|
||||
import SwitchDate from '../components/SwitchDate'
|
||||
import FailedAccountSummary from './FailedAccountSummary.vue'
|
||||
import DataSummary from './DataSummary.vue'
|
||||
import CardSummary from './CardSummary.vue'
|
||||
import AccountSummary from './AccountSummary.vue'
|
||||
import Page403 from '@/views/403'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AccountSummary,
|
||||
Page,
|
||||
SwitchDate,
|
||||
DataSummary,
|
||||
FailedAccountSummary,
|
||||
CardSummary,
|
||||
Page403
|
||||
CardSummary
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<AccountListTable ref="table" v-bind="tableConfig" :origin="'pam'" />
|
||||
|
||||
<Drawer v-if="showTableDetailDrawer" :title="drawerTitle" @close-drawer="showTableDetailDrawer = !showTableDetailDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
import AssetDetail from '@/views/assets/Asset/AssetDetail'
|
||||
import { DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import AccountListTable from '@/components/Apps/AccountListTable/AccountList.vue'
|
||||
import AssetAccountDetail from '@/views/accounts/Account/AccountDetail/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
Drawer,
|
||||
AssetDetail,
|
||||
AccountListTable,
|
||||
AssetAccountDetail
|
||||
AccountListTable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showTableDetailDrawer: false,
|
||||
currentTemplate: null,
|
||||
drawerTitle: '',
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/accounts/',
|
||||
@@ -33,39 +22,28 @@ export default {
|
||||
hasImport: true,
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: (row) => {
|
||||
return (
|
||||
<span style={{ color: '#1c84c6', cursor: 'pointer' }} onClick={() => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
this.currentTemplate = 'AssetAccountDetail'
|
||||
this.showTableDetailDrawer = true
|
||||
this.drawerTitle = this.$t('AssetAccountDetail')
|
||||
}}>
|
||||
{row.name}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
isPam: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AssetAccountList',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
},
|
||||
formatter: DetailFormatter
|
||||
},
|
||||
asset: {
|
||||
formatter: (row) => {
|
||||
return (
|
||||
this.$hasPerm('assets.view_asset') ? (
|
||||
<span
|
||||
style={{ color: '#1c84c6', cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
this.$route.params.id = row.asset.id
|
||||
this.currentTemplate = 'AssetDetail'
|
||||
this.showTableDetailDrawer = true
|
||||
this.drawerTitle = this.$t('AssetDetail')
|
||||
}}
|
||||
>
|
||||
{row.name}
|
||||
</span>
|
||||
) : (
|
||||
<span>{row.asset ? row.asset.name : ''}</span>
|
||||
)
|
||||
)
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
isPam: true,
|
||||
can: this.$hasPerm('assets.view_asset'),
|
||||
getTitle: ({ row }) => row.asset.name,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset.id },
|
||||
query: { type: 'pam', tab: 'Basic' }
|
||||
})
|
||||
}
|
||||
},
|
||||
connect: {
|
||||
@@ -138,24 +116,4 @@ export default {
|
||||
.asset-user-table {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
::v-deep .page.tab-page {
|
||||
.page-heading .el-row--flex {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.page-heading-left .el-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
//.page-content {
|
||||
// height: 100% !important;
|
||||
// overflow-x: unset;
|
||||
//
|
||||
// .tab-page-content {
|
||||
// height: calc(100% - 120px);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListPage :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:account="account"
|
||||
:show-password-record="false"
|
||||
:url="secretUrl"
|
||||
type="template"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
<AccountTemplateTable />
|
||||
|
||||
<Drawer v-if="showTableUpdateDrawer" :title="drawerTitle" @close-drawer="showTableUpdateDrawer = !showTableUpdateDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
@@ -17,120 +9,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { ActionsFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret'
|
||||
import AccountTemplateDetail from '@/views/accounts/AccountTemplate/AccountTemplateDetail/index.vue'
|
||||
import AccountTemplateUpdate from '@/views/accounts/AccountTemplate/AccountTemplateCreateUpdate.vue'
|
||||
import AccountTemplateTable from '@/views/accounts/AccountTemplate/AccountTemplateTable.vue'
|
||||
// import AccountTemplateDetail from '@/views/accounts/AccountTemplate/AccountTemplateDetail/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountTemplateList',
|
||||
components: {
|
||||
AccountTemplateTable,
|
||||
Drawer,
|
||||
ViewSecret,
|
||||
GenericListPage,
|
||||
AccountTemplateUpdate,
|
||||
AccountTemplateDetail
|
||||
AccountTemplateUpdate: () => import('@/views/accounts/AccountTemplate/AccountTemplateCreateUpdate.vue')
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
showTableUpdateDrawer: false,
|
||||
drawerTitle: '',
|
||||
account: {},
|
||||
secretUrl: '',
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
columns: null,
|
||||
columnsExclude: ['spec_info', 'password_rules', 'push_params'],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: (row) => {
|
||||
return (
|
||||
<span style={{ color: '#1c84c6', cursor: 'pointer' }} onClick={() => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
this.currentTemplate = 'AccountTemplateDetail'
|
||||
this.showTableUpdateDrawer = true
|
||||
this.drawerTitle = this.$t('AccountTemplate')
|
||||
}}>
|
||||
{row.name}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
},
|
||||
privileged: {
|
||||
width: '120px',
|
||||
formatterArgs: {
|
||||
showText: false,
|
||||
showFalse: false
|
||||
}
|
||||
},
|
||||
has_secret: {
|
||||
formatterArgs: {
|
||||
showFalse: false,
|
||||
showText: false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
hasDelete: true,
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('View'),
|
||||
can: this.$hasPerm('accounts.view_accounttemplatesecret'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
vm.secretUrl = `/api/v1/accounts/account-template-secrets/${row.id}/`
|
||||
vm.account = row
|
||||
vm.showViewSecretDialog = false
|
||||
setTimeout(() => {
|
||||
vm.showViewSecretDialog = true
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
onUpdate: ({ row }) => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
this.currentTemplate = 'AccountTemplateUpdate'
|
||||
this.drawerTitle = this.$t('UpdateAccountTemplate')
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: this.$hasPerm('accounts.view_accounttemplatesecret'),
|
||||
hasMoreActions: false,
|
||||
hasLabelSearch: true,
|
||||
exportOptions: {
|
||||
url: '/api/v1/accounts/account-template-secrets/',
|
||||
mfaVerifyRequired: true,
|
||||
tips: this.$t('AccountExportTips')
|
||||
},
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AccountTemplateCreate',
|
||||
isPam: true
|
||||
}
|
||||
}
|
||||
}
|
||||
secretUrl: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,24 +9,21 @@
|
||||
|
||||
<script>
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
import AssetDetail from '@/views/assets/Asset/AssetDetail'
|
||||
import BaseList from '@/views/assets/Asset/AssetList/components/BaseList'
|
||||
|
||||
import HostUpdate from '@/views/assets/Asset/AssetCreateUpdate/HostCreateUpdate.vue'
|
||||
import { ActionsFormatter } from '@/components/Table/TableFormatters'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Drawer,
|
||||
BaseList,
|
||||
HostUpdate,
|
||||
AssetDetail
|
||||
HostUpdate: () => import('@/views/assets/Asset/AssetCreateUpdate/HostCreateUpdate.vue')
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawerTitle: '',
|
||||
currentTemplate: null,
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
config: {
|
||||
url: '/api/v1/assets/assets/',
|
||||
category: 'all'
|
||||
@@ -35,40 +32,35 @@ export default {
|
||||
columnsExclude: ['date_verified'],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: (row) => {
|
||||
return (
|
||||
<span style={{ color: '#1c84c6', cursor: 'pointer' }} onClick={() => {
|
||||
this.$route.params.id = row.id
|
||||
this.$route.query.tab = 'Basic'
|
||||
|
||||
this.currentTemplate = 'AssetDetail'
|
||||
this.showTableUpdateDrawer = true
|
||||
this.drawerTitle = this.$t('AssetDetail')
|
||||
}}>
|
||||
{row.name}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
isPam: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
},
|
||||
formatter: DetailFormatter
|
||||
},
|
||||
accounts_amount: {
|
||||
formatter: (row) => {
|
||||
return (
|
||||
<span style={{ color: '#1c84c6', cursor: 'pointer' }} onClick={() => {
|
||||
this.$route.params.id = row.id
|
||||
this.$route.query.tab = 'Account'
|
||||
|
||||
this.currentTemplate = 'AssetDetail'
|
||||
this.showTableUpdateDrawer = true
|
||||
this.drawerTitle = this.$t('AssetDetail')
|
||||
}}>
|
||||
{row.name}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
isPam: true,
|
||||
getTitle: ({ row }) => row.accounts_amount,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam', tab: 'Account' }
|
||||
})
|
||||
},
|
||||
formatter: DetailFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
isPam: true,
|
||||
updateRoute: 'HostUpdate',
|
||||
onUpdate: ({ row }) => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
@@ -80,7 +72,6 @@ export default {
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
{
|
||||
name: 'AccountTemplateList',
|
||||
title: this.$t('AccountTemplate'),
|
||||
icon: 'fa-copy',
|
||||
icon: 'template',
|
||||
component: () => import('@/views/pam/Account/AccountTemplate.vue')
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,51 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<SmallCard ref="table" v-bind="$data" />
|
||||
<ListTable ref="table" v-bind="$data" />
|
||||
|
||||
<Drawer v-if="showTableUpdateDrawer" :title="drawerTitle" @close-drawer="showTableUpdateDrawer = !showTableUpdateDrawer">
|
||||
<component :is="currentTemplate" />
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import SmallCard from '@/components/Table/CardTable/SmallCard.vue'
|
||||
import { toSafeLocalDateStr } from '@/utils/time'
|
||||
import Drawer from '@/components/Drawer/index.vue'
|
||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
||||
import CopyableFormatter from '@/components/Table/TableFormatters/CopyableFormatter.vue'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'CloudAccountList',
|
||||
components: {
|
||||
SmallCard
|
||||
Drawer,
|
||||
ListTable,
|
||||
IntegrationApplicationUpdate: () => import('@/views/pam/Integration/ApplicationCreateUpdate.vue'),
|
||||
IntegrationApplicationCreate: () => import('@/views/pam/Integration/ApplicationCreateUpdate.vue')
|
||||
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
drawerTitle: '',
|
||||
showTableUpdateDrawer: false,
|
||||
currentTemplate: null,
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/integration-applications/',
|
||||
columnsMeta: {
|
||||
id: {
|
||||
width: '300px',
|
||||
formatter: CopyableFormatter
|
||||
},
|
||||
logo: {
|
||||
width: '80px',
|
||||
formatter: (row) => {
|
||||
return (
|
||||
<img src={row.logo} alt={row.name}
|
||||
style='width: 40px; height: 40px; border-radius: 50%;'
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
accounts: {
|
||||
width: '100px',
|
||||
formatter: (row) => {
|
||||
return row.accounts_amount
|
||||
}
|
||||
},
|
||||
name: {
|
||||
formatterArgs: {
|
||||
isPam: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'IntegrationApplicationDetail',
|
||||
params: { id: row.id },
|
||||
query: { type: 'pam' }
|
||||
})
|
||||
},
|
||||
formatter: DetailFormatter
|
||||
},
|
||||
secret: {
|
||||
label: this.$t('Secret'),
|
||||
formatter: CopyableFormatter,
|
||||
formatterArgs: {
|
||||
shadow: true,
|
||||
getText: async function({ row }) {
|
||||
const app = await vm.$axios.get(`/api/v1/accounts/integration-applications/${row.id}/secret/`)
|
||||
return app.secret
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
isPam: true,
|
||||
updateRoute: 'IntegrationApplicationUpdate',
|
||||
onUpdate: ({ row }) => {
|
||||
this.$route.params.id = row.id
|
||||
|
||||
// 解决表单详情中的跳转
|
||||
this.$route.query.type = 'pam'
|
||||
|
||||
this.currentTemplate = 'IntegrationApplicationUpdate'
|
||||
this.drawerTitle = this.$t('IntegrationApplicationUpdate')
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
columnsExtra: ['secret'],
|
||||
columnsShow: {
|
||||
default: [
|
||||
'logo', 'id', 'secret', 'name', 'accounts', 'date_last_used', 'active'
|
||||
]
|
||||
},
|
||||
permissions: { app: 'accounts', resource: 'integrationapplication' }
|
||||
},
|
||||
headerActions: {
|
||||
hasImport: false,
|
||||
hasExport: false,
|
||||
hasColumnSetting: false,
|
||||
hasMoreActions: false,
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
}
|
||||
},
|
||||
subComponentProps: {
|
||||
getImage: (obj) => {
|
||||
return obj.logo
|
||||
},
|
||||
getInfos: (obj) => {
|
||||
return [
|
||||
{ title: `${this.$tc('RelevantApp')} ID`, content: obj.id },
|
||||
{ title: this.$tc('DataLastUsed'), content: toSafeLocalDateStr(obj.date_last_used) },
|
||||
{ title: this.$tc('AccountAmount'), content: obj.accounts_amount },
|
||||
{ title: this.$tc('Comment'), content: obj.comment || this.$tc('Nothing') }
|
||||
]
|
||||
},
|
||||
handleUpdate: (obj) => {
|
||||
this.$router.push({ name: 'IntegrationApplicationUpdate', params: { id: obj.id }})
|
||||
onCreate: () => {
|
||||
this.currentTemplate = 'IntegrationApplicationCreate'
|
||||
this.drawerTitle = this.$t('IntegrationApplicationCreate')
|
||||
this.showTableUpdateDrawer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,25 +3,36 @@
|
||||
<AssetTreeTable
|
||||
ref="AssetTreeTable"
|
||||
:header-actions="headerActions"
|
||||
:quick-filters="quickFilters"
|
||||
:quick-summary="quickSummary"
|
||||
:table-config="tableConfig"
|
||||
:tree-setting="treeSetting"
|
||||
/>
|
||||
<BatchResolveDialog :visible.sync="batchResolveDialog.visible" v-bind="batchResolveDialog" />
|
||||
<RiskScanDialog v-if="detectDialog.visible" :asset="detectDialog.asset" :visible.sync="detectDialog.visible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AssetTreeTable from '@/components/Apps/AssetTreeTable/index.vue'
|
||||
import RiskHandleFormatter from './RiskHandlerFormatter/index.vue'
|
||||
import BatchResolveDialog from '@/views/pam/RiskDetect/RiskHandlerFormatter/BatchResolveDialog.vue'
|
||||
import RiskScanDialog from './RiskScanDialog.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
RiskScanDialog,
|
||||
BatchResolveDialog,
|
||||
AssetTreeTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
gatherAccounts: [],
|
||||
scanVisible: false,
|
||||
detectDialog: {
|
||||
visible: false,
|
||||
asset: ''
|
||||
},
|
||||
treeSetting: {
|
||||
showMenu: true,
|
||||
showRefresh: true,
|
||||
@@ -37,42 +48,39 @@ export default {
|
||||
id: 'check',
|
||||
name: this.$t('Check'),
|
||||
icon: 'scan',
|
||||
callback: () => {}
|
||||
callback: (node) => {
|
||||
vm.detectDialog.asset = node.id
|
||||
setTimeout(() => {
|
||||
vm.detectDialog.visible = true
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
quickFilters: [
|
||||
quickSummary: [
|
||||
{
|
||||
label: '快速过滤',
|
||||
options: [
|
||||
{
|
||||
label: '未同步到资产',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: this.$t('最近一个月'),
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
title: '最近一周发现',
|
||||
filter: {
|
||||
'days': '7'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('最近发现'),
|
||||
options: [
|
||||
{
|
||||
label: '最近一天 (20)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: '最近一周 (300)',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: '最近一个月 (600)',
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
title: '最近一月发现',
|
||||
filter: {
|
||||
'days': '30'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '待处理',
|
||||
filter: {
|
||||
status: '0'
|
||||
}
|
||||
}
|
||||
],
|
||||
batchResolveDialog: {
|
||||
visible: false,
|
||||
risks: []
|
||||
},
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/account-risks/',
|
||||
columns: [
|
||||
@@ -115,7 +123,21 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false
|
||||
hasCreate: false,
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'resolveSelected',
|
||||
title: this.$t('ResolveSelected'),
|
||||
icon: 'el-icon-check',
|
||||
callback: function({ selectedRows }) {
|
||||
vm.batchResolveDialog.risks = selectedRows
|
||||
vm.batchResolveDialog.visible = true
|
||||
},
|
||||
can: function({ selectedRows }) {
|
||||
return selectedRows.length > 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<Dialog :destroy-on-close="true" :show-buttons="false" :title="$tc('ResolveSelected')" :visible.sync="iVisible">
|
||||
<div v-if="iVisible">
|
||||
<el-form class="el-form">
|
||||
<el-form-item class="risk-select" prop="selected">
|
||||
<el-select v-model="riskSelected" :placeholder="$t('Select risk')">
|
||||
<el-option
|
||||
v-for="item in riskTypes"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<HandleDropdown
|
||||
:cell-value="fakeCell"
|
||||
:changed="changed"
|
||||
:row="fakeRow"
|
||||
:rows="tableConfig.totalData"
|
||||
:value="1"
|
||||
class="risk-handler"
|
||||
@processDone="handleProcessDone"
|
||||
/>
|
||||
</el-form>
|
||||
<DataTable ref="table" :config="tableConfig" />
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import DataTable from '@/components/Table/DataTable/index.vue'
|
||||
import HandleDropdown from './index.vue'
|
||||
|
||||
export default {
|
||||
name: 'BatchResolveDialog',
|
||||
components: { DataTable, Dialog, HandleDropdown },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
risks: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
changed: false,
|
||||
riskSelected: '',
|
||||
fakeRow: {
|
||||
id: '',
|
||||
risk: {}
|
||||
},
|
||||
fakeCell: {
|
||||
value: '0',
|
||||
label: this.$t('Pending')
|
||||
},
|
||||
tableConfig: {
|
||||
totalData: [],
|
||||
columns: [
|
||||
{
|
||||
prop: 'asset',
|
||||
label: this.$t('Asset'),
|
||||
formatter: (row) => row.asset.name
|
||||
},
|
||||
{
|
||||
prop: 'username',
|
||||
label: this.$t('Username')
|
||||
},
|
||||
{
|
||||
prop: 'risk',
|
||||
label: this.$t('Risk'),
|
||||
formatter: (row) => row.risk.label
|
||||
},
|
||||
{
|
||||
prop: 'status',
|
||||
label: this.$t('Status'),
|
||||
formatter: (row) => row.status.label
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
},
|
||||
riskTypes() {
|
||||
const types = {}
|
||||
for (const item of this.unconfirmedRisks) {
|
||||
if (!types[item.risk.value]) {
|
||||
types[item.risk.value] = item.risk.label
|
||||
}
|
||||
}
|
||||
return Object.keys(types).map(key => ({ value: key, label: types[key] }))
|
||||
},
|
||||
unconfirmedRisks() {
|
||||
return this.risks.filter(item => item.status.value === '0')
|
||||
},
|
||||
dataTable() {
|
||||
return this.$refs.table.$refs.table
|
||||
},
|
||||
pageSize() {
|
||||
return this.dataTable.size
|
||||
},
|
||||
dataTableCurrentPage() {
|
||||
return this.dataTable.page
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
riskSelected(val) {
|
||||
if (val) {
|
||||
this.tableConfig.totalData = this.unconfirmedRisks.filter(item => item.risk.value === this.riskSelected)
|
||||
} else {
|
||||
this.tableConfig.totalData = this.unconfirmedRisks.filter(item => item.status.value === '0')
|
||||
}
|
||||
this.fakeRow.risk = {
|
||||
value: this.riskSelected
|
||||
}
|
||||
this.changed = true
|
||||
setTimeout(() => {
|
||||
this.changed = false
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.tableConfig.totalData = this.unconfirmedRisks
|
||||
},
|
||||
methods: {
|
||||
handleProcessDone({ index, row }) {
|
||||
const page = this.dataTable.page
|
||||
const size = this.dataTable.size
|
||||
const offset = Math.floor(index / size)
|
||||
if (page < offset + 1) {
|
||||
this.dataTable.gotoNextPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.el-form {
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.risk-select {
|
||||
display: inline-block;
|
||||
|
||||
::v-deep .el-form-item__content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.risk-handler {
|
||||
margin-left: 10px;
|
||||
|
||||
::v-deep button {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -9,18 +9,21 @@
|
||||
@open="handleOpen"
|
||||
>
|
||||
<div class="drawer-body">
|
||||
<el-timeline :reverse="true">
|
||||
<el-timeline-item
|
||||
v-for="detail in row.details"
|
||||
:key="detail.datetime"
|
||||
:icon="getDetailIcon(detail)"
|
||||
:timestamp="formatTimestamp(detail.datetime)"
|
||||
:type="getDetailType(detail)"
|
||||
placement="top"
|
||||
>
|
||||
<span v-html="handleDetail(row, detail)" />
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-for="r in iRows" :key="r.id">
|
||||
<div class="host-username">{{ r.asset ? r.asset.name : r }} - {{ r.username }}</div>
|
||||
<el-timeline :reverse="true">
|
||||
<el-timeline-item
|
||||
v-for="detail in r.details"
|
||||
:key="detail.datetime"
|
||||
:icon="getDetailIcon(detail)"
|
||||
:timestamp="formatTimestamp(detail.datetime)"
|
||||
:type="getDetailType(detail)"
|
||||
placement="top"
|
||||
>
|
||||
<span v-html="handleDetail(r, detail)" />
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showButtons" class="drawer-footer">
|
||||
<span class="buttons">
|
||||
@@ -49,6 +52,10 @@ export default {
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -70,6 +77,13 @@ export default {
|
||||
acc[cur.name] = cur
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
iRows() {
|
||||
if (this.rows.length === 0) {
|
||||
return [this.row]
|
||||
} else {
|
||||
return this.rows
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -142,9 +156,18 @@ ${detail.diff}
|
||||
height: calc(100% - 40px - 40px);
|
||||
overflow: auto;
|
||||
|
||||
::v-deep .el-drawer__body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
::v-deep pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.host-username {
|
||||
margin-left: 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-footer {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const riskActions = [
|
||||
{
|
||||
name: 'disable_remote',
|
||||
label: i18n.t('Disable remote'),
|
||||
has: ['long_time_no_login', 'new_found']
|
||||
},
|
||||
// {
|
||||
// name: 'disable_remote',
|
||||
// label: i18n.t('Disable remote'),
|
||||
// has: ['long_time_no_login', 'new_found']
|
||||
// },
|
||||
{
|
||||
name: 'delete_remote',
|
||||
label: i18n.t('Delete Account'),
|
||||
@@ -21,6 +21,22 @@ export const riskActions = [
|
||||
label: i18n.t('Add to Account'),
|
||||
has: ['new_found'],
|
||||
disabled: async function() {
|
||||
if (!this.row.username) {
|
||||
return false
|
||||
}
|
||||
const url = `/api/v1/accounts/accounts/?username=${this.row.username}&asset=${this.row.asset.id}`
|
||||
const data = await this.$axios.get(url)
|
||||
return data.length > 0
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'add_account_after_change_password',
|
||||
label: i18n.t('Add account after change password'),
|
||||
has: ['new_found'],
|
||||
disabled: async function() {
|
||||
if (!this.row.username) {
|
||||
return false
|
||||
}
|
||||
const url = `/api/v1/accounts/accounts/?username=${this.row.username}&asset=${this.row.asset.id}`
|
||||
const data = await this.$axios.get(url)
|
||||
return data.length > 0
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
{{ iLabel }}
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<ReviewDraw :row="row" :show-buttons="reviewButtons" :visible.sync="reviewDrawer" />
|
||||
<ReviewDraw :row="row" :rows="rows" :show-buttons="reviewButtons" :visible.sync="reviewDrawer" />
|
||||
<ProcessingDialog :visible="processing" />
|
||||
</span>
|
||||
</template>
|
||||
@@ -38,6 +38,7 @@ import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
|
||||
import ReviewDraw from '@/views/pam/RiskDetect/RiskHandlerFormatter/ReviewDraw.vue'
|
||||
import ProcessingDialog from '@/components/Dialog/ProcessingDialog.vue'
|
||||
import { riskActions } from './const'
|
||||
import { sleep } from '@/utils/time'
|
||||
|
||||
export default {
|
||||
name: 'RiskSummaryFormatter',
|
||||
@@ -47,6 +48,14 @@ export default {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
changed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -77,6 +86,11 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
changed() {
|
||||
this.handleVisibleChange(true)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showDetail() {
|
||||
this.reviewButtons = false
|
||||
@@ -87,27 +101,32 @@ export default {
|
||||
this.reviewDrawer = true
|
||||
},
|
||||
async handleCommon(cmd) {
|
||||
const data = {
|
||||
username: this.row.username,
|
||||
asset: this.row.asset.id,
|
||||
risk: this.row.risk.value,
|
||||
action: cmd
|
||||
let rows = this.rows
|
||||
if (this.rows.length === 0) {
|
||||
rows = [this.row]
|
||||
this.processing = true
|
||||
}
|
||||
this.processing = true
|
||||
this.$axios.post(`/api/v1/accounts/account-risks/handle/`, data).then(() => {
|
||||
if (cmd !== 'ignore') {
|
||||
this.row.status = { value: '1', label: this.$t('Confirmed') }
|
||||
} else {
|
||||
this.row.status = { value: '2', label: this.$t('Ignored') }
|
||||
|
||||
for (const [i, row] of Object.entries(rows)) {
|
||||
const data = {
|
||||
username: row.username,
|
||||
asset: row.asset.id,
|
||||
risk: row.risk.value,
|
||||
action: cmd
|
||||
}
|
||||
}).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.processing = false
|
||||
this.$axios.get(`/api/v1/accounts/account-risks/${this.row.id}/`).then((res) => {
|
||||
Object.assign(this.row, res)
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
row.status = { value: '3', label: this.$t('Processing') }
|
||||
await this.$axios.post(`/api/v1/accounts/account-risks/handle/`, data)
|
||||
await sleep(100)
|
||||
if (cmd !== 'ignore') {
|
||||
row.status = { value: '1', label: this.$t('Confirmed') }
|
||||
} else {
|
||||
row.status = { value: '2', label: this.$t('Ignored') }
|
||||
}
|
||||
this.$emit('processDone', { index: i, row })
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.processing = false
|
||||
}, 500)
|
||||
},
|
||||
handleRisk(cmd) {
|
||||
if (cmd === 'review') {
|
||||
@@ -125,9 +144,9 @@ export default {
|
||||
},
|
||||
async handleVisibleChange(visible) {
|
||||
if (!visible) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
if (this.actions.length === 0) {
|
||||
if (this.actions.length === 0 || this.changed === true) {
|
||||
this.actions = await this.getActions()
|
||||
}
|
||||
return this.actions.length > 0
|
||||
|
||||
62
src/views/pam/RiskDetect/RiskScanDialog.vue
Normal file
62
src/views/pam/RiskDetect/RiskScanDialog.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="iVisible"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="$tc('Detecting')"
|
||||
:visible.sync="iVisible"
|
||||
top="35vh"
|
||||
width="80%"
|
||||
@close="loading=true"
|
||||
>
|
||||
<span v-if="loading" v-loading="loading" class="loading" />
|
||||
<iframe :src="url" frameborder="0" @load="onIframeLoad" />
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
export default {
|
||||
name: 'RiskScanDialog',
|
||||
components: { Dialog },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
asset: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
url: `/api/v1/accounts/check-account-executions/adhoc/?asset_id=${this.asset}`
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onIframeLoad() {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -21,7 +21,6 @@ export default {
|
||||
return {
|
||||
group: { name: '', comment: '', users: [] },
|
||||
config: {
|
||||
url: '/api/v1/users/groups',
|
||||
activeMenu: 'GroupInfo',
|
||||
submenu: [
|
||||
{
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-if="!loading" class="user-create-update" v-bind="$data" @getObjectDone="afterGetUser" />
|
||||
<GenericCreateUpdatePage
|
||||
v-if="!loading"
|
||||
class="user-create-update"
|
||||
v-bind="$data"
|
||||
@getObjectDone="afterGetUser"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -164,14 +170,6 @@ export default {
|
||||
el: {}
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
},
|
||||
afterGetFormValue(obj) {
|
||||
if (obj?.id) {
|
||||
obj.org_roles = obj.org_roles?.map(({ id }) => id)
|
||||
|
||||
@@ -263,9 +263,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mfa-setting ::v-deep .el-slider__runway {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.mfa-setting ::v-deep .el-slider__runway {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListPage
|
||||
<GenericListDrawerPage
|
||||
ref="GenericListPage"
|
||||
:header-actions="headerActions"
|
||||
:quick-filters="quickFilters"
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { GenericListPage, GenericUpdateFormDialog } from '@/layout/components'
|
||||
import { GenericUpdateFormDialog } from '@/layout/components'
|
||||
import GenericListDrawerPage from '@/layout/components/GenericListDrawerPage/index.vue'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import { getDayFuture } from '@/utils/time'
|
||||
import InviteUsersDialog from './components/InviteUsersDialog'
|
||||
@@ -28,7 +29,7 @@ import AmountFormatter from '@/components/Table/TableFormatters/AmountFormatter.
|
||||
export default {
|
||||
components: {
|
||||
InviteUsersDialog,
|
||||
GenericListPage,
|
||||
GenericListDrawerPage,
|
||||
GenericUpdateFormDialog
|
||||
},
|
||||
data() {
|
||||
@@ -43,6 +44,8 @@ export default {
|
||||
return !vm.currentOrgIsRoot
|
||||
}
|
||||
return {
|
||||
createDrawer: () => import('@/views/users/User/UserCreateUpdate.vue'),
|
||||
detailDrawer: () => import('@/views/users/User/UserDetail/index.vue'),
|
||||
quickFilters: [
|
||||
{
|
||||
label: '快速筛选',
|
||||
|
||||
177
yarn.lock
177
yarn.lock
@@ -1086,6 +1086,11 @@
|
||||
dependencies:
|
||||
katex "^0.16.0"
|
||||
|
||||
"@trysound/sax@0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
||||
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||
|
||||
"@types/glob@^7.1.1":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.npmmirror.com/@types/glob/-/glob-7.2.0.tgz"
|
||||
@@ -3339,6 +3344,11 @@ commander@^6.1.0, commander@^6.2.0:
|
||||
resolved "https://registry.npmmirror.com/commander/-/commander-6.2.1.tgz"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
||||
commander@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||
|
||||
commander@^8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz"
|
||||
@@ -3738,6 +3748,17 @@ css-select@^4.1.3:
|
||||
domutils "^2.8.0"
|
||||
nth-check "^2.0.1"
|
||||
|
||||
css-select@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.npmmirror.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
|
||||
integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
css-what "^6.1.0"
|
||||
domhandler "^5.0.2"
|
||||
domutils "^3.0.1"
|
||||
nth-check "^2.0.1"
|
||||
|
||||
css-selector-tokenizer@^0.7.0:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.npmmirror.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz"
|
||||
@@ -3746,22 +3767,6 @@ css-selector-tokenizer@^0.7.0:
|
||||
cssesc "^3.0.0"
|
||||
fastparse "^1.1.2"
|
||||
|
||||
css-tree@1.0.0-alpha.28:
|
||||
version "1.0.0-alpha.28"
|
||||
resolved "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz"
|
||||
integrity sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==
|
||||
dependencies:
|
||||
mdn-data "~1.1.0"
|
||||
source-map "^0.5.3"
|
||||
|
||||
css-tree@1.0.0-alpha.29:
|
||||
version "1.0.0-alpha.29"
|
||||
resolved "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz"
|
||||
integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==
|
||||
dependencies:
|
||||
mdn-data "~1.1.0"
|
||||
source-map "^0.5.3"
|
||||
|
||||
css-tree@1.0.0-alpha.37:
|
||||
version "1.0.0-alpha.37"
|
||||
resolved "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz"
|
||||
@@ -3778,17 +3783,28 @@ css-tree@^1.1.2:
|
||||
mdn-data "2.0.14"
|
||||
source-map "^0.6.1"
|
||||
|
||||
css-url-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/css-url-regex/-/css-url-regex-1.1.0.tgz"
|
||||
integrity sha512-hLKuvifwoKvwqpctblTp0BovBuOXzxof8JgkA8zeqxxL+vcynHQjtIqqlFfQI1gEAZAjbqKm9gFTa88fxTAX4g==
|
||||
css-tree@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
|
||||
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
|
||||
dependencies:
|
||||
mdn-data "2.0.30"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
css-tree@~2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032"
|
||||
integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==
|
||||
dependencies:
|
||||
mdn-data "2.0.28"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
css-what@^3.2.1:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.npmmirror.com/css-what/-/css-what-3.4.2.tgz"
|
||||
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
|
||||
|
||||
css-what@^6.0.1:
|
||||
css-what@^6.0.1, css-what@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz"
|
||||
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
|
||||
@@ -3886,13 +3902,6 @@ cssnano@^4.0.0, cssnano@^4.1.10:
|
||||
is-resolvable "^1.0.0"
|
||||
postcss "^7.0.0"
|
||||
|
||||
csso@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.npmmirror.com/csso/-/csso-3.5.1.tgz"
|
||||
integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==
|
||||
dependencies:
|
||||
css-tree "1.0.0-alpha.29"
|
||||
|
||||
csso@^4.0.2:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz"
|
||||
@@ -3900,6 +3909,13 @@ csso@^4.0.2:
|
||||
dependencies:
|
||||
css-tree "^1.1.2"
|
||||
|
||||
csso@^5.0.5:
|
||||
version "5.0.5"
|
||||
resolved "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6"
|
||||
integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==
|
||||
dependencies:
|
||||
css-tree "~2.2.0"
|
||||
|
||||
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
|
||||
version "0.3.8"
|
||||
resolved "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz"
|
||||
@@ -4295,6 +4311,15 @@ dom-serializer@^1.0.1:
|
||||
domhandler "^4.2.0"
|
||||
entities "^2.0.0"
|
||||
|
||||
dom-serializer@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
|
||||
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.2"
|
||||
entities "^4.2.0"
|
||||
|
||||
domain-browser@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmmirror.com/domain-browser/-/domain-browser-1.2.0.tgz"
|
||||
@@ -4305,7 +4330,7 @@ domelementtype@1, domelementtype@^1.3.1:
|
||||
resolved "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz"
|
||||
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
|
||||
|
||||
domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||
domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz"
|
||||
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||
@@ -4331,6 +4356,13 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1:
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
domhandler@^5.0.2, domhandler@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
|
||||
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
|
||||
dompurify@^3.1.6:
|
||||
version "3.1.6"
|
||||
resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.1.6.tgz"
|
||||
@@ -4358,6 +4390,15 @@ domutils@^2.5.2, domutils@^2.8.0:
|
||||
domelementtype "^2.2.0"
|
||||
domhandler "^4.2.0"
|
||||
|
||||
domutils@^3.0.1:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmmirror.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
|
||||
integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
|
||||
dependencies:
|
||||
dom-serializer "^2.0.0"
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
|
||||
dot-object@^2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.npmmirror.com/dot-object/-/dot-object-2.1.4.tgz"
|
||||
@@ -4554,6 +4595,11 @@ entities@^3.0.1, entities@~3.0.1:
|
||||
resolved "https://registry.npmmirror.com/entities/-/entities-3.0.1.tgz"
|
||||
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
|
||||
|
||||
entities@^4.2.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz"
|
||||
@@ -8509,16 +8555,21 @@ mdn-data@2.0.14:
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz"
|
||||
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||
|
||||
mdn-data@2.0.28:
|
||||
version "2.0.28"
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
|
||||
integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==
|
||||
|
||||
mdn-data@2.0.30:
|
||||
version "2.0.30"
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
|
||||
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
|
||||
|
||||
mdn-data@2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.4.tgz"
|
||||
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
|
||||
|
||||
mdn-data@~1.1.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-1.1.4.tgz"
|
||||
integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==
|
||||
|
||||
mdurl@^1.0.1, mdurl@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz"
|
||||
@@ -9227,7 +9278,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz"
|
||||
integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
|
||||
|
||||
npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2, npm-package-arg@^8.1.4, npm-package-arg@^8.1.5:
|
||||
npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.1, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5:
|
||||
version "8.1.5"
|
||||
resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz"
|
||||
integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==
|
||||
@@ -9733,7 +9784,7 @@ p-try@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz"
|
||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||
|
||||
pacote@^11.1.11, pacote@^11.2.6, pacote@^11.3.1, pacote@^11.3.4, pacote@^11.3.5:
|
||||
pacote@^11.1.11, pacote@^11.2.6, pacote@^11.3.0, pacote@^11.3.1, pacote@^11.3.5:
|
||||
version "11.3.5"
|
||||
resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz"
|
||||
integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg==
|
||||
@@ -11812,6 +11863,11 @@ source-list-map@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/source-list-map/-/source-list-map-2.0.1.tgz"
|
||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||
|
||||
source-map-js@^1.0.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz"
|
||||
@@ -12060,7 +12116,7 @@ string-length@^2.0.0:
|
||||
astral-regex "^1.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@@ -12078,15 +12134,6 @@ string-width@^1.0.1:
|
||||
is-fullwidth-code-point "^1.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-2.1.1.tgz"
|
||||
@@ -12187,7 +12234,7 @@ stringify-package@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz"
|
||||
integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@@ -12215,13 +12262,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
||||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz"
|
||||
@@ -12363,25 +12403,18 @@ svg-tags@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz"
|
||||
integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==
|
||||
|
||||
svgo@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmmirror.com/svgo/-/svgo-1.2.2.tgz"
|
||||
integrity sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA==
|
||||
svgo@1.2.4:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.npmmirror.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8"
|
||||
integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==
|
||||
dependencies:
|
||||
chalk "^2.4.1"
|
||||
coa "^2.0.2"
|
||||
css-select "^2.0.0"
|
||||
css-select-base-adapter "^0.1.1"
|
||||
css-tree "1.0.0-alpha.28"
|
||||
css-url-regex "^1.1.0"
|
||||
csso "^3.5.1"
|
||||
js-yaml "^3.13.1"
|
||||
mkdirp "~0.5.1"
|
||||
object.values "^1.1.0"
|
||||
sax "~1.2.4"
|
||||
stable "^0.1.8"
|
||||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
"@trysound/sax" "0.2.0"
|
||||
commander "^7.2.0"
|
||||
css-select "^5.1.0"
|
||||
css-tree "^2.3.1"
|
||||
css-what "^6.1.0"
|
||||
csso "^5.0.5"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
svgo@^1.0.0:
|
||||
version "1.3.2"
|
||||
|
||||
Reference in New Issue
Block a user