Compare commits

...

33 Commits

Author SHA1 Message Date
fit2bot
3c1aca17ec feat: Update v3.10.16 2024-11-26 16:47:11 +08:00
feng
66532f4d4b fix: ticket duplicate submission 2024-11-26 16:27:27 +08:00
wangruidong
42f27eb30f perf: users use Select2 2024-10-17 10:34:54 +08:00
wangruidong
57920bf771 fix: user lack permission to view the type tree 2024-10-10 17:11:40 +08:00
ZhaoJiSen
290772f44e
Merge pull request #4384 from jumpserver/pr@v3@fix_special_chara
fixed: Fixed + and - not being special characters
2024-10-09 18:37:03 +08:00
zhaojisen
f140f2f59e fixed: Fixed + and - not being special characters 2024-10-09 18:35:40 +08:00
zhaojisen
7b1883e012 fixed: Fixed + and - not being special characters 2024-10-09 18:28:00 +08:00
wisonic
352ac7e828 fix: assets proportion tooltip cause page bounced at first time 2024-09-27 15:16:26 +08:00
ZhaoJiSen
1cbd58664c
Merge pull request #4375 from jumpserver/pr@v3@perf_import_style
perf: Import UI style optimizations
2024-09-26 17:35:26 +08:00
zhaojisen
e48da6be9b perf: Import UI style optimizations 2024-09-26 17:30:14 +08:00
wangruidong
fa31b36550 fix: xterm output truncate 2024-09-26 17:16:22 +08:00
ibuler
6b93a6563d perf: revert pre org if logout 2024-08-30 17:02:49 +08:00
feng
d561701049 perf: Ticket flow rule user display name(username) 2024-08-19 16:06:55 +08:00
Ewall555
edbf477c1e perf: Translate ticket cancel button 2024-08-15 15:49:49 +08:00
ZhaoJiSen
6a2578b339
Merge pull request #4292 from jumpserver/pr@v3@fix_loading
perf: add button loading status
2024-08-08 15:01:35 +08:00
zhaojisen
2cb7569cb0 perf: add button loading status 2024-08-08 15:00:11 +08:00
fit2bot
9e0c623b9a
perf: add button loading status (#4287)
* perf: add button loading status

* perf: add button loading status

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2024-08-08 11:38:04 +08:00
feng
40bf040501 perf: Asset authorization: The number of accounts displayed is incorrect ignore @SPEC 2024-08-07 17:51:21 +08:00
halo
efee7c7bbf perf: Email service authentication username is optional 2024-07-26 14:20:20 +08:00
ZhaoJiSen
5daecb84ae
Merge pull request #4251 from jumpserver/pr@v3@fix_data_refresh
fixed: Fixed an issue where the validation date does not refresh
2024-07-23 10:09:16 +08:00
feng
3be325214d perf: You can modify sudo permissions multiple times 2024-07-22 18:18:11 +08:00
zhaojisen
581509f42f fixed: Fixed an issue where the validation date does not refresh 2024-07-22 14:52:45 +08:00
zhaojisen
654b36b064 fixed: Fixed the issue that parameter push parameters could not be saved 2024-07-17 10:49:02 +08:00
feng626
dcec73ae67
Merge pull request #4224 from jumpserver/pr@v3@download
perf: Downloading files does not trigger the beforeunload event
2024-07-16 12:42:48 +08:00
feng
00bafa8164 perf: Downloading files does not trigger the beforeunload event 2024-07-16 12:40:31 +08:00
zhaojisen
da09af79a7 fixed:Fixed an issue where the user was unable to enter non-MD content 2024-07-15 19:08:46 +08:00
zhaojisen
b596815ea5 fixed:Fixed an issue where the user was unable to enter non-MD content 2024-07-15 19:08:46 +08:00
ibuler
cb37273e80 perf: table search two times, one init one search 2024-07-12 10:20:55 +08:00
zhaojisen
c5bf7d0ad2 fixed: Fixed an issue where push parameters could not be saved when adding an asset account 2024-07-11 15:18:11 +08:00
wangruidong
c31195a67a perf: profile improvement mfa disabled 2024-07-10 14:21:27 +08:00
wangruidong
1eb59b379a perf: profile improvement mfa cancel hide 2024-07-09 16:14:43 +08:00
ZhaoJiSen
b7cee17156
Merge pull request #4188 from jumpserver/pr@v3@fix_unuse_item
fixed: Remove unused fields
2024-07-08 19:01:07 +08:00
zhaojisen
f1c8874010 fixed: Remove unused fields 2024-07-08 18:57:37 +08:00
39 changed files with 2137 additions and 1987 deletions

1
GITSHA Normal file
View File

@ -0,0 +1 @@
66532f4d4bf4751a26a931fa021ea1e136c477d1

View File

@ -34,6 +34,7 @@
"css-color-function": "^1.3.3", "css-color-function": "^1.3.3",
"decimal.js": "^10.4.3", "decimal.js": "^10.4.3",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"dompurify": "^3.1.6",
"echarts": "4.7.0", "echarts": "4.7.0",
"element-ui": "2.13.2", "element-ui": "2.13.2",
"eslint-plugin-html": "^6.0.0", "eslint-plugin-html": "^6.0.0",

View File

@ -151,6 +151,3 @@ export default {
} }
} }
</script> </script>
<style scoped>
</style>

