Compare commits

...

12 Commits

Author SHA1 Message Date
zhaojisen
3e009c17fd fixed: Fixed an issue where multiple selections across pages could not be reversed 2024-12-04 15:14:44 +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
12 changed files with 101 additions and 33 deletions

View File

@@ -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,9 @@ 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'}`,
callback: {
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -34,12 +34,14 @@ 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()
}
@@ -50,12 +52,12 @@ class StrategyNormal extends StrategyAbstract {
*/
class StrategyPersistSelection extends StrategyAbstract {
/**
* el-tableselection-change事件不适用于开启跨页保存的情况。
* 比如当开启persistSelection时发生以下两个场景
* el-tableselection-change 事件不适用于开启跨页保存的情况。
* 比如,当开启 persistSelection时发生以下两个场景
* 1. 用户点击翻页
* 2. 用户点击行首的切换全选项按钮,清空当前页多选项数据
* 其中场景1应该保持selected不变而场景2只应该从selected移除当前页所有行保留其他页面的多选状态。
* 但el-tableselection-change事件在两个场景中无差别发生所以这里不处理这个事件
* 其中场景 1 应该保持 selected 不变;而场景 2 只应该从 selected 移除当前页所有行,保留其他页面的多选状态。
* 但 el-tableselection-change 事件在两个场景中无差别发生,所以这里不处理这个事件
*/
/**
@@ -63,13 +65,19 @@ class StrategyPersistSelection extends StrategyAbstract {
*/
onSelect(selection, row) {
const isChosen = selection.indexOf(row) > -1
this.toggleRowSelection(row, isChosen)
}
/**
* 用户切换当前页的多选
*/
onSelectAll(selection, selectable = () => true) {
const isSelected = !!selection.length
// 获取当前所有已选择的项
const selectedRows = this.elDataTable.data.filter(r => selection.includes(r))
// 判断是否已全选
const isSelected = this.elDataTable.data.every(r => selectable(r) && selectedRows.includes(r))
this.elDataTable.data.forEach(r => {
if (selectable(r)) {
this.toggleRowSelection(r, isSelected)
@@ -83,33 +91,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
}
this.elTable.toggleRowSelection(r, isSelected)
})
}

View File

@@ -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>

View File

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

View File

@@ -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,12 @@ export default {
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

@@ -24,6 +24,9 @@ async function checkLogin({ to, from, next }) {
} catch (e) {
Vue.$log.error(e)
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

View File

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

View File

@@ -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: []
}
},

View File

@@ -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,7 +45,7 @@
</template>
<el-button
v-if="isSelfTicket"
:disabled="object.status.value === 'closed'"
:disabled="isDisabled || object.status.value === 'closed'"
size="small"
type="danger"
@click="handleClose"
@@ -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(

View File

@@ -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>