mirror of
https://github.com/jumpserver/lina.git
synced 2025-11-09 03:14:48 +00:00
Compare commits
25 Commits
pr@v3@fixe
...
v3.10.21-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81b3c79ac1 | ||
|
|
710fa9c109 | ||
|
|
b3a3ca13a7 | ||
|
|
e04abd8b79 | ||
|
|
e3a207f7b6 | ||
|
|
40379ac761 | ||
|
|
33862c716e | ||
|
|
4162bdb74d | ||
|
|
d231bd503c | ||
|
|
b42c5f9170 | ||
|
|
f823257515 | ||
|
|
d3f4b2b2e8 | ||
|
|
5eba19946c | ||
|
|
b4e889316e | ||
|
|
4a72e5aa2b | ||
|
|
6ea86c7efe | ||
|
|
dc0a0ae868 | ||
|
|
5b3b8f72cd | ||
|
|
df26679166 | ||
|
|
9efccb8ada | ||
|
|
d784530539 | ||
|
|
afcc60f29c | ||
|
|
f8d581e455 | ||
|
|
dba1540953 | ||
|
|
456227abcf |
@@ -72,7 +72,6 @@
|
|||||||
"vue-i18n": "^8.15.5",
|
"vue-i18n": "^8.15.5",
|
||||||
"vue-json-editor": "^1.4.3",
|
"vue-json-editor": "^1.4.3",
|
||||||
"vue-markdown": "^2.2.4",
|
"vue-markdown": "^2.2.4",
|
||||||
"vue-moment": "^4.1.0",
|
|
||||||
"vue-password-strength-meter": "^1.7.2",
|
"vue-password-strength-meter": "^1.7.2",
|
||||||
"vue-router": "3.0.6",
|
"vue-router": "3.0.6",
|
||||||
"vue-select": "^3.9.5",
|
"vue-select": "^3.9.5",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<table style="width: 100%">
|
<table style="width: 100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<AssetSelect ref="assetSelect" :can-select="canSelect" :disabled="disabled" />
|
<AssetSelect ref="assetSelect" :can-select="canSelect" :disabled="disabled" :tree-setting="treeSetting" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -59,6 +59,10 @@ export default {
|
|||||||
default(row, index) {
|
default(row, index) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
treeSetting: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -108,6 +108,11 @@ export default {
|
|||||||
url: this.typeUrl,
|
url: this.typeUrl,
|
||||||
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
||||||
treeUrl: `${this.typeUrl}?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
treeUrl: `${this.typeUrl}?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
},
|
||||||
callback: {
|
callback: {
|
||||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ export default {
|
|||||||
url: this.tableUrl,
|
url: this.tableUrl,
|
||||||
// ?assets=0不显示资产. =1显示资产
|
// ?assets=0不显示资产. =1显示资产
|
||||||
treeUrl: this.treeUrl,
|
treeUrl: this.treeUrl,
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
},
|
||||||
callback: {
|
callback: {
|
||||||
onSelected: (event, node) => vm.onSelected(node, vm),
|
onSelected: (event, node) => vm.onSelected(node, vm),
|
||||||
refresh: vm.refreshObjectAssetPermission
|
refresh: vm.refreshObjectAssetPermission
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default {
|
|||||||
formatterData = data
|
formatterData = data
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span>{formatterData}</span>
|
<span style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', lineHeight: '1.2' }}>{formatterData}</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (this.value instanceof Array) {
|
if (this.value instanceof Array) {
|
||||||
|
|||||||
@@ -13,15 +13,28 @@ class StrategyAbstract {
|
|||||||
this.onSelect = this.onSelect.bind(this)
|
this.onSelect = this.onSelect.bind(this)
|
||||||
this.onSelectAll = this.onSelectAll.bind(this)
|
this.onSelectAll = this.onSelectAll.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
get elTable() {
|
get elTable() {
|
||||||
return this.elDataTable.$refs.table
|
return this.elDataTable.$refs.table
|
||||||
}
|
}
|
||||||
onSelectionChange() {}
|
|
||||||
onSelect() {}
|
onSelectionChange() {
|
||||||
onSelectAll() {}
|
}
|
||||||
toggleRowSelection() {}
|
|
||||||
clearSelection() {}
|
onSelect() {
|
||||||
updateElTableSelection() {}
|
}
|
||||||
|
|
||||||
|
onSelectAll() {
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleRowSelection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
updateElTableSelection() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,14 +47,16 @@ class StrategyNormal extends StrategyAbstract {
|
|||||||
onSelectionChange(val) {
|
onSelectionChange(val) {
|
||||||
this.elDataTable.selected = val
|
this.elDataTable.selected = val
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toggleRowSelection和clearSelection的表现与el-table一致
|
* toggleRowSelection和clearSelection的表现与el-table一致
|
||||||
*/
|
*/
|
||||||
toggleRowSelection(...args) {
|
toggleRowSelection(...args) {
|
||||||
return this.elTable.toggleRowSelection(...args)
|
return this.elTable.toggleRowSelection(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
return this.elTable.clearSelection()
|
return this.elTable?.clearSelection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,17 +80,55 @@ class StrategyPersistSelection extends StrategyAbstract {
|
|||||||
const isChosen = selection.indexOf(row) > -1
|
const isChosen = selection.indexOf(row) > -1
|
||||||
this.toggleRowSelection(row, isChosen)
|
this.toggleRowSelection(row, isChosen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户切换当前页的多选
|
* 用户切换当前页的多选
|
||||||
*/
|
*/
|
||||||
onSelectAll(selection, selectable = () => true) {
|
onSelectAll(selection, selectable = () => true) {
|
||||||
const isSelected = !!selection.length
|
const { id, selected, data } = this.elDataTable
|
||||||
this.elDataTable.data.forEach(r => {
|
const selectableRows = data.filter(selectable)
|
||||||
if (selectable(r)) {
|
// const isSelected = !!selection.length
|
||||||
this.toggleRowSelection(r, isSelected)
|
|
||||||
}
|
// 创建已选择项的 id 集合,用于快速查找
|
||||||
})
|
const selectedIds = new Set(selected.map(r => r[id]))
|
||||||
|
const currentPageIds = new Set(selectableRows.map(row => row[id]))
|
||||||
|
|
||||||
|
// 前页面的选中状态
|
||||||
|
const currentPageSelectedCount = selectableRows.filter(row =>
|
||||||
|
selectedIds.has(row[id])
|
||||||
|
).length
|
||||||
|
|
||||||
|
// 判断是全选还是取消全选
|
||||||
|
const shouldSelectAll = currentPageSelectedCount < selectableRows.length
|
||||||
|
|
||||||
|
this.elTable?.clearSelection()
|
||||||
|
|
||||||
|
if (shouldSelectAll) {
|
||||||
|
selectableRows.forEach(row => {
|
||||||
|
if (!selectedIds.has(row[id])) selected.push(row)
|
||||||
|
|
||||||
|
this.elTable.toggleRowSelection(row, true)
|
||||||
|
|
||||||
|
// ! 这里需要触发事件,否则在 el-table 中无法触发 selection-change 事件
|
||||||
|
this.elDataTable.$emit('toggle-row-selection', true, row)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const newSelected = []
|
||||||
|
|
||||||
|
selected.forEach(row => {
|
||||||
|
if (!currentPageIds.has(row[id])) {
|
||||||
|
newSelected.push(row)
|
||||||
|
} else {
|
||||||
|
this.elDataTable.$emit('toggle-row-selection', false, row)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.elDataTable.selected = newSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elDataTable.$emit('selection-change', this.elDataTable.selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toggleRowSelection和clearSelection管理elDataTable的selected数组
|
* toggleRowSelection和clearSelection管理elDataTable的selected数组
|
||||||
* 记得最后要将状态同步到el-table中
|
* 记得最后要将状态同步到el-table中
|
||||||
@@ -83,34 +136,42 @@ class StrategyPersistSelection extends StrategyAbstract {
|
|||||||
toggleRowSelection(row, isSelected) {
|
toggleRowSelection(row, isSelected) {
|
||||||
const { id, selected } = this.elDataTable
|
const { id, selected } = this.elDataTable
|
||||||
const foundIndex = selected.findIndex(r => r[id] === row[id])
|
const foundIndex = selected.findIndex(r => r[id] === row[id])
|
||||||
|
|
||||||
if (typeof isSelected === 'undefined') {
|
if (typeof isSelected === 'undefined') {
|
||||||
isSelected = foundIndex <= -1
|
isSelected = foundIndex <= -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSelected && foundIndex === -1) {
|
if (isSelected && foundIndex === -1) {
|
||||||
selected.push(row)
|
selected.push(row)
|
||||||
} else if (!isSelected && foundIndex > -1) {
|
} else if (!isSelected && foundIndex > -1) {
|
||||||
selected.splice(foundIndex, 1)
|
selected.splice(foundIndex, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
|
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
|
||||||
this.updateElTableSelection()
|
this.updateElTableSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
this.elDataTable.selected = []
|
this.elDataTable.selected = []
|
||||||
this.updateElTableSelection()
|
this.updateElTableSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将selected状态同步到el-table中
|
* 将selected状态同步到el-table中
|
||||||
*/
|
*/
|
||||||
updateElTableSelection() {
|
updateElTableSelection() {
|
||||||
const { data, id, selected } = this.elDataTable
|
const { data, id, selected } = this.elDataTable
|
||||||
// 历史勾选的行已经不在当前页了,所以要将当前页的行数据和selected合并
|
const selectedIds = new Set(selected.map(r => r[id]))
|
||||||
const mergeData = _.uniqWith([...data, ...selected], _.isEqual)
|
|
||||||
mergeData.forEach(r => {
|
this.elTable?.clearSelection()
|
||||||
const isSelected = !!selected.find(r2 => r[id] === r2[id])
|
|
||||||
if (!this.elTable) {
|
data.forEach(row => {
|
||||||
return
|
const shouldBeSelected = selectedIds.has(row[id])
|
||||||
|
if (!this.elTable) return
|
||||||
|
|
||||||
|
if (shouldBeSelected) {
|
||||||
|
this.elTable.toggleRowSelection(row, true)
|
||||||
}
|
}
|
||||||
this.elTable.toggleRowSelection(r, isSelected)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,8 +122,13 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
iExportOptions() {
|
iExportOptions() {
|
||||||
const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
// const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
||||||
return options
|
// return options
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: this.tableUrl,
|
||||||
|
...this.exportOptions
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
class="text edit-input"
|
class="text edit-input"
|
||||||
@blur="onEditBlur"
|
@blur="onEditBlur"
|
||||||
/>
|
/>
|
||||||
<span v-if="realValue" class="action">
|
<span class="action">
|
||||||
<template v-for="(item, index) in iActions">
|
<template v-for="(item, index) in iActions">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="item.has"
|
v-if="item.has"
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export default {
|
|||||||
</a>`
|
</a>`
|
||||||
const treeActions = `${showSearch ? searchIcon : ''}${showRefresh ? refreshIcon : ''}`
|
const treeActions = `${showSearch ? searchIcon : ''}${showRefresh ? refreshIcon : ''}`
|
||||||
const icons = `
|
const icons = `
|
||||||
<span style="float: right; margin-right: 10px">
|
<span>
|
||||||
${treeActions}
|
${treeActions}
|
||||||
</span>`
|
</span>`
|
||||||
if (rootNode) {
|
if (rootNode) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default {
|
|||||||
showRenameBtn: false,
|
showRenameBtn: false,
|
||||||
drag: {
|
drag: {
|
||||||
isCopy: false,
|
isCopy: false,
|
||||||
isMove: true
|
isMove: !this.$store.getters.currentOrgIsRoot
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback: {
|
callback: {
|
||||||
|
|||||||
@@ -54,24 +54,24 @@ export default {
|
|||||||
resizeObserver: null,
|
resizeObserver: null,
|
||||||
span: 12,
|
span: 12,
|
||||||
isShow: true,
|
isShow: true,
|
||||||
iValue: this.value
|
iValue: this.sanitizeContent(this.value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sanitizedValue() {
|
sanitizedValue() {
|
||||||
// 转义特殊字符
|
const content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
|
||||||
let content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
|
|
||||||
|
|
||||||
// 使用 DOMPurify 进行 XSS 过滤
|
return this.sanitizeContent(content)
|
||||||
content = DOMPurify.sanitize(content)
|
}
|
||||||
|
},
|
||||||
return content
|
watch: {
|
||||||
|
value(newVal) {
|
||||||
|
this.iValue = this.sanitizeContent(newVal)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.resizeObserver = new ResizeObserver(entries => {
|
this.resizeObserver = new ResizeObserver(entries => {
|
||||||
// 监听高度变化
|
|
||||||
const height = entries[0].target.offsetHeight
|
const height = entries[0].target.offsetHeight
|
||||||
if (height) {
|
if (height) {
|
||||||
this.height = height
|
this.height = height
|
||||||
@@ -91,8 +91,19 @@ export default {
|
|||||||
this.resizeObserver = null
|
this.resizeObserver = null
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
sanitizeContent(content) {
|
||||||
|
if (!content) return ''
|
||||||
|
|
||||||
|
return DOMPurify.sanitize(content, {
|
||||||
|
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'strong', 'em', 'code', 'pre', 'blockquote', 'a'],
|
||||||
|
FORBID_TAGS: ['script', 'style', 'iframe', 'frame', 'object', 'embed'],
|
||||||
|
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover']
|
||||||
|
})
|
||||||
|
},
|
||||||
onChange() {
|
onChange() {
|
||||||
this.$emit('change', this.iValue)
|
const sanitizedValue = this.sanitizeContent(this.iValue)
|
||||||
|
this.iValue = sanitizedValue
|
||||||
|
this.$emit('change', sanitizedValue)
|
||||||
},
|
},
|
||||||
onView() {
|
onView() {
|
||||||
this.isShow = !this.isShow
|
this.isShow = !this.isShow
|
||||||
|
|||||||
@@ -74,9 +74,11 @@ export default {
|
|||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
const currentOrg = this.$store.getters.currentOrg
|
const currentOrg = this.$store.getters.currentOrg
|
||||||
if (currentOrg.autoEnter) {
|
|
||||||
|
if (currentOrg.autoEnter || currentOrg.is_system) {
|
||||||
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
|
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import { message } from '@/utils/message'
|
|||||||
import xss from '@/utils/xss'
|
import xss from '@/utils/xss'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
import ElTableTooltipPatch from '@/utils/elTableTooltipPatch.js'
|
import ElTableTooltipPatch from '@/utils/elTableTooltipPatch.js'
|
||||||
|
import moment from 'moment'
|
||||||
|
moment.locale('zh-cn')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you don't want to use mock-server
|
* If you don't want to use mock-server
|
||||||
@@ -50,11 +52,7 @@ Vue.config.productionTip = false
|
|||||||
Vue.use(VueCookie)
|
Vue.use(VueCookie)
|
||||||
window.$cookie = VueCookie
|
window.$cookie = VueCookie
|
||||||
|
|
||||||
const moment = require('moment')
|
Vue.prototype.$moment = moment
|
||||||
require('moment/locale/zh-cn')
|
|
||||||
Vue.use(require('vue-moment'), {
|
|
||||||
moment
|
|
||||||
})
|
|
||||||
|
|
||||||
Vue.use(VueLogger, loggerOptions)
|
Vue.use(VueLogger, loggerOptions)
|
||||||
|
|
||||||
|
|||||||
@@ -71,10 +71,12 @@ const mutations = {
|
|||||||
state.consoleOrgs = state.consoleOrgs.filter(i => i.id !== org.id)
|
state.consoleOrgs = state.consoleOrgs.filter(i => i.id !== org.id)
|
||||||
},
|
},
|
||||||
SET_CURRENT_ORG(state, org) {
|
SET_CURRENT_ORG(state, org) {
|
||||||
// 系统组织和全局组织不设置成 Pre org
|
// 系统组织不设置成 Pre org
|
||||||
if (!state.currentOrg?.autoEnter && !state.currentOrg?.is_root) {
|
const currentOrg = state.currentOrg
|
||||||
state.preOrg = state.currentOrg
|
|
||||||
setPreOrgLocal(state.username, state.currentOrg)
|
if (currentOrg && !currentOrg.autoEnter && !currentOrg.is_system) {
|
||||||
|
state.preOrg = currentOrg
|
||||||
|
setPreOrgLocal(state.username, currentOrg)
|
||||||
}
|
}
|
||||||
state.currentOrg = org
|
state.currentOrg = org
|
||||||
saveCurrentOrgLocal(state.username, org)
|
saveCurrentOrgLocal(state.username, org)
|
||||||
@@ -144,6 +146,7 @@ const actions = {
|
|||||||
const systemOrg = {
|
const systemOrg = {
|
||||||
id: orgUtil.SYSTEM_ORG_ID,
|
id: orgUtil.SYSTEM_ORG_ID,
|
||||||
name: 'SystemSetting',
|
name: 'SystemSetting',
|
||||||
|
is_system: true,
|
||||||
autoEnter: new Date().getTime()
|
autoEnter: new Date().getTime()
|
||||||
}
|
}
|
||||||
commit('SET_CURRENT_ORG', systemOrg)
|
commit('SET_CURRENT_ORG', systemOrg)
|
||||||
|
|||||||
@@ -42,8 +42,14 @@ export function getCurrentOrgLocal(username) {
|
|||||||
|
|
||||||
export function saveCurrentOrgLocal(username, org) {
|
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)
|
if (org) {
|
||||||
|
localStorage.setItem(key, JSON.stringify(org))
|
||||||
|
VueCookie.set('X-JMS-ORG', org.id)
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
VueCookie.delete('X-JMS-ORG')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setPreOrgLocal(username, org) {
|
export function setPreOrgLocal(username, org) {
|
||||||
|
|||||||
@@ -366,16 +366,32 @@ export function download(downloadUrl, filename) {
|
|||||||
const iframe = document.createElement('iframe')
|
const iframe = document.createElement('iframe')
|
||||||
iframe.style.display = 'none'
|
iframe.style.display = 'none'
|
||||||
document.body.appendChild(iframe)
|
document.body.appendChild(iframe)
|
||||||
const a = document.createElement('a')
|
const timeout = 1000 * 60 * 30
|
||||||
a.href = downloadUrl
|
|
||||||
if (filename) {
|
if (filename) {
|
||||||
a.download = filename
|
fetch(downloadUrl)
|
||||||
|
.then(response => response.blob())
|
||||||
|
.then(blob => {
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = iframe.contentWindow.document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = filename
|
||||||
|
iframe.contentWindow.document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
setTimeout(() => {
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
document.body.removeChild(iframe)
|
||||||
|
}, timeout) // If you can't download it in half an hour, don't download it.
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
document.body.removeChild(iframe)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
iframe.src = downloadUrl
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(iframe)
|
||||||
|
}, timeout) // If you can't download it in half an hour, don't download it.
|
||||||
}
|
}
|
||||||
iframe.contentWindow.document.body.appendChild(a)
|
|
||||||
a.click()
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(iframe)
|
|
||||||
}, 1000 * 60 * 30) // If you can't download it in half an hour, don't download it.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function diffObject(object, base) {
|
export function diffObject(object, base) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function getPropOrg() {
|
|||||||
if (defaultOrg) {
|
if (defaultOrg) {
|
||||||
return defaultOrg
|
return defaultOrg
|
||||||
}
|
}
|
||||||
return orgs.filter(item => !item['is_root'] && item.id !== SYSTEM_ORG_ID)[0]
|
return orgs.filter(item => !item['is_root'] && !item['is_system'])[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function change2PropOrg() {
|
async function change2PropOrg() {
|
||||||
|
|||||||
@@ -90,10 +90,20 @@ function ifBadRequest({ response, error }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function logout() {
|
||||||
|
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${location.pathname}`
|
||||||
|
}
|
||||||
|
|
||||||
export function flashErrorMsg({ response, error }) {
|
export function flashErrorMsg({ response, error }) {
|
||||||
if (!response.config.disableFlashErrorMsg) {
|
if (!response.config.disableFlashErrorMsg) {
|
||||||
const responseErrorMsg = getErrorResponseMsg(error)
|
const responseErrorMsg = getErrorResponseMsg(error)
|
||||||
const msg = responseErrorMsg || error.message
|
const msg = responseErrorMsg || error.message
|
||||||
|
|
||||||
|
if (response.status === 403 && msg === 'CSRF Failed: CSRF token missing.') {
|
||||||
|
setTimeout(() => {
|
||||||
|
logout()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
message({
|
message({
|
||||||
message: msg,
|
message: msg,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import orgs from '@/api/orgs'
|
|||||||
import { getPropView, isViewHasOrgs } from '@/utils/jms'
|
import { getPropView, isViewHasOrgs } from '@/utils/jms'
|
||||||
|
|
||||||
const whiteList = ['/login', process.env.VUE_APP_LOGIN_PATH] // no redirect whitelist
|
const whiteList = ['/login', process.env.VUE_APP_LOGIN_PATH] // no redirect whitelist
|
||||||
const autoEnterOrgs = ['00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000000']
|
const autoEnterOrgs = [
|
||||||
|
'00000000-0000-0000-0000-000000000001',
|
||||||
|
'00000000-0000-0000-0000-000000000000'
|
||||||
|
]
|
||||||
|
|
||||||
function reject(msg) {
|
function reject(msg) {
|
||||||
return new Promise((resolve, reject) => reject(msg))
|
return new Promise((resolve, reject) => reject(msg))
|
||||||
@@ -23,6 +26,19 @@ async function checkLogin({ to, from, next }) {
|
|||||||
return await store.dispatch('users/getProfile')
|
return await store.dispatch('users/getProfile')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Vue.$log.error(e)
|
Vue.$log.error(e)
|
||||||
|
// remove currentOrg: System org item
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i)
|
||||||
|
if (!key.startsWith('jms_current_org_')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let value = localStorage.getItem(key)
|
||||||
|
value = JSON.parse(value)
|
||||||
|
if (!value.is_system) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
}
|
||||||
const status = e.response.status
|
const status = e.response.status
|
||||||
if (store.getters.currentOrg.autoEnter) {
|
if (store.getters.currentOrg.autoEnter) {
|
||||||
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
|
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
|
||||||
@@ -154,18 +170,24 @@ export async function startup({ to, from, next }) {
|
|||||||
if (store.getters.inited) {
|
if (store.getters.inited) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
await store.dispatch('app/init')
|
|
||||||
|
|
||||||
// set page title
|
try {
|
||||||
// await getOpenPublicSetting({ to, from, next })
|
await store.dispatch('app/init')
|
||||||
await getPublicSetting({ to, from, next }, true)
|
|
||||||
await checkLogin({ to, from, next })
|
// set page title
|
||||||
await getPublicSetting({ to, from, next }, false)
|
// await getOpenPublicSetting({ to, from, next })
|
||||||
await changeCurrentViewIfNeed({ to, from, next })
|
await getPublicSetting({ to, from, next }, true)
|
||||||
await changeCurrentOrgIfNeed({ to, from, next })
|
await checkLogin({ to, from, next })
|
||||||
await generatePageRoutes({ to, from, next })
|
await getPublicSetting({ to, from, next }, false)
|
||||||
await checkUserFirstLogin({ to, from, next })
|
await changeCurrentViewIfNeed({ to, from, next })
|
||||||
await store.dispatch('assets/getAssetCategories')
|
await changeCurrentOrgIfNeed({ to, from, next })
|
||||||
|
await generatePageRoutes({ to, from, next })
|
||||||
|
await checkUserFirstLogin({ to, from, next })
|
||||||
|
await store.dispatch('assets/getAssetCategories')
|
||||||
|
} catch (e) {
|
||||||
|
Vue.$log.error('Startup error: ', e)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ export default {
|
|||||||
showMenu: false,
|
showMenu: false,
|
||||||
showAssets: true,
|
showAssets: true,
|
||||||
url: '/api/v1/accounts/accounts/',
|
url: '/api/v1/accounts/accounts/',
|
||||||
countResource: 'account'
|
countResource: 'account',
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,9 +122,53 @@ export default {
|
|||||||
return {
|
return {
|
||||||
name: 'AccountChangeSecretCreate'
|
name: 'AccountChangeSecretCreate'
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
extraMoreActions: [
|
||||||
|
{
|
||||||
|
name: 'BatchDisable',
|
||||||
|
title: this.$t('common.BatchDisable'),
|
||||||
|
icon: 'fa fa-ban',
|
||||||
|
can: ({ selectedRows }) => selectedRows.length > 0 && this.$hasPerm('accounts.change_changesecretautomation'),
|
||||||
|
callback: ({ selectedRows, reloadTable }) => this.bulkDisableCallback(selectedRows, reloadTable)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BatchActivate',
|
||||||
|
title: this.$t('common.BatchActivate'),
|
||||||
|
icon: 'fa fa-check-circle-o',
|
||||||
|
can: ({ selectedRows }) => selectedRows.length > 0 && this.$hasPerm('accounts.change_changesecretautomation'),
|
||||||
|
callback: ({ selectedRows, reloadTable }) => this.bulkActivateCallback(selectedRows, reloadTable)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
bulkDisableCallback(selectedRows, reloadTable) {
|
||||||
|
const url = '/api/v1/accounts/change-secret-automations/'
|
||||||
|
const data = selectedRows.map(row => {
|
||||||
|
return { id: row.id, is_active: false }
|
||||||
|
})
|
||||||
|
if (data.length === 0) return
|
||||||
|
this.$axios.patch(url, data).then(() => {
|
||||||
|
reloadTable()
|
||||||
|
this.$message.success(this.$t('common.disableSuccessMsg'))
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error(this.$t('common.updateError') + ' ' + error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
bulkActivateCallback(selectedRows, reloadTable) {
|
||||||
|
const url = '/api/v1/accounts/change-secret-automations/'
|
||||||
|
const data = selectedRows.map(row => {
|
||||||
|
return { id: row.id, is_active: true }
|
||||||
|
})
|
||||||
|
if (data.length === 0) return
|
||||||
|
this.$axios.patch(url, data).then(() => {
|
||||||
|
reloadTable()
|
||||||
|
this.$message.success(this.$t('common.activateSuccessMsg'))
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error(this.$t('common.updateError') + ' ' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -127,9 +127,58 @@ export default {
|
|||||||
headerActions: {
|
headerActions: {
|
||||||
hasRefresh: true,
|
hasRefresh: true,
|
||||||
hasExport: false,
|
hasExport: false,
|
||||||
hasImport: false
|
hasImport: false,
|
||||||
|
createRoute: () => {
|
||||||
|
return {
|
||||||
|
name: 'AccountPushCreate'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extraMoreActions: [
|
||||||
|
{
|
||||||
|
name: 'BatchDisable',
|
||||||
|
title: this.$t('common.BatchDisable'),
|
||||||
|
icon: 'fa fa-ban',
|
||||||
|
can: ({ selectedRows }) => selectedRows.length > 0 && this.$hasPerm('accounts.change_pushaccountautomation'),
|
||||||
|
callback: ({ selectedRows, reloadTable }) => this.bulkDisableCallback(selectedRows, reloadTable)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BatchActivate',
|
||||||
|
title: this.$t('common.BatchActivate'),
|
||||||
|
icon: 'fa fa-check-circle-o',
|
||||||
|
can: ({ selectedRows }) => selectedRows.length > 0 && this.$hasPerm('accounts.change_pushaccountautomation'),
|
||||||
|
callback: ({ selectedRows, reloadTable }) => this.bulkActivateCallback(selectedRows, reloadTable)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
bulkDisableCallback(selectedRows, reloadTable) {
|
||||||
|
const url = '/api/v1/accounts/push-account-automations/'
|
||||||
|
const data = selectedRows.map(row => {
|
||||||
|
return { id: row.id, is_active: false }
|
||||||
|
})
|
||||||
|
if (data.length === 0) return
|
||||||
|
this.$axios.patch(url, data).then(() => {
|
||||||
|
reloadTable()
|
||||||
|
this.$message.success(this.$t('common.disableSuccessMsg'))
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error(this.$t('common.updateError') + ' ' + error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
bulkActivateCallback(selectedRows, reloadTable) {
|
||||||
|
const url = '/api/v1/accounts/push-account-automations/'
|
||||||
|
const data = selectedRows.map(row => {
|
||||||
|
return { id: row.id, is_active: true }
|
||||||
|
})
|
||||||
|
if (data.length === 0) return
|
||||||
|
this.$axios.patch(url, data).then(() => {
|
||||||
|
reloadTable()
|
||||||
|
this.$message.success(this.$t('common.activateSuccessMsg'))
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error(this.$t('common.updateError') + ' ' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -65,9 +65,11 @@ export default {
|
|||||||
columns: ['name', 'username', 'secret_type', 'privileged'],
|
columns: ['name', 'username', 'secret_type', 'privileged'],
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
formatterArgs: {
|
formatter: (row) => <span>{row.name}</span>
|
||||||
route: 'AccountTemplateDetail'
|
// 暂禁用远程应用中账号模板的详情跳转
|
||||||
}
|
// formatterArgs: {
|
||||||
|
// route: 'AccountTemplateDetail'
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
privileged: {
|
privileged: {
|
||||||
width: '100px'
|
width: '100px'
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
|||||||
[vmware]: {
|
[vmware]: {
|
||||||
name: vmware,
|
name: vmware,
|
||||||
title: 'VMware',
|
title: 'VMware',
|
||||||
attrs: ['host', 'port', 'username', 'password']
|
attrs: ['host', 'port', 'username', 'password', 'auto_sync_node']
|
||||||
},
|
},
|
||||||
[nutanix]: {
|
[nutanix]: {
|
||||||
name: nutanix,
|
name: nutanix,
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export const platformFieldsMeta = (vm) => {
|
|||||||
'change_secret_enabled', 'change_secret_method', 'change_secret_params',
|
'change_secret_enabled', 'change_secret_method', 'change_secret_params',
|
||||||
'push_account_enabled', 'push_account_method', 'push_account_params',
|
'push_account_enabled', 'push_account_method', 'push_account_params',
|
||||||
'verify_account_enabled', 'verify_account_method', 'verify_account_params',
|
'verify_account_enabled', 'verify_account_method', 'verify_account_params',
|
||||||
'gather_accounts_enabled', 'gather_accounts_method', 'gather_accounts_params'
|
'gather_accounts_enabled', 'gather_accounts_method', 'gather_accounts_params',
|
||||||
|
'remove_account_enabled', 'remove_account_method', 'remove_account_params'
|
||||||
],
|
],
|
||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
ansible_config: {
|
ansible_config: {
|
||||||
@@ -28,12 +29,15 @@ export const platformFieldsMeta = (vm) => {
|
|||||||
hidden: (formValue) => !formValue['ansible_enabled']
|
hidden: (formValue) => !formValue['ansible_enabled']
|
||||||
},
|
},
|
||||||
gather_facts_enabled: {},
|
gather_facts_enabled: {},
|
||||||
|
remove_account_enabled: {},
|
||||||
ping_method: {},
|
ping_method: {},
|
||||||
ping_params: {
|
ping_params: {
|
||||||
label: ''
|
label: ''
|
||||||
},
|
},
|
||||||
gather_facts_method: {},
|
gather_facts_method: {},
|
||||||
push_account_method: {},
|
push_account_method: {},
|
||||||
|
remove_account_method: {},
|
||||||
|
remove_account_params: {},
|
||||||
push_account_params: {
|
push_account_params: {
|
||||||
label: ''
|
label: ''
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ export default {
|
|||||||
this.tableConfig.url = `/api/v1/perms/users/self/nodes/${currentNodeId}/assets/?cache_policy=1`
|
this.tableConfig.url = `/api/v1/perms/users/self/nodes/${currentNodeId}/assets/?cache_policy=1`
|
||||||
}
|
}
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
|
|||||||
@@ -205,6 +205,11 @@ export default {
|
|||||||
view: {
|
view: {
|
||||||
dblClickExpand: false,
|
dblClickExpand: false,
|
||||||
showLine: true
|
showLine: true
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
iShowTree: true,
|
iShowTree: true,
|
||||||
|
|||||||
@@ -290,6 +290,11 @@ export default {
|
|||||||
view: {
|
view: {
|
||||||
dblClickExpand: false,
|
dblClickExpand: false,
|
||||||
showLine: true
|
showLine: true
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
iShowTree: true
|
iShowTree: true
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||||
import AssetSelect from '@/components/Apps/AssetSelect'
|
import AssetSelect from '@/components/Apps/AssetSelect'
|
||||||
import { getDayFuture } from '@/utils/common'
|
|
||||||
import AccountFormatter from './components/AccountFormatter'
|
import AccountFormatter from './components/AccountFormatter'
|
||||||
import { AllAccount } from '../const'
|
import { AllAccount } from '../const'
|
||||||
import ProtocolsSelect from '@/components/Form/FormFields/AllOrSpec.vue'
|
import ProtocolsSelect from '@/components/Form/FormFields/AllOrSpec.vue'
|
||||||
@@ -34,7 +33,6 @@ export default {
|
|||||||
initial: {
|
initial: {
|
||||||
is_active: true,
|
is_active: true,
|
||||||
date_start: new Date().toISOString(),
|
date_start: new Date().toISOString(),
|
||||||
date_expired: getDayFuture(25550, new Date()).toISOString(),
|
|
||||||
nodes: nodesInitial,
|
nodes: nodesInitial,
|
||||||
assets: assetsInitial,
|
assets: assetsInitial,
|
||||||
accounts: [AllAccount]
|
accounts: [AllAccount]
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ export default {
|
|||||||
this.$log.debug('AssetSelect value', that.assets)
|
this.$log.debug('AssetSelect value', that.assets)
|
||||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||||
this.$store.commit('common/reload')
|
this.$store.commit('common/reload')
|
||||||
|
},
|
||||||
|
treeSetting: {
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
nodeRelationConfig: {
|
nodeRelationConfig: {
|
||||||
|
|||||||
@@ -39,7 +39,12 @@ export default {
|
|||||||
notShowBuiltinTree: true,
|
notShowBuiltinTree: true,
|
||||||
url: '/api/v1/perms/asset-permissions/',
|
url: '/api/v1/perms/asset-permissions/',
|
||||||
nodeUrl: '/api/v1/perms/asset-permissions/',
|
nodeUrl: '/api/v1/perms/asset-permissions/',
|
||||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1'
|
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: '/api/v1/perms/asset-permissions/',
|
url: '/api/v1/perms/asset-permissions/',
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
v-model="requestForm.accounts"
|
v-model="requestForm.accounts"
|
||||||
:nodes="requestForm.nodes"
|
:nodes="requestForm.nodes"
|
||||||
:assets="requestForm.assets"
|
:assets="requestForm.assets"
|
||||||
|
:oid="requestForm.oid"
|
||||||
:show-add-template="false"
|
:show-add-template="false"
|
||||||
style="width: 50% !important"
|
style="width: 50% !important"
|
||||||
/>
|
/>
|
||||||
@@ -83,6 +84,7 @@ export default {
|
|||||||
assets: this.object.apply_assets?.map(i => i.id),
|
assets: this.object.apply_assets?.map(i => i.id),
|
||||||
accounts: this.object.apply_accounts,
|
accounts: this.object.apply_accounts,
|
||||||
actions: this.object.apply_actions,
|
actions: this.object.apply_actions,
|
||||||
|
oid: this.object.org_id,
|
||||||
apply_date_expired: this.object.apply_date_expired,
|
apply_date_expired: this.object.apply_date_expired,
|
||||||
apply_date_start: this.object.apply_date_start
|
apply_date_start: this.object.apply_date_start
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default {
|
|||||||
},
|
},
|
||||||
onMonitor() {
|
onMonitor() {
|
||||||
const joinUrl = `/luna/monitor/${this.session.id}?ticket_id=${this.object.id}`
|
const joinUrl = `/luna/monitor/${this.session.id}?ticket_id=${this.object.id}`
|
||||||
window.open(joinUrl, 'height=600, width=850, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')
|
window.open(joinUrl, '_blank', 'height=600, width=850, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')
|
||||||
},
|
},
|
||||||
onToggleLock() {
|
onToggleLock() {
|
||||||
const url = '/api/v1/terminal/tasks/toggle-lock-session-for-ticket/'
|
const url = '/api/v1/terminal/tasks/toggle-lock-session-for-ticket/'
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ export default {
|
|||||||
showRefresh: true,
|
showRefresh: true,
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
treeUrl: '',
|
treeUrl: '',
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
},
|
||||||
check: {
|
check: {
|
||||||
enable: true
|
enable: true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ export default {
|
|||||||
system_roles: {
|
system_roles: {
|
||||||
component: Select2,
|
component: Select2,
|
||||||
label: this.$t('users.SystemRoles'),
|
label: this.$t('users.SystemRoles'),
|
||||||
|
rules: [
|
||||||
|
rules.Required
|
||||||
|
],
|
||||||
el: {
|
el: {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
ajax: {
|
ajax: {
|
||||||
|
|||||||
@@ -8987,7 +8987,7 @@ moment-parseformat@^4.0.0:
|
|||||||
resolved "https://registry.npmmirror.com/moment-parseformat/-/moment-parseformat-4.0.0.tgz#44cffc3b3be3b3d033475869fbfa9066abb66cb0"
|
resolved "https://registry.npmmirror.com/moment-parseformat/-/moment-parseformat-4.0.0.tgz#44cffc3b3be3b3d033475869fbfa9066abb66cb0"
|
||||||
integrity sha512-0V4ICKnI1npglqrMSDK2y8WxOdN79DkMoIexzY3P+jr2wNfbB4J81BgjFfHsj18wBsV7FdKCWyCHcezzH0xlyg==
|
integrity sha512-0V4ICKnI1npglqrMSDK2y8WxOdN79DkMoIexzY3P+jr2wNfbB4J81BgjFfHsj18wBsV7FdKCWyCHcezzH0xlyg==
|
||||||
|
|
||||||
moment@^2.19.2, moment@^2.29.4:
|
moment@^2.29.4:
|
||||||
version "2.29.4"
|
version "2.29.4"
|
||||||
resolved "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
resolved "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||||
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||||
@@ -13226,13 +13226,6 @@ vue-markdown@^2.2.4:
|
|||||||
markdown-it-task-lists "^2.0.1"
|
markdown-it-task-lists "^2.0.1"
|
||||||
markdown-it-toc-and-anchor "^4.1.2"
|
markdown-it-toc-and-anchor "^4.1.2"
|
||||||
|
|
||||||
vue-moment@^4.1.0:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.npmmirror.com/vue-moment/-/vue-moment-4.1.0.tgz#092a8ff723a96c6f85a0a8e23ad30f0bf320f3b0"
|
|
||||||
integrity sha512-Gzisqpg82ItlrUyiD9d0Kfru+JorW2o4mQOH06lEDZNgxci0tv/fua1Hl0bo4DozDV2JK1r52Atn/8QVCu8qQw==
|
|
||||||
dependencies:
|
|
||||||
moment "^2.19.2"
|
|
||||||
|
|
||||||
vue-password-strength-meter@^1.7.2:
|
vue-password-strength-meter@^1.7.2:
|
||||||
version "1.7.2"
|
version "1.7.2"
|
||||||
resolved "https://registry.npmmirror.com/vue-password-strength-meter/-/vue-password-strength-meter-1.7.2.tgz#ddaae2246fb8a53fd8cdc5b1084b1d16cc401505"
|
resolved "https://registry.npmmirror.com/vue-password-strength-meter/-/vue-password-strength-meter-1.7.2.tgz#ddaae2246fb8a53fd8cdc5b1084b1d16cc401505"
|
||||||
|
|||||||
Reference in New Issue
Block a user