View File

@ -1,6 +1,7 @@
<template> <template>
<Dialog <Dialog
:close-on-click-modal="false" :close-on-click-modal="false"
:loading-status="!isLoaded"
:title="$tc('assets.Assets')" :title="$tc('assets.Assets')"
custom-class="asset-select-dialog" custom-class="asset-select-dialog"
top="2vh" top="2vh"
@ -21,6 +22,7 @@
:tree-setting="treeSetting" :tree-setting="treeSetting"
class="tree-table" class="tree-table"
v-bind="$attrs" v-bind="$attrs"
@loaded="handleTableLoaded"
/> />
</Dialog> </Dialog>
</template> </template>
@ -63,6 +65,7 @@ export default {
data() { data() {
const vm = this const vm = this
return { return {
isLoaded: false,
dialogVisible: false, dialogVisible: false,
rowSelected: _.cloneDeep(this.value) || [], rowSelected: _.cloneDeep(this.value) || [],
rowsAdd: [], rowsAdd: [],
@ -143,6 +146,9 @@ export default {
if (selectValueIndex > -1) { if (selectValueIndex > -1) {
this.rowSelected.splice(selectValueIndex, 1) this.rowSelected.splice(selectValueIndex, 1)
} }
},
handleTableLoaded() {
this.isLoaded = true
} }
} }
} }

View File

