mirror of
https://github.com/jumpserver/lina.git
synced 2025-11-08 02:18:44 +00:00
Compare commits
51 Commits
pr@dev@ass
...
v3.10.18
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
587ed881df | ||
|
|
4162bdb74d | ||
|
|
d231bd503c | ||
|
|
b42c5f9170 | ||
|
|
f823257515 | ||
|
|
d3f4b2b2e8 | ||
|
|
5eba19946c | ||
|
|
b4e889316e | ||
|
|
4a72e5aa2b | ||
|
|
6ea86c7efe | ||
|
|
dc0a0ae868 | ||
|
|
5b3b8f72cd | ||
|
|
df26679166 | ||
|
|
9efccb8ada | ||
|
|
d784530539 | ||
|
|
afcc60f29c | ||
|
|
f8d581e455 | ||
|
|
dba1540953 | ||
|
|
456227abcf | ||
|
|
66532f4d4b | ||
|
|
42f27eb30f | ||
|
|
57920bf771 | ||
|
|
290772f44e | ||
|
|
f140f2f59e | ||
|
|
7b1883e012 | ||
|
|
352ac7e828 | ||
|
|
1cbd58664c | ||
|
|
e48da6be9b | ||
|
|
fa31b36550 | ||
|
|
6b93a6563d | ||
|
|
d561701049 | ||
|
|
edbf477c1e | ||
|
|
6a2578b339 | ||
|
|
2cb7569cb0 | ||
|
|
9e0c623b9a | ||
|
|
40bf040501 | ||
|
|
efee7c7bbf | ||
|
|
5daecb84ae | ||
|
|
3be325214d | ||
|
|
581509f42f | ||
|
|
654b36b064 | ||
|
|
dcec73ae67 | ||
|
|
00bafa8164 | ||
|
|
da09af79a7 | ||
|
|
b596815ea5 | ||
|
|
cb37273e80 | ||
|
|
c5bf7d0ad2 | ||
|
|
c31195a67a | ||
|
|
1eb59b379a | ||
|
|
b7cee17156 | ||
|
|
f1c8874010 |
@@ -34,6 +34,7 @@
|
||||
"css-color-function": "^1.3.3",
|
||||
"decimal.js": "^10.4.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
"dompurify": "^3.1.6",
|
||||
"echarts": "4.7.0",
|
||||
"element-ui": "2.13.2",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
|
||||
@@ -151,6 +151,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<AssetSelect ref="assetSelect" :can-select="canSelect" :disabled="disabled" />
|
||||
<AssetSelect ref="assetSelect" :can-select="canSelect" :disabled="disabled" :tree-setting="treeSetting" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -59,6 +59,10 @@ export default {
|
||||
default(row, index) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
treeSetting: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:close-on-click-modal="false"
|
||||
:loading-status="!isLoaded"
|
||||
:title="$tc('assets.Assets')"
|
||||
custom-class="asset-select-dialog"
|
||||
top="2vh"
|
||||
@@ -21,6 +22,7 @@
|
||||
:tree-setting="treeSetting"
|
||||
class="tree-table"
|
||||
v-bind="$attrs"
|
||||
@loaded="handleTableLoaded"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -63,6 +65,7 @@ export default {
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isLoaded: false,
|
||||
dialogVisible: false,
|
||||
rowSelected: _.cloneDeep(this.value) || [],
|
||||
rowsAdd: [],
|
||||
@@ -143,6 +146,9 @@ export default {
|
||||
if (selectValueIndex > -1) {
|
||||
this.rowSelected.splice(selectValueIndex, 1)
|
||||
}
|
||||
},
|
||||
handleTableLoaded() {
|
||||
this.isLoaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
<script>
|
||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||
import AssetSelectDialog from './dialog.vue'
|
||||
import { b } from 'css-color-function/lib/adjusters'
|
||||
|
||||
export default {
|
||||
componentName: 'AssetSelect',
|
||||
@@ -81,7 +80,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
b,
|
||||
handleFocus() {
|
||||
this.$refs.select2.selectRef.blur()
|
||||
this.dialogVisible = true
|
||||
|
||||
@@ -31,6 +31,10 @@ export default {
|
||||
type: String,
|
||||
default: '/api/v1/assets/assets/'
|
||||
},
|
||||
typeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/category/tree/'
|
||||
},
|
||||
nodeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/'
|
||||
@@ -101,9 +105,14 @@ export default {
|
||||
showAssets: false,
|
||||
showSearch: false,
|
||||
customTreeHeaderName: this.$t('assets.BuiltinTree'),
|
||||
url: '/api/v1/assets/nodes/category/tree/',
|
||||
url: this.typeUrl,
|
||||
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
||||
treeUrl: `/api/v1/assets/nodes/category/tree/?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: {
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ export default {
|
||||
url: this.tableUrl,
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
treeUrl: this.treeUrl,
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
onSelected: (event, node) => vm.onSelected(node, vm),
|
||||
refresh: vm.refreshObjectAssetPermission
|
||||
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
formatterData = data
|
||||
}
|
||||
return (
|
||||
<span>{formatterData}</span>
|
||||
<span style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', lineHeight: '1.2' }}>{formatterData}</span>
|
||||
)
|
||||
}
|
||||
if (this.value instanceof Array) {
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<slot />
|
||||
<div v-loading="loadingStatus">
|
||||
<slot />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<slot name="footer">
|
||||
<el-button v-if="showCancel && showButtons" @click="onCancel">{{ cancelTitle }}</el-button>
|
||||
<el-button v-if="showConfirm && showButtons" :loading="loadingStatus" type="primary" @click="onConfirm">
|
||||
<el-button v-if="showConfirm && showButtons" :disabled="loadingStatus" type="primary" @click="onConfirm">
|
||||
{{ confirmTitle }}
|
||||
</el-button>
|
||||
</slot>
|
||||
@@ -71,13 +73,16 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iWidth() {
|
||||
return this.$store.getters.isMobile ? '1000px' : this.width
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
onCancel() {
|
||||
this.$emit('cancel')
|
||||
|
||||
@@ -7,7 +7,6 @@ import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
|
||||
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
import TagInput from '@/components/Form/FormFields/TagInput.vue'
|
||||
import TransferSelect from '@/components/Form/FormFields/TransferSelect.vue'
|
||||
|
||||
export class FormFieldGenerator {
|
||||
constructor(emit) {
|
||||
@@ -134,9 +133,6 @@ export class FormFieldGenerator {
|
||||
case 'comment':
|
||||
field.el.type = 'textarea'
|
||||
break
|
||||
case 'users':
|
||||
field.component = TransferSelect
|
||||
field.el.label = field.label
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
/>
|
||||
<Dialog
|
||||
v-if="showTransfer"
|
||||
:loading-status="!isLoaded"
|
||||
:close-on-click-modal="false"
|
||||
:title="label"
|
||||
:visible.sync="showTransfer"
|
||||
@@ -18,6 +19,7 @@
|
||||
width="730px"
|
||||
@cancel="handleTransCancel"
|
||||
@confirm="handleTransConfirm"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<krryPaging v-if="selectInitialized" ref="pageTransfer" class="transfer" v-bind="pagingTransfer" />
|
||||
</Dialog>
|
||||
@@ -77,13 +79,16 @@ export default {
|
||||
if (keyword) {
|
||||
params['search'] = keyword
|
||||
}
|
||||
this.isLoaded = false
|
||||
const data = await this.$axios.get(url, { params })
|
||||
this.isLoaded = true
|
||||
return data['results'].map(item => {
|
||||
const n = transformOption(item)
|
||||
return { id: n.value, label: n.label }
|
||||
})
|
||||
}
|
||||
return {
|
||||
isLoaded: false,
|
||||
showTransfer: false,
|
||||
selectInitialized: false,
|
||||
select2: {
|
||||
@@ -166,7 +171,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
patterns.push([/\d/, i18n.t('common.password.NUMBER_REQUIRED')])
|
||||
}
|
||||
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
|
||||
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]")
|
||||
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?_+-]")
|
||||
patterns.push([pattern, i18n.t('common.password.SPECIAL_CHAR_REQUIRED')])
|
||||
}
|
||||
for (const [pattern, msg] of patterns) {
|
||||
|
||||
@@ -13,15 +13,28 @@ class StrategyAbstract {
|
||||
this.onSelect = this.onSelect.bind(this)
|
||||
this.onSelectAll = this.onSelectAll.bind(this)
|
||||
}
|
||||
|
||||
get elTable() {
|
||||
return this.elDataTable.$refs.table
|
||||
}
|
||||
onSelectionChange() {}
|
||||
onSelect() {}
|
||||
onSelectAll() {}
|
||||
toggleRowSelection() {}
|
||||
clearSelection() {}
|
||||
updateElTableSelection() {}
|
||||
|
||||
onSelectionChange() {
|
||||
}
|
||||
|
||||
onSelect() {
|
||||
}
|
||||
|
||||
onSelectAll() {
|
||||
}
|
||||
|
||||
toggleRowSelection() {
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
}
|
||||
|
||||
updateElTableSelection() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,14 +47,16 @@ class StrategyNormal extends StrategyAbstract {
|
||||
onSelectionChange(val) {
|
||||
this.elDataTable.selected = val
|
||||
}
|
||||
|
||||
/**
|
||||
* toggleRowSelection和clearSelection的表现与el-table一致
|
||||
*/
|
||||
toggleRowSelection(...args) {
|
||||
return this.elTable.toggleRowSelection(...args)
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
return this.elTable.clearSelection()
|
||||
return this.elTable?.clearSelection()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,17 +80,55 @@ class StrategyPersistSelection extends StrategyAbstract {
|
||||
const isChosen = selection.indexOf(row) > -1
|
||||
this.toggleRowSelection(row, isChosen)
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户切换当前页的多选
|
||||
*/
|
||||
onSelectAll(selection, selectable = () => true) {
|
||||
const isSelected = !!selection.length
|
||||
this.elDataTable.data.forEach(r => {
|
||||
if (selectable(r)) {
|
||||
this.toggleRowSelection(r, isSelected)
|
||||
}
|
||||
})
|
||||
const { id, selected, data } = this.elDataTable
|
||||
const selectableRows = data.filter(selectable)
|
||||
// const isSelected = !!selection.length
|
||||
|
||||
// 创建已选择项的 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数组
|
||||
* 记得最后要将状态同步到el-table中
|
||||
@@ -83,34 +136,42 @@ class StrategyPersistSelection extends StrategyAbstract {
|
||||
toggleRowSelection(row, isSelected) {
|
||||
const { id, selected } = this.elDataTable
|
||||
const foundIndex = selected.findIndex(r => r[id] === row[id])
|
||||
|
||||
if (typeof isSelected === 'undefined') {
|
||||
isSelected = foundIndex <= -1
|
||||
}
|
||||
|
||||
if (isSelected && foundIndex === -1) {
|
||||
selected.push(row)
|
||||
} else if (!isSelected && foundIndex > -1) {
|
||||
selected.splice(foundIndex, 1)
|
||||
}
|
||||
|
||||
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
|
||||
this.updateElTableSelection()
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
this.elDataTable.selected = []
|
||||
this.updateElTableSelection()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将selected状态同步到el-table中
|
||||
*/
|
||||
updateElTableSelection() {
|
||||
const { data, id, selected } = this.elDataTable
|
||||
// 历史勾选的行已经不在当前页了,所以要将当前页的行数据和selected合并
|
||||
const mergeData = _.uniqWith([...data, ...selected], _.isEqual)
|
||||
mergeData.forEach(r => {
|
||||
const isSelected = !!selected.find(r2 => r[id] === r2[id])
|
||||
if (!this.elTable) {
|
||||
return
|
||||
const selectedIds = new Set(selected.map(r => r[id]))
|
||||
|
||||
this.elTable?.clearSelection()
|
||||
|
||||
data.forEach(row => {
|
||||
const shouldBeSelected = selectedIds.has(row[id])
|
||||
if (!this.elTable) return
|
||||
|
||||
if (shouldBeSelected) {
|
||||
this.elTable.toggleRowSelection(row, true)
|
||||
}
|
||||
this.elTable.toggleRowSelection(r, isSelected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,8 @@ export default {
|
||||
this.toggleRowSelection(row, true)
|
||||
}
|
||||
}
|
||||
|
||||
this.$emit('loaded')
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
localStorage.setItem('paginationSize', val)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query'
|
||||
import { download } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'ExportDialog',
|
||||
@@ -187,10 +188,7 @@ export default {
|
||||
})
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
download(url)
|
||||
},
|
||||
async defaultPerformExport(selectRows, exportOption, q, exportTypeOption) {
|
||||
const url = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import ImportTable from '@/components/Table/ListTable/TableAction/ImportTable.vue'
|
||||
import { getErrorResponseMsg } from '@/utils/common'
|
||||
import { download, getErrorResponseMsg } from '@/utils/common'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
|
||||
export default {
|
||||
@@ -221,10 +221,7 @@ export default {
|
||||
this.$message.success(msg)
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
download(url)
|
||||
},
|
||||
async handleImportConfirm() {
|
||||
await this.$refs['importTable'].performUpload()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-row type="flex" align="center">
|
||||
<el-col :md="8" :sm="24">
|
||||
<div class="tableFilter">
|
||||
<el-radio-group v-model="importStatusFilter" size="small">
|
||||
@@ -11,7 +11,7 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :md="8" :sm="24" style="text-align: center">
|
||||
<el-col :md="16" :sm="24" style="text-align: center; display: flex; align-items: center">
|
||||
<span class="summary-item summary-total"> {{ $t('common.Total') }}: {{ totalCount }}</span>
|
||||
<span class="summary-item summary-success"> {{ $t('common.Success') }}: {{ successCount }}</span>
|
||||
<span class="summary-item summary-failed"> {{ $t('common.Failed') }}: {{ failedCount }}</span>
|
||||
|
||||
@@ -122,8 +122,13 @@ export default {
|
||||
})
|
||||
},
|
||||
iExportOptions() {
|
||||
const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
||||
return options
|
||||
// const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
||||
// return options
|
||||
|
||||
return {
|
||||
url: this.tableUrl,
|
||||
...this.exportOptions
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -122,6 +122,9 @@ export default {
|
||||
return this.hasLeftActions ? 'right' : 'left'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('done')
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
@@ -144,119 +147,121 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.table-header {
|
||||
/*display: flex;*/
|
||||
/*flex-direction: row;*/
|
||||
/*justify-content: space-between;*/
|
||||
}
|
||||
.table-header {
|
||||
/*display: flex;*/
|
||||
/*flex-direction: row;*/
|
||||
/*justify-content: space-between;*/
|
||||
}
|
||||
|
||||
.right-side-item {
|
||||
}
|
||||
.right-side-item {
|
||||
}
|
||||
|
||||
.right-side-actions >>> .el-button {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #888;
|
||||
background-color: transparent;
|
||||
}
|
||||
.right-side-actions > > > .el-button {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #888;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.right-side-actions >>> .fa {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
.right-side-actions > > > .fa {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.right-side-actions >>> .el-button:hover {
|
||||
background-color: rgb(0, 0, 0, 0.05);
|
||||
}
|
||||
.right-side-actions > > > .el-button:hover {
|
||||
background-color: rgb(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.action-search >>> .el-input__suffix i {
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
}
|
||||
.action-search > > > .el-input__suffix i {
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.action-search >>> .el-cascader {
|
||||
line-height: 32px !important;
|
||||
}
|
||||
.action-search > > > .el-cascader {
|
||||
line-height: 32px !important;
|
||||
}
|
||||
|
||||
.right-side-actions {
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.right-side-actions {
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.table-action-right-side {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.table-action-right-side {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.export-item {
|
||||
display: block;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
.export-item {
|
||||
display: block;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.datepicker {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
line-height: 32px;
|
||||
}
|
||||
.table-header {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.left-side {
|
||||
float: left;
|
||||
display: block;
|
||||
&>>> .action-item.el-dropdown {
|
||||
height: 33px;
|
||||
&> .el-button {
|
||||
height: 100%;
|
||||
}
|
||||
.left-side {
|
||||
float: left;
|
||||
display: block;
|
||||
|
||||
& > > > .action-item.el-dropdown {
|
||||
height: 33px;
|
||||
|
||||
& > .el-button {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
float: right;
|
||||
}
|
||||
.right-side {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.search {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.mobile .search {
|
||||
display: inherit;
|
||||
}
|
||||
.mobile .search {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.mobile .search .datepicker {
|
||||
margin-left: 0;
|
||||
}
|
||||
.mobile .search .datepicker {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.search.left {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.search.left {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.search.right {
|
||||
float: right;
|
||||
}
|
||||
.search.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mobile .search.right {
|
||||
float: none;
|
||||
}
|
||||
.mobile .search.right {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.mobile .search.right .action-search {
|
||||
width: 100%;
|
||||
}
|
||||
.mobile .search.right .action-search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mobile .right-side {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.mobile .right-side {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.filter-field.right-side-item.action-search {
|
||||
height: 34px;
|
||||
}
|
||||
.filter-field.right-side-item.action-search {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -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,7 +75,10 @@ export default {
|
||||
return {
|
||||
selectedRows: [],
|
||||
init: false,
|
||||
extraQuery: extraQuery
|
||||
isDeactivated: false,
|
||||
extraQuery: extraQuery,
|
||||
actionInit: this.headerActions.has === false,
|
||||
initQuery: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -166,14 +171,36 @@ export default {
|
||||
}
|
||||
},
|
||||
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) {
|
||||
this.$log.debug('ListTable: search table', attrs)
|
||||
const init = this.updateInitQuery(attrs)
|
||||
if (init) {
|
||||
return
|
||||
}
|
||||
this.$emit('TagSearch', attrs)
|
||||
this.$refs.dataTable?.$refs.dataTable?.search(attrs, true)
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span>{{ value }}</span>
|
||||
<span class="date">{{ dateValue }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -10,24 +10,31 @@ export default {
|
||||
name: 'DateFormatter',
|
||||
extends: BaseFormatter,
|
||||
data() {
|
||||
let value
|
||||
if (this.cellValue) {
|
||||
value = toSafeLocalDateStr(this.cellValue)
|
||||
} else {
|
||||
value = '-'
|
||||
}
|
||||
// let value
|
||||
// if (this.cellValue) {
|
||||
// value = toSafeLocalDateStr(this.cellValue)
|
||||
// } else {
|
||||
// value = '-'
|
||||
// }
|
||||
// const locale = this.$i18n.locale
|
||||
// const value = dt.toLocaleString(locale, { hourCycle: 'h23' })
|
||||
// debug(this.$i18n.locale)
|
||||
return {
|
||||
value: value
|
||||
}
|
||||
// return {
|
||||
// value: value
|
||||
// }
|
||||
// return {
|
||||
// value: `${year}-${month}-${date} ${hour}:${minutes}:${seconds}`
|
||||
// }
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
dateValue() {
|
||||
if (this.cellValue) {
|
||||
return toSafeLocalDateStr(this.cellValue)
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -157,7 +157,7 @@ export default {
|
||||
</a>`
|
||||
const treeActions = `${showSearch ? searchIcon : ''}${showRefresh ? refreshIcon : ''}`
|
||||
const icons = `
|
||||
<span style="float: right; margin-right: 10px">
|
||||
<span>
|
||||
${treeActions}
|
||||
</span>`
|
||||
if (rootNode) {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
showRenameBtn: false,
|
||||
drag: {
|
||||
isCopy: false,
|
||||
isMove: true
|
||||
isMove: !this.$store.getters.currentOrgIsRoot
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
/>
|
||||
</el-col>
|
||||
<el-col v-show="isShow" :span="span">
|
||||
<VueMarkdown class="result-html" :source="iValue" :show="true" :html="false" />
|
||||
<VueMarkdown class="result-html" :source="sanitizedValue" :html="false" :show="true" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<VueMarkdown v-else class="source" :source="iValue" :html="false" />
|
||||
<VueMarkdown v-else class="source" :html="false" :source="sanitizedValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DOMPurify from 'dompurify'
|
||||
import VueMarkdown from 'vue-markdown'
|
||||
import 'github-markdown-css/github-markdown-light.css'
|
||||
|
||||
@@ -53,13 +54,24 @@ export default {
|
||||
resizeObserver: null,
|
||||
span: 12,
|
||||
isShow: true,
|
||||
iValue: this.value
|
||||
iValue: this.sanitizeContent(this.value)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sanitizedValue() {
|
||||
const content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
|
||||
|
||||
return this.sanitizeContent(content)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.iValue = this.sanitizeContent(newVal)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
// 监听高度变化
|
||||
const height = entries[0].target.offsetHeight
|
||||
if (height) {
|
||||
this.height = height
|
||||
@@ -79,8 +91,19 @@ export default {
|
||||
this.resizeObserver = null
|
||||
},
|
||||
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() {
|
||||
this.$emit('change', this.iValue)
|
||||
const sanitizedValue = this.sanitizeContent(this.iValue)
|
||||
this.iValue = sanitizedValue
|
||||
this.$emit('change', sanitizedValue)
|
||||
},
|
||||
onView() {
|
||||
this.isShow = !this.isShow
|
||||
|
||||
@@ -48,6 +48,7 @@ export default {
|
||||
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
||||
lineHeight: 1.2,
|
||||
fontSize: 13,
|
||||
scrollback: 9999999,
|
||||
rightClickSelectsWord: true,
|
||||
theme: {
|
||||
background: '#fff',
|
||||
|
||||
@@ -1931,6 +1931,7 @@
|
||||
"CheckViewAcceptor": "View more acceptor",
|
||||
"Assignees": "Assignees",
|
||||
"Close": "Close",
|
||||
"CancelTicket": "Cancel Ticket",
|
||||
"OpenStatus": "Open",
|
||||
"CloseStatus": "Close",
|
||||
"Comment": "Comment",
|
||||
|
||||
@@ -1927,6 +1927,7 @@
|
||||
"Assignee": "処理者",
|
||||
"Assignees": "処理待ち",
|
||||
"Close": "閉じる",
|
||||
"CancelTicket": "作業指示をキャンセルする",
|
||||
"OpenStatus": "オン",
|
||||
"CloseStatus": "閉じる",
|
||||
"Comment": "備考",
|
||||
|
||||
@@ -1913,6 +1913,7 @@
|
||||
"Assignee": "处理人",
|
||||
"Assignees": "待处理人",
|
||||
"Close": "关闭",
|
||||
"CancelTicket": "取消工单",
|
||||
"OpenStatus": "审批中",
|
||||
"CloseStatus": "已完成",
|
||||
"Comment": "备注",
|
||||
|
||||
@@ -1913,6 +1913,7 @@
|
||||
"Assignee": "處理人",
|
||||
"Assignees": "待處理人",
|
||||
"Close": "關閉",
|
||||
"CancelTicket": "取消工單",
|
||||
"OpenStatus": "審批中",
|
||||
"CloseStatus": "已完成",
|
||||
"Comment": "備註",
|
||||
|
||||
@@ -81,7 +81,7 @@ export default {
|
||||
},
|
||||
{
|
||||
label: this.$t('common.Version'),
|
||||
value: 'version-dev'
|
||||
value: 'v3.10.18'
|
||||
},
|
||||
{
|
||||
label: this.$t('common.PermissionCompany'),
|
||||
|
||||
@@ -61,7 +61,6 @@ export default {
|
||||
break
|
||||
case 'logout':
|
||||
this.logout()
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
break
|
||||
case 'apiKey':
|
||||
this.$router.push('/profile/api-keys')
|
||||
@@ -73,7 +72,14 @@ export default {
|
||||
this.$router.push('/profile/user/setting')
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
async logout() {
|
||||
const currentOrg = this.$store.getters.currentOrg
|
||||
|
||||
if (currentOrg.autoEnter || currentOrg.is_system) {
|
||||
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
|
||||
}
|
||||
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,10 +71,12 @@ const mutations = {
|
||||
state.consoleOrgs = state.consoleOrgs.filter(i => i.id !== org.id)
|
||||
},
|
||||
SET_CURRENT_ORG(state, org) {
|
||||
// 系统组织和全局组织不设置成 Pre org
|
||||
if (!state.currentOrg?.autoEnter && !state.currentOrg?.is_root) {
|
||||
state.preOrg = state.currentOrg
|
||||
setPreOrgLocal(state.username, state.currentOrg)
|
||||
// 系统组织不设置成 Pre org
|
||||
const currentOrg = state.currentOrg
|
||||
|
||||
if (currentOrg && !currentOrg.autoEnter && !currentOrg.is_system) {
|
||||
state.preOrg = currentOrg
|
||||
setPreOrgLocal(state.username, currentOrg)
|
||||
}
|
||||
state.currentOrg = org
|
||||
saveCurrentOrgLocal(state.username, org)
|
||||
@@ -144,6 +146,7 @@ const actions = {
|
||||
const systemOrg = {
|
||||
id: orgUtil.SYSTEM_ORG_ID,
|
||||
name: 'SystemSetting',
|
||||
is_system: true,
|
||||
autoEnter: new Date().getTime()
|
||||
}
|
||||
commit('SET_CURRENT_ORG', systemOrg)
|
||||
|
||||
@@ -42,8 +42,14 @@ export function getCurrentOrgLocal(username) {
|
||||
|
||||
export function saveCurrentOrgLocal(username, org) {
|
||||
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) {
|
||||
|
||||
@@ -363,13 +363,35 @@ export function downloadText(content, filename) {
|
||||
}
|
||||
|
||||
export function download(downloadUrl, filename) {
|
||||
const a = document.createElement('a')
|
||||
a.href = downloadUrl
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.style.display = 'none'
|
||||
document.body.appendChild(iframe)
|
||||
const timeout = 1000 * 60 * 30
|
||||
|
||||
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.
|
||||
}
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(downloadUrl)
|
||||
}
|
||||
|
||||
export function diffObject(object, base) {
|
||||
|
||||
@@ -17,7 +17,7 @@ function getPropOrg() {
|
||||
if (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() {
|
||||
|
||||
@@ -8,7 +8,10 @@ import orgs from '@/api/orgs'
|
||||
import { getPropView, isViewHasOrgs } from '@/utils/jms'
|
||||
|
||||
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) {
|
||||
return new Promise((resolve, reject) => reject(msg))
|
||||
@@ -23,7 +26,23 @@ async function checkLogin({ to, from, next }) {
|
||||
return await store.dispatch('users/getProfile')
|
||||
} catch (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
|
||||
if (store.getters.currentOrg.autoEnter) {
|
||||
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
|
||||
}
|
||||
if (status === 401 || status === 403) {
|
||||
setTimeout(() => {
|
||||
window.location = process.env.VUE_APP_LOGIN_PATH
|
||||
@@ -151,18 +170,24 @@ export async function startup({ to, from, next }) {
|
||||
if (store.getters.inited) {
|
||||
return true
|
||||
}
|
||||
await store.dispatch('app/init')
|
||||
|
||||
// set page title
|
||||
// await getOpenPublicSetting({ to, from, next })
|
||||
await getPublicSetting({ to, from, next }, true)
|
||||
await checkLogin({ to, from, next })
|
||||
await getPublicSetting({ to, from, next }, false)
|
||||
await changeCurrentViewIfNeed({ to, from, next })
|
||||
await changeCurrentOrgIfNeed({ to, from, next })
|
||||
await generatePageRoutes({ to, from, next })
|
||||
await checkUserFirstLogin({ to, from, next })
|
||||
await store.dispatch('assets/getAssetCategories')
|
||||
try {
|
||||
await store.dispatch('app/init')
|
||||
|
||||
// set page title
|
||||
// await getOpenPublicSetting({ to, from, next })
|
||||
await getPublicSetting({ to, from, next }, true)
|
||||
await checkLogin({ to, from, next })
|
||||
await getPublicSetting({ to, from, next }, false)
|
||||
await changeCurrentViewIfNeed({ to, from, next })
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,12 @@ export default {
|
||||
showMenu: false,
|
||||
showAssets: true,
|
||||
url: '/api/v1/accounts/accounts/',
|
||||
countResource: 'account'
|
||||
countResource: 'account',
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
columns: null,
|
||||
columnsExclude: ['spec_info'],
|
||||
columnsExclude: ['spec_info', 'password_rules', 'push_params'],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions']
|
||||
|
||||
@@ -65,9 +65,11 @@ export default {
|
||||
columns: ['name', 'username', 'secret_type', 'privileged'],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatterArgs: {
|
||||
route: 'AccountTemplateDetail'
|
||||
}
|
||||
formatter: (row) => <span>{row.name}</span>
|
||||
// 暂禁用远程应用中账号模板的详情跳转
|
||||
// formatterArgs: {
|
||||
// route: 'AccountTemplateDetail'
|
||||
// }
|
||||
},
|
||||
privileged: {
|
||||
width: '100px'
|
||||
|
||||
@@ -102,7 +102,7 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
[vmware]: {
|
||||
name: vmware,
|
||||
title: 'VMware',
|
||||
attrs: ['host', 'port', 'username', 'password']
|
||||
attrs: ['host', 'port', 'username', 'password', 'auto_sync_node']
|
||||
},
|
||||
[nutanix]: {
|
||||
name: nutanix,
|
||||
|
||||
@@ -10,13 +10,11 @@
|
||||
@click="onSetting"
|
||||
/>
|
||||
<Dialog
|
||||
v-if="isVisible"
|
||||
width="60%"
|
||||
:visible.sync="isVisible"
|
||||
:title="title"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:destroy-on-close="true"
|
||||
@close="onDialogClose"
|
||||
>
|
||||
<AutoDataForm
|
||||
@@ -34,7 +32,7 @@
|
||||
<script>
|
||||
import Dialog from '../../../components/Dialog'
|
||||
import AutoDataForm from '../../../components/Form/AutoDataForm'
|
||||
import { DynamicInput } from '../../../components/Form/FormFields'
|
||||
import { DynamicInput, Switcher } from '../../../components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -147,6 +145,9 @@ export default {
|
||||
case 'list':
|
||||
component = DynamicInput
|
||||
break
|
||||
case 'boolean':
|
||||
component = Switcher
|
||||
break
|
||||
}
|
||||
|
||||
if (param) {
|
||||
|
||||
@@ -128,7 +128,8 @@ export default {
|
||||
tip += current.label + ':' + current.total + '<br/>'
|
||||
}
|
||||
return tip
|
||||
}
|
||||
},
|
||||
appendToBody: true
|
||||
},
|
||||
grid: {
|
||||
top: '60%',
|
||||
|
||||
@@ -36,6 +36,11 @@ export default {
|
||||
this.tableConfig.url = `/api/v1/perms/users/self/nodes/${currentNodeId}/assets/?cache_policy=1`
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
}
|
||||
},
|
||||
tableConfig: {
|
||||
|
||||
@@ -205,6 +205,11 @@ export default {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showLine: true
|
||||
},
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
}
|
||||
},
|
||||
iShowTree: true,
|
||||
|
||||
@@ -98,6 +98,7 @@ export default {
|
||||
el: {
|
||||
baseUrl: '/api/v1/perms/users/self/assets/',
|
||||
baseNodeUrl: '/api/v1/perms/users/self/nodes/',
|
||||
typeUrl: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree',
|
||||
value: []
|
||||
}
|
||||
},
|
||||
|
||||
@@ -290,6 +290,11 @@ export default {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showLine: true
|
||||
},
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
}
|
||||
},
|
||||
iShowTree: true
|
||||
|
||||
@@ -107,6 +107,13 @@ export default {
|
||||
this.$log.debug('AssetSelect value', that.assets)
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
this.$store.commit('common/reload')
|
||||
},
|
||||
treeSetting: {
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
nodeRelationConfig: {
|
||||
|
||||
@@ -39,7 +39,12 @@ export default {
|
||||
notShowBuiltinTree: true,
|
||||
url: '/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: {
|
||||
url: '/api/v1/perms/asset-permissions/',
|
||||
@@ -105,6 +110,7 @@ export default {
|
||||
formatter: AmountFormatter,
|
||||
formatterArgs: {
|
||||
async: true,
|
||||
cellValueToRemove: ['@SPEC'],
|
||||
routeQuery: {
|
||||
activeTab: 'AssetPermissionUser'
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
component: PhoneInput
|
||||
},
|
||||
mfa_level: {
|
||||
hidden: (formValue) => {
|
||||
disabled: (formValue) => {
|
||||
return formValue.mfa_level === 2
|
||||
},
|
||||
helpText: this.$t('users.HelpText.MFAOfUserFirstLoginPersonalInformationImprovementPage')
|
||||
@@ -90,8 +90,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import isFalsey from '@/components/Table/DataTable/compenents/el-data-table/util
|
||||
import deepmerge from 'deepmerge'
|
||||
import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import { download } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'CommandList',
|
||||
@@ -144,10 +145,7 @@ export default {
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
url = url + queryStr
|
||||
this.$log.debug('Export url: ', this.url, '=>', url)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url + queryStr)
|
||||
download(url + queryStr)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -55,8 +55,7 @@ export default {
|
||||
fieldsMeta: {
|
||||
EMAIL_HOST_USER: {
|
||||
rules: [
|
||||
rules.EmailCheck,
|
||||
rules.Required
|
||||
rules.EmailCheck
|
||||
]
|
||||
},
|
||||
EMAIL_FROM: {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
v-model="requestForm.accounts"
|
||||
:nodes="requestForm.nodes"
|
||||
:assets="requestForm.assets"
|
||||
:oid="requestForm.oid"
|
||||
:show-add-template="false"
|
||||
style="width: 50% !important"
|
||||
/>
|
||||
@@ -83,6 +84,7 @@ export default {
|
||||
assets: this.object.apply_assets?.map(i => i.id),
|
||||
accounts: this.object.apply_accounts,
|
||||
actions: this.object.apply_actions,
|
||||
oid: this.object.org_id,
|
||||
apply_date_expired: this.object.apply_date_expired,
|
||||
apply_date_start: this.object.apply_date_start
|
||||
},
|
||||
|
||||
@@ -48,7 +48,12 @@ export default {
|
||||
}
|
||||
],
|
||||
select2Option: {
|
||||
url: '/api/v1/users/users/?oid=root'
|
||||
url: '/api/v1/users/users/?oid=root',
|
||||
ajax: {
|
||||
transformOption: (item) => {
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
]
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<el-form-item style="float: right">
|
||||
<template v-if="hasActionPerm">
|
||||
<el-button
|
||||
:disabled="object.status.value === 'closed'"
|
||||
:disabled="isDisabled || object.status.value === 'closed'"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleApprove"
|
||||
@@ -35,7 +35,7 @@
|
||||
<i class="fa fa-check" /> {{ $t('tickets.Accept') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="object.status.value === 'closed'"
|
||||
:disabled="isDisabled || object.status.value === 'closed'"
|
||||
size="small"
|
||||
type="warning"
|
||||
@click="handleReject"
|
||||
@@ -45,12 +45,12 @@
|
||||
</template>
|
||||
<el-button
|
||||
v-if="isSelfTicket"
|
||||
:disabled="object.status.value === 'closed'"
|
||||
:disabled="isDisabled || object.status.value === 'closed'"
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="handleClose"
|
||||
>
|
||||
<i class="fa fa-times" /> {{ $t('tickets.Close') }}
|
||||
<i class="fa fa-times" /> {{ $t('tickets.CancelTicket') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="object.status.value === 'closed'"
|
||||
@@ -94,6 +94,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isDisabled: false,
|
||||
comments: '',
|
||||
type_api: '',
|
||||
imageUrl: require('@/assets/img/avatar.png'),
|
||||
@@ -156,17 +157,35 @@ export default {
|
||||
this.createComment(function() {
|
||||
})
|
||||
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/approve/`
|
||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
this.$axios.put(url).then(res => {
|
||||
this.reloadPage()
|
||||
}).catch(err => {
|
||||
this.$message.error(err)
|
||||
}).finally(() => {
|
||||
this.isDisabled = false
|
||||
})
|
||||
},
|
||||
defaultReject() {
|
||||
this.createComment(function() {
|
||||
})
|
||||
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/reject/`
|
||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
this.$axios.put(url).then(res => {
|
||||
this.reloadPage()
|
||||
}).catch(err => {
|
||||
this.$message.error(err)
|
||||
}).finally(() => {
|
||||
this.isDisabled = false
|
||||
})
|
||||
},
|
||||
defaultClose() {
|
||||
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/close/`
|
||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
||||
this.$axios.put(url).then(res => {
|
||||
this.reloadPage()
|
||||
}).catch(err => {
|
||||
this.$message.error(err)
|
||||
}).finally(() => {
|
||||
this.isDisabled = false
|
||||
})
|
||||
},
|
||||
createComment(successCallback) {
|
||||
const commentText = this.form.comments
|
||||
@@ -187,17 +206,42 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
handleAction(actionType) {
|
||||
if (this.isDisabled) {
|
||||
return
|
||||
}
|
||||
|
||||
this.isDisabled = true
|
||||
let handler
|
||||
switch (actionType) {
|
||||
case 'approve':
|
||||
handler = this.approve || this.defaultApprove
|
||||
break
|
||||
case 'reject':
|
||||
handler = this.reject || this.defaultReject
|
||||
break
|
||||
case 'close':
|
||||
handler = this.close || this.defaultClose
|
||||
break
|
||||
default:
|
||||
handler = null
|
||||
break
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
handler()
|
||||
} else {
|
||||
this.$message.error('No handler for action')
|
||||
}
|
||||
},
|
||||
handleApprove() {
|
||||
const handler = this.approve || this.defaultApprove
|
||||
handler()
|
||||
this.handleAction('approve')
|
||||
},
|
||||
handleReject() {
|
||||
const handler = this.reject || this.defaultReject
|
||||
handler()
|
||||
this.handleAction('reject')
|
||||
},
|
||||
handleClose() {
|
||||
const handler = this.close || this.defaultClose
|
||||
handler()
|
||||
this.handleAction('close')
|
||||
},
|
||||
handleComment() {
|
||||
this.createComment(
|
||||
|
||||
@@ -134,7 +134,7 @@ export default {
|
||||
},
|
||||
onMonitor() {
|
||||
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() {
|
||||
const url = '/api/v1/terminal/tasks/toggle-lock-session-for-ticket/'
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import TransSelect from '@/components/Form/FormFields/TransferSelect.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -23,7 +22,6 @@ export default {
|
||||
],
|
||||
fieldsMeta: {
|
||||
users: {
|
||||
component: TransSelect,
|
||||
el: {
|
||||
url: '/api/v1/users/users/?fields_size=mini&order=name',
|
||||
ajax: {
|
||||
@@ -38,8 +36,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ export default {
|
||||
showRefresh: true,
|
||||
showSearch: false,
|
||||
treeUrl: '',
|
||||
edit: {
|
||||
drag: {
|
||||
isMove: false
|
||||
}
|
||||
},
|
||||
check: {
|
||||
enable: true
|
||||
},
|
||||
|
||||
@@ -121,6 +121,9 @@ export default {
|
||||
system_roles: {
|
||||
component: Select2,
|
||||
label: this.$t('users.SystemRoles'),
|
||||
rules: [
|
||||
rules.Required
|
||||
],
|
||||
el: {
|
||||
multiple: true,
|
||||
ajax: {
|
||||
|
||||
Reference in New Issue
Block a user