mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-13 19:35:24 +00:00
Compare commits
25 Commits
v4.0
...
repr@dev_v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
200b83c105 | ||
|
|
d22079446f | ||
|
|
d57b99b00c | ||
|
|
e7321356af | ||
|
|
a0edc2c527 | ||
|
|
10ebcfd64d | ||
|
|
a3fedb9697 | ||
|
|
1eb8a18c66 | ||
|
|
f491c57c34 | ||
|
|
50b7f54652 | ||
|
|
5bf298a5bf | ||
|
|
124ff9a8c2 | ||
|
|
387ab4f1b3 | ||
|
|
d6fbdfa7ea | ||
|
|
cd4260fd8d | ||
|
|
baeece62d3 | ||
|
|
576c8f5891 | ||
|
|
5e97600807 | ||
|
|
4e2eb3a37d | ||
|
|
e92173f8e8 | ||
|
|
412d9c804e | ||
|
|
b84a725d4a | ||
|
|
58b39743e0 | ||
|
|
7651443c25 | ||
|
|
8d64e331d1 |
@@ -1,4 +1,4 @@
|
||||
FROM node:16.20-bullseye-slim as stage-build
|
||||
FROM node:16.20-bullseye-slim AS stage-build
|
||||
ARG TARGETARCH
|
||||
|
||||
ARG DEPENDENCIES=" \
|
||||
|
||||
@@ -217,7 +217,7 @@ export default {
|
||||
|
||||
.el-form-item__label {
|
||||
padding: 0 30px 0 0;
|
||||
line-height: 32px;
|
||||
line-height: 30px;
|
||||
color: var(--color-text-primary);
|
||||
|
||||
i {
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="rawValue.phone" required :placeholder="$tc('InputPhone')" @input="OnInputChange">
|
||||
<el-input v-model="rawValue.phone" :placeholder="$tc('InputPhone')" required @input="onInputChange">
|
||||
<el-select
|
||||
slot="prepend"
|
||||
:placeholder="$tc('Select')"
|
||||
:value="rawValue.code"
|
||||
style="width: 75px;"
|
||||
@change="OnChange"
|
||||
style="width: 105px;"
|
||||
@change="onChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="country in countries"
|
||||
:key="country.value"
|
||||
:key="country.name"
|
||||
:label="country.value"
|
||||
:value="country.value"
|
||||
style="width: 200px;"
|
||||
>
|
||||
<span style="float: left">{{ country.name }}</span>
|
||||
<span class="country-name">{{ country.name }}</span>
|
||||
<span style="float: right; font-size: 13px">{{ country.value }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
@@ -24,19 +23,19 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'PhoneInput',
|
||||
props: {
|
||||
value: {
|
||||
type: [Object, String],
|
||||
default: () => ({ 'code': '+86', 'phone': '' })
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rawValue: {}
|
||||
rawValue: {},
|
||||
countries: [{ 'name': 'China', 'value': '+86' }]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -45,26 +44,38 @@ export default {
|
||||
return ''
|
||||
}
|
||||
return `${this.rawValue.code}${this.rawValue.phone}`
|
||||
},
|
||||
countries: {
|
||||
get() {
|
||||
return this.publicSettings.COUNTRY_CALLING_CODES
|
||||
}
|
||||
},
|
||||
...mapGetters(['publicSettings'])
|
||||
},
|
||||
mounted() {
|
||||
this.rawValue = this.value || { code: '+86', phone: '' }
|
||||
const defaults = { code: localStorage.getItem('prePhoneCode') || '+86', phone: '' }
|
||||
this.rawValue = this.value || defaults
|
||||
this.$axios.get('/api/v1/common/countries/').then(res => {
|
||||
this.countries = res.map(item => {
|
||||
return { name: `${item.flag} ${item.name}`, value: item.phone_code }
|
||||
})
|
||||
})
|
||||
this.$emit('input', this.fullPhone)
|
||||
},
|
||||
methods: {
|
||||
OnChange(countryCode) {
|
||||
onChange(countryCode) {
|
||||
this.rawValue.code = countryCode
|
||||
this.OnInputChange()
|
||||
this.onInputChange()
|
||||
localStorage.setItem('prePhoneCode', countryCode)
|
||||
},
|
||||
OnInputChange() {
|
||||
onInputChange() {
|
||||
this.$emit('input', this.fullPhone)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.country-name {
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-right: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -144,9 +144,6 @@ export default {
|
||||
watch: {
|
||||
choices: {
|
||||
handler(value, oldValue) {
|
||||
if (value?.length === oldValue?.length) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
setTimeout(() => {
|
||||
this.setDefaultItems(value)
|
||||
@@ -345,6 +342,10 @@ export default {
|
||||
.protocol-item {
|
||||
display: flex;
|
||||
margin: 5px 0;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.input-button {
|
||||
|
||||
@@ -124,6 +124,9 @@ export default {
|
||||
return this.iHasLeftActions ? 'right' : 'left'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('done')
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
:selected-rows="selectedRows"
|
||||
:table-url="tableUrl"
|
||||
v-bind="iHeaderActions"
|
||||
@done="handleActionInitialDone"
|
||||
/>
|
||||
<IBox class="table-content">
|
||||
<AutoDataTable
|
||||
v-if="actionInit"
|
||||
ref="dataTable"
|
||||
:config="iTableConfig"
|
||||
:filter-table="filter"
|
||||
@@ -73,9 +75,11 @@ export default {
|
||||
return {
|
||||
selectedRows: [],
|
||||
init: false,
|
||||
extraQuery: extraQuery,
|
||||
urlUpdated: {},
|
||||
isDeactivated: false
|
||||
isDeactivated: false,
|
||||
extraQuery: extraQuery,
|
||||
actionInit: this.headerActions.has === false,
|
||||
initQuery: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -203,13 +207,35 @@ export default {
|
||||
}, 500)
|
||||
},
|
||||
methods: {
|
||||
handleActionInitialDone() {
|
||||
setTimeout(() => {
|
||||
this.actionInit = true
|
||||
}, 100)
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.selectedRows = val
|
||||
},
|
||||
reloadTable() {
|
||||
this.dataTable?.getList()
|
||||
},
|
||||
updateInitQuery(attrs) {
|
||||
if (!this.actionInit) {
|
||||
this.initQuery = attrs
|
||||
for (const key in attrs) {
|
||||
this.$set(this.extraQuery, key, attrs[key])
|
||||
}
|
||||
return true
|
||||
}
|
||||
const removeKeys = Object.keys(this.initQuery).filter(key => !attrs[key])
|
||||
for (const key of removeKeys) {
|
||||
this.$delete(this.extraQuery, key)
|
||||
}
|
||||
},
|
||||
search(attrs) {
|
||||
const init = this.updateInitQuery(attrs)
|
||||
if (init) {
|
||||
return
|
||||
}
|
||||
this.$log.debug('ListTable: search table', attrs)
|
||||
this.$emit('TagSearch', attrs)
|
||||
this.$refs.dataTable?.$refs.dataTable?.search(attrs, true)
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<span v-if="!iLabels || iLabels.length === 0" style="vertical-align: top;">
|
||||
-
|
||||
</span>
|
||||
<div v-else>
|
||||
<div
|
||||
<span v-else class="label-wrapper">
|
||||
<span
|
||||
v-for="label of iLabels"
|
||||
:key="label.id"
|
||||
>
|
||||
@@ -15,8 +15,9 @@
|
||||
class="tag-formatter"
|
||||
@click="handleLabelSearch(label)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span />
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="formatterArgs.showEditBtn"
|
||||
@@ -30,15 +31,16 @@
|
||||
v-if="showDialog"
|
||||
:title="$tc('BindLabel')"
|
||||
:visible.sync="showDialog"
|
||||
class="tag-dialog"
|
||||
width="600px"
|
||||
@cancel="handleCancel"
|
||||
@confirm="handleConfirm"
|
||||
>
|
||||
<el-row :gutter="1" class="tag-select">
|
||||
<el-row class="tag-select">
|
||||
<el-col :span="12">
|
||||
<Select2 v-model="keySelect2.value" v-bind="keySelect2" @change="handleKeyChanged" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :span="12" style="padding-left: 5px">
|
||||
<Select2
|
||||
v-model="valueSelect2.value"
|
||||
:disabled="!keySelect2.value"
|
||||
@@ -207,18 +209,11 @@ export default {
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-select {
|
||||
::v-deep .el-input__inner::placeholder {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
@@ -233,9 +228,14 @@ export default {
|
||||
|
||||
.label-container {
|
||||
display: flex;
|
||||
height: 28px;
|
||||
|
||||
.label-formatter-col {
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -249,6 +249,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.tag-dialog {
|
||||
.tag-zone {
|
||||
margin: 20px 0 0 0;
|
||||
border: solid 1px #ebeef5;
|
||||
@@ -261,6 +262,13 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.tag-select {
|
||||
::v-deep .el-input__inner::placeholder {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-button .el-button.el-button--mini {
|
||||
padding: 5px;
|
||||
height: 28px;
|
||||
@@ -270,7 +278,6 @@ export default {
|
||||
|
||||
.tag-formatter {
|
||||
margin: 2px 0;
|
||||
|
||||
}
|
||||
|
||||
.tag-tip {
|
||||
|
||||
@@ -55,8 +55,9 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tag {
|
||||
display: flex;
|
||||
display: inline-block;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
methods: {
|
||||
async handleSelectView(key, keyPath) {
|
||||
const routeName = this.viewsMapper[key] || '/'
|
||||
localStorage.setItem('PreView', key)
|
||||
localStorage.setItem('preView', key)
|
||||
// Next 之前要重置 init 状态,否则这些路由守卫就不走了
|
||||
await store.dispatch('app/reset')
|
||||
if (!this.tipHasRead) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
return this.$t('LicenseExpired')
|
||||
}
|
||||
if (intervalDays < 7) {
|
||||
return this.$t('LicenseWillBe') + this.licenseData.date_expired + this.$t('Expire')
|
||||
return this.$t('LicenseWillBe') + ' ' + this.licenseData.date_expired + ' ' + this.$t('Expire')
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
@@ -201,7 +201,7 @@ export default [
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('CommandGroupDetail'),
|
||||
activeMenu: ''
|
||||
activeMenu: '/console/perms/acls/cmd-acls'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -586,3 +586,10 @@ li.rmenu i.fa {
|
||||
color: var(--color-link);
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__row {
|
||||
::-webkit-scrollbar {
|
||||
width: 6px; /* 设置垂直滚动条的宽度 */
|
||||
height: 6px; /* 设置水平滚动条的高度 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import VueCookie from 'vue-cookie'
|
||||
|
||||
const CURRENT_ORG_KEY = 'jms_current_org'
|
||||
const CURRENT_ROLE_KEY = 'jms_current_role'
|
||||
const CURRENT_ORG_KEY = 'currentOrg'
|
||||
const CURRENT_ROLE_KEY = 'currentRole'
|
||||
let cookieNamePrefix = VueCookie.get('SESSION_COOKIE_NAME_PREFIX')
|
||||
if (!cookieNamePrefix || ['""', "''"].indexOf(cookieNamePrefix) > -1) {
|
||||
cookieNamePrefix = ''
|
||||
@@ -17,7 +17,7 @@ export function setTokenToCookie(value, expires) {
|
||||
}
|
||||
|
||||
export function getCurrentRoleLocal(username) {
|
||||
const key = CURRENT_ROLE_KEY + '_' + username
|
||||
const key = CURRENT_ROLE_KEY + ':' + username
|
||||
const role = localStorage.getItem(key)
|
||||
if (role) {
|
||||
return parseInt(role) || null
|
||||
@@ -26,12 +26,12 @@ export function getCurrentRoleLocal(username) {
|
||||
}
|
||||
|
||||
export function saveCurrentRoleLocal(username, role) {
|
||||
const key = CURRENT_ROLE_KEY + '_' + username
|
||||
const key = CURRENT_ROLE_KEY + ':' + username
|
||||
return localStorage.setItem(key, role)
|
||||
}
|
||||
|
||||
export function getCurrentOrgLocal(username) {
|
||||
const key = CURRENT_ORG_KEY + '_' + username
|
||||
const key = CURRENT_ORG_KEY + ':' + username
|
||||
const value = localStorage.getItem(key)
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
@@ -41,18 +41,18 @@ export function getCurrentOrgLocal(username) {
|
||||
}
|
||||
|
||||
export function saveCurrentOrgLocal(username, org) {
|
||||
const key = CURRENT_ORG_KEY + '_' + username
|
||||
const key = CURRENT_ORG_KEY + ':' + username
|
||||
localStorage.setItem(key, JSON.stringify(org))
|
||||
VueCookie.set('X-JMS-ORG', org.id)
|
||||
}
|
||||
|
||||
export function setPreOrgLocal(username, org) {
|
||||
const key = 'PRE_ORG_' + username
|
||||
const key = 'preOrg' + ':' + username
|
||||
localStorage.setItem(key, JSON.stringify(org))
|
||||
}
|
||||
|
||||
export function getPreOrgLocal(username) {
|
||||
const key = 'PRE_ORG_' + username
|
||||
const key = 'preOrg' + ':' + username
|
||||
const value = localStorage.getItem(key)
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
|
||||
@@ -109,7 +109,7 @@ export function isSameView(to, from) {
|
||||
|
||||
export function getPropView() {
|
||||
const hasPermedViews = getPermedViews()
|
||||
const preView = localStorage.getItem('PreView')
|
||||
const preView = localStorage.getItem('preView')
|
||||
const hasPerm = hasPermedViews.indexOf(preView) > -1
|
||||
if (hasPerm) {
|
||||
return preView
|
||||
|
||||
@@ -164,8 +164,8 @@ export default {
|
||||
},
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'BatchRetry',
|
||||
title: this.$t('BatchRetry'),
|
||||
name: 'RetrySelected',
|
||||
title: this.$t('RetrySelected'),
|
||||
type: 'primary',
|
||||
fa: 'fa-retweet',
|
||||
can: ({ selectedRows }) => {
|
||||
|
||||
@@ -164,8 +164,8 @@ export default {
|
||||
},
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'BatchRetry',
|
||||
title: this.$t('BatchRetry'),
|
||||
name: 'RetrySelected',
|
||||
title: this.$t('RetrySelected'),
|
||||
type: 'primary',
|
||||
fa: 'fa-retweet',
|
||||
can: ({ selectedRows }) => {
|
||||
|
||||
@@ -68,11 +68,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
setTimeout(() => {
|
||||
this.$refs.listTable.reloadTable()
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -32,15 +32,9 @@ export default {
|
||||
title: this.$t('Basic'),
|
||||
name: 'Detail'
|
||||
}
|
||||
],
|
||||
actions: {
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -53,11 +53,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -36,8 +36,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
handleTabClick(tab) {
|
||||
const query = _.cloneDeep(this.$route.query)
|
||||
|
||||
@@ -47,6 +47,7 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
platform: {},
|
||||
changePlatformID: '',
|
||||
defaultConfig: {
|
||||
initial: {},
|
||||
platform: {},
|
||||
@@ -131,7 +132,7 @@ export default {
|
||||
const { defaultConfig } = this
|
||||
const { node, platform } = this.$route?.query || {}
|
||||
const nodesInitial = node ? [node] : []
|
||||
const platformId = platform || 'Linux'
|
||||
const platformId = this.changePlatformID ? this.changePlatformID : (platform || 'Linux')
|
||||
const url = `/api/v1/assets/platforms/${platformId}/`
|
||||
this.platform = await this.$axios.get(url)
|
||||
const initial = {
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
@click.native="createAsset(platform)"
|
||||
>
|
||||
<img :src="loadImage(platform)" alt="icon" class="asset-icon">
|
||||
<el-tooltip :content="platform.name">
|
||||
<span class="platform-name">{{ platform.name }}</span>
|
||||
</el-tooltip>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-collapse-item>
|
||||
@@ -195,7 +197,11 @@ export default {
|
||||
margin: 5px 0;
|
||||
|
||||
& ::v-deep .el-card__body {
|
||||
padding: 10px
|
||||
padding: 10px;
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
border-left: solid 4px;
|
||||
@@ -234,10 +240,12 @@ export default {
|
||||
height: 1.5em;
|
||||
vertical-align: -0.2em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.platform-name {
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -29,10 +29,10 @@ export const filterSelectValues = (values) => {
|
||||
function updatePlatformProtocols(vm, platformType, updateForm, isPlatformChanged = false) {
|
||||
setTimeout(() => vm.init().then(() => {
|
||||
const isCreate = vm?.$route?.meta.action === 'create' && vm?.$route?.query.clone_from === undefined
|
||||
if (platformType === 'website') {
|
||||
const need_modify = isCreate || isPlatformChanged
|
||||
if (!need_modify) return
|
||||
const platformProtocols = vm.platform.protocols
|
||||
if (!need_modify) return
|
||||
if (platformType === 'website') {
|
||||
const setting = Array.isArray(platformProtocols) ? platformProtocols[0].setting : platformProtocols.setting
|
||||
updateForm({
|
||||
'autofill': setting.autofill ? setting.autofill : 'basic',
|
||||
@@ -42,6 +42,8 @@ function updatePlatformProtocols(vm, platformType, updateForm, isPlatformChanged
|
||||
'username_selector': setting.username_selector
|
||||
})
|
||||
}
|
||||
vm.iConfig.fieldsMeta.protocols.el.choices = platformProtocols.map(item => ({ name: item.name, port: item.port }))
|
||||
updateForm({ protocols: [] })
|
||||
}), 100)
|
||||
}
|
||||
|
||||
@@ -100,12 +102,10 @@ export const assetFieldsMeta = (vm) => {
|
||||
change: ([event], updateForm) => {
|
||||
const pk = event.pk
|
||||
const url = window.location.href
|
||||
const newURL = url.replace(/platform=[^&]*/, 'platform=' + pk)
|
||||
|
||||
vm.changePlatformID = pk
|
||||
if (url.includes('clone')) {
|
||||
updatePlatformProtocols(vm, platformType, updateForm, true)
|
||||
} else {
|
||||
window.history.replaceState(null, null, newURL)
|
||||
vm.$nextTick(() => {
|
||||
updatePlatformProtocols(vm, platformType, updateForm, true)
|
||||
})
|
||||
|
||||
@@ -430,7 +430,7 @@ export default {
|
||||
}
|
||||
createJob(data).then(res => {
|
||||
this.executionInfo.timeCost = 0
|
||||
this.executionInfo.status = 'running'
|
||||
this.executionInfo.status = { value: 'running', label: this.$t('Running') }
|
||||
this.currentTaskId = res.task_id
|
||||
this.xtermConfig = { taskId: this.currentTaskId, type: 'shortcut_cmd' }
|
||||
this.setCostTimeInterval()
|
||||
@@ -451,12 +451,12 @@ export default {
|
||||
})
|
||||
},
|
||||
setBtn() {
|
||||
if (this.executionInfo.status !== 'running') {
|
||||
if (this.executionInfo.status.value !== 'running') {
|
||||
clearInterval(this.executionInfo.cancel)
|
||||
this.toolbar.left.run.icon = 'fa fa-play'
|
||||
}
|
||||
this.toolbar.left.run.isVisible = this.executionInfo.status === 'running'
|
||||
this.toolbar.left.stop.isVisible = this.executionInfo.status !== 'running'
|
||||
this.toolbar.left.run.isVisible = this.executionInfo.status.value === 'running'
|
||||
this.toolbar.left.stop.isVisible = this.executionInfo.status.value !== 'running'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
<span class="status-item">
|
||||
<span>{{ $tc('Status') }}: </span>
|
||||
<span
|
||||
:class="{'status_success':executionInfo.status==='success',
|
||||
'status_warning':executionInfo.status==='timeout',
|
||||
'status_danger':executionInfo.status==='failed'
|
||||
:class="{'status_success':executionInfo.status.value==='success',
|
||||
'status_warning':executionInfo.status.value==='timeout',
|
||||
'status_danger':executionInfo.status.value==='failed'
|
||||
}"
|
||||
>{{ $tc('' + executionInfo.status) }}</span>
|
||||
>{{ $tc('' + executionInfo.status.label) }}</span>
|
||||
</span>
|
||||
<span class="status-item">
|
||||
<span>{{ $tc('TimeDelta') }}: </span>
|
||||
@@ -66,9 +66,7 @@ export default {
|
||||
executionInfo: {
|
||||
type: Object,
|
||||
// eslint-disable-next-line vue/require-valid-default-prop
|
||||
default: {
|
||||
status: 'success'
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="15" :sm="24">
|
||||
<AutoDetailCard :excludes="excludes" :object="object" :url="url" />
|
||||
<AutoDetailCard
|
||||
:excludes="excludes"
|
||||
:object="object"
|
||||
:url="url"
|
||||
:fields="detailFields"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col v-if="hasSummary" :md="9" :sm="24">
|
||||
<IBox
|
||||
@@ -82,6 +87,19 @@ export default {
|
||||
excludes: [
|
||||
'job', 'parameters', 'summary', 'task_id', 'timedelta'
|
||||
],
|
||||
detailFields: [
|
||||
'task_id', 'time_cost',
|
||||
{
|
||||
key: this.$t('IsFinished'),
|
||||
value: `${this.object.is_finished ? '是' : '否'}`
|
||||
},
|
||||
{
|
||||
key: this.$t('IsSuccess'),
|
||||
value: `${this.object.is_success ? '是' : '否'}`
|
||||
},
|
||||
'job_type', 'material', 'org_name',
|
||||
'date_start', 'date_finished', 'date_created'
|
||||
],
|
||||
url: `/api/v1/ops/job-executions/${this.object.id}/`
|
||||
}
|
||||
},
|
||||
@@ -90,6 +108,9 @@ export default {
|
||||
return this.object.is_finished && Object.keys(this.object.summary).length
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.object)
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/job-executions/',
|
||||
columnsExclude: [
|
||||
'summary', 'parameters'
|
||||
'summary', 'parameters', 'timedelta'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['material', 'actions'],
|
||||
|
||||
129
src/views/ops/Job/components/Adhoc.vue
Normal file
129
src/views/ops/Job/components/Adhoc.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<JobRunDialog v-if="showJobRunDialog" :item="item" :visible.sync="showJobRunDialog" @submit="runJob" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JobRunDialog from '@/views/ops/Job/JobRunDialog'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { ActionsFormatter, DateFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListTable,
|
||||
JobRunDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {},
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/jobs/?type=adhoc',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'type', 'asset_amount', 'average_time_cost',
|
||||
'summary', 'date_last_run', 'actions'
|
||||
]
|
||||
},
|
||||
columns: [
|
||||
'name', 'type', 'summary', 'average_time_cost', 'asset_amount',
|
||||
'date_last_run', 'comment', 'date_updated', 'date_created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '140px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'JobDetail',
|
||||
params: { id: row.id }
|
||||
})
|
||||
}
|
||||
},
|
||||
type: {
|
||||
width: '96px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('Summary'),
|
||||
formatter: (row) => {
|
||||
return row.summary['success'] + '/' + row.summary['total']
|
||||
}
|
||||
},
|
||||
average_time_cost: {
|
||||
formatter: (row) => {
|
||||
return row.average_time_cost.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
asset_amount: {
|
||||
label: this.$t('AssetsOfNumber'),
|
||||
formatter: (row) => {
|
||||
return row.assets.length
|
||||
}
|
||||
},
|
||||
date_last_run: {
|
||||
width: '140px',
|
||||
formatter: DateFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
|
||||
updateRoute: 'JobUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_job'),
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
title: this.$t('Run'),
|
||||
name: 'run',
|
||||
can: this.$hasPerm('ops.add_jobexecution') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
if (row?.use_parameter_define && row?.parameters_define) {
|
||||
const params = JSON.parse(row.parameters_define)
|
||||
if (Object.keys(params).length > 0) {
|
||||
this.item = row
|
||||
this.showJobRunDialog = true
|
||||
}
|
||||
} else {
|
||||
this.runJob(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: 'JobCreate',
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false
|
||||
},
|
||||
showJobRunDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
runJob(row, parameters) {
|
||||
this.$axios.post('/api/v1/ops/job-executions/', {
|
||||
job: row.id,
|
||||
parameters: parameters
|
||||
}).then((resp) => {
|
||||
openTaskPage(resp.task_id)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
136
src/views/ops/Job/components/PlayBook.vue
Normal file
136
src/views/ops/Job/components/PlayBook.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<JobRunDialog v-if="showJobRunDialog" :item="item" :visible.sync="showJobRunDialog" @submit="runJob" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JobRunDialog from '@/views/ops/Job/JobRunDialog'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { ActionsFormatter, DateFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListTable,
|
||||
JobRunDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {},
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/jobs/?type=playbook',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'type', 'asset_amount', 'average_time_cost',
|
||||
'summary', 'date_last_run', 'actions'
|
||||
]
|
||||
},
|
||||
columns: [
|
||||
'name', 'type', 'summary', 'average_time_cost', 'asset_amount',
|
||||
'date_last_run', 'comment', 'date_updated', 'date_created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '140px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'JobDetail',
|
||||
params: { id: row.id }
|
||||
})
|
||||
}
|
||||
},
|
||||
type: {
|
||||
width: '96px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('Summary'),
|
||||
formatter: (row) => {
|
||||
return row.summary['success'] + '/' + row.summary['total']
|
||||
}
|
||||
},
|
||||
average_time_cost: {
|
||||
formatter: (row) => {
|
||||
return row.average_time_cost.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
asset_amount: {
|
||||
label: this.$t('AssetsOfNumber'),
|
||||
formatter: (row) => {
|
||||
return row.assets.length
|
||||
}
|
||||
},
|
||||
date_last_run: {
|
||||
width: '140px',
|
||||
formatter: DateFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
|
||||
updateRoute: 'JobUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_job'),
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
title: this.$t('Run'),
|
||||
name: 'run',
|
||||
can: this.$hasPerm('ops.add_jobexecution') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
if (row?.use_parameter_define && row?.parameters_define) {
|
||||
const params = JSON.parse(row.parameters_define)
|
||||
if (Object.keys(params).length > 0) {
|
||||
this.item = row
|
||||
this.showJobRunDialog = true
|
||||
}
|
||||
} else {
|
||||
this.runJob(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'JobCreate',
|
||||
query: {
|
||||
type: 'playbook'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
showJobRunDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
runJob(row, parameters) {
|
||||
this.$axios.post('/api/v1/ops/job-executions/', {
|
||||
job: row.id,
|
||||
parameters: parameters
|
||||
}).then((resp) => {
|
||||
openTaskPage(resp.task_id)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,151 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<JobRunDialog v-if="showJobRunDialog" :item="item" :visible.sync="showJobRunDialog" @submit="runJob" />
|
||||
<GenericListPage :header-actions="headerActions" :table-config="tableConfig" />
|
||||
</div>
|
||||
<TabPage
|
||||
:active-menu.sync="activeMenu"
|
||||
:submenu="submenu"
|
||||
>
|
||||
<component :is="activeMenu" />
|
||||
</TabPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListPage from '@/layout/components/GenericListPage'
|
||||
import { ActionsFormatter, DateFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import JobRunDialog from '@/views/ops/Job/JobRunDialog'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import Adhoc from './components/Adhoc.vue'
|
||||
import Playbook from './components/PlayBook.vue'
|
||||
import TabPage from '@/layout/components/TabPage/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
JobRunDialog,
|
||||
GenericListPage
|
||||
Adhoc,
|
||||
TabPage,
|
||||
Playbook
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {},
|
||||
runtime_parameters: {},
|
||||
showJobRunDialog: false,
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/jobs/',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'type', 'asset_amount', 'average_time_cost',
|
||||
'summary', 'date_last_run', 'actions'
|
||||
]
|
||||
},
|
||||
columns: [
|
||||
'name', 'type', 'summary', 'average_time_cost', 'asset_amount',
|
||||
'date_last_run', 'comment', 'date_updated', 'date_created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '140px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'JobDetail',
|
||||
params: { id: row.id }
|
||||
})
|
||||
}
|
||||
},
|
||||
type: {
|
||||
width: '96px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('Summary'),
|
||||
formatter: (row) => {
|
||||
return row.summary['success'] + '/' + row.summary['total']
|
||||
}
|
||||
},
|
||||
average_time_cost: {
|
||||
formatter: (row) => {
|
||||
return row.average_time_cost.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
asset_amount: {
|
||||
label: this.$t('AssetsOfNumber'),
|
||||
formatter: (row) => {
|
||||
return row.assets.length
|
||||
}
|
||||
},
|
||||
date_last_run: {
|
||||
width: '140px',
|
||||
formatter: DateFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
|
||||
updateRoute: 'JobUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_job'),
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
activeMenu: 'Adhoc',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('Run'),
|
||||
name: 'run',
|
||||
can: this.$hasPerm('ops.add_jobexecution') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
if (row?.use_parameter_define && row?.parameters_define) {
|
||||
const params = JSON.parse(row.parameters_define)
|
||||
if (Object.keys(params).length > 0) {
|
||||
this.item = row
|
||||
this.showJobRunDialog = true
|
||||
}
|
||||
} else {
|
||||
this.runJob(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: 'JobCreate',
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
moreCreates: {
|
||||
callback: (item) => {
|
||||
this.$router.push({
|
||||
name: 'JobCreate',
|
||||
query: { type: item.name }
|
||||
})
|
||||
},
|
||||
dropdown: [
|
||||
{
|
||||
name: 'adhoc',
|
||||
title: this.$t('Command')
|
||||
title: this.$t('AdhocManage'),
|
||||
name: 'Adhoc',
|
||||
hidden: () => !this.$hasPerm('ops.view_adhoc')
|
||||
},
|
||||
{
|
||||
name: 'playbook',
|
||||
title: 'Playbook'
|
||||
title: this.$t('PlaybookManage'),
|
||||
name: 'Playbook',
|
||||
hidden: () => !this.$hasPerm('ops.view_playbook')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
runJob(row, parameters) {
|
||||
this.$axios.post('/api/v1/ops/job-executions/', {
|
||||
job: row.id,
|
||||
parameters: parameters
|
||||
}).then((resp) => {
|
||||
openTaskPage(resp.task_id)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -49,7 +49,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -36,12 +36,6 @@ export default {
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -42,6 +42,9 @@ export default {
|
||||
component: PhoneInput
|
||||
},
|
||||
mfa_level: {
|
||||
disabled: (formValue) => {
|
||||
return formValue.mfa_level === 2
|
||||
},
|
||||
helpText: this.$t('MFAOfUserFirstLoginPersonalInformationImprovementPage')
|
||||
},
|
||||
public_key: {
|
||||
|
||||
@@ -146,8 +146,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ export default {
|
||||
label: this.$t('DisplayName'),
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: vm.$hasPerm('assets.view_asset'),
|
||||
getTitle: ({ row }) => row.host.name,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'AppletHostDetail',
|
||||
|
||||
@@ -41,14 +41,14 @@ export default {
|
||||
title: this.$t('VirtualApp'),
|
||||
name: 'VirtualApp',
|
||||
hidden: () => {
|
||||
return !store.getters.publicSettings['VIRTUAL_APP_ENABLED'] || !this.$store.getters.hasValidLicense
|
||||
return !store.getters.publicSettings['VIRTUAL_APP_ENABLED'] || !this.$store.getters.hasValidLicense || !this.$hasPerm('terminal.view_virtualapp')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('AppProvider'),
|
||||
name: 'AppProvider',
|
||||
hidden: () => {
|
||||
return !store.getters.publicSettings['VIRTUAL_APP_ENABLED'] || !this.$store.getters.hasValidLicense
|
||||
return !store.getters.publicSettings['VIRTUAL_APP_ENABLED'] || !this.$store.getters.hasValidLicense || !this.$hasPerm('terminal.view_appprovider')
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -234,7 +234,7 @@ export default {
|
||||
} else {
|
||||
value = values[key]
|
||||
}
|
||||
if (value) {
|
||||
if (value !== undefined) {
|
||||
form.append(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Page v-bind="$attrs">
|
||||
<div v-if="!loading">
|
||||
<el-alert v-if="!hasValidLicense" type="success">
|
||||
<el-alert v-if="publicSettings.XPACK_ENABLED" type="success">
|
||||
{{ this.$t('ImportLicenseTip') }}
|
||||
</el-alert>
|
||||
<el-row :gutter="20">
|
||||
@@ -93,8 +93,22 @@ export default {
|
||||
if (!this.hasValidLicense) {
|
||||
return [
|
||||
{
|
||||
key: this.$t('License'),
|
||||
key: this.$t('Version'),
|
||||
value: this.$t('CommunityEdition')
|
||||
},
|
||||
{
|
||||
key: this.$t('Expired'),
|
||||
value: this.$t('Never')
|
||||
},
|
||||
{
|
||||
key: this.$t('License'),
|
||||
value: 'GPLv3'
|
||||
},
|
||||
{
|
||||
key: 'Github',
|
||||
formatter: () => {
|
||||
return (<a href='https://github.com/jumpserver/jumpserver' target='_blank'> JumpServer </a>)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button size="mini" type="primary" icon="el-icon-setting" @click="visible = !visible"> {{ $t("Settings...") }} </el-button>
|
||||
<el-button size="mini" type="primary" icon="el-icon-setting" @click="visible = !visible"> {{ $t("Setting") }} </el-button>
|
||||
<Dialog
|
||||
v-if="visible"
|
||||
:show-cancel="false"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button size="mini" type="primary" icon="el-icon-setting" @click="visible=true">{{ $t('Settings...') }}</el-button>
|
||||
<el-button size="mini" type="primary" icon="el-icon-setting" @click="visible=true">{{ $t('Setting') }}</el-button>
|
||||
<Dialog
|
||||
v-if="visible"
|
||||
:destroy-on-close="true"
|
||||
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
canCreate: this.$hasPerm('orgs.add_organization'),
|
||||
extraActions: [
|
||||
{
|
||||
title: this.$t('Settings...'),
|
||||
title: this.$t('Setting'),
|
||||
icon: 'el-icon-setting',
|
||||
callback: () => {
|
||||
this.visible = true
|
||||
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
hasMoreActions: true,
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'BatchApproval',
|
||||
name: 'ApproveSelected',
|
||||
title: this.$t('ApproveSelected'),
|
||||
can: ({ selectedRows }) => { return selectedRows.length > 0 },
|
||||
callback: function({ selectedRows }) {
|
||||
|
||||
@@ -86,6 +86,7 @@ export default {
|
||||
this.setting.InviteDialogVisible = false
|
||||
this.$emit('close', res)
|
||||
this.$store.dispatch('users/currentUserJoinNewOrg', res.users)
|
||||
this.$router.push({ name: 'UserList', query: { order: '-date_updated' }})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user