@ -28,7 +28,6 @@
<script> <script>
import Select2 from '@/components/Form/FormFields/Select2.vue' import Select2 from '@/components/Form/FormFields/Select2.vue'
import AssetSelectDialog from './dialog.vue' import AssetSelectDialog from './dialog.vue'
import { b } from 'css-color-function/lib/adjusters'
export default { export default {
componentName: 'AssetSelect', componentName: 'AssetSelect',
@ -81,7 +80,6 @@ export default {
} }
}, },
methods: { methods: {
b,
handleFocus() { handleFocus() {
this.$refs.select2.selectRef.blur() this.$refs.select2.selectRef.blur()
this.dialogVisible = true this.dialogVisible = true

View File

@ -31,6 +31,10 @@ export default {
type: String, type: String,
default: '/api/v1/assets/assets/' default: '/api/v1/assets/assets/'
}, },
typeUrl: {
type: String,
default: '/api/v1/assets/nodes/category/tree/'
},
nodeUrl: { nodeUrl: {
type: String, type: String,
default: '/api/v1/assets/nodes/' default: '/api/v1/assets/nodes/'
@ -101,9 +105,9 @@ export default {
showAssets: false, showAssets: false,
showSearch: false, showSearch: false,
customTreeHeaderName: this.$t('assets.BuiltinTree'), customTreeHeaderName: this.$t('assets.BuiltinTree'),
url: '/api/v1/assets/nodes/category/tree/', url: this.typeUrl,
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl, 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'}`,
callback: { callback: {
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode) onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
} }

View File

@ -9,11 +9,13 @@
v-bind="$attrs" v-bind="$attrs"
v-on="$listeners" v-on="$listeners"
> >
<div v-loading="loadingStatus">
<slot /> <slot />
</div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<slot name="footer"> <slot name="footer">
<el-button v-if="showCancel && showButtons" @click="onCancel">{{ cancelTitle }}</el-button> <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 }} {{ confirmTitle }}
</el-button> </el-button>
</slot> </slot>
@ -71,13 +73,16 @@ export default {
} }
}, },
data() { data() {
return {} return {
}
}, },
computed: { computed: {
iWidth() { iWidth() {
return this.$store.getters.isMobile ? '1000px' : this.width return this.$store.getters.isMobile ? '1000px' : this.width
} }
}, },
mounted() {
},
methods: { methods: {
onCancel() { onCancel() {
this.$emit('cancel') this.$emit('cancel')

View File

@ -7,7 +7,6 @@ import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue' import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
import { assignIfNot } from '@/utils/common' import { assignIfNot } from '@/utils/common'
import TagInput from '@/components/Form/FormFields/TagInput.vue' import TagInput from '@/components/Form/FormFields/TagInput.vue'
import TransferSelect from '@/components/Form/FormFields/TransferSelect.vue'
export class FormFieldGenerator { export class FormFieldGenerator {
constructor(emit) { constructor(emit) {
@ -134,9 +133,6 @@ export class FormFieldGenerator {
case 'comment': case 'comment':
field.el.type = 'textarea' field.el.type = 'textarea'
break break
case 'users':
field.component = TransferSelect
field.el.label = field.label
} }
return field return field
} }

View File

@ -11,6 +11,7 @@
/> />
<Dialog <Dialog
v-if="showTransfer" v-if="showTransfer"
:loading-status="!isLoaded"
:close-on-click-modal="false" :close-on-click-modal="false"
:title="label" :title="label"
:visible.sync="showTransfer" :visible.sync="showTransfer"
@ -18,6 +19,7 @@
width="730px" width="730px"
@cancel="handleTransCancel" @cancel="handleTransCancel"
@confirm="handleTransConfirm" @confirm="handleTransConfirm"
v-on="$listeners"
> >
<krryPaging v-if="selectInitialized" ref="pageTransfer" class="transfer" v-bind="pagingTransfer" /> <krryPaging v-if="selectInitialized" ref="pageTransfer" class="transfer" v-bind="pagingTransfer" />
</Dialog> </Dialog>
@ -77,13 +79,16 @@ export default {
if (keyword) { if (keyword) {
params['search'] = keyword params['search'] = keyword
} }
this.isLoaded = false
const data = await this.$axios.get(url, { params }) const data = await this.$axios.get(url, { params })
this.isLoaded = true
return data['results'].map(item => { return data['results'].map(item => {
const n = transformOption(item) const n = transformOption(item)
return { id: n.value, label: n.label } return { id: n.value, label: n.label }
}) })
} }
return { return {
isLoaded: false,
showTransfer: false, showTransfer: false,
selectInitialized: false, selectInitialized: false,
select2: { select2: {
@ -166,7 +171,3 @@ export default {
} }
} }
</script> </script>
<style scoped>
</style>

View File

@ -42,7 +42,7 @@ export default {
patterns.push([/\d/, i18n.t('common.password.NUMBER_REQUIRED')]) patterns.push([/\d/, i18n.t('common.password.NUMBER_REQUIRED')])
} }
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) { if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?]") const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?_+-]")
patterns.push([pattern, i18n.t('common.password.SPECIAL_CHAR_REQUIRED')]) patterns.push([pattern, i18n.t('common.password.SPECIAL_CHAR_REQUIRED')])
} }
for (const [pattern, msg] of patterns) { for (const [pattern, msg] of patterns) {

View File

@ -153,6 +153,8 @@ export default {
this.toggleRowSelection(row, true) this.toggleRowSelection(row, true)
} }
} }
this.$emit('loaded')
}, },
handleSizeChange(val) { handleSizeChange(val) {
localStorage.setItem('paginationSize', val) localStorage.setItem('paginationSize', val)

View File

@ -48,6 +48,7 @@
import Dialog from '@/components/Dialog/index.vue' import Dialog from '@/components/Dialog/index.vue'
import { createSourceIdCache } from '@/api/common' import { createSourceIdCache } from '@/api/common'
import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query' import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query'
import { download } from '@/utils/common'
export default { export default {
name: 'ExportDialog', name: 'ExportDialog',
@ -187,10 +188,7 @@ export default {
}) })
}, },
downloadCsv(url) { downloadCsv(url) {
const a = document.createElement('a') download(url)
a.href = url
a.click()
window.URL.revokeObjectURL(url)
}, },
async defaultPerformExport(selectRows, exportOption, q, exportTypeOption) { async defaultPerformExport(selectRows, exportOption, q, exportTypeOption) {
const url = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`) const url = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)

View File

@ -68,7 +68,7 @@
<script> <script>
import Dialog from '@/components/Dialog/index.vue' import Dialog from '@/components/Dialog/index.vue'
import ImportTable from '@/components/Table/ListTable/TableAction/ImportTable.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' import { createSourceIdCache } from '@/api/common'
export default { export default {
@ -221,10 +221,7 @@ export default {
this.$message.success(msg) this.$message.success(msg)
}, },
downloadCsv(url) { downloadCsv(url) {
const a = document.createElement('a') download(url)
a.href = url
a.click()
window.URL.revokeObjectURL(url)
}, },
async handleImportConfirm() { async handleImportConfirm() {
await this.$refs['importTable'].performUpload() await this.$refs['importTable'].performUpload()

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<el-row> <el-row type="flex" align="center">
<el-col :md="8" :sm="24"> <el-col :md="8" :sm="24">
<div class="tableFilter"> <div class="tableFilter">
<el-radio-group v-model="importStatusFilter" size="small"> <el-radio-group v-model="importStatusFilter" size="small">
@ -11,7 +11,7 @@
</el-radio-group> </el-radio-group>
</div> </div>
</el-col> </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-total"> {{ $t('common.Total') }}: {{ totalCount }}</span>
<span class="summary-item summary-success"> {{ $t('common.Success') }}: {{ successCount }}</span> <span class="summary-item summary-success"> {{ $t('common.Success') }}: {{ successCount }}</span>
<span class="summary-item summary-failed"> {{ $t('common.Failed') }}: {{ failedCount }}</span> <span class="summary-item summary-failed"> {{ $t('common.Failed') }}: {{ failedCount }}</span>

View File

@ -122,6 +122,9 @@ export default {
return this.hasLeftActions ? 'right' : 'left' return this.hasLeftActions ? 'right' : 'left'
} }
}, },
created() {
this.$emit('done')
},
methods: { methods: {
handleTagSearch(val) { handleTagSearch(val) {
this.searchTable(val) this.searchTable(val)
@ -209,8 +212,10 @@ export default {
.left-side { .left-side {
float: left; float: left;
display: block; display: block;
& > > > .action-item.el-dropdown { & > > > .action-item.el-dropdown {
height: 33px; height: 33px;
& > .el-button { & > .el-button {
height: 100%; height: 100%;
} }

View File

@ -8,9 +8,11 @@
:selected-rows="selectedRows" :selected-rows="selectedRows"
:table-url="tableUrl" :table-url="tableUrl"
v-bind="iHeaderActions" v-bind="iHeaderActions"
@done="handleActionInitialDone"
/> />
<IBox class="table-content"> <IBox class="table-content">
<AutoDataTable <AutoDataTable
v-if="actionInit"
ref="dataTable" ref="dataTable"
:config="iTableConfig" :config="iTableConfig"
:filter-table="filter" :filter-table="filter"
@ -73,7 +75,10 @@ export default {
return { return {
selectedRows: [], selectedRows: [],
init: false, init: false,
extraQuery: extraQuery isDeactivated: false,
extraQuery: extraQuery,
actionInit: this.headerActions.has === false,
initQuery: {}
} }
}, },
computed: { computed: {
@ -166,14 +171,36 @@ export default {
} }
}, },
methods: { methods: {
handleActionInitialDone() {
setTimeout(() => {
this.actionInit = true
}, 100)
},
handleSelectionChange(val) { handleSelectionChange(val) {
this.selectedRows = val this.selectedRows = val
}, },
reloadTable() { reloadTable() {
this.dataTable.getList() 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) { search(attrs) {
this.$log.debug('ListTable: search table', attrs) this.$log.debug('ListTable: search table', attrs)
const init = this.updateInitQuery(attrs)
if (init) {
return
}
this.$emit('TagSearch', attrs) this.$emit('TagSearch', attrs)
this.$refs.dataTable?.$refs.dataTable?.search(attrs, true) this.$refs.dataTable?.$refs.dataTable?.search(attrs, true)
}, },

View File

@ -1,5 +1,5 @@
<template> <template>
<span>{{ value }}</span> <span class="date">{{ dateValue }}</span>
</template> </template>
<script> <script>
@ -10,24 +10,31 @@ export default {
name: 'DateFormatter', name: 'DateFormatter',
extends: BaseFormatter, extends: BaseFormatter,
data() { data() {
let value // let value
if (this.cellValue) { // if (this.cellValue) {
value = toSafeLocalDateStr(this.cellValue) // value = toSafeLocalDateStr(this.cellValue)
} else { // } else {
value = '-' // value = '-'
} // }
// const locale = this.$i18n.locale // const locale = this.$i18n.locale
// const value = dt.toLocaleString(locale, { hourCycle: 'h23' }) // const value = dt.toLocaleString(locale, { hourCycle: 'h23' })
// debug(this.$i18n.locale) // debug(this.$i18n.locale)
return { // return {
value: value // value: value
} // }
// return { // return {
// value: `${year}-${month}-${date} ${hour}:${minutes}:${seconds}` // value: `${year}-${month}-${date} ${hour}:${minutes}:${seconds}`
// } // }
return {}
},
computed: {
dateValue() {
if (this.cellValue) {
return toSafeLocalDateStr(this.cellValue)
} else {
return '-'
}
}
} }
} }
</script> </script>
<style scoped>
</style>

View File

@ -18,14 +18,15 @@
/> />
</el-col> </el-col>
<el-col v-show="isShow" :span="span"> <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-col>
</el-row> </el-row>
<VueMarkdown v-else class="source" :source="iValue" :html="false" /> <VueMarkdown v-else class="source" :html="false" :source="sanitizedValue" />
</div> </div>
</template> </template>
<script> <script>
import DOMPurify from 'dompurify'
import VueMarkdown from 'vue-markdown' import VueMarkdown from 'vue-markdown'
import 'github-markdown-css/github-markdown-light.css' import 'github-markdown-css/github-markdown-light.css'
@ -56,6 +57,17 @@ export default {
iValue: this.value iValue: this.value
} }
}, },
computed: {
sanitizedValue() {
//
let content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
// 使 DOMPurify XSS
content = DOMPurify.sanitize(content)
return content
}
},
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.resizeObserver = new ResizeObserver(entries => { this.resizeObserver = new ResizeObserver(entries => {

View File

@ -48,6 +48,7 @@ export default {
fontFamily: 'monaco, Consolas, "Lucida Console", monospace', fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
lineHeight: 1.2, lineHeight: 1.2,
fontSize: 13, fontSize: 13,
scrollback: 9999999,
rightClickSelectsWord: true, rightClickSelectsWord: true,
theme: { theme: {
background: '#fff', background: '#fff',

View File

@ -1931,6 +1931,7 @@
"CheckViewAcceptor": "View more acceptor", "CheckViewAcceptor": "View more acceptor",
"Assignees": "Assignees", "Assignees": "Assignees",
"Close": "Close", "Close": "Close",
"CancelTicket": "Cancel Ticket",
"OpenStatus": "Open", "OpenStatus": "Open",
"CloseStatus": "Close", "CloseStatus": "Close",
"Comment": "Comment", "Comment": "Comment",

View File

@ -1927,6 +1927,7 @@
"Assignee": "処理者", "Assignee": "処理者",
"Assignees": "処理待ち", "Assignees": "処理待ち",
"Close": "閉じる", "Close": "閉じる",
"CancelTicket": "作業指示をキャンセルする",
"OpenStatus": "オン", "OpenStatus": "オン",
"CloseStatus": "閉じる", "CloseStatus": "閉じる",
"Comment": "備考", "Comment": "備考",

View File

@ -1913,6 +1913,7 @@
"Assignee": "处理人", "Assignee": "处理人",
"Assignees": "待处理人", "Assignees": "待处理人",
"Close": "关闭", "Close": "关闭",
"CancelTicket": "取消工单",
"OpenStatus": "审批中", "OpenStatus": "审批中",
"CloseStatus": "已完成", "CloseStatus": "已完成",
"Comment": "备注", "Comment": "备注",

View File

@ -1913,6 +1913,7 @@
"Assignee": "處理人", "Assignee": "處理人",
"Assignees": "待處理人", "Assignees": "待處理人",
"Close": "關閉", "Close": "關閉",
"CancelTicket": "取消工單",
"OpenStatus": "審批中", "OpenStatus": "審批中",
"CloseStatus": "已完成", "CloseStatus": "已完成",
"Comment": "備註", "Comment": "備註",

View File

@ -81,7 +81,7 @@ export default {
}, },
{ {
label: this.$t('common.Version'), label: this.$t('common.Version'),
value: 'version-dev' value: 'v3.10.16'
}, },
{ {
label: this.$t('common.PermissionCompany'), label: this.$t('common.PermissionCompany'),

View File

@ -61,7 +61,6 @@ export default {
break break
case 'logout': case 'logout':
this.logout() this.logout()
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
break break
case 'apiKey': case 'apiKey':
this.$router.push('/profile/api-keys') this.$router.push('/profile/api-keys')
@ -73,7 +72,12 @@ export default {
this.$router.push('/profile/user/setting') this.$router.push('/profile/user/setting')
} }
}, },
logout() { async logout() {
const currentOrg = this.$store.getters.currentOrg
if (currentOrg.autoEnter) {
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
}
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
} }
} }
} }

View File

@ -363,13 +363,19 @@ export function downloadText(content, filename) {
} }
export function download(downloadUrl, filename) { export function download(downloadUrl, filename) {
const iframe = document.createElement('iframe')
iframe.style.display = 'none'
document.body.appendChild(iframe)
const a = document.createElement('a') const a = document.createElement('a')
a.href = downloadUrl a.href = downloadUrl
if (filename) { if (filename) {
a.download = filename a.download = filename
} }
iframe.contentWindow.document.body.appendChild(a)
a.click() a.click()
window.URL.revokeObjectURL(downloadUrl) 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) {

View File

@ -24,6 +24,9 @@ async function checkLogin({ to, from, next }) {
} catch (e) { } catch (e) {
Vue.$log.error(e) Vue.$log.error(e)
const status = e.response.status const status = e.response.status
if (store.getters.currentOrg.autoEnter) {
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
}
if (status === 401 || status === 403) { if (status === 401 || status === 403) {
setTimeout(() => { setTimeout(() => {
window.location = process.env.VUE_APP_LOGIN_PATH window.location = process.env.VUE_APP_LOGIN_PATH

View File

@ -32,7 +32,7 @@ export default {
tableConfig: { tableConfig: {
url: '/api/v1/accounts/account-templates/', url: '/api/v1/accounts/account-templates/',
columns: null, columns: null,
columnsExclude: ['spec_info'], columnsExclude: ['spec_info', 'password_rules', 'push_params'],
columnsShow: { columnsShow: {
min: ['name', 'actions'], min: ['name', 'actions'],
default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions'] default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions']

View File

@ -10,13 +10,11 @@
@click="onSetting" @click="onSetting"
/> />
<Dialog <Dialog
v-if="isVisible"
width="60%" width="60%"
:visible.sync="isVisible" :visible.sync="isVisible"
:title="title" :title="title"
:show-cancel="false" :show-cancel="false"
:show-confirm="false" :show-confirm="false"
:destroy-on-close="true"
@close="onDialogClose" @close="onDialogClose"
> >
<AutoDataForm <AutoDataForm
@ -34,7 +32,7 @@
<script> <script>
import Dialog from '../../../components/Dialog' import Dialog from '../../../components/Dialog'
import AutoDataForm from '../../../components/Form/AutoDataForm' import AutoDataForm from '../../../components/Form/AutoDataForm'
import { DynamicInput } from '../../../components/Form/FormFields' import { DynamicInput, Switcher } from '../../../components/Form/FormFields'
export default { export default {
components: { components: {
@ -147,6 +145,9 @@ export default {
case 'list': case 'list':
component = DynamicInput component = DynamicInput
break break
case 'boolean':
component = Switcher
break
} }
if (param) { if (param) {

View File

@ -128,7 +128,8 @@ export default {
tip += current.label + '' + current.total + '<br/>' tip += current.label + '' + current.total + '<br/>'
} }
return tip return tip
} },
appendToBody: true
}, },
grid: { grid: {
top: '60%', top: '60%',

View File

@ -98,6 +98,7 @@ export default {
el: { el: {
baseUrl: '/api/v1/perms/users/self/assets/', baseUrl: '/api/v1/perms/users/self/assets/',
baseNodeUrl: '/api/v1/perms/users/self/nodes/', baseNodeUrl: '/api/v1/perms/users/self/nodes/',
typeUrl: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree',
value: [] value: []
} }
}, },

View File

@ -105,6 +105,7 @@ export default {
formatter: AmountFormatter, formatter: AmountFormatter,
formatterArgs: { formatterArgs: {
async: true, async: true,
cellValueToRemove: ['@SPEC'],
routeQuery: { routeQuery: {
activeTab: 'AssetPermissionUser' activeTab: 'AssetPermissionUser'
} }

View File

@ -41,7 +41,7 @@ export default {
component: PhoneInput component: PhoneInput
}, },
mfa_level: { mfa_level: {
hidden: (formValue) => { disabled: (formValue) => {
return formValue.mfa_level === 2 return formValue.mfa_level === 2
}, },
helpText: this.$t('users.HelpText.MFAOfUserFirstLoginPersonalInformationImprovementPage') helpText: this.$t('users.HelpText.MFAOfUserFirstLoginPersonalInformationImprovementPage')
@ -90,8 +90,7 @@ export default {
} }
} }
}, },
methods: { methods: {}
}
} }
</script> </script>

View File

@ -22,6 +22,7 @@ import isFalsey from '@/components/Table/DataTable/compenents/el-data-table/util
import deepmerge from 'deepmerge' import deepmerge from 'deepmerge'
import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query' import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query'
import { createSourceIdCache } from '@/api/common' import { createSourceIdCache } from '@/api/common'
import { download } from '@/utils/common'
export default { export default {
name: 'CommandList', name: 'CommandList',
@ -144,10 +145,7 @@ export default {
queryUtil.stringify(query, '=', '&') queryUtil.stringify(query, '=', '&')
url = url + queryStr url = url + queryStr
this.$log.debug('Export url: ', this.url, '=>', url) this.$log.debug('Export url: ', this.url, '=>', url)
const a = document.createElement('a') download(url + queryStr)
a.href = url
a.click()
window.URL.revokeObjectURL(url + queryStr)
} }
} }
}, },

View File

@ -55,8 +55,7 @@ export default {
fieldsMeta: { fieldsMeta: {
EMAIL_HOST_USER: { EMAIL_HOST_USER: {
rules: [ rules: [
rules.EmailCheck, rules.EmailCheck
rules.Required
] ]
}, },
EMAIL_FROM: { EMAIL_FROM: {

View File

@ -48,7 +48,12 @@ export default {
} }
], ],
select2Option: { 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: [ fields: [
] ]

View File

@ -27,7 +27,7 @@
<el-form-item style="float: right"> <el-form-item style="float: right">
<template v-if="hasActionPerm"> <template v-if="hasActionPerm">
<el-button <el-button
:disabled="object.status.value === 'closed'" :disabled="isDisabled || object.status.value === 'closed'"
size="small" size="small"
type="primary" type="primary"
@click="handleApprove" @click="handleApprove"
@ -35,7 +35,7 @@
<i class="fa fa-check" /> {{ $t('tickets.Accept') }} <i class="fa fa-check" /> {{ $t('tickets.Accept') }}
</el-button> </el-button>
<el-button <el-button
:disabled="object.status.value === 'closed'" :disabled="isDisabled || object.status.value === 'closed'"
size="small" size="small"
type="warning" type="warning"
@click="handleReject" @click="handleReject"
@ -45,12 +45,12 @@
</template> </template>
<el-button <el-button
v-if="isSelfTicket" v-if="isSelfTicket"
:disabled="object.status.value === 'closed'" :disabled="isDisabled || object.status.value === 'closed'"
size="small" size="small"
type="danger" type="danger"
@click="handleClose" @click="handleClose"
> >
<i class="fa fa-times" /> {{ $t('tickets.Close') }} <i class="fa fa-times" /> {{ $t('tickets.CancelTicket') }}
</el-button> </el-button>
<el-button <el-button
:disabled="object.status.value === 'closed'" :disabled="object.status.value === 'closed'"
@ -94,6 +94,7 @@ export default {
}, },
data() { data() {
return { return {
isDisabled: false,
comments: '', comments: '',
type_api: '', type_api: '',
imageUrl: require('@/assets/img/avatar.png'), imageUrl: require('@/assets/img/avatar.png'),
@ -156,17 +157,35 @@ export default {
this.createComment(function() { this.createComment(function() {
}) })
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/approve/` 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() { defaultReject() {
this.createComment(function() { this.createComment(function() {
}) })
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/reject/` 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() { defaultClose() {
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/close/` 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) { createComment(successCallback) {
const commentText = this.form.comments const commentText = this.form.comments
@ -187,17 +206,42 @@ export default {
} }
}) })
}, },
handleApprove() { handleAction(actionType) {
const handler = this.approve || this.defaultApprove 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() handler()
} else {
this.$message.error('No handler for action')
}
},
handleApprove() {
this.handleAction('approve')
}, },
handleReject() { handleReject() {
const handler = this.reject || this.defaultReject this.handleAction('reject')
handler()
}, },
handleClose() { handleClose() {
const handler = this.close || this.defaultClose this.handleAction('close')
handler()
}, },
handleComment() { handleComment() {
this.createComment( this.createComment(

View File

@ -4,7 +4,6 @@
<script> <script>
import { GenericCreateUpdatePage } from '@/layout/components' import { GenericCreateUpdatePage } from '@/layout/components'
import TransSelect from '@/components/Form/FormFields/TransferSelect.vue'
export default { export default {
components: { components: {
@ -23,7 +22,6 @@ export default {
], ],
fieldsMeta: { fieldsMeta: {
users: { users: {
component: TransSelect,
el: { el: {
url: '/api/v1/users/users/?fields_size=mini&order=name', url: '/api/v1/users/users/?fields_size=mini&order=name',
ajax: { ajax: {
@ -38,8 +36,7 @@ export default {
} }
} }
}, },
methods: { methods: {}
}
} }
</script> </script>

3659
yarn.lock

File diff suppressed because it is too large Load Diff