mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-30 05:42:19 +00:00
perf: 解决冲突
This commit is contained in:
@@ -36,3 +36,11 @@ export function uploadPlaybook(form) {
|
||||
data: form
|
||||
})
|
||||
}
|
||||
|
||||
export function renameFile(playbookId, node) {
|
||||
return request({
|
||||
url: `/api/v1/ops/playbook/${playbookId}/file/`,
|
||||
method: 'patch',
|
||||
data: node
|
||||
})
|
||||
}
|
||||
|
||||
BIN
src/assets/img/logo_text_green.png
Normal file
BIN
src/assets/img/logo_text_green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<AutoDataForm
|
||||
v-if="!loading"
|
||||
ref="AutoDataForm"
|
||||
v-bind="$data"
|
||||
@submit="confirm"
|
||||
/>
|
||||
@@ -71,7 +72,9 @@ export default {
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
if (!this.usernameChanged) {
|
||||
updateForm({ username: value })
|
||||
if (!this.account?.name) {
|
||||
updateForm({ username: value })
|
||||
}
|
||||
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
@@ -150,7 +153,8 @@ export default {
|
||||
},
|
||||
push_now: {
|
||||
hidden: () => {
|
||||
return !this.iPlatform.automation?.['push_account_enabled']
|
||||
const automation = this.iPlatform.automation || {}
|
||||
return !automation.push_account_enabled || !automation.ansible_enabled || !this.$hasPerm('assets.push_assetaccount')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
>
|
||||
<AccountCreateUpdateForm
|
||||
v-if="!loading"
|
||||
ref="form"
|
||||
:account="account"
|
||||
:asset="asset"
|
||||
@add="addAccount"
|
||||
@@ -94,7 +95,7 @@ export default {
|
||||
this.iVisible = false
|
||||
this.$emit('add', true)
|
||||
this.$message.success(this.$tc('common.createSuccessMsg'))
|
||||
})
|
||||
}).catch(error => this.setFieldError(error))
|
||||
},
|
||||
editAccount(form) {
|
||||
const data = { ...form }
|
||||
@@ -102,7 +103,35 @@ export default {
|
||||
this.iVisible = false
|
||||
this.$emit('add', true)
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
})
|
||||
}).catch(error => this.setFieldError(error))
|
||||
},
|
||||
setFieldError(error) {
|
||||
const response = error.response
|
||||
const data = response.data
|
||||
const refsAutoDataForm = this.$refs.form.$refs.AutoDataForm
|
||||
if (response.status === 400) {
|
||||
for (const key of Object.keys(data)) {
|
||||
let err = ''
|
||||
let current = key
|
||||
let errorTips = data[current]
|
||||
if (errorTips instanceof Array) {
|
||||
errorTips = _.filter(errorTips, (item) => Object.keys(item).length > 0)
|
||||
for (const i of errorTips) {
|
||||
if (i instanceof Object) {
|
||||
err += i?.port?.join(',')
|
||||
} else {
|
||||
err += errorTips
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = errorTips
|
||||
}
|
||||
if (current === 'secret') {
|
||||
current = refsAutoDataForm.form.secret_type?.value || key
|
||||
}
|
||||
refsAutoDataForm.setFieldError(current, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,10 @@ export default {
|
||||
width: '70px'
|
||||
},
|
||||
secret_type: {
|
||||
width: '100px'
|
||||
width: '100px',
|
||||
formatter: function(row) {
|
||||
return row.secret_type.label
|
||||
}
|
||||
},
|
||||
source: {
|
||||
formatter: function(row) {
|
||||
@@ -246,6 +249,13 @@ export default {
|
||||
hasCreate: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport && this.$hasPerm('accounts.view_accountsecret'),
|
||||
handleImportClick: ({ selectedRows }) => {
|
||||
this.$eventBus.$emit('showImportDialog', {
|
||||
selectedRows,
|
||||
url: '/api/v1/accounts/accounts/',
|
||||
name: this?.name
|
||||
})
|
||||
},
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<el-form-item :label="$tc('common.DateUpdated')">
|
||||
<span>{{ account['date_updated'] | date }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$tc('accounts.PasswordRecord')">
|
||||
<el-form-item v-if="showPasswordRecord" :label="$tc('accounts.PasswordRecord')">
|
||||
<el-button type="text" @click="onShowPasswordHistory">{{ secretInfo.version }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -78,6 +78,10 @@ export default {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
showPasswordRecord: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
>
|
||||
<AssetTreeTable
|
||||
ref="ListPage"
|
||||
v-bind="$attrs"
|
||||
:header-actions="headerActions"
|
||||
:table-config="tableConfig"
|
||||
class="tree-table"
|
||||
@@ -183,4 +184,7 @@ export default {
|
||||
.page ::v-deep .treebox {
|
||||
height: inherit !important;
|
||||
}
|
||||
.asset-select-dialog ::v-deep .el-icon-circle-check {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
:visible.sync="dialogVisible"
|
||||
v-bind="$attrs"
|
||||
:base-url="baseUrl"
|
||||
:tree-url-query="treeUrlQuery"
|
||||
@cancel="handleCancel"
|
||||
@confirm="handleConfirm"
|
||||
v-on="$listeners"
|
||||
@@ -34,6 +35,10 @@ export default {
|
||||
type: String,
|
||||
default: '/api/v1/assets/assets/'
|
||||
},
|
||||
treeUrlQuery: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
|
||||
@@ -39,6 +39,10 @@ export default {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/children/tree/'
|
||||
},
|
||||
treeUrlQuery: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
treeSetting: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
@@ -54,6 +58,9 @@ export default {
|
||||
},
|
||||
data() {
|
||||
const showAssets = this.treeSetting?.showAssets || this.showAssets
|
||||
const treeUrlQuery = this.setTreeUrlQuery()
|
||||
const assetTreeUrl = `${this.treeUrl}?assets=${showAssets ? '1' : '0'}&${treeUrlQuery}`
|
||||
|
||||
return {
|
||||
treeTabConfig: {
|
||||
activeMenu: 'CustomTree',
|
||||
@@ -72,7 +79,7 @@ export default {
|
||||
showSearch: true,
|
||||
url: this.url,
|
||||
nodeUrl: this.nodeUrl,
|
||||
treeUrl: `${this.treeUrl}?assets=${showAssets ? '1' : '0'}`,
|
||||
treeUrl: assetTreeUrl,
|
||||
callback: {
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||
},
|
||||
@@ -117,6 +124,15 @@ export default {
|
||||
treeSetting.showDelete = this.$hasPerm('assets.delete_node')
|
||||
},
|
||||
methods: {
|
||||
setTreeUrlQuery() {
|
||||
let str = ''
|
||||
for (const key in this.treeUrlQuery) {
|
||||
str += `${key}=${this.treeUrlQuery[key]}&`
|
||||
}
|
||||
str = str.substr(0, str.length - 1)
|
||||
|
||||
return str
|
||||
},
|
||||
decorateRMenu() {
|
||||
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
||||
if (show_current_asset === '1') {
|
||||
@@ -145,6 +161,8 @@ export default {
|
||||
} else if (treeNode.meta.type === 'platform') {
|
||||
url = setUrlParam(url, 'platform', treeNode.id)
|
||||
}
|
||||
const query = this.setTreeUrlQuery()
|
||||
url = query ? `${url}&${query}` : url
|
||||
this.$set(this.tableConfig, 'url', url)
|
||||
setRouterQuery(this, url)
|
||||
}
|
||||
|
||||
@@ -237,7 +237,8 @@ export default {
|
||||
col.formatter = (row, column, cellValue) => {
|
||||
let value = cellValue
|
||||
let padding = '0'
|
||||
if (!value && value !== 0) {
|
||||
const excludes = [undefined, null, '']
|
||||
if (excludes.indexOf(value) !== -1) {
|
||||
padding = '6px'
|
||||
value = '-'
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const IpCheck = {
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
value = value?.trim()
|
||||
if (/^[\w://.?]+$/.test(value)) {
|
||||
if (/^[\w://.?-]+$/.test(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error(i18n.t('common.FormatError')))
|
||||
|
||||
@@ -59,6 +59,9 @@ export default {
|
||||
if (!fieldMeta) {
|
||||
continue
|
||||
}
|
||||
if (fieldMeta['write_only']) {
|
||||
continue
|
||||
}
|
||||
|
||||
let value = this.object[name]
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [Array],
|
||||
type: [String, Array],
|
||||
default: () => []
|
||||
},
|
||||
title: {
|
||||
@@ -147,7 +147,7 @@ export default {
|
||||
item.port = selected.port
|
||||
},
|
||||
setDefaultItems(choices) {
|
||||
if (this.value.length > 0) {
|
||||
if (this.value instanceof Array && this.value.length > 0) {
|
||||
const protocols = []
|
||||
this.value.forEach(item => {
|
||||
// 有默认值的情况下,设置为只读或者有id、有setting是平台
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<UserConfirmDialog
|
||||
v-if="mfaDialogShow"
|
||||
:url="url"
|
||||
@UserConfirmDone="showExportDialog"
|
||||
@UserConfirmCancel="handleExportCancel"
|
||||
/>
|
||||
<div v-if="mfaDialogShow">
|
||||
<UserConfirmDialog
|
||||
:url="url"
|
||||
@UserConfirmDone="showExportDialog"
|
||||
@UserConfirmCancel="handleExportCancel"
|
||||
@AuthMFAError="handleAuthMFAError"
|
||||
/>
|
||||
</div>
|
||||
<Dialog
|
||||
v-if="exportDialogShow"
|
||||
:title="$tc('common.Export')"
|
||||
@@ -218,6 +220,9 @@ export default {
|
||||
vm.exportDialogShow = false
|
||||
vm.mfaDialogShow = false
|
||||
}, 100)
|
||||
},
|
||||
handleAuthMFAError() {
|
||||
this.mfaDialogShow = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDataZTree from '../AutoDataZTree'
|
||||
import TabTree from '../TabTree'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import ListTable from '../ListTable'
|
||||
import IBox from '../IBox'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import FileTree from '@/components/TreeTable/components/FileTree.vue'
|
||||
import IBox from '../IBox'
|
||||
import TabTree from '../TabTree'
|
||||
import AutoDataZTree from '../AutoDataZTree'
|
||||
|
||||
export default {
|
||||
name: 'TreeTable',
|
||||
|
||||
@@ -165,6 +165,8 @@ export default {
|
||||
}
|
||||
this.title = this.$t('common.CurrentUserVerify')
|
||||
this.visible = true
|
||||
}).catch(() => {
|
||||
this.$emit('AuthMFAError', true)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -130,7 +130,8 @@
|
||||
"users": "User",
|
||||
"Rules": "Rule",
|
||||
"Action": "Action",
|
||||
"ip_group_help_text": "The format is a comma-separated string, * means match all. Example: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64"
|
||||
"ip_group_help_text": "The format is a comma-separated string, * means match all. Example: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64",
|
||||
"apply_login_account": "Apply login account"
|
||||
},
|
||||
"applications": {
|
||||
"": "",
|
||||
@@ -197,6 +198,7 @@
|
||||
},
|
||||
"assets": {
|
||||
"Secure": "Secure",
|
||||
"AssetBulkUpdateTips": "device、cloud、web,Batch update of domain is not supported",
|
||||
"LabelInputFormatValidation": "Format error, correct format is:name:value",
|
||||
"IP/Host": "IP/Host",
|
||||
"OrganizationAsset": "Organization asset",
|
||||
@@ -429,6 +431,8 @@
|
||||
"ReLoginErr": "Login time has exceeded 5 minutes, please login again"
|
||||
},
|
||||
"common": {
|
||||
"About": "About",
|
||||
"PermissionCompany": "Permission company",
|
||||
"ApproverNumbers": "Approver numbers",
|
||||
"ConvenientOperate": "Convenient operate",
|
||||
"Overview": "Overview",
|
||||
@@ -588,7 +592,7 @@
|
||||
"bulkRemoveErrorMsg": "Bulk remove failed: ",
|
||||
"bulkRemoveSuccessMsg": "Bulk remove success",
|
||||
"SelectAtLeastOneAssetOrNodeErrMsg": "Select at least one asset or node",
|
||||
"RequiredSystemUserErrMsg": "Required systemuser",
|
||||
"RequiredSystemUserErrMsg": "Required account",
|
||||
"createBy": "Create by",
|
||||
"cloneFrom": "Clone from",
|
||||
"createErrorMsg": "Create error",
|
||||
@@ -819,6 +823,7 @@
|
||||
"Weekly": "Weekly"
|
||||
},
|
||||
"ops": {
|
||||
"RunAgain": "Run again",
|
||||
"ManualInput": "Manual input",
|
||||
"Execute": "Execute",
|
||||
"ID": "ID",
|
||||
@@ -1555,6 +1560,10 @@
|
||||
"basicTools": "Basic tool"
|
||||
},
|
||||
"tickets": {
|
||||
"ApplyAsset": "Apply asset",
|
||||
"LoginConfirm": "User login confirm",
|
||||
"CommandConfirm": "Command confirm",
|
||||
"LoginAssetConfirm": "Asset login confirm",
|
||||
"PermissionName": "Permission name",
|
||||
"Accept": "Accept",
|
||||
"AssignedMe": "Assigned me",
|
||||
@@ -1996,13 +2005,13 @@
|
||||
"favicon": "Website icon",
|
||||
"faviconTip": "Tips: website icon. (suggest image size: 16px*16px)",
|
||||
"loginImage": "Image of login page",
|
||||
"loginImageTip": "Tips: This will be displayed on the enterprise user login page. (suggest image size: 492px*472px)",
|
||||
"loginImageTip": "Tips: It will be displayed on the enterprise version user login page (recommended image size: 492*472px)",
|
||||
"loginTitle": "Title of login page",
|
||||
"loginTitleTip": "Tips: This will be displayed on the enterprise user login page. (eg: Welcome to the JumpServer open source fortress)",
|
||||
"logoIndex": "Logo (It contains text)",
|
||||
"logoIndexTip": "Tips: This will appear at the top left of the administration page. (suggest image size: 185px*55px)",
|
||||
"logoLogout": "Logo (It contains no text)",
|
||||
"logoLogoutTip": "Tips: This will be displayed on the enterprise user logout page. (suggest image size: 82px*82px)",
|
||||
"logoLogoutTip": "Tips: It will be displayed on the web terminal page of enterprise users (recommended image size: 82px*82px)",
|
||||
"restoreDialogMessage": "This will restore default Settings of the interface !!!",
|
||||
"restoreDialogTitle": "Are you sure?",
|
||||
"technologyConsult": "Technology Consult",
|
||||
|
||||
@@ -130,7 +130,8 @@
|
||||
"apply_login_system_user": "システムユーザーへのログイン申請",
|
||||
"apply_login_user": "ログインユーザーの申請",
|
||||
"login_confirm_user": "登録再確認受付者",
|
||||
"RuleDetail": "ルールの詳細"
|
||||
"RuleDetail": "ルールの詳細",
|
||||
"apply_login_account": "ログインアカウントの申請"
|
||||
},
|
||||
"applications": {
|
||||
"": "",
|
||||
@@ -197,6 +198,7 @@
|
||||
},
|
||||
"assets": {
|
||||
"Secure": "安全である",
|
||||
"AssetBulkUpdateTips": "ネットワークデバイス、クラウドサービス、Web、一括更新ネットワークドメインはサポートされていません",
|
||||
"LabelInputFormatValidation": "フォーマットが正しくありません:name:value",
|
||||
"IP/Host": "IP/ノア",
|
||||
"OrganizationAsset": "組織資産",
|
||||
@@ -429,6 +431,8 @@
|
||||
"ReLoginErr": "ログイン時間が 5 分を超えました。もう一度ログインしてください"
|
||||
},
|
||||
"common": {
|
||||
"About": "について",
|
||||
"PermissionCompany": "授权公司",
|
||||
"ApproverNumbers": "承認者数",
|
||||
"ConvenientOperate": "便利な操作",
|
||||
"Overview": "概要",
|
||||
@@ -588,7 +592,7 @@
|
||||
"bulkDeleteSuccessMsg": "一括削除に成功しました",
|
||||
"bulkRemoveErrorMsg": "一括削除に失敗しました:",
|
||||
"SelectAtLeastOneAssetOrNodeErrMsg": "資産またはノードは少なくとも1つを選択します",
|
||||
"RequiredSystemUserErrMsg": "先にシステムユーザーを選択してください",
|
||||
"RequiredSystemUserErrMsg": "必要なアカウント",
|
||||
"bulkRemoveSuccessMsg": "一括削除に成功しました",
|
||||
"createBy": "作成者",
|
||||
"cloneFrom": "クローン",
|
||||
@@ -818,6 +822,7 @@
|
||||
"Weekly": "週ごと"
|
||||
},
|
||||
"ops": {
|
||||
"RunAgain": "再実行",
|
||||
"ManualInput": "手動入力",
|
||||
"Execute": "実行",
|
||||
"ID": "ID",
|
||||
@@ -1547,6 +1552,10 @@
|
||||
"Applets": "リモート アプリケーション"
|
||||
},
|
||||
"tickets": {
|
||||
"ApplyAsset": "リソースの適用",
|
||||
"LoginConfirm": "ユーザーログインの確認",
|
||||
"CommandConfirm": "コマンドの確認",
|
||||
"LoginAssetConfirm": "資産ログインの確認",
|
||||
"OneAssigneeType": "一次受付者タイプ",
|
||||
"OneAssignee": "一級受付者",
|
||||
"TwoAssigneeType": "二級受付者タイプ",
|
||||
@@ -1991,13 +2000,13 @@
|
||||
"favicon": "Webサイトのアイコン",
|
||||
"faviconTip": "ヒント: webサイトのアイコン (推奨画像サイズ: 16px * 16px)",
|
||||
"loginImage": "ログインページの画像",
|
||||
"loginImageTip": "ヒント: 企業版ユーザーのログインページに表示されます (推奨画像サイズ: 492 * 472px)",
|
||||
"loginImageTip": "ヒント: エンタープライズ版のユーザーログインページに表示されます (推奨画像サイズ: 492*472px)",
|
||||
"loginTitle": "ログインページのタイトル",
|
||||
"loginTitleTip": "ヒント: 企業版ユーザーログインページに表示されます",
|
||||
"logoIndex": "ロゴ (文字付き)",
|
||||
"logoIndexTip": "ヒント: 管理ページの左上に表示されます (推奨画像サイズ: 185px * 55px)",
|
||||
"logoLogout": "ロゴ (文字なし)",
|
||||
"logoLogoutTip": "ヒント: 企業版ユーザーの終了ページに表示されます (推奨画像サイズ: 82px * 82px)",
|
||||
"logoLogoutTip": "ヒント: エンタープライズ ユーザーの Web 端末ページに表示されます (推奨画像サイズ: 82px*82px)",
|
||||
"restoreDialogMessage": "本当にデフォルトの初期化を再開しますか?",
|
||||
"restoreDialogTitle": "確認しますか",
|
||||
"technologyConsult": "技術コンサルティング",
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
}
|
||||
},
|
||||
"acl": {
|
||||
"apply_login_account": "申请登录账号",
|
||||
"IgnoreCase": "忽略大小写",
|
||||
"Content": "内容",
|
||||
"CommandFilterACL": "命令过滤",
|
||||
@@ -197,6 +198,7 @@
|
||||
},
|
||||
"assets": {
|
||||
"Secure": "安全",
|
||||
"AssetBulkUpdateTips": "网络设备、云服务、web,不支持批量更新网域",
|
||||
"LabelInputFormatValidation": "标签格式错误,正确格式为:name:value",
|
||||
"GatewayList": "网关列表",
|
||||
"AccessKey": "访问密钥",
|
||||
@@ -425,6 +427,8 @@
|
||||
"ReLoginErr": "登录时长已超过 5 分钟,请重新登录"
|
||||
},
|
||||
"common": {
|
||||
"About": "关于",
|
||||
"PermissionCompany": "授权公司",
|
||||
"Filename": "文件名",
|
||||
"ApproverNumbers": "审批人数量",
|
||||
"ConvenientOperate": "便捷操作",
|
||||
@@ -600,7 +604,7 @@
|
||||
"bulkDeleteSuccessMsg": "批量删除成功",
|
||||
"bulkRemoveErrorMsg": "批量移除失败: ",
|
||||
"SelectAtLeastOneAssetOrNodeErrMsg": "资产或者节点至少选择一项",
|
||||
"RequiredSystemUserErrMsg": "请先选择系统用户",
|
||||
"RequiredSystemUserErrMsg": "请选择账号",
|
||||
"bulkRemoveSuccessMsg": "批量移除成功",
|
||||
"createBy": "创建者",
|
||||
"cloneFrom": "克隆自",
|
||||
@@ -814,6 +818,7 @@
|
||||
"Weekly": "按周"
|
||||
},
|
||||
"ops": {
|
||||
"RunAgain": "再次执行",
|
||||
"AdhocUpdate": "更新命令",
|
||||
"Add": "新增",
|
||||
"Modify": "修改",
|
||||
@@ -1541,6 +1546,10 @@
|
||||
"testHelpText": "请输入目的地址进行测试"
|
||||
},
|
||||
"tickets": {
|
||||
"ApplyAsset": "申请资产",
|
||||
"LoginConfirm": "用户登录复合",
|
||||
"CommandConfirm": "命令复合",
|
||||
"LoginAssetConfirm": "资产登录复合",
|
||||
"RelevantAssignees": "相关受理人",
|
||||
"OneAssigneeType": "一级受理人类型",
|
||||
"OneAssignee": "一级受理人",
|
||||
@@ -1905,11 +1914,11 @@
|
||||
"loginImage": "登录页面图片",
|
||||
"loginImageTip": "提示:将会显示在企业版用户登录页面(建议图片大小为: 492*472px)",
|
||||
"loginTitle": "登录页面标题",
|
||||
"loginTitleTip": "提示:将会显示在企业版用户登录页面(eg: 欢迎使用JumpServer开源堡垒机)",
|
||||
"loginTitleTip": "提示:将会显示在企业版用户 SSH 登录 KoKo 登录页面(eg: 欢迎使用JumpServer开源堡垒机)",
|
||||
"logoIndex": "Logo (带文字)",
|
||||
"logoIndexTip": "提示:将会显示在管理页面左上方(建议图片大小为: 185px*55px)",
|
||||
"logoLogout": "Logo (不带文字)",
|
||||
"logoLogoutTip": "提示:将会显示在企业版用户退出页面(建议图片大小为:82px*82px)",
|
||||
"logoLogoutTip": "提示:将会显示在企业版用户的 Web 终端页面(建议图片大小为:82px*82px)",
|
||||
"restoreDialogMessage": "您确定要恢复默认初始化吗?",
|
||||
"restoreDialogTitle": "你确认吗",
|
||||
"technologyConsult": "技术咨询",
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
top="1vh"
|
||||
width="70%"
|
||||
>
|
||||
<el-alert v-if="tips" class="tips" type="success">
|
||||
{{ tips }}
|
||||
</el-alert>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="4" :sm="24">
|
||||
<div class="select-prop-label">
|
||||
@@ -56,6 +59,10 @@ export default {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
tips: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -152,4 +159,7 @@ export default {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
124
src/layout/components/NavHeader/About.vue
Normal file
124
src/layout/components/NavHeader/About.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="iVisible"
|
||||
:title="''"
|
||||
class="about-dialog"
|
||||
:visible.sync="iVisible"
|
||||
width="50%"
|
||||
top="10%"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
>
|
||||
<div class="box">
|
||||
<div class="head">
|
||||
<img :src="logoTextSrc" class="sidebar-logo-text" alt="logo">
|
||||
</div>
|
||||
<div class="text">{{ $tc('ops.version') }}:<strong> dev </strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv3. </span></div>
|
||||
<div class="text">{{ $tc('common.PermissionCompany') }}:{{ corporation }}</div>
|
||||
<el-divider class="divider" />
|
||||
<div class="text">
|
||||
<span v-for="(i, index) in actions" :key="index" class="text-link" @click="onClick(i.name)">
|
||||
<i class="icon" :class="i.icon" />{{ i.label }}
|
||||
<el-divider v-if="index !== actions.length - 1" direction="vertical" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
logoTextSrc: require('@/assets/img/logo_text_green.png'),
|
||||
actions: [
|
||||
{
|
||||
name: 'github',
|
||||
label: 'github',
|
||||
icon: 'fa fa-github'
|
||||
},
|
||||
{
|
||||
name: 'download',
|
||||
label: this.$tc('common.Download'),
|
||||
icon: 'fa fa-download'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'publicSettings'
|
||||
]),
|
||||
iVisible: {
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
},
|
||||
get() {
|
||||
return this.visible
|
||||
}
|
||||
},
|
||||
corporation() {
|
||||
return this.publicSettings.XPACK_LICENSE_INFO.corporation
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick(type) {
|
||||
switch (type) {
|
||||
case 'download':
|
||||
window.open('/core/download/', '_blank')
|
||||
break
|
||||
case 'github':
|
||||
window.open('https://github.com/jumpserver/jumpserver', '_blank')
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.about-dialog {
|
||||
&>>> .el-dialog__header {
|
||||
background-color: #FAFBFD;
|
||||
border-bottom: none;
|
||||
}
|
||||
&>>> .el-dialog__body {
|
||||
background-color: #FAFBFD;
|
||||
padding: 10px 40px 20px;
|
||||
}
|
||||
&>>> .el-dialog__footer {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.head {
|
||||
text-align: center;
|
||||
}
|
||||
.box {
|
||||
.text {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
.icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
>>> .divider.el-divider {
|
||||
margin: 15px 0!important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +1,31 @@
|
||||
<template>
|
||||
<el-dropdown :show-timeout="50" @command="handleCommand">
|
||||
<span class="el-dropdown-link" style="vertical-align: middle;">
|
||||
<svg-icon icon-class="question-mark" style="font-size: 16px;" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="docs">{{ $t('common.nav.Docs') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="support">{{ $t('common.nav.Support') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="toolsDownload">{{ $t('common.nav.Download') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="!hasLicence" command="enterprise">{{ $t('common.nav.EnterpriseEdition') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="github">GITHUB</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<div>
|
||||
<el-dropdown :show-timeout="50" @command="handleCommand">
|
||||
<span class="el-dropdown-link" style="vertical-align: middle;">
|
||||
<svg-icon icon-class="question-mark" style="font-size: 16px;" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="docs">{{ $t('common.nav.Docs') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="support">{{ $t('common.nav.Support') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="!hasLicence" command="enterprise">{{ $t('common.nav.EnterpriseEdition') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="about">{{ $tc('common.About') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<About :visible.sync="visible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import About from './About.vue'
|
||||
|
||||
export default {
|
||||
name: 'Help',
|
||||
components: {
|
||||
About
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
URLSite: {
|
||||
HELP_DOCUMENT_URL: '',
|
||||
HELP_SUPPORT_URL: ''
|
||||
@@ -45,11 +53,8 @@ export default {
|
||||
case 'enterprise':
|
||||
window.open('https://jumpserver.org/enterprise.html', '_blank')
|
||||
break
|
||||
case 'toolsDownload':
|
||||
window.open('/core/download/', '_blank')
|
||||
break
|
||||
case 'github':
|
||||
window.open('https://github.com/jumpserver/jumpserver', '_blank')
|
||||
case 'about':
|
||||
this.visible = true
|
||||
break
|
||||
default:
|
||||
window.open(this.URLSite.HELP_DOCUMENT_URL, '_blank')
|
||||
|
||||
@@ -166,7 +166,7 @@ export default [
|
||||
name: 'AccountGatherList',
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountGather.AccountGatherTaskList'),
|
||||
permissions: ['accounts.view_gatheraccountsautomation']
|
||||
permissions: ['accounts.view_gatheredaccount']
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
107
src/views/accounts/AccountGather/AccountGatherList.vue
Normal file
107
src/views/accounts/AccountGather/AccountGatherList.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<TreeTable :table-config="tableConfig" :tree-setting="treeSetting" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TreeTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: true,
|
||||
url: '/api/v1/accounts/gathered-accounts/',
|
||||
nodeUrl: '/api/v1/assets/nodes/',
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1'
|
||||
},
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/gathered-accounts/',
|
||||
hasTree: true,
|
||||
columns: [
|
||||
'asset', 'username', 'date_last_login', 'present', 'address_last_login', 'date_updated'
|
||||
],
|
||||
columnsMeta: {
|
||||
asset: {
|
||||
label: vm.$t('assets.Asset'),
|
||||
formatter: function(row) {
|
||||
const to = {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset.id }
|
||||
}
|
||||
if (vm.$hasPerm('assets.view_asset')) {
|
||||
return <router-link to={to}>{row.asset.name}</router-link>
|
||||
} else {
|
||||
return <span>{row.asset.name}</span>
|
||||
}
|
||||
}
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
present: {
|
||||
width: 80
|
||||
},
|
||||
address_last_login: {
|
||||
width: 120
|
||||
},
|
||||
date_updated: {
|
||||
formatter: function(row, col, cell) {
|
||||
return toSafeLocalDateStr(row.date_updated)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasClone: false,
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.Sync'),
|
||||
can: this.$hasPerm('accounts.add_gatheredaccount'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
console.log('row', row.id)
|
||||
this.$axios.post(
|
||||
`/api/v1/accounts/gathered-accounts/${row.id}/sync/`,
|
||||
).then(res => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
}).catch(res => {
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasCreate: false,
|
||||
hasImport: false,
|
||||
hasExport: false,
|
||||
searchConfig: {
|
||||
exclude: ['asset'],
|
||||
options: [
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -16,6 +16,12 @@ export default {
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('accounts.AccountGather.AccountGatherTaskList'),
|
||||
name: 'AccountGatherList',
|
||||
hidden: !this.$hasPerm('accounts.view_gatheredaccount'),
|
||||
component: () => import('@/views/accounts/AccountGather/AccountGatherList.vue')
|
||||
},
|
||||
{
|
||||
title: this.$t('accounts.AccountGather.AccountGatherList'),
|
||||
name: 'AccountGatherTaskList',
|
||||
hidden: !this.$hasPerm('accounts.view_gatheraccountsautomation'),
|
||||
component: () => import('@/views/accounts/AccountGather/AccountGatherTaskList.vue')
|
||||
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
}
|
||||
],
|
||||
url: `/api/v1/accounts/account-templates/${this.object.id}/`,
|
||||
excludes: ['privileged', 'secret', 'passphrase', 'specific']
|
||||
excludes: ['privileged', 'secret', 'passphrase', 'spec_info']
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ViewSecret v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" :url="secretUrl" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
:url="secretUrl"
|
||||
:account="account"
|
||||
:show-password-record="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -32,6 +32,24 @@ export default {
|
||||
|
||||
return {
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('common.Activate'),
|
||||
type: 'switcher',
|
||||
attrs: {
|
||||
model: vm.object.is_active,
|
||||
disabled: !vm.$hasPerm('accounts.change_account')
|
||||
},
|
||||
callbacks: Object.freeze({
|
||||
change: (val) => {
|
||||
this.$axios.patch(
|
||||
`/api/v1/accounts/accounts/${this.object.id}/`,
|
||||
{ is_active: val }
|
||||
).then(res => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.Privileged'),
|
||||
type: 'switcher',
|
||||
@@ -79,7 +97,7 @@ export default {
|
||||
model: vm.object.su_from?.id || '',
|
||||
label: vm.object.su_from?.name ? vm.object.su_from?.name + `(${vm.object.su_from?.username})` : '',
|
||||
ajax: {
|
||||
url: `/api/v1/accounts/accounts/${vm.object.id}/su-from-accounts/?fields_size=mini`,
|
||||
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${vm.object.id}&fields_size=mini`,
|
||||
transformOption: (item) => {
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
|
||||
@@ -61,24 +61,11 @@ export default {
|
||||
cleanFormValue(values) {
|
||||
// Update 的时候
|
||||
const { id = '' } = this.$route.params
|
||||
const accounts = values?.accounts
|
||||
const query = this.$route.query || {}
|
||||
if (id) delete values['accounts']
|
||||
|
||||
if (values.nodes && values.nodes.length === 0) {
|
||||
delete values['nodes']
|
||||
}
|
||||
|
||||
if (accounts && accounts.length !== 0) {
|
||||
accounts.forEach(i => {
|
||||
if (i.hasOwnProperty('id')) {
|
||||
// 克隆资产时 template 为 false
|
||||
i.template = !query.hasOwnProperty('clone_from')
|
||||
}
|
||||
return i
|
||||
})
|
||||
}
|
||||
console.log('values[\'accounts\']', values['accounts'])
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,24 @@ export default {
|
||||
components: { BaseAssetCreateUpdate },
|
||||
data() {
|
||||
return {
|
||||
url: '/api/v1/assets/clouds/'
|
||||
url: '/api/v1/assets/clouds/',
|
||||
addFieldsMeta: {
|
||||
protocols: {
|
||||
hidden: (formValue) => {
|
||||
const address = formValue['address']
|
||||
if (!address) return
|
||||
let port = address.startsWith('https://') ? 443 : 80
|
||||
try {
|
||||
const url = new URL(address)
|
||||
if (url.port) { port = url.port }
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
const protocols = formValue['protocols']?.[0] || {}
|
||||
protocols.port = port
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,18 +21,18 @@ export default {
|
||||
getAddFields() {
|
||||
const platform = this.$route.query.platform_type
|
||||
const baseFields = [[this.$t('common.Basic'), ['db_name']]]
|
||||
let tlsParams = ['use_ssl', 'ca_cert']
|
||||
let tlsFields = ['use_ssl', 'ca_cert']
|
||||
switch (platform) {
|
||||
case 'redis':
|
||||
tlsParams = tlsParams.concat(['client_cert', 'client_key'])
|
||||
tlsFields = tlsFields.concat(['client_cert', 'client_key'])
|
||||
break
|
||||
case 'mongodb':
|
||||
tlsParams = tlsParams.concat(['client_key', 'allow_invalid_cert'])
|
||||
tlsFields = tlsFields.concat(['client_key', 'allow_invalid_cert'])
|
||||
break
|
||||
}
|
||||
if (tlsParams.length > 2) {
|
||||
if (tlsFields.length > 2) {
|
||||
const secureField = [
|
||||
this.$t('assets.Secure'), tlsParams, 3
|
||||
this.$t('assets.Secure'), tlsFields, 2
|
||||
]
|
||||
baseFields.push(secureField)
|
||||
}
|
||||
|
||||
@@ -132,9 +132,14 @@ export default {
|
||||
handleConfirm() {
|
||||
this.iVisible = false
|
||||
// 过滤掉添加里还没有id的账号
|
||||
const hasIdAccounts = this.accounts.filter(i => i?.id)
|
||||
const data = _.xorBy(hasIdAccounts, this.accountsSelected, 'id')
|
||||
this.accounts.push(...data)
|
||||
const hasIdAccounts = this.accounts.filter(i => i?.id).map(item => item.id)
|
||||
const newAddAccounts = this.accountsSelected.filter(i => {
|
||||
if (!hasIdAccounts.includes(i.id)) {
|
||||
i.template = true
|
||||
return i
|
||||
}
|
||||
})
|
||||
this.accounts.push(...newAddAccounts)
|
||||
this.$emit('onConfirm', this.accounts)
|
||||
},
|
||||
handleCancel() {
|
||||
|
||||
@@ -40,6 +40,10 @@ export default {
|
||||
formConfig: {
|
||||
initial: { secret_type: 'password' },
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
getUrl: function() {
|
||||
return '/api/v1/accounts/account-templates/'
|
||||
},
|
||||
needGetObjectDetail: false,
|
||||
hasDetailInMsg: false,
|
||||
fields: [
|
||||
...templateFields(this)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
v-if="visible"
|
||||
:form-setting="formSetting"
|
||||
:selected-rows="selectedRows"
|
||||
:tips="tips"
|
||||
:visible="visible"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
@@ -30,6 +31,7 @@ export default {
|
||||
data() {
|
||||
const meta = assetFieldsMeta(this)
|
||||
return {
|
||||
tips: this.$t('assets.AssetBulkUpdateTips'),
|
||||
formSetting: {
|
||||
url: '/api/v1/assets/assets/',
|
||||
hasSaveContinue: false,
|
||||
|
||||
@@ -69,6 +69,7 @@ export default {
|
||||
}
|
||||
if (action === 'Clone') {
|
||||
route.query.clone_from = row.id
|
||||
route.query.platform = row.platform.id
|
||||
} else if (action === 'Update') {
|
||||
route.params.id = row.id
|
||||
route.query.platform = row.platform.id
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { Required, specialEmojiCheck } from '@/components/DataForm/rules'
|
||||
import { RequiredChange, specialEmojiCheck } from '@/components/DataForm/rules'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun } from '../const'
|
||||
import { UploadKey } from '@/components'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
const updateNotRequiredFields = ['access_key_secret', 'client_secret', 'password', 'sc_password', 'oc_password', 'cert_file', 'key_file']
|
||||
for (const item of accountProviderAttrs?.attrs) {
|
||||
fieldsObject[item] = {
|
||||
rules: updateNotRequiredFields.includes(item) && vm.$route.params.id ? [] : [Required]
|
||||
rules: updateNotRequiredFields.includes(item) && vm.$route.params.id ? [] : [RequiredChange]
|
||||
}
|
||||
}
|
||||
return fieldsObject
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
rules: [Required, specialEmojiCheck]
|
||||
rules: [RequiredChange, specialEmojiCheck]
|
||||
},
|
||||
attrs: {
|
||||
encryptedFields: ['access_key_secret'],
|
||||
@@ -76,12 +76,12 @@ export default {
|
||||
}
|
||||
},
|
||||
password: {
|
||||
rules: this.$route.params.id ? [] : [Required]
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
}
|
||||
}
|
||||
},
|
||||
provider: {
|
||||
rules: [Required],
|
||||
rules: [RequiredChange],
|
||||
el: {
|
||||
disabled: true
|
||||
}
|
||||
|
||||
@@ -119,13 +119,24 @@ export default {
|
||||
updateSuccessNextRoute: { name: 'CloudCenter' },
|
||||
createSuccessNextRoute: { name: 'CloudCenter' },
|
||||
afterGetFormValue(formValue) {
|
||||
formValue.protocols = formValue.protocols?.split(' ').map(i => {
|
||||
const [name, port] = i.split('/')
|
||||
return { name, port }
|
||||
})
|
||||
formValue.ip_network_segment_group = formValue.ip_network_segment_group.toString()
|
||||
return formValue
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
if (!Array.isArray(value.ip_network_segment_group)) {
|
||||
value.ip_network_segment_group = value.ip_network_segment_group ? value.ip_network_segment_group.split(',') : []
|
||||
let protocols = ''
|
||||
const ipNetworkSegments = value.ip_network_segment_group
|
||||
if (!Array.isArray(ipNetworkSegments)) {
|
||||
value.ip_network_segment_group = ipNetworkSegments ? ipNetworkSegments.split(',') : []
|
||||
}
|
||||
if (value.protocols.length > 0) {
|
||||
protocols = value.protocols.map(i => (i.name + '/' + i.port)).join(' ')
|
||||
}
|
||||
value.protocols = protocols
|
||||
|
||||
return value
|
||||
},
|
||||
onPerformError(error, method, vm) {
|
||||
|
||||
@@ -58,6 +58,10 @@ export default {
|
||||
prop: 'date_sync',
|
||||
label: this.$t('xpack.Cloud.DateSync'),
|
||||
formatter: DateFormatter
|
||||
},
|
||||
{
|
||||
prop: 'actions',
|
||||
has: false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ export default {
|
||||
formatter: DateFormatter
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
label: this.$t('common.Action'),
|
||||
prop: 'actions',
|
||||
label: this.$t('common.Actions'),
|
||||
align: 'center',
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
|
||||
@@ -52,20 +52,9 @@ export default {
|
||||
url: `/api/v1/xpack/cloud/accounts/${this.object.id}`,
|
||||
detailFields: [
|
||||
'name', 'account_display', 'node_display',
|
||||
{
|
||||
key: this.$t('xpack.Cloud.LinuxAdminUser'),
|
||||
value: this.object.unix_admin_user?.name
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.WindowsAdminUser'),
|
||||
value: this.object.windows_admin_user?.name
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Protocols'),
|
||||
value: this.object.protocols,
|
||||
formatter: (item, val) => {
|
||||
return <div>{val.map((v) => <el-tag size='small'>{v['name']}/{v['port']}</el-tag>)}</div>
|
||||
}
|
||||
value: this.object.protocols
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.IPNetworkSegment'),
|
||||
@@ -85,7 +74,7 @@ export default {
|
||||
value: this.object.regions,
|
||||
formatter(row, value) {
|
||||
return (<div>{
|
||||
value.map((content) => {
|
||||
value?.map((content) => {
|
||||
return <div>{ content }</div>
|
||||
})}
|
||||
</div>)
|
||||
|
||||
@@ -21,15 +21,27 @@ export default {
|
||||
app: 'xpack',
|
||||
resource: 'syncinstancetask'
|
||||
},
|
||||
columnsShow: {
|
||||
min: ['name', 'account', 'hostname_strategy', 'actions'],
|
||||
default: [
|
||||
'name', 'account', 'hostname_strategy', 'protocols', 'is_periodic', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
sync_ip_type: {
|
||||
width: '120px'
|
||||
},
|
||||
hostname_strategy: {
|
||||
width: '120px'
|
||||
width: '150px',
|
||||
formatter: function(row) {
|
||||
return <span>{ row.hostname_strategy.label }</span>
|
||||
}
|
||||
},
|
||||
account_display: {
|
||||
label: this.$t('xpack.Cloud.Account')
|
||||
account: {
|
||||
label: this.$t('xpack.Cloud.Account'),
|
||||
formatter: function(row) {
|
||||
return <span>{ row.account.name }</span>
|
||||
}
|
||||
},
|
||||
periodic_display: {
|
||||
width: '150px'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu" />
|
||||
<TabPage :active-menu.sync="config.activeMenu" v-bind="config" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -27,7 +27,10 @@ export default {
|
||||
hidden: () => !this.$hasPerm('xpack.view_account'),
|
||||
component: () => import('@/views/assets/Cloud/Account/AccountList.vue')
|
||||
}
|
||||
]
|
||||
],
|
||||
actions: {
|
||||
deleteSuccessRoute: 'AssetList'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,13 @@ export default {
|
||||
label: this.$t('assets.Assets'),
|
||||
el: {
|
||||
value: [],
|
||||
baseUrl: '/api/v1/assets/assets/?domain_enabled=true'
|
||||
baseUrl: '/api/v1/assets/assets/?domain_enabled=true',
|
||||
treeUrlQuery: {
|
||||
domain_enabled: true
|
||||
},
|
||||
canSelect: (row) => {
|
||||
return row.platform?.name !== 'Gateway'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:has-detail-in-msg="false"
|
||||
:clean-form-value="cleanFormValue"
|
||||
:after-get-form-value="afterGetFormValue"
|
||||
:after-get-remote-meta="handleAfterGetRemoteMeta"
|
||||
|
||||
@@ -42,23 +42,27 @@ export default {
|
||||
options() {
|
||||
const seriesList = []
|
||||
const labels = this.data.map(item => item.label)
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
const total = _.sumBy(this.data, function(i) { return i.total })
|
||||
for (let i = 0, len = this.data.length; i < len; i++) {
|
||||
const current = this.data[i]
|
||||
let num = (current.total / total) * 100
|
||||
num = _.floor(num, 2)
|
||||
const color = '#' + Math.floor(Math.random() * (256 * 256 * 256 - 1)).toString(16)
|
||||
seriesList.push({
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
barWidth: 32,
|
||||
name: current.label,
|
||||
itemStyle: {
|
||||
borderRadius: 0
|
||||
},
|
||||
data: [{
|
||||
value: current.total,
|
||||
itemStyle: {
|
||||
color: this.colors[i]
|
||||
borderRadius: 0,
|
||||
color: () => {
|
||||
return this.colors[i] || color
|
||||
}
|
||||
}],
|
||||
color: this.colors[i]
|
||||
},
|
||||
data: [num],
|
||||
color: () => {
|
||||
return this.colors[i] || color
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
@@ -69,8 +73,9 @@ export default {
|
||||
itemHeight: 10,
|
||||
textStyle: {
|
||||
color: '#000',
|
||||
lineHeight: 30
|
||||
lineHeight: 10
|
||||
},
|
||||
bottom: 30,
|
||||
data: labels
|
||||
|
||||
},
|
||||
@@ -126,11 +131,11 @@ export default {
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: '0%',
|
||||
top: '60%',
|
||||
containLabel: true,
|
||||
bottom: '-50%',
|
||||
bottom: '-10',
|
||||
left: '0%',
|
||||
right: '0%'
|
||||
right: 1
|
||||
},
|
||||
series: seriesList,
|
||||
xAxis: {
|
||||
|
||||
@@ -13,6 +13,7 @@ import CodeEditor from '@/components/FormFields/CodeEditor'
|
||||
import { CronTab } from '@/components'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import VariableHelpDialog from '@/views/ops/Job/VariableHelpDialog'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -46,6 +47,7 @@ export default {
|
||||
},
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
rules: [Required],
|
||||
hidden: (formValue) => {
|
||||
return this.instantTask
|
||||
}
|
||||
@@ -77,6 +79,7 @@ export default {
|
||||
}
|
||||
},
|
||||
playbook: {
|
||||
rules: [Required],
|
||||
hidden: (formValue) => {
|
||||
return formValue.type !== 'playbook'
|
||||
},
|
||||
@@ -95,9 +98,7 @@ export default {
|
||||
type: 'assetSelect',
|
||||
component: AssetSelect,
|
||||
label: this.$t('perms.Asset'),
|
||||
rules: [{
|
||||
required: false
|
||||
}],
|
||||
rules: [Required],
|
||||
el: {
|
||||
baseUrl: '/api/v1/perms/users/self/assets/',
|
||||
baseNodeUrl: '/api/v1/perms/users/self/nodes/',
|
||||
@@ -105,6 +106,7 @@ export default {
|
||||
}
|
||||
},
|
||||
args: {
|
||||
rules: [Required],
|
||||
hidden: (formValue) => {
|
||||
return formValue.type !== 'adhoc'
|
||||
},
|
||||
|
||||
@@ -48,10 +48,7 @@ export default {
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('ops.Summary(success/total)'),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:visible.sync="iVisible"
|
||||
width="20%"
|
||||
top="1vh"
|
||||
:show-cancel="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="true"
|
||||
@confirm="onConfirm"
|
||||
>
|
||||
|
||||
@@ -45,6 +45,7 @@ import { TreeTable } from '@/components'
|
||||
import CodeEditor from '@/components/FormFields/CodeEditor'
|
||||
import item from '@/layout/components/NavLeft/Item'
|
||||
import NewNodeDialog from '@/views/ops/Template/Playbook/PlaybookDetail/Editor/NewNodeDialog.vue'
|
||||
import { renameFile } from '@/api/ops'
|
||||
|
||||
export default {
|
||||
name: 'CommandExecution',
|
||||
@@ -113,16 +114,17 @@ export default {
|
||||
}
|
||||
}.bind(this),
|
||||
refresh: function(event, treeNode) {
|
||||
// const parentNode = this.zTree.getNodeByParam('id', this.parentId)
|
||||
// this.zTree.expandNode(parentNode, true, false, false, false)
|
||||
},
|
||||
onRename: function(event, treeId, treeNode, isCancel) {
|
||||
const url = `/api/v1/ops/playbook/${this.object.id}/file/`
|
||||
if (isCancel) {
|
||||
return
|
||||
}
|
||||
this.$axios.patch(url, { key: treeNode.id, new_name: treeNode.name, is_directory: treeNode.isParent })
|
||||
.then(data => {
|
||||
renameFile(this.object.id, {
|
||||
key: treeNode.id,
|
||||
new_name: treeNode.name,
|
||||
is_directory: treeNode.isParent
|
||||
}).then()
|
||||
.finally(() => {
|
||||
this.refreshTree()
|
||||
})
|
||||
}.bind(this)
|
||||
|
||||
@@ -79,7 +79,7 @@ export default {
|
||||
this.$emit('completed')
|
||||
this.$message.success('terminal.UploadSucceed')
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.$message.error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export default {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'users', 'user_groups', 'assets',
|
||||
'nodes', 'accounts', 'is_valid'
|
||||
'nodes', 'accounts', 'is_valid', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
|
||||
@@ -73,6 +73,9 @@ export default {
|
||||
choicesSelected.push(this.SPEC)
|
||||
this.showSpecAccounts = true
|
||||
}
|
||||
if (this.value.indexOf(this.SPEC) > -1) {
|
||||
this.showSpecAccounts = true
|
||||
}
|
||||
this.choicesSelected = choicesSelected
|
||||
this.specAccountsInput = specAccountsInput
|
||||
},
|
||||
@@ -95,11 +98,12 @@ export default {
|
||||
this.outputValue()
|
||||
},
|
||||
outputValue() {
|
||||
let choicesSelected = this.choicesSelected
|
||||
if (this.showSpecAccounts) {
|
||||
this.$emit('change', [...this.choicesSelected, ...this.specAccountsInput])
|
||||
} else {
|
||||
this.$emit('change', this.choicesSelected)
|
||||
choicesSelected = [...this.choicesSelected, ...this.specAccountsInput]
|
||||
}
|
||||
this.$emit('input', choicesSelected)
|
||||
this.$emit('change', choicesSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,10 @@ export default {
|
||||
accounts: fieldsManager.accounts,
|
||||
date_start: fieldsManager.date_start,
|
||||
date_expired: fieldsManager.date_expired,
|
||||
is_active: fieldsManager.is_active
|
||||
is_active: fieldsManager.is_active,
|
||||
actions: {
|
||||
label: this.$t('common.Action')
|
||||
}
|
||||
}
|
||||
if (this.permType !== 'asset') {
|
||||
url = '/api/v1/perms/application-permissions/'
|
||||
|
||||
@@ -22,7 +22,6 @@ export default {
|
||||
helpMessage: this.$t('setting.helpText.ConnectionTokenList'),
|
||||
tableConfig: {
|
||||
url: ajaxUrl,
|
||||
columnsExclude: ['actions'],
|
||||
columnsExtra: ['action'],
|
||||
columnsShow: {
|
||||
min: ['id', 'actions'],
|
||||
@@ -38,7 +37,9 @@ export default {
|
||||
action: {
|
||||
label: this.$t('common.Action'),
|
||||
formatter: function(row) {
|
||||
return row.actions.map(item => { return item.label }).join(', ')
|
||||
return row.actions.map(item => {
|
||||
return item.label
|
||||
}).join(', ')
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -51,7 +52,7 @@ export default {
|
||||
name: 'Expired',
|
||||
title: this.$t('setting.Expire'),
|
||||
type: 'info',
|
||||
can: ({ row }) => row['is_valid'],
|
||||
can: ({ row }) => !row['is_expired'],
|
||||
callback: function({ row }) {
|
||||
this.$axios.patch(`${ajaxUrl}${row.id}/expire/`,
|
||||
).then(res => {
|
||||
|
||||
@@ -63,7 +63,7 @@ export default {
|
||||
title: this.$t('users.setWeCom'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$store.state.users.profile.is_wecom_bound ? this.$t('common.unbind') : this.$t('common.bind')
|
||||
label: this.$store.state.users.profile.wecom_id ? this.$t('common.unbind') : this.$t('common.bind')
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_WECOM,
|
||||
callbacks: {
|
||||
@@ -77,7 +77,7 @@ export default {
|
||||
title: this.$t('users.setDingTalk'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$store.state.users.profile.is_dingtalk_bound ? this.$t('common.unbind') : this.$t('common.bind')
|
||||
label: this.$store.state.users.profile.dingtalk_id ? this.$t('common.unbind') : this.$t('common.bind')
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_DINGTALK,
|
||||
callbacks: {
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
title: this.$t('users.setFeiShu'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$store.state.users.profile.is_feishu_bound ? this.$t('common.unbind') : this.$t('common.bind')
|
||||
label: this.$store.state.users.profile.feishu_id ? this.$t('common.unbind') : this.$t('common.bind')
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_FEISHU,
|
||||
callbacks: {
|
||||
|
||||
@@ -68,7 +68,7 @@ export default {
|
||||
width: '105px',
|
||||
formatter: (row, col, cellValue) => {
|
||||
const display = row['risk_level'].label
|
||||
if (cellValue === 0) {
|
||||
if (cellValue?.value === 0) {
|
||||
return display
|
||||
} else {
|
||||
return <span class='text-danger'> {display} </span>
|
||||
|
||||
@@ -44,8 +44,10 @@ export default {
|
||||
formatter: function(row) {
|
||||
return toSafeLocalDateStr(row.timestamp * 1000)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
has: false
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
|
||||
@@ -116,8 +116,9 @@ export default {
|
||||
value: this.session.asset
|
||||
},
|
||||
{
|
||||
key: this.$t('sessions.systemUser'),
|
||||
value: this.session.system_user
|
||||
key: this.$t('assets.Account'),
|
||||
value: this.session.account
|
||||
|
||||
},
|
||||
{
|
||||
key: this.$t('sessions.protocol'),
|
||||
@@ -125,7 +126,7 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$t('sessions.loginFrom'),
|
||||
value: this.session.login_from
|
||||
value: this.session.login_from?.label || '-'
|
||||
},
|
||||
{
|
||||
key: this.$t('sessions.remoteAddr'),
|
||||
|
||||
@@ -138,7 +138,8 @@ export default {
|
||||
dateStart: dateFrom
|
||||
},
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
getUrlQuery: false,
|
||||
exclude: ['is_finished']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TabPage :submenu="submenu" :active-menu.sync="activeMenu">
|
||||
<TabPage :active-menu.sync="activeMenu" :submenu="submenu">
|
||||
<keep-alive>
|
||||
<component :is="activeMenu" />
|
||||
</keep-alive>
|
||||
@@ -38,22 +38,9 @@ export default {
|
||||
OAuth2
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
activeMenu: 'Basic',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('common.Basic'),
|
||||
name: 'Basic'
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.Ldap'),
|
||||
name: 'LDAP'
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.CAS'),
|
||||
name: 'CAS'
|
||||
},
|
||||
let extraBackends = []
|
||||
if (this.$store.getters.hasValidLicense) {
|
||||
extraBackends = [
|
||||
{
|
||||
title: this.$t('setting.OIDC'),
|
||||
name: 'OIDC'
|
||||
@@ -88,6 +75,25 @@ export default {
|
||||
}
|
||||
]
|
||||
}
|
||||
return {
|
||||
loading: true,
|
||||
activeMenu: 'Basic',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('common.Basic'),
|
||||
name: 'Basic'
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.Ldap'),
|
||||
name: 'LDAP'
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.CAS'),
|
||||
name: 'CAS'
|
||||
},
|
||||
...extraBackends
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
componentData() {
|
||||
@@ -96,8 +102,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,19 +1,99 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<Dialog
|
||||
:visible.sync="dialogSettings.visible"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:title="$tc('sessions.terminalUpdateStorage')"
|
||||
:show-confirm="false"
|
||||
>
|
||||
<GenericCreateUpdateForm v-bind="dialogSettings.iFormSetting" />
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { GenericCreateUpdateForm } from '@/layout/components'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ListTable
|
||||
ListTable,
|
||||
Dialog,
|
||||
GenericCreateUpdateForm
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
dialogSettings: {
|
||||
selectedRows: [],
|
||||
visible: false,
|
||||
iFormSetting: {
|
||||
url: '/api/v1/terminal/terminals/',
|
||||
getUrl: () => '/api/v1/terminal/terminals/',
|
||||
fields: [
|
||||
['', ['command_storage', 'replay_storage']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
command_storage: {
|
||||
label: this.$t('sessions.commandStorage'),
|
||||
component: Select2,
|
||||
el: {
|
||||
ajax: {
|
||||
url: `/api/v1/terminal/command-storages/`
|
||||
},
|
||||
multiple: false
|
||||
}
|
||||
},
|
||||
replay_storage: {
|
||||
label: this.$t('sessions.replayStorage'),
|
||||
component: Select2,
|
||||
el: {
|
||||
ajax: {
|
||||
url: `/api/v1/terminal/replay-storages/`
|
||||
},
|
||||
multiple: false
|
||||
}
|
||||
}
|
||||
},
|
||||
submitMethod: () => 'post',
|
||||
cleanFormValue: (value) => {
|
||||
const formValue = []
|
||||
let object = {}
|
||||
for (const row of this.dialogSettings.selectedRows) {
|
||||
object = Object.assign({}, value, { id: row.id })
|
||||
formValue.push(object)
|
||||
}
|
||||
return formValue
|
||||
},
|
||||
onSubmit: (validValues) => {
|
||||
const url = '/api/v1/terminal/terminals/'
|
||||
const msg = this.$t('common.updateSuccessMsg')
|
||||
validValues = Object.values(validValues)
|
||||
this.$axios.patch(url, validValues).then((res) => {
|
||||
this.$message.success(msg)
|
||||
this.dialogSettings.visible = false
|
||||
}).catch(error => {
|
||||
this.$emit('submitError', error)
|
||||
const response = error.response
|
||||
const data = response.data
|
||||
if (response.status === 400) {
|
||||
for (const key of Object.keys(data)) {
|
||||
let value = data[key]
|
||||
if (value instanceof Array) {
|
||||
value = value.join(';')
|
||||
}
|
||||
this.$refs.form.setFieldError(key, value)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
hasSaveContinue: false
|
||||
}
|
||||
},
|
||||
tableConfig: {
|
||||
url: '/api/v1/terminal/terminals/',
|
||||
permissions: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ListTable :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ListTable ref="list" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
@@ -18,6 +18,7 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
hasSelection: false,
|
||||
@@ -75,10 +76,20 @@ export default {
|
||||
{
|
||||
name: 'detail',
|
||||
title: this.$t('ops.output'),
|
||||
type: 'primary',
|
||||
callback: function({ row, tableData }) {
|
||||
openTaskPage(row.id)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'run',
|
||||
title: this.$t('ops.RunAgain'),
|
||||
type: 'primary',
|
||||
callback: function({ row, tableData }) {
|
||||
this.$axios.post(`/api/v1/ops/task-executions/?from=${row.id}`, {}).then(data => {
|
||||
vm.refreshTable()
|
||||
openTaskPage(data.task_id)
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -89,6 +100,11 @@ export default {
|
||||
hasLeftActions: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshTable() {
|
||||
this.$refs.list.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -34,8 +34,8 @@ export default {
|
||||
url: this.url,
|
||||
columnsExclude: ['process_map', 'rel_snapshot'],
|
||||
columnsShow: {
|
||||
min: ['title', 'type', 'state', 'actions'],
|
||||
default: ['title', 'type', 'state', 'status', 'actions']
|
||||
min: ['title', 'serial_num', 'type', 'state', 'date_created'],
|
||||
default: ['title', 'serial_num', 'type', 'state', 'status', 'date_created']
|
||||
},
|
||||
columnsMeta: {
|
||||
serial_num: {
|
||||
@@ -141,7 +141,7 @@ export default {
|
||||
valueLabel: this.$t('tickets.Pending')
|
||||
}
|
||||
},
|
||||
exclude: ['state', 'id', 'title'],
|
||||
exclude: ['state', 'id', 'title', 'type'],
|
||||
options: [
|
||||
{
|
||||
value: 'state',
|
||||
@@ -163,6 +163,29 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'type',
|
||||
label: this.$t('assets.Type'),
|
||||
type: 'choice',
|
||||
children: [
|
||||
{
|
||||
value: 'apply_asset',
|
||||
label: this.$t('tickets.ApplyAsset')
|
||||
},
|
||||
{
|
||||
value: 'login_confirm',
|
||||
label: this.$t('tickets.LoginConfirm')
|
||||
},
|
||||
{
|
||||
value: 'command_confirm',
|
||||
label: this.$t('tickets.CommandConfirm')
|
||||
},
|
||||
{
|
||||
value: 'login_asset_confirm',
|
||||
label: this.$t('tickets.LoginAssetConfirm')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'id',
|
||||
label: 'ID'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<GenericTicketDetail :object="object" :special-card-items="specialCardItems" />
|
||||
<GenericTicketDetail :object="object" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -28,23 +28,6 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
specialCardItems() {
|
||||
const { object } = this
|
||||
return object.type === 'login_confirm' ? [] : [
|
||||
{
|
||||
key: this.$t('acl.apply_login_asset'),
|
||||
value: object.rel_snapshot.apply_login_asset
|
||||
},
|
||||
{
|
||||
key: this.$t('acl.apply_login_system_user'),
|
||||
value: object.rel_snapshot.apply_login_system_user
|
||||
},
|
||||
{
|
||||
key: this.$t('acl.apply_login_user'),
|
||||
value: object.rel_snapshot.apply_login_user
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<el-form-item :label="$tc('tickets.Asset')">
|
||||
<Select2 v-model="requestForm.assets" v-bind="assetSelect2" style="width: 50% !important" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$tc('tickets.SystemUser')" :rules="isRequired">
|
||||
<el-form-item :label="$tc('perms.Account')" :rules="isRequired">
|
||||
<AccountFormatter v-model="requestForm.accounts" style="width: 50% !important" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$tc('common.DateStart')" required>
|
||||
@@ -72,8 +72,8 @@ export default {
|
||||
treeNodes,
|
||||
statusMap: this.object.status.value === 'open' ? STATUS_MAP['pending'] : STATUS_MAP[this.object.state.value],
|
||||
requestForm: {
|
||||
nodes: this.object.apply_nodes,
|
||||
assets: this.object.apply_assets,
|
||||
nodes: this.object.apply_nodes?.map(i => i.id),
|
||||
assets: this.object.apply_assets?.map(i => i.id),
|
||||
accounts: this.object.apply_accounts,
|
||||
actions: this.object.apply_actions,
|
||||
apply_date_expired: this.object.apply_date_expired,
|
||||
@@ -119,14 +119,14 @@ export default {
|
||||
return [
|
||||
{
|
||||
key: this.$tc('perms.Node'),
|
||||
value: object.apply_nodes.map(item => item.value).join(', ')
|
||||
value: object.apply_nodes.map(item => item.name).join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$tc('tickets.Asset'),
|
||||
value: object.apply_assets.map(item => item.name).join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$tc('assets.Accounts'),
|
||||
key: this.$tc('perms.Account'),
|
||||
value: object.apply_accounts.join(', ')
|
||||
},
|
||||
{
|
||||
@@ -146,7 +146,6 @@ export default {
|
||||
assignedCardItems() {
|
||||
const vm = this
|
||||
const { object } = this
|
||||
const rel_snapshot = object.rel_snapshot
|
||||
return [
|
||||
{
|
||||
key: this.$tc('tickets.PermissionName'),
|
||||
@@ -162,14 +161,14 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$tc('perms.Node'),
|
||||
value: rel_snapshot.apply_nodes.map(item => item.value).join(', ')
|
||||
value: object.apply_nodes.map(item => item.name).join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$tc('assets.Asset'),
|
||||
value: rel_snapshot.apply_assets.map(item => item.name).join(', ')
|
||||
value: object.apply_assets.map(item => item.name).join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$tc('perms.Accounts'),
|
||||
key: this.$tc('perms.Account'),
|
||||
value: (object.apply_accounts || []).join(', ')
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ i + 1 + ' ' + vm.$t('tickets.LevelApproval') }}</span>
|
||||
</div>
|
||||
<el-radio-group v-model="item.strategy" @change="onChange()">
|
||||
<el-radio-group v-model="item.strategy.value" @change="onChange()">
|
||||
<el-radio label="super_admin">{{ vm.$t('tickets.SuperAdmin') }}</el-radio>
|
||||
<el-radio label="org_admin">{{ vm.$t('tickets.OrgAdmin') }}</el-radio>
|
||||
<el-radio label="super_org_admin">{{ vm.$t('tickets.SuperOrgAdmin') }}</el-radio>
|
||||
<el-radio label="custom_user">{{ vm.$t('tickets.CustomUser') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<br>
|
||||
<Select2 v-show="item.strategy === 'custom_user'" v-model="item.assignees" v-bind="select2Option" @change="onChange()" />
|
||||
<Select2 v-show="item.strategy.value === 'custom_user'" v-model="item.assignees" v-bind="select2Option" @change="onChange()" />
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<span class="item-value">{{ session.asset }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('tickets.Account') }}:</span>
|
||||
<span class="item-label">{{ $t('assets.Account') }}:</span>
|
||||
<span class="item-value">{{ session.account }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<div style="height: 540px;">
|
||||
<div style="height: 660px;">
|
||||
<el-steps direction="vertical" :active="ticketSteps">
|
||||
<el-step
|
||||
:title="`${this.$t('tickets.OpenTicket')}:${object.title}`"
|
||||
@@ -19,11 +19,11 @@
|
||||
<div slot="description">
|
||||
<div class="processors">
|
||||
<div class="processors-content">
|
||||
<span v-for="assignee of item.assignees_display" :key="assignee" style="display: block">
|
||||
<span v-for="assignee of item.assignees_display.slice(0,4)" :key="assignee" style="display: block">
|
||||
{{ assignee }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button v-if="item.assignees.length > 5" type="text" @click="lookOver(item.assignees_display)">
|
||||
<el-button v-if="item.assignees.length > 4" type="text" @click="lookOver(item.assignees_display)">
|
||||
{{ $tc('tickets.CheckViewAcceptor') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
'view_audit': ['rbac.view_audit'],
|
||||
'view_workbench': ['rbac.view_workbench'],
|
||||
'view_setting': ['settings.view_setting'],
|
||||
'cloud_import': ['assets.view_asset'],
|
||||
'cloud_import': ['assets.view_asset', 'assets.view_platform'],
|
||||
'terminal_node': ['settings.change_terminal'],
|
||||
'rbac.orgrolebinding': ['rbac.view_orgrole', 'users.view_user'],
|
||||
'rbac.systemrolebinding': ['rbac.view_systemrole', 'users.view_user'],
|
||||
@@ -96,9 +96,11 @@ export default {
|
||||
],
|
||||
'acls.loginacl': ['users.view_user'],
|
||||
'acls.loginassetacl': ['users.view_user'],
|
||||
'assets.view_asset': ['assets.view_node'],
|
||||
'assets.view_asset': ['assets.view_node', 'assets.view_platform'],
|
||||
'assets.commandfilterrule': ['assets.view_commandfilter'],
|
||||
'assets.gateway': ['assets.view_domain'],
|
||||
'assets.add_gateway': ['assets.view_domain', 'assets.view_platform', 'assets.view_node'],
|
||||
'assets.change_gateway': ['assets.view_domain', 'assets.view_platform', 'assets.view_node'],
|
||||
'assets.add_asset': ['assets.view_platform'],
|
||||
'assets.change_asset': ['assets.view_platform'],
|
||||
'accounts.view_account': ['assets.view_node'],
|
||||
|
||||
Reference in New Issue
Block a user