Compare commits

...

23 Commits

Author SHA1 Message Date
jiangweidong
8556b91fc4 perf: Allow admins to configure available MFA services for user auth 2025-12-05 10:53:07 +08:00
feng
3dd199ef01 fix: Ticket comment not display 2025-11-20 14:39:32 +08:00
w940853815
20e1c833a6 perf: Update asset detection logic in AccountRiskList for improved menu display 2025-11-20 10:32:05 +08:00
feng
f2c7a6bc71 perf: Account bulk error prompt 2025-11-19 17:38:52 +08:00
w940853815
6063e03d89 perf: Enhance error message handling for nested plain objects in getErrorResponseMsg 2025-11-19 15:42:26 +08:00
zhaojisen
e2d9eb5bae Fixed: Fix the issue where the global organization text is not displayed completely in the organization dropdown. 2025-11-19 15:31:10 +08:00
w940853815
84caf35f67 fix: Ensure progress display is shown correctly during job execution 2025-11-19 15:10:57 +08:00
zhaojisen
1d7a1bfff1 Fixed: Fix the issue where creating an SSH public key for a third-party user redirects to the login password tab. 2025-11-18 18:34:04 +08:00
w940853815
9039f572ac fix: Click Review to enter comments, and the page will be dimmed 2025-11-18 16:43:50 +08:00
w940853815
7798324a4b perf: Add support for error message extraction from plain objects in getErrorResponseMsg 2025-11-18 15:45:40 +08:00
feng
8e69206cfb fix: Account push show params 2025-11-17 17:29:44 +08:00
w940853815
338706247a perf: Add condition to check node type in AccountRiskList for asset detection 2025-11-17 11:15:53 +08:00
w940853815
2ae3e06484 perf: Refactor error handling and improve parameter assignment in job forms 2025-11-17 10:55:47 +08:00
w940853815
c1dcea1b29 perf: Improve error message formatting for array of strings 2025-11-12 16:22:44 +08:00
w940853815
59adc54f2d perf: Enhance AccountFormatter with options to exclude accounts 2025-11-11 18:55:34 +08:00
w940853815
afeafcbf49 perf: Job execution detail content add text-overflow class 2025-11-10 16:54:45 +08:00
w940853815
4e23107a21 perf: Support batch import of leak passwords 2025-11-06 18:36:40 +08:00
ibuler
3e267d07c1 perf: small size some png 2025-11-05 18:19:09 +08:00
fit2bot
7df6854a65 perf: upgrade vue version (#5260)
* perf: upgrade vue version

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-05 17:36:47 +08:00
w940853815
4db8edce98 perf: Add confirmation dialog before syncing platform protocols 2025-11-04 15:09:30 +08:00
feng
8d69418613 perf: Bulk account support node 2025-10-31 17:19:11 +08:00
w940853815
2d798053b3 perf: Translate select assets 2025-10-31 10:35:56 +08:00
ibuler
e48385c70e perf: revert keyword to search 2025-10-30 16:27:40 +08:00
46 changed files with 341 additions and 233 deletions

View File

@@ -1,4 +1,4 @@
FROM jumpserver/lina-base:20251030_071727 AS stage-build
FROM jumpserver/lina-base:20251105_092554 AS stage-build
ARG VERSION
ENV VERSION=$VERSION

View File

@@ -61,7 +61,7 @@
"path-to-regexp": "3.3.0",
"sortablejs": "^1.15.6",
"v-sanitize": "^0.0.13",
"vue": "2.6.10",
"vue": "2.7.16",
"vue-codemirror": "4.0.6",
"vue-cookie": "^1.1.4",
"vue-echarts": "^5.0.0-beta.0",
@@ -119,11 +119,11 @@
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue-i18n-extract": "^1.1.1",
"vue-template-compiler": "2.6.10",
"vue-template-compiler": "2.7.16",
"webpack": "^4.28.4"
},
"engines": {
"node": ">=8.9",
"node": ">=12",
"npm": ">= 3.0.0"
},
"browserslist": [

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -15,10 +15,25 @@ export const accountFieldsMeta = (vm) => {
}
return {
nodes: {
component: Select2,
label: vm.$t('Node'),
el: {
value: [],
ajax: {
url: '/api/v1/assets/nodes/',
transformOption: (item) => {
return { label: item.full_value, value: item.id }
}
}
},
hidden: () => {
return !vm.addTemplate
}
},
assets: {
component: AssetSelect,
label: vm.$t('Asset'),
rules: [Required],
el: {
multiple: false
},
@@ -33,7 +48,7 @@ export const accountFieldsMeta = (vm) => {
get disabled() {
return vm.isDisabled
},
multiple: false,
multiple: vm.addTemplate,
ajax: {
url: '/api/v1/accounts/account-templates/',
transformOption: (item) => {

View File

@@ -63,7 +63,7 @@ export default {
encryptedFields: ['secret'],
fields: [
[this.$t('Basic'), ['name', 'username', 'privileged', 'su_from', 'su_from_username', 'template']],
[this.$t('Asset'), ['assets']],
[this.$t('Asset'), ['nodes', 'assets']],
[this.$t('Secret'), [
'secret_type', 'password', 'ssh_key', 'token',
'access_key', 'passphrase', 'api_key',

View File

@@ -93,8 +93,8 @@ export default {
iVisible = true
data = formValue
url = `/api/v1/accounts/accounts/bulk/`
if (data.assets.length === 0) {
this.$message.error(this.$tc('PleaseSelectAsset'))
if ((!data.assets || data.assets.length === 0) && (!data.nodes || data.nodes.length === 0)) {
this.$message.error(this.$tc('PleaseSelectAssetOrNode'))
return
}
}
@@ -107,6 +107,10 @@ export default {
this.$emit('add', true)
}
}).catch(error => {
if (error?.response?.data?.code === 'no_valid_assets') {
this.$message.error(error?.response?.data?.detail)
return
}
this.iVisible = true
this.handleResult(null, error)
})

View File

@@ -49,6 +49,10 @@ export default {
prop: 'asset',
label: this.$t('Asset')
},
{
prop: 'account',
label: this.$t('Account')
},
{
prop: 'state',
label: this.$t('Status'),

View File

@@ -37,8 +37,12 @@ export default {
},
headerActions: {
hasExport: false,
hasImport: false,
hasExport: true,
hasImport: true,
importOptions: {
encryptFields: [''], // 这里不加密 password''只是为了保证数组有值
canImportUpdate: false
},
hasCreate: true,
hasSearch: true,
hasRefresh: true,

View File

@@ -73,28 +73,28 @@ export default {
}
</script>
<style>
.help-dialog.dialog .el-dialog__footer {
<style lang="scss" scoped>
::v-deep .help-dialog.dialog .el-dialog__footer {
border-top: none;
padding: 8px;
}
</style>
<style lang="scss" scoped>
.help-table {
width: 100%;
border-collapse: collapse;
border: 1px solid #dee2e6;
}
&::v-deep th, td {
height: 40px;
padding: 0 8px;
text-align: left;
}
::v-deep .help-table th,
::v-deep .help-table td {
height: 40px;
padding: 0 8px;
text-align: left;
}
&::v-deep .item-td, .item-label {
cursor: pointer;
color: var(--color-primary);
}
::v-deep .help-table .item-td,
::v-deep .help-table .item-label {
cursor: pointer;
color: var(--color-primary);
}
</style>

View File

@@ -223,8 +223,21 @@ export default {
const mapped = {}
Object.entries(errors || {}).forEach(([k, v]) => {
let msg = v
if (Array.isArray(v)) msg = v.join('; ')
else if (typeof v === 'object' && v !== null) msg = JSON.stringify(v)
console.log(k, v)
// v是数组并且数组都是字符串则拼接为字符串
if (Array.isArray(v) && v.every(item => typeof item === 'string')) msg = v.join('; ')
// 处理 [{"port":["请确保该值小于或者等于 65535。"]},{},{}] 这种情况
else if (Array.isArray(v) && v.every(item => _.isPlainObject(item))) {
const subMsg = []
v.forEach((subItem) => {
Object.values(subItem).forEach((subMsgArr) => {
if (Array.isArray(subMsgArr)) {
subMsg.push(...subMsgArr)
}
})
})
msg = subMsg.join(' ')
} else if (typeof v === 'object' && v !== null) msg = JSON.stringify(v)
mapped[k] = String(msg || '')
})
this.serverErrors = mapped

View File

@@ -58,6 +58,7 @@
:import-option="importOption"
:json-data="jsonData"
:url="url"
v-bind="$attrs"
@cancel="cancelUpload"
@finish="closeDialog"
/>
@@ -247,46 +248,46 @@ export default {
</script>
<style lang='scss' scoped>
@import "~@/styles/variables";
@import "~@/styles/variables";
.error-msg {
color: $--color-danger;
.error-msg {
color: $--color-danger;
}
.error-msg.error-results {
background-color: #f3f3f4;
max-height: 200px;
overflow: auto
}
.file-uploader ::v-deep .el-upload {
width: 100%;
//padding-right: 150px;
}
.file-uploader ::v-deep .el-upload-dragger {
width: 100%;
}
.importTableZone {
padding: 0 20px;
.importTable {
overflow: auto;
}
.error-msg.error-results {
background-color: #f3f3f4;
max-height: 200px;
overflow: auto
.tableFilter {
padding-bottom: 10px;
}
}
.file-uploader ::v-deep .el-upload {
width: 100%;
//padding-right: 150px;
}
.importTable ::v-deep .el-dialog__body {
padding-bottom: 20px;
}
.file-uploader ::v-deep .el-upload-dragger {
width: 100%;
}
.importTableZone {
padding: 0 20px;
.importTable {
overflow: auto;
}
.tableFilter {
padding-bottom: 10px;
}
}
.importTable ::v-deep .el-dialog__body {
padding-bottom: 20px;
}
.export-item {
margin-left: 80px;
}
.export-item {
margin-left: 80px;
}
.export-item:first-child {
margin-left: 0;

View File

@@ -97,6 +97,10 @@ export default {
origin: {
type: String,
default: ''
},
encryptFields: {
type: Array,
default: () => []
}
},
data() {
@@ -273,11 +277,15 @@ export default {
}
return columns
},
getEncryptFields() {
const fromProp = Array.isArray(this.encryptFields) && this.encryptFields.length ? this.encryptFields : null
return fromProp || ['password', 'secret', 'private_key']
},
generateTableData(tableTitles, tableData) {
const totalData = []
tableData.forEach(item => {
this.$set(item, '@status', 'pending')
const encryptFields = ['password', 'secret', 'private_key']
const encryptFields = this.getEncryptFields()
for (const field of encryptFields) {
if (item[field]) {
item[field] = encryptPassword(item[field])

View File

@@ -88,7 +88,7 @@ export default {
},
filterMaps() {
const data = {}
const keyword = 'q'
const keyword = 'search'
for (let key in this.filterTags) {
const value = this.filterTags[key]['value']
if (key === '') {

View File

@@ -421,12 +421,8 @@ export default {
display: inline-block;
}
.data-z-tree {
::v-deep {
.icon {
width: 10px;
margin-right: 3px;
}
}
.data-z-tree ::v-deep .icon {
width: 10px;
margin-right: 3px;
}
</style>

View File

@@ -78,6 +78,3 @@ export default {
}
}
</style>
<style lang="scss">
</style>

View File

@@ -65,6 +65,17 @@ export default {
'usingOrgs',
'currentViewRoute'
]),
currentOrgDisplayName() {
const currentOrgId = this.currentOrg?.id
if (!currentOrgId) {
return this.$tc('Select')
}
const matchedOrg = this.usingOrgs.find(item => item.id === currentOrgId)
if (matchedOrg?.name) {
return matchedOrg.name
}
return this.currentOrg.name || this.$tc('Select')
},
orgActionsGroup() {
const orgActions = {
label: this.$t('OrganizationList'),
@@ -110,11 +121,8 @@ export default {
}
},
watch: {
currentOrg: {
handler() {
this.updateWidth()
},
deep: true
currentOrgDisplayName() {
this.updateWidth()
}
},
mounted() {
@@ -133,8 +141,9 @@ export default {
tempSpan.style.fontWeight = 'normal'
tempSpan.style.letterSpacing = 'normal'
// 获取当前组织名称
const orgName = this.currentOrg.name || this.$tc('Select')
// 获取当前组织显示名称
const orgName = this.currentOrgDisplayName
tempSpan.textContent = orgName
document.body.appendChild(tempSpan)

View File

@@ -369,7 +369,7 @@ export default {
}
}
.el-input__prefix .el-input__icon{
.el-input__prefix .el-input__icon {
font-size: 15px;
line-height: 32px;
}
@@ -402,11 +402,9 @@ export default {
}
}
}
</style>
<style lang="scss">
/* 搜索模态框全局样式 */
.search-modal {
::v-deep .search-modal {
&.el-dialog {
position: fixed;
top: 5px;
@@ -427,17 +425,17 @@ export default {
}
}
body .v-modal {
::v-deep body .v-modal {
opacity: 0.3;
}
.search-modal-content {
::v-deep .search-modal-content {
height: 70vh;
display: flex;
flex-direction: column;
}
.search-input-wrapper {
::v-deep .search-input-wrapper {
padding: 20px;
border-bottom: 1px solid #f0f0f0;
// background: #fff;
@@ -451,7 +449,7 @@ body .v-modal {
}
}
.search-results {
::v-deep .search-results {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
@@ -476,7 +474,7 @@ body .v-modal {
}
}
.section-title {
::v-deep .section-title {
padding: 12px 24px 6px;
font-size: 12px;
line-height: 1.5;
@@ -505,12 +503,12 @@ body .v-modal {
.clear-icon {
font-size: 14px;
color: red;
color: red;
}
}
}
.list {
::v-deep .list {
list-style: none;
margin: 0;
padding: 0;
@@ -581,15 +579,15 @@ body .v-modal {
}
}
.loading,
.empty {
::v-deep .loading,
::v-deep .empty {
padding: 32px 24px;
color: #909399;
text-align: center;
font-size: 14px;
}
.section.placeholder {
::v-deep .section.placeholder {
padding: 32px 24px;
.placeholder-content {
@@ -637,4 +635,3 @@ body .v-modal {
}
}
</style>

View File

@@ -167,8 +167,7 @@ export default {
}
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.page.no-title {
::v-deep {
.page-submenu .el-tabs__header {
@@ -199,6 +198,14 @@ export default {
opacity: 1;
}
}
&.is-disabled {
cursor: not-allowed;
&:hover {
color: #c0c4cc;
}
}
}
.el-tabs__nav-next {

View File

@@ -276,10 +276,8 @@ export default {
}
}
}
</style>
<style lang="scss">
//reset element css of el-icon-close
// reset element css of el-icon-close
.tags-view-wrapper {
.tags-view-item {
.el-icon-close {

View File

@@ -149,6 +149,11 @@ export function getErrorResponseMsg(error) {
.join('; ')
} else if (typeof data === 'string') {
return data
} else if (_.isPlainObject(data)) {
return Object.values(data)
.map(item => getErrorResponseMsg(item))
.filter(i => i)
.join('; ')
} else {
msg = error.toString()
}

View File

@@ -144,11 +144,6 @@ export default {
deep: true
}
},
mounted() {
if (!this.$store.getters.hasValidLicense) {
delete this.fields[3]
}
},
methods: {
handleAfterGetRemoteMeta(meta) {
const needSetOptionFields = [

View File

@@ -43,7 +43,9 @@ export default {
asset: ''
},
treeSetting: {
showMenu: true,
showMenu: (node) => {
return node?.meta?.type === 'asset'
},
showRefresh: true,
showSearch: true,
showAssets: true,
@@ -55,7 +57,7 @@ export default {
menu: [
{
id: 'check',
name: this.$t('Check'),
name: this.$t('RiskDetection'),
icon: 'scan',
callback: (node) => {
vm.detectDialog.asset = node.id

View File

@@ -6,7 +6,6 @@
class="risk-review-drawer"
destroy-on-close
direction="rtl"
style="z-index: 999"
>
<div class="drawer-container">
<div class="drawer-body">

View File

@@ -143,7 +143,20 @@ export default {
disabled: !this.canEdit
},
callbacks: Object.freeze({
click: () => {
click: async () => {
try {
await this.$confirm(
this.$t('overwriteProtocolsAndPortsMsg'),
this.$t('Confirm'),
{
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}
)
} catch (e) {
return
}
const data = { platform_id: this.object.id }
this.$axios.post(
'/api/v1/assets/assets/sync-platform-protocols/', data).then(res => {

View File

@@ -125,17 +125,21 @@ export default {
}
</script>
<style lang="scss">
<style lang="scss" scoped>
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg:#283443;
$light_gray:#fff;
$cursor: #fff;
$input-bg: #283443;
$input-light-gray: #fff;
$input-cursor: #fff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
$login-bg: #2d3a4b;
$login-dark-gray: #889aa4;
$login-light-gray: #eee;
@supports (-webkit-mask: none) and (not (cater-color: $input-cursor)) {
.login-container .el-input input {
color: $cursor;
color: $input-cursor;
}
}
@@ -147,17 +151,17 @@ $cursor: #fff;
input {
background: transparent;
border: 0px;
border: 0;
-webkit-appearance: none;
border-radius: 0px;
border-radius: 0;
padding: 12px 5px 12px 15px;
color: $light_gray;
color: $input-light-gray;
height: 47px;
caret-color: $cursor;
caret-color: $input-cursor;
&:-webkit-autofill {
box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !important;
box-shadow: 0 0 0 1000px $input-bg inset !important;
-webkit-text-fill-color: $input-cursor !important;
}
}
}
@@ -169,17 +173,11 @@ $cursor: #fff;
color: #454545;
}
}
</style>
<style lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;
.login-container {
min-height: 100%;
width: 100%;
background-color: $bg;
background-color: $login-bg;
overflow: hidden;
.login-form {
@@ -205,7 +203,7 @@ $light_gray:#eee;
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
color: $login-dark-gray;
vertical-align: middle;
width: 30px;
display: inline-block;
@@ -216,7 +214,7 @@ $light_gray:#eee;
.title {
font-size: 26px;
color: $light_gray;
color: $login-light-gray;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
@@ -228,7 +226,7 @@ $light_gray:#eee;
right: 10px;
top: 7px;
font-size: 16px;
color: $dark_gray;
color: $login-dark-gray;
cursor: pointer;
user-select: none;
}

View File

@@ -515,6 +515,8 @@ export default {
this.setBtn()
this.selectAssets = assets
this.selectNodes = nodes
}).catch(() => {
this.lastRequestPayload = null
})
},
viewConfirmRunAssets() {
@@ -525,8 +527,8 @@ export default {
.then(() => {
this.xterm.write(
'\x1b[31m' +
this.$tc('StopLogOutput').replace('currentTaskId', this.currentTaskId) +
'\x1b[0m'
this.$tc('StopLogOutput').replace('currentTaskId', this.currentTaskId) +
'\x1b[0m'
)
this.xterm.write(this.wrapperError(''))
this.getTaskStatus()

View File

@@ -67,28 +67,28 @@ export default {
}
</script>
<style>
.help-dialog.dialog .el-dialog__footer {
<style lang="scss" scoped>
::v-deep .help-dialog.dialog .el-dialog__footer {
border-top: none;
padding: 8px;
}
</style>
<style lang="scss" scoped>
.help-table {
width: 100%;
border-collapse: collapse;
border: 1px solid #dee2e6;
}
&::v-deep th, td {
height: 40px;
padding: 0 8px;
text-align: left;
}
::v-deep .help-table th,
::v-deep .help-table td {
height: 40px;
padding: 0 8px;
text-align: left;
}
&::v-deep .item-td, .item-label {
cursor: pointer;
color: var(--color-primary);
}
::v-deep .help-table .item-td,
::v-deep .help-table .item-label {
cursor: pointer;
color: var(--color-primary);
}
</style>

View File

@@ -2,14 +2,14 @@
<div class="asset-select">
<el-card>
<div slot="header" class="clearfix">
<span>已选资产({{ selectAssets.length }})</span>
<span>{{ $t('selectedAssets') }}({{ selectAssets.length }})</span>
<el-button
v-if="selectAssets.length > 0"
style="float: right; padding: 3px 0"
type="text"
@click="handleClick"
>
请选择资产
{{ $t('pleaseSelectAssets') }}
</el-button>
</div>
<div
@@ -21,8 +21,8 @@
@click=" handleClick()"
>
<i class="icon el-icon-plus" />
<span class="title">请选择资产</span>
<span class="subtitle">点击添加</span>
<span class="title">{{ $t('pleaseSelectAssets') }}</span>
<span class="subtitle">{{ $t('clickToAdd') }}</span>
</div>
<div v-else class="asset-list">
<div
@@ -62,7 +62,7 @@
>{{ item.name }}</span>
<i
class="el-icon-minus asset-remove-icon"
title="移除"
:title="$tc('Remove')"
@click.stop="removeAsset(item)"
/>
</el-checkbox>

View File

@@ -1,66 +1,72 @@
<template>
<TwoCol>
<template>
<AutoDetailCard
:excludes="excludes"
:fields="detailFields"
:object="object"
:url="url"
/>
<AutoDetailCard :excludes="excludes" :fields="detailFields" :object="object" :url="url" />
</template>
<template v-if="hasSummary" #right class="detail-right-quick-actions">
<IBox
v-if="object.summary.ok"
:title="`${$tc('SuccessAsset')} (${object.summary.ok.length})` "
:title="`${$tc('SuccessAsset')} (${object.summary.ok.length})`"
type="success"
>
<el-collapse>
<el-collapse-item
v-for="(item,index) in object.summary.ok"
v-for="(item, index) in object.summary.ok"
:key="index"
:name="index"
:title="item"
disabled
/>
>
<template #title>
<el-tooltip :content="item" placement="top" class="text-overflow">
<span>{{ item }}</span>
</el-tooltip>
</template>
</el-collapse-item>
</el-collapse>
</IBox>
<IBox
v-if="object.summary.excludes"
:title="`${$tc('ExcludeAsset')} (${Object.keys(object.summary.excludes).length})` "
:title="`${$tc('ExcludeAsset')} (${Object.keys(object.summary.excludes).length})`"
type="warning"
>
<el-collapse>
<el-collapse-item
v-for="(val,key,index) in object.summary.excludes"
v-for="(val, key, index) in object.summary.excludes"
:key="index"
:name="index"
:title="key"
>
<template #title>
<el-tooltip :content="key" placement="top" class="text-overflow">
<span>{{ key }}</span>
</el-tooltip>
</template>
<div>{{ $tc('Reason') }}: {{ val }}</div>
</el-collapse-item>
</el-collapse>
</IBox>
<IBox
v-if="object.summary.failures"
:title="`${$tc('FailedAsset')} (${Object.keys(Object.assign(object.summary.failures,object.summary.dark)).length})` "
:title="`${$tc('FailedAsset')} (${Object.keys(Object.assign(object.summary.failures, object.summary.dark)).length})`"
type="danger"
>
<el-collapse>
<el-collapse-item
v-for="(val,key,index) in Object.assign(object.summary.failures,object.summary.dark)"
v-for="(val, key, index) in Object.assign(object.summary.failures, object.summary.dark)"
:key="index"
:name="index"
:title="key"
>
<template #title>
<el-tooltip :content="key" placement="top" class="text-overflow">
<span>{{ key }}</span>
</el-tooltip>
</template>
<div>{{ $tc('Reason') }}: {{ val }}</div>
</el-collapse-item>
</el-collapse>
</IBox>
<IBox
v-if="object.summary.error"
:title="$tc('SystemError') "
type="danger"
>
<IBox v-if="object.summary.error" :title="$tc('SystemError')" type="danger">
{{ object.summary.error }}
</IBox>
</template>
@@ -86,11 +92,10 @@ export default {
},
data() {
return {
excludes: [
'job', 'parameters', 'summary', 'task_id', 'timedelta'
],
excludes: ['job', 'parameters', 'summary', 'task_id', 'timedelta'],
detailFields: [
'task_id', 'time_cost',
'task_id',
'time_cost',
{
key: this.$t('IsFinished'),
value: this.object.is_finished ? this.$t('Yes') : this.$t('No')
@@ -99,8 +104,12 @@ export default {
key: this.$t('IsSuccess'),
value: this.object.is_success ? this.$t('Yes') : this.$t('No')
},
'job_type', 'material', 'org_name',
'date_start', 'date_finished', 'date_created'
'job_type',
'material',
'org_name',
'date_start',
'date_finished',
'date_created'
],
url: `/api/v1/ops/job-executions/${this.object.id}/`
}

View File

@@ -408,7 +408,6 @@ export default {
createJob(data).then(res => {
this.progressLength = 0
this.executionInfo.timeCost = 0
this.showProgress = true
this.speedText = ''
const form = new FormData()
const start = Date.now()
@@ -436,13 +435,13 @@ export default {
}
}
}).then(res => {
this.showProgress = true
this.executionInfo.status = 'running'
this.currentTaskId = res.task_id
this.xtermConfig = { taskId: this.currentTaskId, type: 'shortcut_cmd' }
this.setCostTimeInterval()
this.writeExecutionOutput()
}).catch((error) => {
this.$message.error(this.$tc('Error'), error)
}).catch(() => {
this.execute_stop()
})
})

View File

@@ -224,11 +224,12 @@ export default {
title: this.$t('ExecuteAfterSaving'),
callback: (value, form, btn) => {
form.value.run_after_save = true
const parameters = form.value.variable.reduce((acc, item) => {
acc[item.var_name] = item.default_value || ''
return acc
}, {})
form.value['parameters'] = parameters
if (form.value?.variable) {
form.value['parameters'] = form.value.variable.reduce((acc, item) => {
acc[item.var_name] = item.default_value || ''
return acc
}, {})
}
this.submitForm(form, btn)
}
}

View File

@@ -110,6 +110,8 @@ export default {
type: 'input',
component: AccountFormatter,
el: {
enableExcludeAccounts: true,
enableNoneAccount: true,
assets: [],
nodes: []
},

View File

@@ -3,7 +3,7 @@
<el-form-item>
<el-radio-group v-model="realRadioSelected" @input="handleRadioChanged">
<el-radio
v-for="(i) in realChoices"
v-for="(i) in iRealChoices"
:key="i.label"
:disabled="i.disabled"
:label="i.value"
@@ -125,6 +125,14 @@ export default {
type: Boolean,
default: true
},
enableNoneAccount: {
type: Boolean,
default: false
},
enableExcludeAccounts: {
type: Boolean,
default: false
},
addTemplateHelpText: {
type: String,
default() {
@@ -185,6 +193,18 @@ export default {
computed: {
virtualAccount() {
return virtualAccount
},
iRealChoices: {
get() {
let choices = this.realChoices.slice()
if (!this.enableNoneAccount) {
choices = choices.filter(i => i.value !== NoneAccount)
}
if (!this.enableExcludeAccounts) {
choices = choices.filter(i => i.value !== ExcludeAccount)
}
return choices
}
}
},
watch: {

View File

@@ -32,6 +32,22 @@ export default {
}
}
},
computed: {
passwordMenuDisabled() {
return this.$store.state.users.profile.source.value !== 'local'
}
},
watch: {
passwordMenuDisabled: {
immediate: true,
handler(disabled) {
this.config.submenu = this.getSubmenu()
if (disabled && this.config.activeMenu === 'Password') {
this.config.activeMenu = 'SSHKeyList'
}
}
}
},
methods: {
getSubmenu() {
return [

View File

@@ -222,13 +222,11 @@ export default {
}
</script>
<style>
.el-select-dropdown.select-org-dropdown {
<style lang="scss" scoped>
::v-deep .el-select-dropdown.select-org-dropdown {
max-width: 300px !important;
}
</style>
<style lang="scss" scoped>
.org-select {
float: left;
width: 300px;
@@ -245,5 +243,4 @@ export default {
width: 180px !important;
}
}
</style>

View File

@@ -31,7 +31,7 @@ export default {
[
'SECURITY_MFA_AUTH',
'SECURITY_MFA_IN_LOGIN_PAGE',
'SECURITY_MFA_BY_EMAIL',
'SECURITY_MFA_ENABLED_BACKENDS',
'SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY',
'SECURITY_MFA_VERIFY_TTL',
'OTP_ISSUER_NAME', 'OTP_VALID_WINDOW'

View File

@@ -73,28 +73,28 @@ export default {
}
</script>
<style>
.help-dialog.dialog .el-dialog__footer {
<style lang="scss" scoped>
::v-deep .help-dialog.dialog .el-dialog__footer {
border-top: none;
padding: 8px;
}
</style>
<style lang="scss" scoped>
.help-table {
width: 100%;
border-collapse: collapse;
border: 1px solid #dee2e6;
}
&::v-deep th, td {
height: 40px;
padding: 0 8px;
text-align: left;
}
::v-deep .help-table th,
::v-deep .help-table td {
height: 40px;
padding: 0 8px;
text-align: left;
}
&::v-deep .item-td, .item-label {
cursor: pointer;
color: var(--color-primary);
}
::v-deep .help-table .item-td,
::v-deep .help-table .item-label {
cursor: pointer;
color: var(--color-primary);
}
</style>

View File

@@ -219,11 +219,12 @@ export default {
}
</script>
<style lang="scss">
.el-popover.el-popper.monitor-popover {
<style lang="scss" scoped>
:global(.el-popover.el-popper.monitor-popover) {
ul {
padding-left: 15px;
margin: 0;
li {
line-height: 24px;
color: var(--color-primary);
@@ -231,19 +232,19 @@ export default {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&:hover {
filter: opacity(65%)!important;
filter: opacity(65%) !important;
}
}
}
}
</style>
<style lang="less" scoped>
.echarts {
width: 100%;
height: 150px;
}
.name {
display: inline-block;
margin-bottom: 8px;
@@ -256,6 +257,7 @@ export default {
justify-content: space-between;
font-size: 14px;
margin-bottom: 8px;
.num {
margin-left: 2px;
cursor: pointer;
@@ -279,9 +281,9 @@ export default {
line-height: 20px;
padding: 2px 0;
color: #fff;
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
-webkit-transition: width 0.6s ease;
-o-transition: width 0.6s ease;
transition: width 0.6s ease;
overflow: hidden;
}
@@ -292,9 +294,11 @@ export default {
margin-right: 5px;
border-radius: 4px;
background-color: currentColor;
&:hover {
transform: scale(1.2);
}
&:last-child {
margin-right: 0;
}
@@ -303,6 +307,7 @@ export default {
.session {
margin-top: 13px;
border-left: 1px solid #f3f3f3;
.session-title {
margin-bottom: 8px;
color: #a3a3a4;

View File

@@ -147,7 +147,7 @@ export default {
this.loading = true
const url = `/api/v1/tickets/comments/?ticket_id=${this.object.id}`
this.$axios.get(url).then(res => {
this.comments = res.results
this.comments = res
}).catch(err => {
this.$message.error(err)
}).finally(() => {

View File

@@ -115,14 +115,12 @@ export default {
}
</script>
<style>
.acceptance .el-message-box__content {
<style lang="scss" scoped>
::v-deep .acceptance .el-message-box__content {
overflow-y: auto;
max-height: 400px;
}
</style>
<style lang='scss' scoped>
.box {
margin-bottom: 15px;
}

View File

@@ -161,8 +161,7 @@ module.exports = {
target: process.env.VUE_APP_CORE_HOST || 'http://127.0.0.1:8080',
changeOrigin: true
}
},
after: require('./mock/mock-server.js')
}
},
css: {},
configureWebpack: {

View File

@@ -6916,7 +6916,7 @@ hasown@^2.0.0, hasown@^2.0.2:
dependencies:
function-bind "^1.1.2"
he@1.2.x, he@^1.1.0, he@^1.1.1:
he@1.2.x, he@^1.1.1, he@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -14423,32 +14423,27 @@ vue-style-loader@^4.1.0:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
vue-template-compiler@2.6.10:
version "2.6.10"
resolved "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz#323b4f3495f04faa3503337a82f5d6507799c9cc"
integrity sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==
vue-template-compiler@2.7.16:
version "2.7.16"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b"
integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
he "^1.2.0"
vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0:
version "1.9.1"
resolved "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue@2.6.10:
version "2.6.10"
resolved "https://registry.npmmirror.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==
vue@2.6.11:
version "2.6.11"
resolved "https://registry.npmmirror.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
vue@^2.2.6:
vue@2.7.16, vue@^2.2.6:
version "2.7.16"
resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
dependencies:
"@vue/compiler-sfc" "2.7.16"