v3.0.0-rc1 (#2400)
* feat: 资产选择优化 * perf: 修改用户列表显示字段 * perf: 修改 改密 收集账号名字 * perf: 添加AssetTreeTable组件;资产列表、资产选择、资产账号、资产授权使用AssetTreeTable组件 * perf: 修改资产列表 tab * perf: 修改路径 * perf: 修改 tree * perf: 修改 tab icon * fix: 修复资产列表点击创建按钮页面空白问题 * feat: 支持超时时间设置 * perf: 改密去除tab * perf: table column添加属性showFullContent: 折行显示全部内容 * perf: 添加 account push * perf: 修改 asset select padding * perf: 账号收集 * feat: terminal 页面增加padding * perf: account list source * perf: AssetTreeTable配置默认不显示右键菜单 * perf: 修改账号 * fix: 修复用户登录规则动作是审批时没有显示审批人选项 * refactor: 修改列表显示字段1 (#2309) * refactor: 修改列表显示字段(用户组列表) * refactor: 修改列表显示字段(角色列表) * perf: 修改列表显示字段(资产列表); 修改DataTable showOverflowTooltip 参数默认为 true; 优化 ArrayFormatter 组件, 支持传递 delimiter 参数; * perf: 修改列表显示字段(主机列表、网络设备、数据库、云服务、Web) * perf: 修改列表字段显示(云同步,同步实例任务列表,云账号列表) * perf: 修改列表字段显示(网域列表,网关列表) * perf: 修改列表字段显示(平台列表) * perf: 修改列表字段显示(标签列表) * perf: 修改 SyncInstanceTask 组件; 删除 GatheredUserList.vue 页面; Co-authored-by: Bai <baijiangjie@gmail.com> * feat: 修复一些页面上的问题 * fix: 修复资产详情协议修改端口不生效问题 * perf: 用户组详情不显示授权的资产 * fix: 修复账号收集详情key相同控制台报错问题 * fix: 修复显示密码组件样式布局问题 * feat: 增加 terminal 滚动控制和清屏按钮 * feat: 增加 terminal 滚动控制和清屏按钮 * perf: 调整terminal 按钮样式 * feat: 优化detail 字段 * perf: 资产批量更新去掉特权用户 * feat: 修改按钮样式 * fix: 修复节点选择错误的问题 * perf: 修改账号推送 * perf: 优化列表显示字段2 (#2322) * perf: 优化表格字段允许显示 ID 列 * perf: 修改列表字段显示(账号列表) * perf: 修改列表字段显示(账号模版) * perf: 修改列表字段显示(收集账号) * perf: 修改列表字段显示(账号改密) * perf: 修改列表字段显示(账号备份) * perf: 修改列表字段显示(授权列表) * perf: 修改列表字段显示(授权详情页面-用户/用户组,资产/节点,授权账号) * perf: 修改权限位 * perf: 修改账号地址 * feat: 防止xss攻击;可以自定义配置xss * perf: 修改账号 * perf: 修改列表字段显示(资产登录ACL, 命令过滤ACL) * perf: 优化xss过滤规则判断 * fix: 远程应用修改操作选项 * perf: 修改对应权限位 各种 * perf: 修改账号 * perf: 优化TreeTable组件布局 * perf: 远程应用不进入详情页 * perf: 修改列表字段显示(命令过滤, 命令组) * perf: 修改列表字段显示(用户详情, 授权的资产, 资产授权规则, 用户登录规则) * perf: 修改列表字段显示(任务列表,任务执行历史列表) * perf: 上传远程应用接口报错时给出提示 * perf: 修改 applet host * perf: 修改 accounts 添加 * feat: ops增加rbac * perf: 修改 assets 结构 * perf: 修改列表字段显示(会话列表, 在线会话, 历史会话) * perf: 修改列表字段显示(命令列表) * perf: 修改列表字段显示(文件传输) * perf: 修改列表字段显示(登录日志,操作日志,改密日志,作业日志) * perf: 优化PageHeading组件标题对齐 * perf: 修改列表字段显示(工作台,概览,我的资产,作业中心,作业管理,模版管理,执行历史) * perf: 修改翻译 * perf: 修改 tag search * fix: 修复账号改密更新、克隆页面空白问题 * perf: 修改列表字段显示(系统设置,终端列表,命令存储,录像存储,终端端点,终端规则) * perf: 修改列表字段显示(工单管理,我的申请,待我审批,流程设置) * perf: 修改列表字段显示(个人信息,API Key,连接令牌) * perf: 修改列表字段显示(远程应用,应用发布机) * perf: 修改 tableConfig 字段 excludes -> columnsExclude, extraColumns -> columnsExtra * perf: 修改 ConnectionToken 组件 * perf: 修改 AutoDataTable 组件 * perf: 修改 AssetAclCreateUpdate 组件 reviewers 字段 * feat: 修改字段 * perf: 优化table组件表头高度对齐 * fix: 修复资产列表-所有列表不能导入问题 * perf: 我的资产收藏资产添加提示 * feat: 修改列顺序 * perf: automation * merge: v3 * perf: 修改账号自创建 * perf: 快捷命令select组件筛选功能 * perf: select参数兼容 * feat: 修改选择用户样式 * perf: 修改账号自动化路由 * perf: 修改 form json editor * perf: account gather 翻译 * fix: 修复平台列表上传功能 * feat: 支持账号、资产在详情页查看活动(Activities)时间线 * fix: 修复 powershell 不能选择问题 * fix: 修复 powershell 不能选择问题 * perf: 优化云资产同步 * perf: account 翻译 * fix: 修复账号列表导出报错问题 * fix: 修复账号列表测试功能权限位 * fix: 修复角色列表权限位不准确问题 * feat: 增加快捷命令页面刷新后续接功能 * fix: 应用发布机的测试 * fix: 修复账号列表不能批量删除问题 * perf: 修改 form tree * perf: account 翻译 * fix: 修复资产树组件搜索后关闭搜索框无法显示搜索框问题 * perf: 移除发布机和应用的导入导出 * perf: 树组件关闭按钮增加刷新树功能 * fix: 修复资产树显示当前节点资产报错问题 * perf: 优化资产树组件关闭按钮执行事件顺序 * perf: upload applet and reload table list * perf: 优化显示发布机的远程应用 * perf: 修改 settings 组织 * perf: 系统设置-远程应用详情资产不能跳转 * perf: 修改 applet status * perf: 修改 applet status * perf: account * perf: account backup * perf: verify code ttl * perf: 修改菜单 * perf: 修改网关创建 * perf: asset ping api * perf: 再次优化 menu * perf: 优化菜单 * perf: menu 去掉一下 css * perf: 资产创建、更新标签支持自定义输入创建 * perf: 修改 gateway * perf: account change secret * fix: 修复资产详情标签不显示问题 * perf: 修改 accounts 路由 * perf: gateway test perm * fix: 修复账号备份-创建、更新、删除跳转到空白页面问题 * fix: 修复账号收集创建、删除后跳转到空白页面问题 * perf: 修改 choices formattor * fix: 修改资产详情页面字段 spec_info * fix: 修复资产详情-授权用户-查看授权字段显示不全问题 * fix: 修复工作台列表字段显示 * perf: gateways * feat: ops 修改一些交互细节 * feat: 删除模版执行的功能 * perf: 修改 endpoint 端口监听 * perf: gateway ui * perf: 优化字段显示 * feat: 支持资源用户在详情页查看活动(Activities)时间线 * perf: 细化权限 * fix: 修改审计日志列 * Merge remote-tracking branch 'origin/v3' into v3 # Conflicts: # src/views/assets/Domain/DomainDetail/GatewayList.vue * perf: 控制台仪表盘资产类型占比取消hover效果 * perf: gateway * fix: 修改工单详情 type.value 参数 * perf: gateway dialog * perf: 优化ProtoSelector组件逻辑 * fix: 修改 terminal 颜色,修复文字被背景色覆盖的问题 * feat: 增加检测命令长度 * fix: 修复账号收集-执行列表详情报错问题 * fix: 修复资产列表-数据库克隆没有协议问题 * perf: push_account_enabled * fix: 修复资产授权详情用户、资产不能删除问题 * feat: Redis/MongoDB 支持SSL * perf: push account * fix: 修复命令过滤-命令组克隆页面跳转不准确问题 * fix: 修改授权规则列表过滤字段 asset_name,node_name; * fix: 修复测试连接报错问题 * perf: account platform * perf: 调整平台详情展示字段 * perf: account push * fix: 修复detail 详情页面 404 * pref: 修改 assets 布局 * pref: 去掉页面滚动 * pref: 暂时修改 choice left align * perf: account template access_key * perf: 优化activity记录都保存至operatelog中 * perf: 优化页面布局 * perf: asset add account template * fix: 修复table搜索项重复问题 * perf: 账号收集详情 * pref: table - * perf: 创建资产-模版添加列表增加刷新按钮 * fix: 修复任务列表操作内容显示 * perf: 先去掉自动创建 * perf: change secret * perf: 优化账号列表资产树数量按照账号进行计算 * perf: account change secret * pref: 修改 select hover 效果 * pref: 去掉视图 icon * perf: executed amount * pref: 修改 set pre org,避免回到 system * perf: 创建资产-创建账号模版弹窗显示 * perf: account task 修改 * perf: account trigger * perf: user reset password * fix: 修复平台克隆失败问题 * fix: 修复资产授权批量更新报错问题 * perf: 优化账号列表资产树数量按照账号进行计算增加默认值 asset * fix: 修复用户详情-资产授权规则跳转页面报错问题 * perf: 资产树-类型树关闭搜索功能 * fix: 修复执行窗口有空白问题 * fix: 修复账号改密详情权限不准确问题 * fix: 文件传输列表不显示操作 Co-authored-by: Aaron3S <chenyang@fit2cloud.com> Co-authored-by: Bai <baijiangjie@gmail.com> Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> Co-authored-by: “huailei000” <2280131253@qq.com> Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Jiangjie.Bai <bugatti_it@163.com> Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com> Co-authored-by: Eric <xplzv@126.com> Co-authored-by: huailei <31801270+huailei000@users.noreply.github.com>
@ -71,6 +71,7 @@
|
||||
"vue-select": "^3.9.5",
|
||||
"vuejs-logger": "^1.5.4",
|
||||
"vuex": "3.1.0",
|
||||
"xss": "^1.0.14",
|
||||
"xterm": "^4.5.0",
|
||||
"xterm-addon-fit": "^0.3.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
|
@ -194,21 +194,15 @@ td .el-button.el-button--mini {
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
color: white;
|
||||
.option-group .el-select-dropdown__item.hover, .option-group .el-select-dropdown__item.selected {
|
||||
background-color: primary;
|
||||
font-weight: 400;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.hover, .el-select-dropdown__item:hover {
|
||||
.option-group:has(.hover) .el-select-dropdown__item.selected {
|
||||
background-color: light-2;
|
||||
color: white!important;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.hover {
|
||||
background-color: primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.is-disabled:hover{
|
||||
color:#c0c4cc;
|
||||
@ -220,7 +214,7 @@ td .el-button.el-button--mini {
|
||||
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover {
|
||||
color: white;
|
||||
background-color: light-2;
|
||||
background-color: light-4;
|
||||
}
|
||||
|
||||
.el-tag.el-tag--info {
|
||||
@ -409,11 +403,6 @@ td .el-button.el-button--mini {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.el-button--danger.is-plain.is-disabled {
|
||||
background: #ffffff!important;
|
||||
}
|
||||
|
||||
|
||||
.el-alert .el-alert__description {
|
||||
margin: 1px 0 0;
|
||||
}
|
||||
@ -483,3 +472,27 @@ td .el-button.el-button--mini {
|
||||
.el-step__description.is-finish {
|
||||
color: #676a6c;
|
||||
}
|
||||
|
||||
.el-alert {
|
||||
border: solid 1px #e7eaec;
|
||||
}
|
||||
|
||||
.el-alert.el-alert--success.is-light {
|
||||
border-color: var(--color-success-light);
|
||||
}
|
||||
|
||||
.el-alert.el-alert--primary.is-light {
|
||||
border-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
.el-alert.el-alert--info.is-light {
|
||||
border-color: var(--color-info-light);
|
||||
}
|
||||
|
||||
.el-alert.el-alert--warning.is-light {
|
||||
border-color: var(--color-warning-light);
|
||||
}
|
||||
|
||||
.el-alert.el-alert--error.is-light {
|
||||
border-color: var(--color-danger-light);
|
||||
}
|
||||
|
@ -1,12 +1,5 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getTaskDetail(id) {
|
||||
return request({
|
||||
url: `/api/v1/ops/tasks/${id}/`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getAdhocDetail(id) {
|
||||
return request({
|
||||
url: `/api/v1/ops/adhoc/${id}/`,
|
||||
@ -20,3 +13,26 @@ export function getHistoryExecutionDetail(id) {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getTaskDetail(id) {
|
||||
return request({
|
||||
url: `/api/v1/ops/job-execution/task-detail/${id}/`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getJob(id) {
|
||||
return request({
|
||||
url: `/api/v1/ops/jobs/${id}/`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadPlaybook(form) {
|
||||
return request({
|
||||
url: '/api/v1/ops/playbooks/',
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data: form
|
||||
})
|
||||
}
|
||||
|
@ -1,23 +1,30 @@
|
||||
<template>
|
||||
<AutoDataForm
|
||||
v-if="!loading"
|
||||
v-bind="$data"
|
||||
@submit="confirm"
|
||||
@afterRemoteMeta="afterGetRemoteMeta"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
import { UpdateToken } from '@/components/FormFields'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import AssetSelect from '@/components/AssetSelect'
|
||||
|
||||
export default {
|
||||
name: 'AccountCreateForm',
|
||||
components: {
|
||||
AutoDataForm
|
||||
},
|
||||
props: {
|
||||
asset: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
platform: {
|
||||
type: Object,
|
||||
required: true
|
||||
default: null
|
||||
},
|
||||
account: {
|
||||
type: Object,
|
||||
@ -28,20 +35,47 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
usernameChanged: false,
|
||||
url: '/api/v1/assets/accounts/',
|
||||
defaultPrivilegedAccounts: ['root', 'administrator'],
|
||||
iPlatform: {
|
||||
automation: {},
|
||||
protocols: [
|
||||
{
|
||||
name: 'ssh',
|
||||
secret_types: ['password', 'ssh_key', 'token', 'api_key']
|
||||
}
|
||||
]
|
||||
},
|
||||
url: '/api/v1/accounts/accounts/',
|
||||
form: this.account || {},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'username', 'privileged']],
|
||||
[this.$t('assets.Secret'), ['secret_type', 'secret', 'ssh_key', 'token', 'api_key', 'passphrase']],
|
||||
[this.$t('assets.Asset'), ['assets']],
|
||||
[this.$t('common.Basic'), ['name', 'username', 'privileged', 'su_from']],
|
||||
[this.$t('assets.Secret'), [
|
||||
'secret_type', 'secret', 'ssh_key', 'token',
|
||||
'api_key', 'passphrase'
|
||||
]],
|
||||
[this.$t('common.Other'), ['push_now', 'comment']]
|
||||
],
|
||||
defaultPrivilegedAccounts: ['root', 'administrator'],
|
||||
fieldsMeta: {
|
||||
assets: {
|
||||
component: AssetSelect,
|
||||
label: this.$t('assets.Asset'),
|
||||
el: {
|
||||
multiple: false
|
||||
},
|
||||
hidden: () => {
|
||||
return this.platform || this.asset
|
||||
}
|
||||
},
|
||||
name: {
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
if (!this.usernameChanged) {
|
||||
updateForm({ username: value })
|
||||
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,12 +86,29 @@ export default {
|
||||
this.usernameChanged = true
|
||||
},
|
||||
change: ([value], updateForm) => {
|
||||
if (this.defaultPrivilegedAccounts.indexOf(value.toLowerCase()) > -1) {
|
||||
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
su_from: {
|
||||
component: Select2,
|
||||
hidden: (formValue) => {
|
||||
return !this.asset?.id
|
||||
},
|
||||
el: {
|
||||
multiple: false,
|
||||
clearable: true,
|
||||
ajax: {
|
||||
url: `/api/v1/accounts/accounts/su-from-accounts/?asset=${this.asset?.id || ''}`,
|
||||
transformOption: (item) => {
|
||||
return { label: `${item.name}(${item.username})`, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
secret: {
|
||||
label: this.$t('assets.Password'),
|
||||
component: UpdateToken,
|
||||
@ -72,12 +123,12 @@ export default {
|
||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key'
|
||||
},
|
||||
passphrase: {
|
||||
label: 'Passphrase',
|
||||
label: this.$t('assets.Passphrase'),
|
||||
component: UpdateToken,
|
||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key'
|
||||
},
|
||||
token: {
|
||||
label: 'Token',
|
||||
label: this.$t('assets.Token'),
|
||||
el: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
@ -86,7 +137,7 @@ export default {
|
||||
},
|
||||
api_key: {
|
||||
id: 'api_key',
|
||||
label: this.$t('assets.SecretKey'),
|
||||
label: this.$t('assets.AccessKey'),
|
||||
el: {
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
@ -99,18 +150,53 @@ export default {
|
||||
},
|
||||
push_now: {
|
||||
hidden: () => {
|
||||
return !this.platform.automation?.['push_account_enabled']
|
||||
return !this.iPlatform.automation?.['push_account_enabled']
|
||||
}
|
||||
}
|
||||
},
|
||||
hasSaveContinue: false
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
await this.getPlatform()
|
||||
this.setSecretTypeOptions()
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
afterGetRemoteMeta(meta) {
|
||||
const choices = meta.secret_type.choices
|
||||
async getPlatform() {
|
||||
if (this.platform) {
|
||||
this.iPlatform = this.platform
|
||||
}
|
||||
if (!this.asset || !this.asset.platform) {
|
||||
return
|
||||
}
|
||||
const platformId = this.asset.platform.id
|
||||
this.iPlatform = await this.$axios.get(`/api/v1/assets/platforms/${platformId}/`)
|
||||
},
|
||||
setSecretTypeOptions() {
|
||||
const choices = [
|
||||
{
|
||||
label: this.$t('assets.Password'),
|
||||
value: 'password'
|
||||
},
|
||||
{
|
||||
label: this.$t('assets.SSHKey'),
|
||||
value: 'ssh_key'
|
||||
},
|
||||
{
|
||||
label: this.$t('assets.Token'),
|
||||
value: 'token'
|
||||
},
|
||||
{
|
||||
label: this.$t('assets.AccessKey'),
|
||||
value: 'api_key'
|
||||
}
|
||||
]
|
||||
const secretTypes = []
|
||||
this.platform.protocols?.forEach(p => {
|
||||
this.iPlatform.protocols?.forEach(p => {
|
||||
secretTypes.push(...p['secret_types'])
|
||||
})
|
||||
if (!this.form.secret_type) {
|
||||
|
@ -12,8 +12,8 @@
|
||||
>
|
||||
<AccountCreateUpdateForm
|
||||
v-if="!loading"
|
||||
:platform="platform"
|
||||
:account="account"
|
||||
:asset="asset"
|
||||
@add="addAccount"
|
||||
@edit="editAccount"
|
||||
/>
|
||||
@ -37,7 +37,7 @@ export default {
|
||||
},
|
||||
asset: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: null
|
||||
},
|
||||
account: {
|
||||
type: Object,
|
||||
@ -46,11 +46,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
loading: false,
|
||||
platform: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
@ -64,21 +63,28 @@ export default {
|
||||
return this.asset ? this.asset.protocol : []
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
await this.getPlatform()
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getPlatform() {
|
||||
const platformId = this.asset?.platform?.id || this.asset?.platform_id
|
||||
this.platform = await this.$axios.get(`/api/v1/assets/platforms/${platformId}/`)
|
||||
},
|
||||
addAccount(form) {
|
||||
const data = { asset: this.asset?.id || '', ...form }
|
||||
this.$axios.post(`/api/v1/assets/accounts/`, data).then(() => {
|
||||
const formValue = Object.assign({}, form)
|
||||
let assets = []
|
||||
if (this.asset) {
|
||||
assets = [this.asset.id]
|
||||
} else {
|
||||
assets = formValue.assets
|
||||
}
|
||||
delete formValue.assets
|
||||
if (assets.length === 0) {
|
||||
this.$message.error(this.$tc('assets.PleaseSelectAsset'))
|
||||
return
|
||||
}
|
||||
const data = []
|
||||
for (const asset of assets) {
|
||||
data.push({
|
||||
...formValue,
|
||||
asset
|
||||
})
|
||||
}
|
||||
this.$axios.post(`/api/v1/accounts/accounts/`, data).then(() => {
|
||||
this.iVisible = false
|
||||
this.$emit('add', true)
|
||||
this.$message.success(this.$tc('common.createSuccessMsg'))
|
||||
@ -88,7 +94,7 @@ export default {
|
||||
},
|
||||
editAccount(form) {
|
||||
const data = { ...form }
|
||||
this.$axios.patch(`/api/v1/assets/accounts/${this.account.id}/`, data).then(() => {
|
||||
this.$axios.patch(`/api/v1/accounts/accounts/${this.account.id}/`, data).then(() => {
|
||||
this.iVisible = false
|
||||
this.$emit('add', true)
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
|
@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ViewSecret v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" :url="secretUrl" />
|
||||
<ViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
:account="account"
|
||||
:url="secretUrl"
|
||||
/>
|
||||
<UpdateSecretInfo
|
||||
v-if="showUpdateSecretDialog"
|
||||
:visible.sync="showUpdateSecretDialog"
|
||||
@ -26,7 +31,6 @@ import UpdateSecretInfo from './UpdateSecretInfo'
|
||||
import AccountCreateUpdate from './AccountCreateUpdate'
|
||||
import { connectivityMeta } from './const'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { hasUUID } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'AccountListTable',
|
||||
@ -44,7 +48,7 @@ export default {
|
||||
exportUrl: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.url.replace('/assets/accounts/', '/assets/account-secrets/')
|
||||
return this.url.replace('/accounts/accounts/', '/accounts/account-secrets/')
|
||||
}
|
||||
},
|
||||
hasLeftActions: {
|
||||
@ -61,7 +65,11 @@ export default {
|
||||
},
|
||||
asset: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: null
|
||||
},
|
||||
hasColumnActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
@ -74,6 +82,10 @@ export default {
|
||||
hasImport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
columnsMeta: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -87,56 +99,64 @@ export default {
|
||||
secretUrl: '',
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
hasColumnActions: this.hasColumnActions,
|
||||
permissions: {
|
||||
app: 'assets',
|
||||
resource: 'account'
|
||||
},
|
||||
columns: [
|
||||
'name', 'asset', 'username', 'version', 'privileged',
|
||||
'secret_type', 'date_created', 'date_updated', 'actions'
|
||||
],
|
||||
columnsExclude: ['specific'],
|
||||
columnsShow: {
|
||||
min: ['name', 'username', 'actions'],
|
||||
default: [
|
||||
'name', 'username', 'version', 'privileged', 'actions', 'secret_type', 'actions'
|
||||
'name', 'username', 'asset', 'privileged',
|
||||
'secret_type', 'source', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
showOverflowTooltip: true,
|
||||
formatter: function(row) {
|
||||
const to = {
|
||||
name: 'AssetAccountDetail',
|
||||
params: { id: row.id }
|
||||
}
|
||||
if (vm.$hasPerm('assets.view_account')) {
|
||||
return <router-link to={ to } >{ row.name }</router-link>
|
||||
if (vm.$hasPerm('accounts.view_account')) {
|
||||
return <router-link to={to}>{row.name}</router-link>
|
||||
} else {
|
||||
return <span>{ row.name }</span>
|
||||
return <span>{row.name}</span>
|
||||
}
|
||||
}
|
||||
},
|
||||
asset: {
|
||||
label: this.$t('assets.Asset'),
|
||||
showOverflowTooltip: true,
|
||||
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>
|
||||
return <router-link to={to}>{row.asset.name}</router-link>
|
||||
} else {
|
||||
return <span>{ row.asset.name }</span>
|
||||
return <span>{row.asset.name}</span>
|
||||
}
|
||||
}
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
version: {
|
||||
width: '70px'
|
||||
},
|
||||
secret_type: {
|
||||
width: '100px'
|
||||
},
|
||||
source: {
|
||||
formatter: function(row) {
|
||||
return row.source.label
|
||||
}
|
||||
},
|
||||
has_secret: {
|
||||
width: '100px',
|
||||
formatterArgs: {
|
||||
showFalse: false
|
||||
}
|
||||
},
|
||||
privileged: {
|
||||
label: this.$t('assets.Privileged'),
|
||||
width: '120px',
|
||||
@ -157,10 +177,10 @@ export default {
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
can: this.$hasPerm('assets.view_accountsecret'),
|
||||
can: this.$hasPerm('accounts.view_accountsecret'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
vm.secretUrl = `/api/v1/assets/account-secrets/${row.id}/`
|
||||
vm.secretUrl = `/api/v1/accounts/account-secrets/${row.id}/`
|
||||
vm.account = row
|
||||
vm.showViewSecretDialog = false
|
||||
setTimeout(() => {
|
||||
@ -171,10 +191,10 @@ export default {
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
can: this.$hasPerm('assets.delete_account'),
|
||||
can: this.$hasPerm('accounts.delete_account'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$axios.delete(`/api/v1/assets/accounts/${row.id}/`).then(() => {
|
||||
this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
@ -186,7 +206,7 @@ export default {
|
||||
can: this.$hasPerm('assets.test_account'),
|
||||
callback: ({ row }) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/${row.id}/verify/`,
|
||||
`/api/v1/accounts/accounts/${row.id}/verify/`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
@ -196,10 +216,10 @@ export default {
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: this.$hasPerm('assets.change_account') && !this.$store.getters.currentOrgIsRoot,
|
||||
can: this.$hasPerm('accounts.change_account') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
vm.account = row
|
||||
vm.$set(this.iAsset, 'platform_id', row.asset.platform_id)
|
||||
vm.iAsset = row.asset
|
||||
vm.showAddDialog = false
|
||||
setTimeout(() => {
|
||||
vm.showAddDialog = true
|
||||
@ -208,7 +228,8 @@ export default {
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
...this.columnsMeta
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
@ -216,7 +237,7 @@ export default {
|
||||
hasMoreActions: true,
|
||||
hasCreate: false,
|
||||
hasImport: true,
|
||||
hasExport: this.hasExport && this.$hasPerm('assets.view_accountsecret'),
|
||||
hasExport: this.hasExport && this.$hasPerm('accounts.view_accountsecret'),
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
@ -226,14 +247,25 @@ export default {
|
||||
name: 'add',
|
||||
title: this.$t('common.Add'),
|
||||
type: 'primary',
|
||||
can: vm.$hasPerm('assets.add_account'),
|
||||
callback: () => {
|
||||
this.account = null
|
||||
this.showAddDialog = true
|
||||
can: () => {
|
||||
return vm.$hasPerm('accounts.add_account')
|
||||
},
|
||||
callback: async() => {
|
||||
await this.getAssetDetail()
|
||||
setTimeout(() => {
|
||||
vm.showAddDialog = true
|
||||
})
|
||||
}
|
||||
}
|
||||
// {
|
||||
// name: 'autocreate',
|
||||
// title: this.$t('accounts.AutoCreate'),
|
||||
// type: 'default'
|
||||
// }
|
||||
],
|
||||
canBulkDelete: vm.$hasPerm('accounts.delete_account'),
|
||||
searchConfig: {
|
||||
getUrlQuery: false,
|
||||
exclude: ['asset']
|
||||
},
|
||||
hasSearch: true
|
||||
@ -243,13 +275,7 @@ export default {
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
this.$set(this.headerActions.exportOptions, 'url', iNew.replace('/accounts/', '/account-secrets/'))
|
||||
},
|
||||
'$route.query.asset': {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.hasAccountPermission()
|
||||
}
|
||||
this.$set(this.headerActions.exportOptions, 'url', iNew.replace(/(.*)accounts/, '$1account-secrets'))
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -272,14 +298,8 @@ export default {
|
||||
},
|
||||
async getAssetDetail() {
|
||||
const { query: { asset }} = this.$route
|
||||
this.iAsset = await this.$axios.get(`/api/v1/assets/assets/${asset}/`)
|
||||
},
|
||||
hasAccountPermission() {
|
||||
const { path, query: { asset }} = this.$route
|
||||
if (!hasUUID(path)) {
|
||||
if (asset) this.getAssetDetail()
|
||||
const hasPerm = this.$hasPerm('assets.add_account') && !!asset
|
||||
this.$set(this.headerActions.extraActions[0], 'can', hasPerm)
|
||||
if (asset) {
|
||||
this.iAsset = await this.$axios.get(`/api/v1/assets/assets/${asset}/`)
|
||||
}
|
||||
},
|
||||
refresh() {
|
@ -27,7 +27,7 @@ export default {
|
||||
visible: false,
|
||||
width: '60%',
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/account-secrets/${this.account.id}/histories/`,
|
||||
url: `/api/v1/accounts/account-secrets/${this.account.id}/histories/`,
|
||||
columns: ['secret', 'secret_type', 'version'],
|
||||
columnsMeta: {
|
||||
secret: {
|
||||
|
@ -70,7 +70,7 @@ export default {
|
||||
if (this.secretInfo.passphrase) data.passphrase = this.secretInfo.passphrase
|
||||
}
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/accounts/${this.account.id}/`,
|
||||
`/api/v1/accounts/accounts/${this.account.id}/`,
|
||||
data,
|
||||
{ disableFlashErrorMsg: true }
|
||||
).then(res => {
|
||||
|
@ -7,12 +7,12 @@
|
||||
/>
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
:width="'50'"
|
||||
:visible.sync="showSecret"
|
||||
v-bind="$attrs"
|
||||
@confirm="showSecret = false"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-form class="password-form" label-position="right" label-width="100px" :model="secretInfo">
|
||||
@ -101,7 +101,7 @@ export default {
|
||||
getAuthInfo() {
|
||||
this.$axios.get(this.url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.secretInfo = resp
|
||||
this.sshKeyFingerprint = resp.specific.ssh_key_fingerprint
|
||||
this.sshKeyFingerprint = resp['specific']['ssh_key_fingerprint']
|
||||
this.showSecret = true
|
||||
})
|
||||
},
|
||||
@ -116,7 +116,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item-textarea > > > .el-textarea__inner {
|
||||
.item-textarea >>> .el-textarea__inner {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
@ -125,12 +125,21 @@ export default {
|
||||
padding: 5px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: #F5F7FA;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
> > > .el-form-item__label {
|
||||
>>> .el-form-item__label {
|
||||
padding-right: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
>>> .el-form-item__content {
|
||||
line-height: 30px;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,12 @@ export const connectivityMeta = {
|
||||
faChoices: {
|
||||
ok: 'fa-check-circle',
|
||||
failed: 'fa-times-circle',
|
||||
unknown: ' '
|
||||
unknown: 'fa fa-question-circle'
|
||||
},
|
||||
classChoices: {
|
||||
ok: 'text-primary',
|
||||
failed: 'text-danger',
|
||||
unknown: ''
|
||||
unknown: 'fa fa-question-circle'
|
||||
},
|
||||
textChoices: {
|
||||
ok: 'Avail',
|
||||
|
@ -18,10 +18,9 @@
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<TreeTable
|
||||
<AssetTreeTAble
|
||||
ref="ListPage"
|
||||
class="tree-table"
|
||||
:tree-setting="treeSetting"
|
||||
:table-config="tableConfig"
|
||||
:header-actions="headerActions"
|
||||
/>
|
||||
@ -30,14 +29,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
import AssetTreeTAble from '@/components/AssetTreeTable'
|
||||
import { DialogDetailFormatter } from '@/components/TableFormatters'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
componentName: 'AssetSelect',
|
||||
components: { TreeTable, Select2, Dialog },
|
||||
components: { AssetTreeTAble, Select2, Dialog },
|
||||
props: {
|
||||
baseUrl: {
|
||||
type: String,
|
||||
@ -88,15 +87,6 @@ export default {
|
||||
initialValue: _.cloneDeep(iValue),
|
||||
rowSelected: [],
|
||||
initSelection: null,
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
url: this.baseUrl,
|
||||
nodeUrl: this.baseNodeUrl,
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
treeUrl: `${this.baseNodeUrl}/children/tree/?assets=0`
|
||||
},
|
||||
select2Config: select2Config,
|
||||
dialogSelect2Config: select2Config,
|
||||
tableConfig: {
|
||||
@ -108,7 +98,6 @@ export default {
|
||||
prop: 'name',
|
||||
label: this.$t('assets.Name'),
|
||||
sortable: true,
|
||||
showOverflowTooltip: true,
|
||||
formatter: DialogDetailFormatter,
|
||||
formatterArgs: {
|
||||
getDialogTitle: function({ col, row }) { this.$t('assets.AssetDetail') }.bind(this),
|
||||
@ -187,7 +176,10 @@ export default {
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasRightActions: false
|
||||
hasRightActions: false,
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -211,7 +203,7 @@ export default {
|
||||
// 如果select2的options中没有,那么可能无法显示正常的值
|
||||
if (selectOptionsHas === undefined) {
|
||||
const option = {
|
||||
label: `${row.hostname}(${row.ip})`,
|
||||
label: `${row.name}(${row.address})`,
|
||||
value: row.id
|
||||
}
|
||||
options.push(option)
|
||||
@ -249,11 +241,22 @@ export default {
|
||||
}
|
||||
|
||||
.el-dialog__wrapper ::v-deep .el-dialog__body {
|
||||
padding: 0;
|
||||
|
||||
padding: 0 0 0 3px;
|
||||
.tree-table {
|
||||
.search {
|
||||
.el-input__inner {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
.el-cascader {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
}
|
||||
.left {
|
||||
padding: 5px;
|
||||
|
||||
.treebox {
|
||||
height: 70vh;
|
||||
}
|
||||
}
|
||||
|
||||
.mini {
|
||||
|
148
src/components/AssetTreeTable/index.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<TreeTable
|
||||
ref="TreeList"
|
||||
component="TabTree"
|
||||
:table-config="tableConfig"
|
||||
:active-menu.sync="treeTabConfig.activeMenu"
|
||||
:tree-tab-config="treeTabConfig"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template #table>
|
||||
<slot name="table" />
|
||||
</template>
|
||||
<div slot="rMenu" slot-scope="{data}">
|
||||
<slot name="rMenu" :data="data" />
|
||||
</div>
|
||||
</TreeTable>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TreeTable from '../TreeTable'
|
||||
import { setRouterQuery, setUrlParam } from '@/utils/common'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TreeTable
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/assets/'
|
||||
},
|
||||
nodeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/'
|
||||
},
|
||||
treeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/children/tree/'
|
||||
},
|
||||
treeSetting: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
showAssets: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const showAssets = this.treeSetting?.showAssets || this.showAssets
|
||||
return {
|
||||
treeTabConfig: {
|
||||
activeMenu: 'CustomTree',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('assets.AssetTree'),
|
||||
name: 'CustomTree',
|
||||
treeSetting: {
|
||||
showAssets,
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showCreate: true,
|
||||
showUpdate: true,
|
||||
showDelete: true,
|
||||
hasRightMenu: true,
|
||||
showSearch: true,
|
||||
url: this.url,
|
||||
nodeUrl: this.nodeUrl,
|
||||
treeUrl: `${this.treeUrl}?assets=${showAssets ? '1' : '0'}`,
|
||||
callback: {
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||
},
|
||||
...this.treeSetting
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.BuiltinTree'),
|
||||
name: 'BuiltinTree',
|
||||
treeSetting: {
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
showSearch: false,
|
||||
customTreeHeaderName: this.$t('assets.BuiltinTree'),
|
||||
url: '/api/v1/assets/nodes/category/tree/',
|
||||
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
||||
treeUrl: `/api/v1/assets/nodes/category/tree/?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
||||
callback: {
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.decorateRMenu()
|
||||
const treeSetting = this.treeTabConfig.submenu[0].treeSetting
|
||||
treeSetting.hasRightMenu = !this.currentOrgIsRoot
|
||||
treeSetting.showCreate = this.$hasPerm('assets.add_node')
|
||||
treeSetting.showUpdate = this.$hasPerm('assets.change_node')
|
||||
treeSetting.showDelete = this.$hasPerm('assets.delete_node')
|
||||
},
|
||||
methods: {
|
||||
decorateRMenu() {
|
||||
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
||||
if (show_current_asset === '1') {
|
||||
$('#m_show_asset_all_children_node').css('color', '#606266')
|
||||
$('#m_show_asset_only_current_node').css('color', 'green')
|
||||
} else {
|
||||
$('#m_show_asset_all_children_node').css('color', 'green')
|
||||
$('#m_show_asset_only_current_node').css('color', '#606266')
|
||||
}
|
||||
},
|
||||
getAssetsUrl(treeNode) {
|
||||
let url = this.treeSetting?.url || this.url
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const nodeId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'node_id', nodeId)
|
||||
url = setUrlParam(url, 'asset', '')
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
const assetId = treeNode.meta.data?.id || treeNode.id
|
||||
url = setUrlParam(url, 'node', '')
|
||||
url = setUrlParam(url, 'asset_id', assetId)
|
||||
} else if (treeNode.meta.type === 'category') {
|
||||
url = setUrlParam(url, 'category', treeNode.meta.category)
|
||||
} else if (treeNode.meta.type === 'type') {
|
||||
url = setUrlParam(url, 'category', treeNode.meta.category)
|
||||
url = setUrlParam(url, 'type', treeNode.meta._type)
|
||||
} else if (treeNode.meta.type === 'platform') {
|
||||
url = setUrlParam(url, 'platform', treeNode.id)
|
||||
}
|
||||
this.$set(this.tableConfig, 'url', url)
|
||||
setRouterQuery(this, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
||||
</style>
|
@ -16,7 +16,7 @@
|
||||
v-if="!groupHidden(group, i)"
|
||||
:group="group"
|
||||
:index="i"
|
||||
:line="i !== 0"
|
||||
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
||||
/>
|
||||
</span>
|
||||
</DataForm>
|
||||
@ -73,6 +73,9 @@ export default {
|
||||
// 初始值是 choice 对象
|
||||
if (value && typeof value === 'object' && value.label && value.value !== undefined) {
|
||||
iForm[key] = value.value
|
||||
} else if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' &&
|
||||
value[0].label && value[0].value !== undefined) {
|
||||
iForm[key] = value.map(item => item.value)
|
||||
} else {
|
||||
iForm[key] = value
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import NestedField from '@/components/AutoDataForm/components/NestedField'
|
||||
import Switcher from '@/components/FormFields/Switcher'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import BasicTree from '@/components/FormFields/BasicTree'
|
||||
import JsonEditor from '@/components/FormFields/JsonEditor'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
|
||||
export class FormFieldGenerator {
|
||||
@ -17,6 +18,7 @@ export class FormFieldGenerator {
|
||||
switch (type) {
|
||||
case 'labeled_choice':
|
||||
case 'choice':
|
||||
// Value 处理事在 AutoDataForm 中处理的
|
||||
if (!fieldRemoteMeta['read_only']) {
|
||||
field.options = fieldRemoteMeta.choices
|
||||
}
|
||||
@ -24,7 +26,6 @@ export class FormFieldGenerator {
|
||||
break
|
||||
case 'multiple choice':
|
||||
field.options = fieldRemoteMeta.choices
|
||||
console.log('multiple choice: ', field.options)
|
||||
type = 'checkbox-group'
|
||||
break
|
||||
case 'tree':
|
||||
@ -37,6 +38,10 @@ export class FormFieldGenerator {
|
||||
type: 'datetime'
|
||||
}
|
||||
break
|
||||
case 'json':
|
||||
type = 'json-editor'
|
||||
field.component = JsonEditor
|
||||
break
|
||||
case 'field':
|
||||
type = ''
|
||||
field.component = Select2
|
||||
@ -81,7 +86,6 @@ export class FormFieldGenerator {
|
||||
}
|
||||
// 上面重写了 type
|
||||
if (type === 'radio-group') {
|
||||
console.log('Field: ', field)
|
||||
if (field.options.length > 4) {
|
||||
type = 'select'
|
||||
field.el.filterable = true
|
||||
|
@ -33,7 +33,8 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
iOption() {
|
||||
return this.options.concat(this.internalOptions)
|
||||
const options = this.options.concat(this.internalOptions)
|
||||
return _.uniqWith(options, _.isEqual)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -5,7 +5,7 @@
|
||||
:visible.sync="showColumnSettingPopover"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
width="35%"
|
||||
width="50%"
|
||||
top="10%"
|
||||
@confirm="handleColumnConfirm()"
|
||||
>
|
||||
@ -24,23 +24,19 @@
|
||||
>
|
||||
<el-checkbox
|
||||
:label="item.prop"
|
||||
:disabled="
|
||||
item.prop==='id' ||
|
||||
item.prop==='actions' ||
|
||||
minColumns.indexOf(item.prop)!==-1
|
||||
"
|
||||
:disabled="item.prop==='actions' || minColumns.indexOf(item.prop)!==-1"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-checkbox-group>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index'
|
||||
|
||||
export default {
|
||||
name: 'ColumnSettingPopover',
|
||||
components: {
|
||||
|
@ -11,8 +11,8 @@
|
||||
/>
|
||||
<ColumnSettingPopover
|
||||
:current-columns="popoverColumns.currentCols"
|
||||
:total-columns-list="popoverColumns.totalColumnsList"
|
||||
:min-columns="popoverColumns.minCols"
|
||||
:total-columns-list="popoverColumns.totalColumnsList"
|
||||
:url="config.url"
|
||||
@columnsUpdate="handlePopoverColumnsChange"
|
||||
/>
|
||||
@ -22,16 +22,13 @@
|
||||
<script type="text/jsx">
|
||||
import DataTable from '../DataTable'
|
||||
import {
|
||||
DateFormatter,
|
||||
DetailFormatter,
|
||||
DisplayFormatter,
|
||||
ActionsFormatter,
|
||||
ChoicesFormatter,
|
||||
ActionsFormatter, ArrayFormatter, ChoicesFormatter, DateFormatter, DetailFormatter, DisplayFormatter,
|
||||
ObjectRelatedFormatter
|
||||
} from '@/components/TableFormatters'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import ColumnSettingPopover from './components/ColumnSettingPopover'
|
||||
import { newURL } from '@/utils/common'
|
||||
import ColumnSettingPopover from './components/ColumnSettingPopover'
|
||||
|
||||
export default {
|
||||
name: 'AutoDataTable',
|
||||
components: {
|
||||
@ -64,13 +61,12 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
config: {
|
||||
handler(iNew) {
|
||||
handler: function(iNew, iOld) {
|
||||
this.optionUrlMetaAndGenCols()
|
||||
this.$log.debug('AutoDataTable Config change found')
|
||||
this.$log.debug('AutoDataTable Config change found: ')
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
@ -80,7 +76,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async optionUrlMetaAndGenCols() {
|
||||
if (this.config.url === '') { return }
|
||||
if (this.config.url === '') {
|
||||
return
|
||||
}
|
||||
const url = (this.config.url.indexOf('?') === -1) ? `${this.config.url}?draw=1&display=1` : `${this.config.url}&draw=1&display=1`
|
||||
this.$store.dispatch('common/getUrlMeta', { url: url }).then(data => {
|
||||
const method = this.method.toUpperCase()
|
||||
@ -124,7 +122,6 @@ export default {
|
||||
false: i18n.t('common.Invalid')
|
||||
}
|
||||
}
|
||||
col.align = 'center'
|
||||
col.width = '80px'
|
||||
break
|
||||
case 'is_active':
|
||||
@ -135,7 +132,6 @@ export default {
|
||||
false: i18n.t('common.Inactive')
|
||||
}
|
||||
}
|
||||
col.align = 'center'
|
||||
col.width = '80px'
|
||||
break
|
||||
case 'datetime':
|
||||
@ -159,7 +155,6 @@ export default {
|
||||
break
|
||||
case 'boolean':
|
||||
col.formatter = ChoicesFormatter
|
||||
col.align = 'center'
|
||||
col.width = '80px'
|
||||
break
|
||||
case 'datetime':
|
||||
@ -172,6 +167,10 @@ export default {
|
||||
case 'm2m_related_field':
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
break
|
||||
case 'list':
|
||||
col.formatter = ArrayFormatter
|
||||
break
|
||||
case 'json':
|
||||
case 'field':
|
||||
if (meta.child && meta.child.type === 'nested object') {
|
||||
col.formatter = ObjectRelatedFormatter
|
||||
@ -190,9 +189,9 @@ export default {
|
||||
return (
|
||||
<span>{column.label}
|
||||
<el-tooltip placement='bottom' effect='light' popperClass='help-tips'>
|
||||
<div slot='content' domPropsInnerHTML={helpTips} />
|
||||
<div slot='content' domPropsInnerHTML={helpTips}/>
|
||||
<el-button style='padding: 0'>
|
||||
<i class='fa fa-info-circle' />
|
||||
<i class='fa fa-info-circle'/>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
@ -234,7 +233,12 @@ export default {
|
||||
setDefaultFormatterIfNeed(col) {
|
||||
if (!col.formatter) {
|
||||
col.formatter = (row, column, cellValue) => {
|
||||
return [undefined, null, ''].indexOf(cellValue) > -1 ? '-' : cellValue
|
||||
const value = cellValue || '-'
|
||||
let padding = '0'
|
||||
if (value === '-') {
|
||||
padding = '6px'
|
||||
}
|
||||
return <span style={{ marginLeft: padding }}>{value}</span>
|
||||
}
|
||||
}
|
||||
return col
|
||||
@ -242,7 +246,7 @@ export default {
|
||||
generateColumn(name) {
|
||||
const colMeta = this.meta[name] || {}
|
||||
const customMeta = this.config.columnsMeta ? this.config.columnsMeta[name] : {}
|
||||
let col = { prop: name, label: colMeta.label }
|
||||
let col = { prop: name, label: colMeta.label, showOverflowTooltip: true }
|
||||
|
||||
col = this.generateColumnByName(name, col)
|
||||
col = this.generateColumnByType(colMeta.type, col, colMeta)
|
||||
@ -255,7 +259,20 @@ export default {
|
||||
generateTotalColumns() {
|
||||
const config = _.cloneDeep(this.config)
|
||||
let columns = []
|
||||
const configColumns = config?.columns || []
|
||||
const allColumns = Object.entries(this.meta)
|
||||
.filter(([name, meta]) => !meta['write_only'])
|
||||
.map(([name, meta]) => name)
|
||||
.concat(config.columnsExtra || [])
|
||||
let configColumns = config.columns || allColumns
|
||||
const columnsExclude = config.columnsExclude || []
|
||||
if (columnsExclude.length > 0) {
|
||||
configColumns = configColumns.filter(item => !columnsExclude.includes(item))
|
||||
}
|
||||
// 解决后端 API 返回字段中包含 actions 的问题;
|
||||
const hasColumnActions = config.hasColumnActions !== undefined ? config.hasColumnActions : true
|
||||
if (hasColumnActions && !configColumns.includes('actions')) {
|
||||
configColumns.push('actions')
|
||||
}
|
||||
if (configColumns.length > 0) {
|
||||
for (let col of configColumns) {
|
||||
if (typeof col === 'object') {
|
||||
@ -266,6 +283,9 @@ export default {
|
||||
}
|
||||
}
|
||||
columns = columns.filter(item => {
|
||||
if (item?.showFullContent) {
|
||||
item.className = 'show-full-content'
|
||||
}
|
||||
let has = item.has
|
||||
if (has === undefined) {
|
||||
has = true
|
||||
@ -274,8 +294,6 @@ export default {
|
||||
}
|
||||
return has
|
||||
})
|
||||
} else {
|
||||
columns = Object.keys(this.meta).map(key => this.generateColumn(key))
|
||||
}
|
||||
// 第一次初始化时记录 totalColumns
|
||||
this.totalColumns = columns
|
||||
|
@ -152,7 +152,8 @@ export default {
|
||||
})
|
||||
},
|
||||
onRename: function(event, treeId, treeNode, isCancel) {
|
||||
const url = `${this.treeSetting.nodeUrl}${this.currentNodeId}/`
|
||||
const currentNodeId = this.currentNodeId || treeNode.meta.data?.id || ''
|
||||
const url = `${this.treeSetting.nodeUrl}${currentNodeId}/`
|
||||
if (isCancel) {
|
||||
return
|
||||
}
|
||||
|
@ -45,7 +45,10 @@
|
||||
>
|
||||
<el-tooltip :disabled="!action.tip" :content="action.tip" placement="top">
|
||||
<span>
|
||||
<svg-icon v-if="action.fa" :icon-class="action.fa" style="font-size: 14px;" />
|
||||
<span v-if="action.fa" style="vertical-align: initial;">
|
||||
<i v-if="action.fa.startsWith('fa-')" :class="'fa ' + action.fa" />
|
||||
<svg-icon v-else :icon-class="action.fa" style="font-size: 14px;" />
|
||||
</span>
|
||||
{{ action.title }}
|
||||
</span>
|
||||
</el-tooltip>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<ElDatableTable
|
||||
ref="table"
|
||||
class="el-table"
|
||||
class="el-data-table"
|
||||
v-bind="tableConfig"
|
||||
@update="onUpdate"
|
||||
v-on="iListeners"
|
||||
@ -21,7 +21,8 @@ export default {
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -90,42 +91,37 @@ export default {
|
||||
}
|
||||
return query
|
||||
},
|
||||
theRowDefaultIsSelected: (row) => { return false }
|
||||
theRowDefaultIsSelected: (row) => {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iListeners() {
|
||||
const defaultListeners = {}
|
||||
return Object.assign(defaultListeners, this.$listeners, this.tableConfig?.listeners)
|
||||
},
|
||||
dataTable() {
|
||||
return this.$refs.table
|
||||
},
|
||||
tableConfig() {
|
||||
const tableDefaultConfig = this.defaultConfig
|
||||
tableDefaultConfig.paginationSize = _.get(this.globalTableConfig, 'paginationSize', 15)
|
||||
tableDefaultConfig.paginationSize = 15
|
||||
let tableAttrs = tableDefaultConfig.tableAttrs
|
||||
if (this.config.tableAttrs) {
|
||||
tableAttrs = Object.assign(tableAttrs, this.config.tableAttrs)
|
||||
}
|
||||
const config = Object.assign(tableDefaultConfig, this.config)
|
||||
config.tableAttrs = tableAttrs
|
||||
this.$log.debug('elTableConfig', config)
|
||||
return config
|
||||
},
|
||||
iListeners() {
|
||||
const defaultListeners = {
|
||||
}
|
||||
return Object.assign(defaultListeners, this.$listeners, this.tableConfig.listeners)
|
||||
},
|
||||
dataTable() {
|
||||
return this.$refs.table
|
||||
},
|
||||
...mapGetters({
|
||||
'globalTableConfig': 'tableConfig'
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
config: {
|
||||
handler() {
|
||||
// this.getList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
getList() {
|
||||
this.$refs.table.clearSelection()
|
||||
@ -170,43 +166,55 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-table ::v-deep .el-table__row {
|
||||
&.selected-row {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
&> td {
|
||||
line-height: 1.5;
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
vertical-align: super;
|
||||
}
|
||||
}
|
||||
.el-table ::v-deep .el-table__row > td> div > span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.el-table ::v-deep .el-table__header > thead > tr > th {
|
||||
padding: 6px 0;
|
||||
background-color: #F5F5F6;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.table{
|
||||
<style lang="scss" scoped>
|
||||
.el-data-table > > > .el-table {
|
||||
.table {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
// .el-button--text{
|
||||
// color: #409EFF;
|
||||
// }
|
||||
.el-table__row {
|
||||
&.selected-row {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
& > td {
|
||||
line-height: 1.5;
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
& > div > span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__header > thead > tr > th {
|
||||
padding: 6px 0;
|
||||
background-color: #ffffff;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
|
||||
.cell {
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-data-table >>> .el-table .el-table__header > thead > tr .is-sortable {
|
||||
padding: 5px 0;
|
||||
.cell {
|
||||
padding-top: 3px!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -10,8 +10,10 @@
|
||||
</span>
|
||||
<span class="tree-banner-icon-zone">
|
||||
<a id="searchIcon" class="tree-search special">
|
||||
<i
|
||||
class="fa fa-search tree-banner-icon"
|
||||
<svg-icon
|
||||
:icon-class="'search'"
|
||||
class="tree-banner-icon"
|
||||
style="font-size: 14px;"
|
||||
@click.stop="treeSearch"
|
||||
/>
|
||||
<input
|
||||
@ -22,11 +24,9 @@
|
||||
class="tree-input"
|
||||
>
|
||||
</a>
|
||||
<i
|
||||
class="fa fa-refresh tree-banner-icon"
|
||||
style="margin-right: 2px;"
|
||||
@click.stop="refresh"
|
||||
/>
|
||||
<span v-if="treeSetting.showRefresh" class="icon-refresh" @click="refresh">
|
||||
<svg-icon :icon-class="'refresh'" style="font-size: 14px;" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<ul v-show="loading" class="ztree">
|
||||
@ -38,6 +38,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="treebox">
|
||||
<div>
|
||||
<el-input
|
||||
v-if="treeSetting.showSearch && showTreeSearch"
|
||||
v-model="treeSearchValue"
|
||||
prefix-icon="fa fa-search"
|
||||
size="mini"
|
||||
class="fixed-tree-search"
|
||||
:placeholder="$tc('common.Search')"
|
||||
@input="treeSearchHandle"
|
||||
>
|
||||
<span slot="suffix">
|
||||
<!-- <i class="fa fa-search" style="font-size: 14px; color: #676A6C;" /> -->
|
||||
<svg-icon
|
||||
:icon-class="'close'"
|
||||
class="icon"
|
||||
style="font-size: 14px;"
|
||||
@click="onClose"
|
||||
/>
|
||||
</span>
|
||||
</el-input>
|
||||
</div>
|
||||
<ul v-show="loading" class="ztree">
|
||||
{{ this.$t('common.tree.Loading') }}...
|
||||
</ul>
|
||||
@ -84,6 +105,7 @@ export default {
|
||||
rMenu: '',
|
||||
init: false,
|
||||
loading: false,
|
||||
showTreeSearch: JSON.parse(localStorage.getItem('showTreeSearch')) || false,
|
||||
treeSearchValue: ''
|
||||
}
|
||||
},
|
||||
@ -94,7 +116,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
window.refresh = this.refresh
|
||||
window.treeSearch = this.treeSearch
|
||||
window.onSearch = this.onSearch
|
||||
this.initTree()
|
||||
},
|
||||
beforeDestroy() {
|
||||
@ -137,7 +159,8 @@ export default {
|
||||
|
||||
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
|
||||
if (!this.treeSetting.customTreeHeader) {
|
||||
this.rootNodeAddDom(this.zTree)
|
||||
const rootNode = this.zTree.getNodes()[0]
|
||||
this.rootNodeAddDom(rootNode)
|
||||
}
|
||||
// 手动上报事件, Tree加载完成
|
||||
this.$emit('TreeInitFinish', this.zTree)
|
||||
@ -153,16 +176,29 @@ export default {
|
||||
vm.init = true
|
||||
})
|
||||
},
|
||||
rootNodeAddDom(ztree) {
|
||||
onSearch() {
|
||||
this.showTreeSearch = !this.showTreeSearch
|
||||
localStorage.setItem('showTreeSearch', JSON.stringify(this.showTreeSearch))
|
||||
},
|
||||
onClose() {
|
||||
this.refresh()
|
||||
this.onSearch()
|
||||
},
|
||||
rootNodeAddDom(rootNode) {
|
||||
const { showSearch, showRefresh } = this.treeSetting
|
||||
const searchIcon = `<a class="tree-search" id="searchIcon">
|
||||
<i class='fa fa-search tree-banner-icon' onclick="treeSearch()" /></i>
|
||||
<input type="text" autocomplete="off" id="searchInput" class="tree-input" />
|
||||
</a>`
|
||||
const refreshIcon = "<a id='tree-refresh' onclick='refresh()'><i class='fa fa-refresh'></i></a>"
|
||||
const searchIcon = `
|
||||
<a class="tree-action-btn" onclick="onSearch()">
|
||||
<i class='fa fa-search tree-banner-icon'></i>
|
||||
</a>`
|
||||
const refreshIcon = `
|
||||
<a id='tree-refresh' class="tree-action-btn" onclick='refresh()'>
|
||||
<i class='fa fa-refresh'></i>
|
||||
</a>`
|
||||
const treeActions = `${showSearch ? searchIcon : ''}${showRefresh ? refreshIcon : ''}`
|
||||
const icons = `<span class="">${treeActions}</span>`
|
||||
const rootNode = ztree.getNodes()[0]
|
||||
const icons = `
|
||||
<span style="float: right; margin-right: 10px">
|
||||
${treeActions}
|
||||
</span>`
|
||||
if (rootNode) {
|
||||
const $rootNodeRef = $('#' + rootNode.tId + '_a')
|
||||
$rootNodeRef.after(icons)
|
||||
@ -193,16 +229,15 @@ export default {
|
||||
searchIcon.classList.toggle('active')
|
||||
}
|
||||
}
|
||||
searchInput.oninput = _.debounce((e) => {
|
||||
e.stopPropagation()
|
||||
const value = e.target.value || ''
|
||||
if (this.treeSetting.async.enable) {
|
||||
this.filterAssetsServer(value)
|
||||
} else {
|
||||
this.filterTree(value)
|
||||
}
|
||||
}, 600)
|
||||
searchInput.oninput = e => this.treeSearchHandle((e.target.value || ''))
|
||||
},
|
||||
treeSearchHandle: _.debounce(function(value) {
|
||||
if (this.treeSetting.async.enable) {
|
||||
this.filterAssetsServer(value)
|
||||
} else {
|
||||
this.filterTree(value)
|
||||
}
|
||||
}, 600),
|
||||
getCheckedNodes: function() {
|
||||
return this.zTree.getCheckedNodes(true)
|
||||
},
|
||||
@ -326,8 +361,12 @@ export default {
|
||||
const newNode = { id: 'search', name: name, isParent: true, open: true, zAsync: true }
|
||||
searchNode = this.zTree.addNodes(null, newNode)[0]
|
||||
searchNode.zAsync = true
|
||||
|
||||
if (!this.treeSetting.customTreeHeader) {
|
||||
this.rootNodeAddDom(searchNode)
|
||||
}
|
||||
const nodesGroupByOrg = this.groupBy(nodes, (node) => {
|
||||
return node.meta.data.org_name
|
||||
return node.meta?.data?.org_name
|
||||
})
|
||||
|
||||
for (const item of nodesGroupByOrg) {
|
||||
@ -385,6 +424,7 @@ export default {
|
||||
top: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.ztree ::v-deep .fa {
|
||||
font: normal normal normal 14px/1 FontAwesome !important;
|
||||
}
|
||||
@ -413,21 +453,42 @@ export default {
|
||||
}
|
||||
|
||||
.treebox {
|
||||
height: 80vh;
|
||||
overflow: auto;
|
||||
height: 70vh;
|
||||
background-color: transparent;
|
||||
>>> .ztree {
|
||||
overflow: auto;
|
||||
background-color: transparent;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
li {
|
||||
background-color: transparent !important;
|
||||
|
||||
.button {
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
ul {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep #tree-refresh {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
::v-deep .tree-banner-icon-zone {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
|
||||
.fa {
|
||||
color: #838385!important;;
|
||||
color: #838385 !important;;
|
||||
|
||||
&:hover {
|
||||
color: #606266!important;;
|
||||
color: #606266 !important;;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,9 +503,11 @@ export default {
|
||||
vertical-align: sub;
|
||||
transition: .25s;
|
||||
overflow: hidden;
|
||||
|
||||
.fa {
|
||||
width: 13px!important;
|
||||
width: 13px !important;
|
||||
}
|
||||
|
||||
.fa-search {
|
||||
padding-top: 1px;
|
||||
}
|
||||
@ -452,23 +515,20 @@ export default {
|
||||
|
||||
::v-deep .tree-search .tree-banner-icon {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
top: 4px;
|
||||
left: 6px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 12px;
|
||||
padding: 10px 6px;
|
||||
overflow: hidden;
|
||||
background-color: transparent!important;
|
||||
background-color: transparent !important;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::v-deep .tree-search.active {
|
||||
::v-deep .tree-search.active {
|
||||
width: 160px;
|
||||
background-color: #ffffff!important;
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
::v-deep .tree-search.active:hover {
|
||||
@ -480,7 +540,7 @@ export default {
|
||||
left: 20px;
|
||||
width: 133px;
|
||||
height: 100%;
|
||||
background-color: #ffffff!important;
|
||||
background-color: #ffffff !important;
|
||||
color: #606266;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -488,11 +548,14 @@ export default {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.tree-header {
|
||||
position: relative;
|
||||
|
||||
.title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
@ -503,19 +566,67 @@ export default {
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background-color: #D7D8DC;
|
||||
|
||||
.rotate {
|
||||
transition: all .1.8s;
|
||||
transition: all .1 .8s;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.fa-caret-down {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.special {
|
||||
top: 1px!important;
|
||||
top: 1px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree-empty {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.fixed-tree-search {
|
||||
margin-bottom: 10px;
|
||||
|
||||
& >>> .el-input__inner {
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
padding-right: 32px;
|
||||
}
|
||||
|
||||
& >>> .el-input__suffix {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
& >>> .el-input__prefix {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
& >>> .el-input__suffix-inner {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
border-radius: 4px;
|
||||
padding: 0 1px;
|
||||
z-index: 1;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
border-color: #d2d2d2;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-action-btn {
|
||||
padding: 0 2px;
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
102
src/components/DetailCard/auto.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<DetailCard v-if="!loading" :items="items" v-bind="$attrs" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DetailCard from './index'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'AutoDetailCard',
|
||||
components: { DetailCard },
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
excludes: {
|
||||
type: Array,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.optionAndGenFields()
|
||||
this.loading = false
|
||||
},
|
||||
methods: {
|
||||
async optionAndGenFields() {
|
||||
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
|
||||
const remoteMeta = data.actions['GET'] || {}
|
||||
let fields = this.fields
|
||||
fields = fields || Object.keys(remoteMeta)
|
||||
const defaultExcludes = ['org_id']
|
||||
const excludes = (this.excludes || []).concat(defaultExcludes)
|
||||
fields = fields.filter(item => !excludes.includes(item))
|
||||
for (const name of fields) {
|
||||
if (typeof name === 'object') {
|
||||
this.items.push(name)
|
||||
continue
|
||||
}
|
||||
const fieldMeta = remoteMeta[name]
|
||||
if (!fieldMeta) {
|
||||
continue
|
||||
}
|
||||
|
||||
let value = this.object[name]
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(item => {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
return
|
||||
}
|
||||
this.items.push({
|
||||
key: item.label,
|
||||
value: item.value
|
||||
})
|
||||
})
|
||||
continue
|
||||
}
|
||||
const label = fieldMeta.label
|
||||
if (value === undefined || value === null || value === '') {
|
||||
value = '-'
|
||||
} else if (fieldMeta.type === 'datetime') {
|
||||
value = toSafeLocalDateStr(value)
|
||||
} else if (fieldMeta.type === 'labeled_choice') {
|
||||
value = value['label']
|
||||
} else if (fieldMeta.type === 'related_field') {
|
||||
value = value['name']
|
||||
} else if (fieldMeta.type === 'm2m_related_field') {
|
||||
value = value.map(item => item['name']).join(', ')
|
||||
} else if (fieldMeta.type === 'boolean') {
|
||||
value = value ? this.$t('common.Yes') : this.$t('common.No')
|
||||
}
|
||||
|
||||
const item = {
|
||||
key: label,
|
||||
value: value
|
||||
}
|
||||
this.items.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<IBox :title="title" fa="fa-info-circle">
|
||||
<IBox :title="title" :fa="fa">
|
||||
<el-form class="content" label-position="left" label-width="25%">
|
||||
<el-form-item v-for="item in items" :key="item.key" :label="item.key">
|
||||
<ItemValue class="item-value" :value="item.value" v-bind="item" />
|
||||
@ -23,6 +23,10 @@ export default {
|
||||
return this.$t('common.BasicInfo')
|
||||
}
|
||||
},
|
||||
fa: {
|
||||
type: String,
|
||||
default: 'fa-info-circle'
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
@ -54,11 +58,18 @@ export default {
|
||||
|
||||
>>> .el-form-item__label {
|
||||
padding-right: 8%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
>>> .el-form-item__content {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
>>> .el-tag--mini {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-value span {
|
||||
|
@ -86,7 +86,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog > > > .el-dialog {
|
||||
.dialog >>> .el-dialog {
|
||||
border-radius: 0.3em;
|
||||
max-width: 1500px;
|
||||
|
||||
@ -98,7 +98,7 @@ export default {
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 30px 40px;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
@ -108,7 +108,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-footer > > > button.el-button {
|
||||
.dialog-footer >>> button.el-button {
|
||||
font-size: 13px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
<template>
|
||||
<el-tree
|
||||
:data="tree"
|
||||
:data="iTree"
|
||||
show-checkbox
|
||||
node-key="value"
|
||||
:default-expand-all="false"
|
||||
:default-expand-all="true"
|
||||
:default-expanded-keys="iValue"
|
||||
:default-checked-keys="iValue"
|
||||
:props="defaultProps"
|
||||
@check="handleCheckChange"
|
||||
/>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -22,6 +21,10 @@ export default {
|
||||
tree: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -40,6 +43,13 @@ export default {
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
iTree() {
|
||||
if (!this.readonly) {
|
||||
return this.tree
|
||||
} else {
|
||||
return this.setTreeReadonly(this.tree)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -47,10 +57,16 @@ export default {
|
||||
const checkedKeys = checkedNodes
|
||||
.filter(item => !item.children)
|
||||
.map(node => node.value)
|
||||
if (checkedKeys.length !== 0) {
|
||||
checkedKeys.push('connect')
|
||||
}
|
||||
this.$emit('input', checkedKeys)
|
||||
},
|
||||
setTreeReadonly(tree) {
|
||||
return tree.map(item => {
|
||||
item.disabled = true
|
||||
if (item.children) {
|
||||
item.children = this.setTreeReadonly(item.children)
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,118 @@
|
||||
<template>
|
||||
<div style="font-size: 12px">
|
||||
<div v-if="toolbar.length>0" class="toolbar">
|
||||
<template v-for="(item,index) in toolbar">
|
||||
<el-tooltip :key="index" :content="item.tip" placement="top">
|
||||
<el-button v-if="item.type==='button'" size="mini" type="primary" @click="item.callback">
|
||||
<i :class="item.icon" />
|
||||
<div style="font-size: 12px" class="code-editor">
|
||||
<div class="toolbar">
|
||||
<div
|
||||
v-for="(item,index) in toolbar.left"
|
||||
:key="index"
|
||||
style="display: inline-block; margin: 0 2px"
|
||||
>
|
||||
<el-tooltip :content="item.tip" placement="top" :disabled="!item.tip">
|
||||
<el-button
|
||||
v-if="item.type ==='button'"
|
||||
size="mini"
|
||||
:type="item.el&&item.el.type"
|
||||
:disabled="item.disabled"
|
||||
@click="item.callback()"
|
||||
>
|
||||
<i :class="item.icon" style="margin-right: 4px;" />{{ item.name }}
|
||||
</el-button>
|
||||
|
||||
<div v-if="item.type==='select' && item.el && item.el.create" class="select-content">
|
||||
<span class="filter-label">
|
||||
{{ item.name }}:
|
||||
</span>
|
||||
<el-select
|
||||
v-if="item.type==='select' && item.el && item.el.create"
|
||||
:key="index"
|
||||
v-model="item.value"
|
||||
class="autoWidth-select"
|
||||
size="mini"
|
||||
default-first-option
|
||||
:multiple="item.el.multiple"
|
||||
:allow-create="item.el.create || false"
|
||||
:filterable="item.el.create || false"
|
||||
:placeholder="item.name"
|
||||
@change="item.callback(item.value)"
|
||||
>
|
||||
<template slot="prefix">
|
||||
{{ item.label + ':' + item.value }}
|
||||
</template>
|
||||
<el-option
|
||||
v-for="(option,id) in item.options"
|
||||
:key="id"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
:title="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-dropdown
|
||||
v-if="item.type==='select' && (!item.el || !item.el.create) "
|
||||
trigger="click"
|
||||
@command="(command) => {
|
||||
item.value= command
|
||||
item.callback(command)
|
||||
}"
|
||||
>
|
||||
<el-button type="default" size="mini">
|
||||
<b>{{ item.name }}:</b> {{ getLabel(item.value, item.options) }} <i
|
||||
class="el-icon-arrow-down el-icon--right"
|
||||
/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="(option,i) in item.options" :key="i" :command="option.value">
|
||||
{{ option.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<el-switch
|
||||
v-if="item.type === 'switch'"
|
||||
v-model="item.value"
|
||||
:disabled="item.disabled"
|
||||
:active-text="item.name"
|
||||
@change="item.callback( item.value)"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div style="float: right" class="right-side">
|
||||
<div
|
||||
v-for="(item,index) in toolbar.right"
|
||||
:key="index"
|
||||
style="display: inline-block"
|
||||
>
|
||||
<el-tooltip :content="item.tip">
|
||||
<el-button
|
||||
v-if="item.type ==='button'"
|
||||
size="mini"
|
||||
type="default"
|
||||
:disabled="item.disabled"
|
||||
style="background-color: transparent"
|
||||
@click="item.callback()"
|
||||
>
|
||||
<i v-if="item.icon.startsWith('fa')" :class="'fa ' + item.icon" />
|
||||
<svg-icon v-else :icon-class="item.icon" style="font-size: 14px;" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<codemirror ref="myCm" v-model="iValue" :options="cmOptions" />
|
||||
<codemirror ref="myCm" v-model="iValue" class="editor" :options="iOptions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { codemirror } from 'vue-codemirror'
|
||||
|
||||
import 'codemirror/mode/shell/shell'
|
||||
import 'codemirror/mode/powershell/powershell'
|
||||
import 'codemirror/mode/python/python'
|
||||
import 'codemirror/mode/ruby/ruby'
|
||||
|
||||
// theme css
|
||||
import 'codemirror/theme/base16-dark.css'
|
||||
import 'codemirror/mode/ruby/ruby' // theme css
|
||||
import 'codemirror/theme/base16-light.css'
|
||||
import 'codemirror/theme/idea.css'
|
||||
import 'codemirror/theme/mbo.css'
|
||||
import 'codemirror/theme/duotone-light.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
|
||||
export default {
|
||||
@ -34,23 +121,17 @@ export default {
|
||||
},
|
||||
props: {
|
||||
toolbar: {
|
||||
type: Array,
|
||||
type: [Array, Object],
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: [String, Object],
|
||||
default: () => ''
|
||||
},
|
||||
cmOptions: {
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
tabSize: 4,
|
||||
mode: 'shell',
|
||||
theme: 'base16-dark',
|
||||
lineNumbers: true,
|
||||
line: true
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -66,23 +147,125 @@ export default {
|
||||
this.$emit('update:value', val)
|
||||
this.$emit('change', val)
|
||||
}
|
||||
},
|
||||
iOptions() {
|
||||
const defaultOptions = {
|
||||
tabSize: 4,
|
||||
mode: 'shell',
|
||||
lineNumbers: true,
|
||||
theme: 'idea',
|
||||
placeholder: 'Code goes here...',
|
||||
autofocus: true
|
||||
}
|
||||
return Object.assign(defaultOptions, this.options)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getLabel(value, items) {
|
||||
for (const item of items) {
|
||||
if (item.value === value) {
|
||||
return item.label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.editor {
|
||||
border: solid 1px #f3f3f3;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
line-height: 29px;
|
||||
vertical-align: bottom;
|
||||
display: inline-block;
|
||||
border-radius: 4px 4px 0 0;
|
||||
background-color: var(--color-primary);
|
||||
padding: 3px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
>>> .CodeMirror pre.CodeMirror-line,
|
||||
>>> .CodeMirror-linenumber.CodeMirror-gutter-elt {
|
||||
line-height: 18px!important;
|
||||
|
||||
> > > .CodeMirror pre.CodeMirror-line,
|
||||
> > > .CodeMirror-linenumber.CodeMirror-gutter-elt {
|
||||
line-height: 18px !important;
|
||||
}
|
||||
|
||||
.runas-input {
|
||||
height: 28px;
|
||||
|
||||
> > > {
|
||||
.el-select {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
.el-button {
|
||||
border: none;
|
||||
padding: 2px;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #888;
|
||||
background-color: transparent;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.autoWidth-select {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.autoWidth-select > > > .el-input__prefix {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
box-sizing: border-box;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.autoWidth-select > > > input {
|
||||
position: absolute;
|
||||
padding-left: 0px;
|
||||
border: none;
|
||||
color: #606266;
|
||||
background-color: #e6e6e6;
|
||||
font-size: 12px;
|
||||
font-weight: 470;
|
||||
line-height: 27px;
|
||||
}
|
||||
|
||||
> > > .el-select {
|
||||
top: -1px;
|
||||
|
||||
.el-input .el-select__caret {
|
||||
color: #7a7c7f;
|
||||
}
|
||||
}
|
||||
|
||||
> > > .el-button.el-button--default {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.select-content {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding-left: 15px;
|
||||
font-size: 0;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 4px;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
</style>
|
||||
|
@ -95,12 +95,12 @@ export default {
|
||||
.datepicker {
|
||||
width: 233px;
|
||||
|
||||
& > > > .el-range__icon {
|
||||
& >>> .el-range__icon {
|
||||
margin-top: 2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
& > > > .el-range-input {
|
||||
& >>> .el-range-input {
|
||||
width: 49%;
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ export default {
|
||||
components: { JsonEditor },
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Object],
|
||||
default: () => ''
|
||||
type: [String, Object, Array],
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -53,20 +53,25 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/styles/variables.scss";
|
||||
|
||||
.json-editor {
|
||||
&>>> .jsoneditor {
|
||||
& > > > .jsoneditor {
|
||||
border: 1px solid #e5e6e7;
|
||||
}
|
||||
&>>> .jsoneditor-compact {
|
||||
|
||||
& > > > .jsoneditor-compact {
|
||||
display: none;
|
||||
}
|
||||
&>>> .jsoneditor-modes {
|
||||
|
||||
& > > > .jsoneditor-modes {
|
||||
display: none;
|
||||
}
|
||||
&>>> .jsoneditor-poweredBy {
|
||||
|
||||
& > > > .jsoneditor-poweredBy {
|
||||
display: none;
|
||||
}
|
||||
&>>> .jsoneditor-menu {
|
||||
|
||||
& > > > .jsoneditor-menu {
|
||||
background: var(--color-primary);
|
||||
border-bottom: 1px solid var(--color-primary);
|
||||
}
|
||||
|
@ -24,6 +24,11 @@ export default {
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 自定义label字段的name
|
||||
customLabelKeyName: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -64,7 +69,10 @@ export default {
|
||||
value = [value]
|
||||
}
|
||||
value = value.map(v => {
|
||||
return typeof v === 'object' ? v : { pk: v }
|
||||
// uuid v4
|
||||
const uuid = /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
|
||||
return typeof v === 'object' ? v
|
||||
: this.$attrs?.allowCreate && !uuid.test(v) ? { [this.customLabelKeyName]: v } : { pk: v }
|
||||
})
|
||||
if (!this.multiple) {
|
||||
value = value[0]
|
||||
@ -78,7 +86,7 @@ export default {
|
||||
}
|
||||
val = val.map((v) => {
|
||||
if (v && typeof v === 'object') {
|
||||
return v.pk || v.id
|
||||
return v.pk || v.id || (this.$attrs?.allowCreate ? (v?.[this.customLabelKeyName] + ':' + v?.value) : '')
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Dialog, AutoDataForm } from '@/components'
|
||||
import { AutoDataForm, Dialog } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'ProtocolSetting',
|
||||
@ -65,21 +65,6 @@ export default {
|
||||
url: '',
|
||||
fields: [
|
||||
[this.$t('common.Basic'), [
|
||||
// {
|
||||
// id: 'primary',
|
||||
// label: this.$t('assets.Primary'),
|
||||
// type: 'switch',
|
||||
// helpText: this.$t('assets.PrimaryOnly'),
|
||||
// on: {
|
||||
// change: ([event], updateForm) => {
|
||||
// if (event) {
|
||||
// updateForm({ required: true })
|
||||
// } else {
|
||||
// updateForm({ required: false })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
{
|
||||
id: 'required',
|
||||
label: this.$t('assets.Required'),
|
||||
@ -172,12 +157,12 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.data-form > > > .el-form-item.form-buttons {
|
||||
.data-form >>> .el-form-item.form-buttons {
|
||||
padding-top: 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.setting-dialog > > > .el-dialog__body {
|
||||
.setting-dialog >>> .el-dialog__body {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
<el-select
|
||||
slot="prepend"
|
||||
v-model="item.name"
|
||||
:disabled="cannotDelete(item)"
|
||||
class="prepend"
|
||||
:disabled="cannotDisable(item)"
|
||||
@change="handleProtocolChange($event, item)"
|
||||
>
|
||||
<el-option v-for="p of remainProtocols" :key="p.name" :label="p.name" :value="p.name" />
|
||||
@ -18,37 +18,38 @@
|
||||
@click="onSettingClick(item)"
|
||||
/>
|
||||
</el-input>
|
||||
<div style="display: flex; margin-left: 20px" class="input-button">
|
||||
<div class="input-button" style="display: flex; margin-left: 20px">
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="cannotDelete(item)"
|
||||
icon="el-icon-minus"
|
||||
style="flex-shrink: 0;"
|
||||
size="mini"
|
||||
:disabled="cannotDisable(item)"
|
||||
style="flex-shrink: 0;"
|
||||
type="danger"
|
||||
@click="handleDelete(index)"
|
||||
/>
|
||||
<el-button
|
||||
v-if="index === items.length - 1"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
style="flex-shrink: 0;"
|
||||
size="mini"
|
||||
:disabled="remainProtocols.length === 0 || !item.port"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
style="flex-shrink: 0;"
|
||||
type="primary"
|
||||
@click="handleAdd(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ProtocolSettingDialog
|
||||
v-if="showDialog"
|
||||
:visible.sync="showDialog"
|
||||
:item="settingItem"
|
||||
:disabled="settingReadonly"
|
||||
:item="settingItem"
|
||||
:visible.sync="showDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProtocolSettingDialog from './ProtocolSettingDialog'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProtocolSettingDialog
|
||||
@ -98,10 +99,16 @@ export default {
|
||||
} else {
|
||||
return this.$t('assets.DefaultPort')
|
||||
}
|
||||
},
|
||||
iChoices() {
|
||||
return this.choices.map(item => {
|
||||
delete item?.id
|
||||
return item
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
choices: {
|
||||
iChoices: {
|
||||
handler(value) {
|
||||
this.setDefaultItems(value)
|
||||
}
|
||||
@ -115,9 +122,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setDefaultItems(this.choices)
|
||||
console.log('Choices: -------------------------------------------', this.choices)
|
||||
console.log('Value: ---------------------------------------------', this.value)
|
||||
this.setDefaultItems(this.iChoices)
|
||||
this.$log.debug('Choices: ', this.choices)
|
||||
this.$log.debug('Value: ', this.value)
|
||||
},
|
||||
methods: {
|
||||
handleDelete(index) {
|
||||
@ -125,8 +132,11 @@ export default {
|
||||
return i !== index
|
||||
})
|
||||
},
|
||||
cannotDisable(item) {
|
||||
return item.primary || item.required
|
||||
cannotDelete(item) {
|
||||
const full = this.iChoices.find(choice => {
|
||||
return choice.name === item.name
|
||||
})
|
||||
return full?.primary || full?.required
|
||||
},
|
||||
handleAdd(index) {
|
||||
this.items.push({ ...this.remainProtocols[0] })
|
||||
@ -138,12 +148,31 @@ export default {
|
||||
},
|
||||
setDefaultItems(choices) {
|
||||
if (this.value.length > 0) {
|
||||
this.items = this.value
|
||||
const protocols = []
|
||||
this.value.forEach(item => {
|
||||
// 有默认值的情况下,设置为只读或者有id、有setting是平台
|
||||
if (!this.settingReadonly || (item?.id && item?.setting)) {
|
||||
protocols.push(item)
|
||||
} else {
|
||||
// 获取资产协议配置
|
||||
const assetDefaultItems = this.getAssetDefaultItems(item, choices)
|
||||
protocols.push(...assetDefaultItems)
|
||||
}
|
||||
})
|
||||
this.items = protocols
|
||||
} else {
|
||||
const defaults = choices.filter(item => (item.required || item.primary || item.default))
|
||||
this.items = defaults
|
||||
}
|
||||
},
|
||||
getAssetDefaultItems(item, choices) {
|
||||
const protocols = []
|
||||
const protocol = choices.find(i => i.name === item.name) || item
|
||||
if (protocol) {
|
||||
protocols.push(protocol)
|
||||
}
|
||||
return protocols
|
||||
},
|
||||
onSettingClick(item) {
|
||||
this.settingItem = item
|
||||
this.showDialog = true
|
||||
@ -181,6 +210,7 @@ export default {
|
||||
height: 25px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.el-input-group__append .el-button {
|
||||
font-size: 14px;
|
||||
color: #1a1a1a;
|
||||
|
@ -139,10 +139,11 @@ export default {
|
||||
}
|
||||
if (val && val.constructor === Object && val.value) {
|
||||
this.$emit('input', val.value)
|
||||
} else if (val && val.constructor === Object && val.id) {
|
||||
this.$emit('input', val.id)
|
||||
} else {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
console.log('Set value: ', val)
|
||||
},
|
||||
get() {
|
||||
return this.value
|
||||
@ -166,6 +167,10 @@ export default {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
const transformOption = this.ajax.transformOption || defaultTransformOption
|
||||
const defaultFilterOption = (item) => {
|
||||
return item
|
||||
}
|
||||
const filterOption = this.ajax.filterOption || defaultFilterOption
|
||||
const defaultProcessResults = (data) => {
|
||||
let results = []
|
||||
let more = false
|
||||
@ -179,7 +184,7 @@ export default {
|
||||
total = data.count
|
||||
}
|
||||
results = results.map(transformOption)
|
||||
results = results.filter(Boolean)
|
||||
results = results.filter(filterOption)
|
||||
return { results: results, pagination: more, total: total }
|
||||
}
|
||||
const defaultAjax = {
|
||||
@ -359,6 +364,7 @@ export default {
|
||||
.select2 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.select2 >>> .el-tag.el-tag--info {
|
||||
height: auto;
|
||||
white-space: normal;
|
||||
|
@ -81,7 +81,7 @@ export default {
|
||||
}
|
||||
.filter-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
padding-left: 2px;
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 1px;
|
||||
@ -100,6 +100,6 @@ export default {
|
||||
font-size: 13px;
|
||||
}
|
||||
.filter-field >>> .el-input__inner {
|
||||
height: 30px;
|
||||
//height: 32px;
|
||||
}
|
||||
</style>
|
||||
|
92
src/components/FormFields/UpdateSelect.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button
|
||||
v-show="!isShow"
|
||||
type="text"
|
||||
class="button-text"
|
||||
:disabled="disabled"
|
||||
@click="isShow=true"
|
||||
>
|
||||
{{ iLabel }}
|
||||
<svg-icon class-name="icon" icon-class="switch" />
|
||||
</el-button>
|
||||
<Select2
|
||||
v-show="isShow"
|
||||
ref="select2"
|
||||
v-model="iValue"
|
||||
v-bind="$attrs"
|
||||
@change="onSelectChange"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Select2 from './Select2'
|
||||
import { hasUUID } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Select2
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
showSelect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: this.showSelect,
|
||||
iLabel: this.label
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iValue: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const { path } = this.$route
|
||||
if (hasUUID(path) && this.value) {
|
||||
this.isShow = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectChange(val) {
|
||||
const options = this.$refs.select2.options.filter(item => item.value === val)
|
||||
const label = options.length > 0 ? options[0].label : ''
|
||||
this.isShow = false
|
||||
this.iLabel = val ? label : '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-text {
|
||||
color: #676a6c;
|
||||
padding-left: 0!important;
|
||||
padding-right: 0!important;
|
||||
}
|
||||
.icon {
|
||||
color: #676a6c!important;
|
||||
}
|
||||
</style>
|
92
src/components/GatewayDialog/index.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="iVisible"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="$tc('assets.TestGatewayTestConnection')"
|
||||
:visible.sync="iVisible"
|
||||
top="35vh"
|
||||
width="40%"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="4" :sm="24">
|
||||
<div style="line-height: 34px">{{ $t('assets.SSHPort') }}</div>
|
||||
</el-col>
|
||||
<el-col :md="14" :sm="24">
|
||||
<el-input v-model="port" />
|
||||
<span class="help-tips help-block">{{ $t('assets.TestGatewayHelpMessage') }}</span>
|
||||
</el-col>
|
||||
<el-col :md="4" :sm="24">
|
||||
<el-button
|
||||
:loading="loading"
|
||||
size="mini"
|
||||
style="line-height:20px "
|
||||
type="primary"
|
||||
@click="dialogConfirm"
|
||||
>
|
||||
{{ this.$t('common.Confirm') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'GatewayDialog',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
port: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
cell: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dialogConfirm() {
|
||||
if (isNaN(this.port)) {
|
||||
return this.$message.error(this.$tc('common.TestPortErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/gateways/${this.cell}/test-connective/`,
|
||||
{ port: this.port }
|
||||
)
|
||||
.then((res) => {
|
||||
openTaskPage(res['task'])
|
||||
}).finally(() => {
|
||||
this.iVisible = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -3,8 +3,8 @@
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import { DetailFormatter, AccountShowFormatter } from '@/components/TableFormatters'
|
||||
import TreeTable from '../TreeTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'GrantedAssets',
|
||||
@ -35,7 +35,7 @@ export default {
|
||||
getShowUrl: {
|
||||
type: Function,
|
||||
default({ row, col }) {
|
||||
return this.tableUrl.replace('/assets/', `/assets/${row.id}/accounts/?cache_policy=1`)
|
||||
return this.tableUrl.replace('/assets/', `/assets/${row.id}/accounts/`)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -57,34 +57,19 @@ export default {
|
||||
tableConfig: {
|
||||
url: this.tableUrl,
|
||||
hasTree: true,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
label: this.$t('assets.Name'),
|
||||
columnsExclude: ['specific'],
|
||||
columnShow: {
|
||||
min: ['name', 'address', 'accounts']
|
||||
},
|
||||
hasColumnActions: false,
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
sortable: true,
|
||||
formatterArgs: {
|
||||
route: 'AssetDetail'
|
||||
},
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'address',
|
||||
label: this.$t('assets.Address'),
|
||||
width: '140px',
|
||||
sortable: 'custom'
|
||||
},
|
||||
{
|
||||
prop: 'accounts',
|
||||
label: this.$t('assets.Account'),
|
||||
align: 'center',
|
||||
formatter: AccountShowFormatter,
|
||||
formatterArgs: {
|
||||
getUrl: this.getShowUrl.bind(this)
|
||||
},
|
||||
showOverflowTooltip: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
|
@ -14,7 +14,7 @@
|
||||
@confirm="handleExportConfirm()"
|
||||
@cancel="handleExportCancel()"
|
||||
>
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form label-position="left" style="padding-left: 20px">
|
||||
<el-form-item :label="$tc('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio
|
||||
@ -230,7 +230,7 @@ export default {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.export-form > > > .el-form-item__label {
|
||||
.export-form >>> .el-form-item__label {
|
||||
line-height: 2
|
||||
}
|
||||
</style>
|
||||
|
@ -11,7 +11,7 @@
|
||||
:show-confirm="false"
|
||||
@close="handleImportCancel"
|
||||
>
|
||||
<el-form v-if="!showTable" label-position="left">
|
||||
<el-form v-if="!showTable" label-position="left" style="padding-left: 20px">
|
||||
<el-form-item :label="$tc('common.Import' )" :label-width="'100px'">
|
||||
<el-radio v-if="canImportCreate" v-model="importOption" class="export-item" label="create">
|
||||
{{ this.$t('common.Create') }}
|
||||
|
@ -225,7 +225,6 @@ export default {
|
||||
prop: item[1],
|
||||
label: item[0],
|
||||
minWidth: colMaxWidth + 'px',
|
||||
showOverflowTooltip: true,
|
||||
formatter: EditableInputFormatter,
|
||||
formatterArgs: {
|
||||
onEnter: ({ row, col, oldValue, newValue }) => {
|
||||
|
@ -206,6 +206,7 @@ export default {
|
||||
|
||||
.search.left {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.search.right {
|
||||
|
@ -21,13 +21,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDataTable from '../AutoDataTable'
|
||||
import IBox from '../IBox'
|
||||
import TableAction from './TableAction'
|
||||
import Emitter from '@/mixins/emitter'
|
||||
import { getResourceFromApiUrl } from '@/utils/jms'
|
||||
import deepmerge from 'deepmerge'
|
||||
import { mapGetters } from 'vuex'
|
||||
import IBox from '../IBox'
|
||||
import TableAction from './TableAction'
|
||||
import Emitter from '@/mixins/emitter'
|
||||
import AutoDataTable from '../AutoDataTable'
|
||||
|
||||
export default {
|
||||
name: 'ListTable',
|
||||
@ -197,22 +197,25 @@ export default {
|
||||
& >>> .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& >>> .el-table__header thead > tr > th {
|
||||
background-color: white;
|
||||
}
|
||||
&>>> .el-table__row .cell {
|
||||
|
||||
& >>> .el-table__row .cell {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&>>> .el-table__expanded-cell pre {
|
||||
|
||||
& >>> .el-table__expanded-cell pre {
|
||||
max-height: 500px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
// .el-button--text{
|
||||
// color: #409EFF;
|
||||
// }
|
||||
.el-button--text{
|
||||
color: #409EFF;
|
||||
}
|
||||
</style>
|
||||
|
@ -19,6 +19,7 @@
|
||||
<script>
|
||||
import Switcher from '../FormFields/Switcher'
|
||||
import Select2 from '../FormFields/Select2'
|
||||
import UpdateSelect from '../FormFields/UpdateSelect'
|
||||
|
||||
class Action {
|
||||
constructor() {
|
||||
@ -33,7 +34,8 @@ export default {
|
||||
name: 'ActionItem',
|
||||
components: {
|
||||
Switcher,
|
||||
Select2
|
||||
Select2,
|
||||
UpdateSelect
|
||||
},
|
||||
props: {
|
||||
action: {
|
||||
@ -55,6 +57,8 @@ export default {
|
||||
return 'Switcher'
|
||||
case 'select2':
|
||||
return 'Select2'
|
||||
case 'updateSelect':
|
||||
return 'UpdateSelect'
|
||||
default:
|
||||
return 'el-button'
|
||||
}
|
||||
|
185
src/components/TabTree/index.vue
Normal file
@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div class="tree-tab">
|
||||
<el-tabs
|
||||
v-if="tabIndices.length > 0"
|
||||
v-model="iActiveMenu"
|
||||
class="page-submenu"
|
||||
stretch
|
||||
@tab-click="handleTabClick"
|
||||
>
|
||||
<template v-for="item in tabIndices">
|
||||
<el-tab-pane
|
||||
:key="item.name"
|
||||
:disabled="item.disabled"
|
||||
:label-content="item.labelContent"
|
||||
:name="item.name"
|
||||
>
|
||||
<span slot="label">
|
||||
<i v-if="item.icon" :class="item.icon" class="fa " />
|
||||
{{ item.title }}
|
||||
<slot :tab="item.name" name="badge" />
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
</el-tabs>
|
||||
<transition appear mode="out-in" name="fade-transform">
|
||||
<slot>
|
||||
<keep-alive v-if="flag">
|
||||
<AutoDataZTree
|
||||
:key="componentKey"
|
||||
ref="AutoDataZTree"
|
||||
:setting="activeTreeSetting"
|
||||
@urlChange="handleUrlChange"
|
||||
>
|
||||
<div slot="rMenu" slot-scope="{data}">
|
||||
<slot :data="data" name="rMenu" />
|
||||
</div>
|
||||
</AutoDataZTree>
|
||||
</keep-alive>
|
||||
</slot>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDataZTree from '../AutoDataZTree'
|
||||
import merge from 'webpack-merge'
|
||||
|
||||
const ACTIVE_TREE_TAB_KEY = 'activeTreeTab'
|
||||
|
||||
export default {
|
||||
name: 'TabTree',
|
||||
components: {
|
||||
AutoDataZTree
|
||||
},
|
||||
props: {
|
||||
submenu: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
activeMenu: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
flag: false,
|
||||
componentKey: 1,
|
||||
activeTreeSetting: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActiveMenu: {
|
||||
get() {
|
||||
return this.activeMenu
|
||||
},
|
||||
set(item) {
|
||||
this.$emit('update:activeMenu', item)
|
||||
this.changeTreeSetting(item)
|
||||
}
|
||||
},
|
||||
tabIndices() {
|
||||
const map = []
|
||||
this.submenu.forEach((v) => {
|
||||
const hidden = typeof v.hidden === 'function' ? v.hidden() : v.hidden
|
||||
if (!hidden) {
|
||||
map.push(v)
|
||||
}
|
||||
})
|
||||
return map
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeMenu(val) {
|
||||
this.changeTreeSetting(val)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.iActiveMenu = await this.getPropActiveTab()
|
||||
},
|
||||
methods: {
|
||||
hideRMenu() {
|
||||
this.$refs.AutoDataZTree.hideRMenu()
|
||||
},
|
||||
getSelectedNodes: function() {
|
||||
return this.$refs.AutoDataZTree.getSelectedNodes()
|
||||
},
|
||||
getNodes: function() {
|
||||
return this.$refs.AutoDataZTree.getNodes()
|
||||
},
|
||||
selectNode: function(node) {
|
||||
return this.$refs.AutoDataZTree.selectNode(node)
|
||||
},
|
||||
handleUrlChange(url) {
|
||||
this.$emit('urlChange', url)
|
||||
},
|
||||
handleTabClick(tab) {
|
||||
this.componentKey += 1
|
||||
this.$emit('tab-click', tab)
|
||||
this.$emit('update:activeMenu', tab.name)
|
||||
this.$cookie.set(ACTIVE_TREE_TAB_KEY, tab.name, 1)
|
||||
|
||||
if (this.$router.currentRoute.query[ACTIVE_TREE_TAB_KEY]) {
|
||||
this.$router.push({
|
||||
query: merge(this.$route.query, { [ACTIVE_TREE_TAB_KEY]: '' })
|
||||
})
|
||||
}
|
||||
},
|
||||
changeTreeSetting(tabName) {
|
||||
const vm = this
|
||||
try {
|
||||
this.flag = false
|
||||
for (const tab of this.submenu) {
|
||||
if (tab.name === tabName) {
|
||||
vm.activeTreeSetting = tab.treeSetting
|
||||
break
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.flag = true
|
||||
}
|
||||
},
|
||||
getPropActiveTab() {
|
||||
let activeTab = ''
|
||||
|
||||
const preActiveTabs = [
|
||||
this.$route.query[ACTIVE_TREE_TAB_KEY],
|
||||
this.$cookie.get(ACTIVE_TREE_TAB_KEY),
|
||||
this.activeMenu
|
||||
]
|
||||
|
||||
for (const preTab of preActiveTabs) {
|
||||
const currentTab = typeof preTab === 'object' ? preTab?.name : preTab
|
||||
for (const tabName of this.tabIndices) {
|
||||
const currentTabName = tabName?.name || ''
|
||||
if (currentTab?.toLowerCase() === currentTabName?.toLowerCase()) {
|
||||
return currentTabName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activeTab = this.tabIndices[0].name
|
||||
return activeTab
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
>>> .ztree,
|
||||
>>> .ztree li,
|
||||
>>> .ztree li ul,
|
||||
.tree-tab {
|
||||
}
|
||||
>>> .ztree {
|
||||
padding: 0;
|
||||
}
|
||||
.page-submenu >>> .el-tabs__nav-wrap {
|
||||
position: static;
|
||||
|
||||
.el-tabs__item.is-active {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -13,7 +13,7 @@
|
||||
<span class="detail-item-name">{{ item }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="reference">{{ cellValue.length }}</span>
|
||||
<span slot="reference">{{ cellValue && cellValue.length }}</span>
|
||||
</el-popover>
|
||||
</template>
|
||||
</DetailFormatter>
|
||||
@ -44,10 +44,10 @@ export default {
|
||||
},
|
||||
items() {
|
||||
const getItem = this.formatterArgs.getItem || function(item) { return item.name }
|
||||
return this.cellValue.map(item => getItem(item))
|
||||
return this.cellValue?.map(item => getItem(item))
|
||||
},
|
||||
showItems() {
|
||||
return this.formatterArgs.showItems !== false && this.cellValue.length > 0
|
||||
return this.formatterArgs.showItems !== false && this.cellValue?.length > 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -70,9 +70,6 @@ export default {
|
||||
&:hover {
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
|
||||
.detail-item-name {
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,12 +1,35 @@
|
||||
<template>
|
||||
<span>{{ cellValue.toString() }}</span>
|
||||
<span>{{ value }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
export default {
|
||||
name: 'ArrayFormatter',
|
||||
extends: BaseFormatter
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
delimiter: ', '
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value() {
|
||||
if (!(this.cellValue instanceof Array)) {
|
||||
return this.cellValue
|
||||
}
|
||||
return this.cellValue.join(this.formatterArgs.delimiter)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
|
||||
export default {
|
||||
name: 'ChoicesFormatter',
|
||||
extends: BaseFormatter,
|
||||
@ -88,8 +89,7 @@ export default {
|
||||
return true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -43,7 +43,8 @@ export default {
|
||||
if (this.col.objects === 'all') {
|
||||
return false
|
||||
}
|
||||
return this.col.objects.indexOf(this.cellValue) === -1
|
||||
const objectIds = this.col.objects.map(i => i.id)
|
||||
return objectIds.indexOf(this.cellValue) === -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
export default {
|
||||
name: 'DisplayFormatter',
|
||||
name: 'ObjectRelatedFormatter',
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
@ -13,7 +13,7 @@ export default {
|
||||
default() {
|
||||
return {
|
||||
displayKey: null,
|
||||
delimiter: ',',
|
||||
delimiter: ', ',
|
||||
cls: ''
|
||||
}
|
||||
}
|
||||
@ -26,11 +26,14 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
value() {
|
||||
if (this.cellValue === null || this.cellValue?.length === 0) {
|
||||
return ''
|
||||
}
|
||||
let objects = this.cellValue
|
||||
if (!Array.isArray(this.cellValue)) {
|
||||
objects = [this.cellValue]
|
||||
}
|
||||
const values = objects.map(object => object[this.iKey])
|
||||
const values = objects.map(object => object?.[this.iKey]) || []
|
||||
return values.join(this.formatterArgs.delimiter)
|
||||
},
|
||||
iKey() {
|
||||
@ -43,8 +46,8 @@ export default {
|
||||
} else {
|
||||
object = this.cellValue
|
||||
}
|
||||
for (const key of ['name', 'value']) {
|
||||
if (object[key]) {
|
||||
for (const key of ['label', 'name', 'value']) {
|
||||
if (object?.[key]) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
@ -72,9 +72,9 @@ export default {
|
||||
},
|
||||
currentValue() {
|
||||
if (this.isShow) {
|
||||
return this.cellValue
|
||||
return this.cellValue || '-'
|
||||
} else {
|
||||
return '******'
|
||||
return this.cellValue ? '******' : '-'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -116,9 +116,10 @@ export default {
|
||||
|
||||
.text {
|
||||
flex: 1;
|
||||
line-height: 1.3;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
//white-space: nowrap;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
|
||||
export default {
|
||||
name: 'StatusFormatter',
|
||||
extends: BaseFormatter,
|
||||
|
@ -1,5 +1,4 @@
|
||||
<template>
|
||||
|
||||
<div class="filter-field">
|
||||
<el-cascader
|
||||
v-show="options.length > 0"
|
||||
@ -31,6 +30,8 @@
|
||||
:placeholder="placeholder"
|
||||
class="search-input"
|
||||
:class="options.length < 1 ? 'search-input2': ''"
|
||||
:validate-event="false"
|
||||
suffix-icon="el-icon-search"
|
||||
@blur="focus = false"
|
||||
@focus="focus = true"
|
||||
@change="handleConfirm"
|
||||
@ -72,9 +73,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
keyLabel() {
|
||||
if (!this.filterKey) return ''
|
||||
for (const field of this.options) {
|
||||
if (field.value === this.filterKey) {
|
||||
return field.label
|
||||
return field?.label
|
||||
}
|
||||
}
|
||||
return ''
|
||||
@ -246,11 +248,20 @@ export default {
|
||||
return true
|
||||
},
|
||||
handleConfirm() {
|
||||
if (this.filterValue === '') return
|
||||
if (this.filterValue === '') {
|
||||
this.handleTagClose(this.filterKey)
|
||||
this.filterKey = ''
|
||||
return
|
||||
}
|
||||
if (this.filterValue && !this.filterKey) {
|
||||
this.filterKey = 'search' + '_' + this.filterValue
|
||||
}
|
||||
const tag = { key: this.filterKey, label: this.keyLabel, value: this.filterValue, valueLabel: this.valueLabel }
|
||||
const tag = {
|
||||
key: this.filterKey,
|
||||
label: this.keyLabel,
|
||||
value: this.filterValue,
|
||||
valueLabel: this.valueLabel
|
||||
}
|
||||
this.$set(this.filterTags, this.filterKey, tag)
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
|
||||
@ -317,12 +328,17 @@ export default {
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 3px;
|
||||
background-color:#fff;
|
||||
|
||||
}
|
||||
.search-input >>> .el-input__suffix {
|
||||
cursor: pointer;
|
||||
}
|
||||
.search-input2 >>> .el-input__inner {
|
||||
text-indent: 5px;
|
||||
}
|
||||
.search-input >>> .el-input__inner {
|
||||
/*max-width:inherit !important;*/
|
||||
|
||||
max-width: 200px;
|
||||
border: none;
|
||||
padding-left: 5px;
|
||||
|
@ -1,30 +1,84 @@
|
||||
<template>
|
||||
<div id="terminal" ref="terminal" class="xterm" />
|
||||
<div>
|
||||
<div v-if="showToolBar" style="position: absolute;z-index: 999;right: 2%;margin-top: 4px">
|
||||
<div
|
||||
v-for="(item,index) in toolbar"
|
||||
:key="index"
|
||||
style="display: inline-block"
|
||||
>
|
||||
<el-tooltip :content="item.tip">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="default"
|
||||
@click="item.callback()"
|
||||
>
|
||||
<i :class="item.icon" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="terminal" ref="terminal" class="xterm" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'xterm/css/xterm.css'
|
||||
import { Terminal } from 'xterm'
|
||||
import { FitAddon } from 'xterm-addon-fit'
|
||||
|
||||
export default {
|
||||
name: 'Term',
|
||||
props: {
|
||||
showToolBar: {
|
||||
type: [Boolean, Object],
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
xterm: ''
|
||||
xterm: new Terminal(
|
||||
{
|
||||
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
||||
lineHeight: 1.2,
|
||||
fontSize: 13,
|
||||
rightClickSelectsWord: true,
|
||||
theme: {
|
||||
background: '#fff',
|
||||
foreground: '#000',
|
||||
selection: '#363535'
|
||||
}
|
||||
}),
|
||||
toolbar: [
|
||||
{
|
||||
tip: this.$tc('ops.ScrollToTop'),
|
||||
icon: 'fa fa-upload',
|
||||
callback: () => {
|
||||
this.xterm.scrollToTop()
|
||||
}
|
||||
},
|
||||
{
|
||||
tip: this.$tc('ops.ScrollToBottom'),
|
||||
icon: 'fa fa-download',
|
||||
callback: () => {
|
||||
this.xterm.scrollToBottom()
|
||||
}
|
||||
},
|
||||
{
|
||||
tip: this.$tc('ops.ClearScreen'),
|
||||
icon: 'fa fa-refresh',
|
||||
callback: () => {
|
||||
this.xterm.reset()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
const terminalContainer = this.$refs.terminal
|
||||
this.xterm = new Terminal(
|
||||
{
|
||||
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
||||
lineHeight: 1.2,
|
||||
fontSize: 13,
|
||||
rightClickSelectsWord: true,
|
||||
theme: {
|
||||
background: '#1f1b1b'
|
||||
}
|
||||
})
|
||||
const fitAddon = new FitAddon()
|
||||
this.xterm.loadAddon(fitAddon)
|
||||
this.xterm.open(terminalContainer)
|
||||
@ -40,11 +94,27 @@ export default {
|
||||
},
|
||||
write: function(val) {
|
||||
this.xterm.write(val)
|
||||
},
|
||||
changeSelectedAssets() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.xterm {
|
||||
padding-left: 5px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
border: none;
|
||||
padding: 2px;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #888;
|
||||
background-color: transparent;
|
||||
margin-left: 2px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,78 +1,57 @@
|
||||
<template>
|
||||
<HeadingIBox v-bind="$attrs">
|
||||
<div class="ibox-content inspinia-timeline">
|
||||
<div v-for="(item, index) in items" :key="item.title + index" class="timeline-item">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="6" class="date">
|
||||
<i v-if="item.fa" :class="'fa ' + item.fa" />
|
||||
<span v-if="item.date">{{ item.date }}</span>
|
||||
<br>
|
||||
<small v-if="item.timeSince" class="text-navy">{{ item.timeSince }}</small>
|
||||
</el-col>
|
||||
<el-col :sm="14" :class="'content ' + (index === 0 ? 'no-top-border' : '')">
|
||||
<p v-if="item.title" class="m-b-xs"><strong>{{ item.title }}</strong></p>
|
||||
<p v-if="item.content">{{ item.content }}</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<IBox :title="title" v-bind="$attrs" class="block">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in activities"
|
||||
:key="index"
|
||||
:icon="activity.icon"
|
||||
:color="activity.color"
|
||||
:size="activity.size"
|
||||
:timestamp="activity.timestamp"
|
||||
type="primary"
|
||||
placement="top"
|
||||
>
|
||||
{{ activity.content }}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="activities.length < 1">
|
||||
{{ this.$t('common.NoContent') }}
|
||||
</div>
|
||||
</HeadingIBox>
|
||||
</IBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeadingIBox from '../IBox/HeadingIBox'
|
||||
import IBox from '@/components/IBox'
|
||||
export default {
|
||||
name: 'TimelineCard',
|
||||
components: { HeadingIBox },
|
||||
components: {
|
||||
IBox
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
activities: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.url) {
|
||||
this.$axios.get(this.url).then(res => {
|
||||
for (const i in res) {
|
||||
this.activities.push(res[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.timeline-item .date {
|
||||
text-align: right;
|
||||
width: 110px;
|
||||
position: relative;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.no-top-border {
|
||||
border-top: 0 !important;
|
||||
}
|
||||
|
||||
.timeline-item .content {
|
||||
border-left: 1px solid #e7eaec;
|
||||
border-top: 1px solid #e7eaec;
|
||||
padding-top: 10px;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.timeline-item .content:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
.timeline-item .date i {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 5px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #e7eaec;
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
border-left: 1px solid #e7eaec;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.timeline-item .content {
|
||||
border-left: 1px solid #e7eaec;
|
||||
border-top: 1px solid #e7eaec;
|
||||
padding-top: 10px;
|
||||
min-height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
@ -11,6 +11,7 @@
|
||||
:key="componentTreeKey"
|
||||
:setting="treeSetting"
|
||||
class="auto-data-ztree"
|
||||
v-bind="treeTabConfig"
|
||||
v-on="$listeners"
|
||||
@urlChange="handleUrlChange"
|
||||
>
|
||||
@ -24,12 +25,15 @@
|
||||
:style="iShowTree?('display: flex;width: calc(100% - 20%);'):('display: flex;width:100%;')"
|
||||
>
|
||||
<div v-if="showTree" class="mini">
|
||||
<div style="display:block" class="mini-button" @click="iShowTree = !iShowTree">
|
||||
<i v-show="iShowTree" class="fa fa-angle-left fa-x" />
|
||||
<i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||
<div class="mini-button" :class="{'is-show': iShowTree}" @click="iShowTree = !iShowTree">
|
||||
<svg-icon
|
||||
:icon-class="'double-left'"
|
||||
class="icon-left"
|
||||
:style="{'transform': iShowTree ? 'none' : 'rotate(180deg)'}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="transition-box" style="width: calc(100% - 17px);">
|
||||
<div class="transition-box" style="width: calc(100% - 7px);">
|
||||
<slot name="table">
|
||||
<ListTable
|
||||
ref="ListTable"
|
||||
@ -37,7 +41,11 @@
|
||||
:table-config="iTableConfig"
|
||||
:header-actions="headerActions"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
>
|
||||
<template v-slot:left>
|
||||
Hello world
|
||||
</template>
|
||||
</ListTable>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
@ -47,6 +55,7 @@
|
||||
|
||||
<script>
|
||||
import AutoDataZTree from '../AutoDataZTree'
|
||||
import TabTree from '../TabTree'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import ListTable from '../ListTable'
|
||||
import IBox from '../IBox'
|
||||
@ -57,6 +66,7 @@ export default {
|
||||
components: {
|
||||
ListTable,
|
||||
AutoDataZTree,
|
||||
TabTree,
|
||||
IBox,
|
||||
Dialog
|
||||
},
|
||||
@ -74,6 +84,10 @@ export default {
|
||||
component: {
|
||||
type: String,
|
||||
default: () => 'AutoDataZTree'
|
||||
},
|
||||
treeTabConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -101,10 +115,12 @@ export default {
|
||||
methods: {
|
||||
initSetTableUrl() {
|
||||
const { asset = '', node = '' } = this.$route.query || {}
|
||||
let url = this.iTableConfig.url
|
||||
url = setUrlParam(url, 'asset', asset)
|
||||
url = setUrlParam(url, 'node', node)
|
||||
this.$set(this.iTableConfig, 'url', url)
|
||||
let url = this.iTableConfig?.url || ''
|
||||
if (url) {
|
||||
url = setUrlParam(url, 'asset', asset)
|
||||
url = setUrlParam(url, 'node', node)
|
||||
this.$set(this.iTableConfig, 'url', url)
|
||||
}
|
||||
},
|
||||
handleUrlChange(url) {
|
||||
this.$set(this.iTableConfig, 'url', url)
|
||||
@ -134,19 +150,38 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.is-show {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-rotate {
|
||||
display: block;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.mini-button {
|
||||
width: 12px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 13px;
|
||||
float: right;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
background-color: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
color: #FFFFFF;
|
||||
border: 1px solid #DCDFE6;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 3px;
|
||||
line-height: 1.428;
|
||||
cursor: pointer;
|
||||
cursor:pointer;
|
||||
height: 30px;
|
||||
|
||||
&:hover {
|
||||
display: block;
|
||||
border: 1px solid #d2d2d2;
|
||||
}
|
||||
|
||||
.icon-left {
|
||||
font-size: 14px;
|
||||
margin-left: -1.1px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tree {
|
||||
@ -154,33 +189,20 @@ export default {
|
||||
}
|
||||
|
||||
.mini {
|
||||
position: relative;
|
||||
margin-right: 5px;
|
||||
width: 12px !important;
|
||||
width: 2px !important;
|
||||
}
|
||||
|
||||
.tree-table-content {
|
||||
.left {
|
||||
border-right: solid 1px #ebeef5;
|
||||
background: #f3f3f3;
|
||||
}
|
||||
|
||||
.right {
|
||||
}
|
||||
|
||||
.treebox {
|
||||
background-color: transparent;
|
||||
|
||||
.ztree {
|
||||
background-color: transparent;
|
||||
|
||||
li {
|
||||
background-color: transparent;
|
||||
&:hover {
|
||||
~ .right .is-show {
|
||||
display: block !important;;
|
||||
}
|
||||
}
|
||||
|
||||
.ztree * {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,6 +210,7 @@ export default {
|
||||
overflow: auto;
|
||||
/*border-right: solid 1px red;*/
|
||||
}
|
||||
|
||||
.transition-box.left {
|
||||
background: #f3f3f3;
|
||||
border: 1px solid #e0e0e0;
|
||||
|
@ -17,12 +17,13 @@ export { default as UploadKey } from './FormFields/UploadKey.vue'
|
||||
export { default as AssetSelect } from './AssetSelect'
|
||||
export { default as SvgIcon } from './SvgIcon'
|
||||
export { default as TreeTable } from './TreeTable'
|
||||
export { default as AssetTreeTAble } from './AssetTreeTable'
|
||||
export { default as IBox } from './IBox'
|
||||
export { default as QuickActions } from './QuickActions'
|
||||
export { default as Switcher } from './FormFields/Switcher'
|
||||
export { default as SummaryCard } from './SummaryCard'
|
||||
export { default as UploadField } from './FormFields/UploadField'
|
||||
export { default as AccountListTable } from './AccountListTable/index'
|
||||
export { default as AccountListTable } from './AccountListTable/AccountList'
|
||||
export { default as AssetRelationCard } from './AssetRelationCard'
|
||||
export { default as UserConfirmDialog } from './UserConfirmDialog'
|
||||
export { default as Announcement } from './Announcement'
|
||||
|
@ -3,7 +3,7 @@
|
||||
"accounts": {
|
||||
"HistoryPassword": "History password",
|
||||
"PasswordRecord": "Password record",
|
||||
"AccountHistableHelpMessage": "Record the historical version of the current account",
|
||||
"AccountHistoryHelpMessage": "Record the historical version of the current account",
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "Asset account list, please click on the assets on the left to view",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "Application account list, please click on the application on the left to view",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "Gathered user list, please click on the assets on the left to view"
|
||||
@ -103,6 +103,11 @@
|
||||
"NoSQLProtocol": "NoSQL Protocol"
|
||||
},
|
||||
"assets": {
|
||||
"Secure": "Secure",
|
||||
"LabelInputFormatValidation": "Format error, correct format is:name:value",
|
||||
"IP/Host": "IP/Host",
|
||||
"OrganizationAsset": "Organization asset",
|
||||
"PersonalAsset": "Personal asset",
|
||||
"UserSwitchFrom": "User switch from",
|
||||
"AssetAddress": "IP/Host",
|
||||
"sshkeyAccount": "ssh key account",
|
||||
@ -111,6 +116,11 @@
|
||||
"InAssetDetail": "Update account information in asset details",
|
||||
"SecretType": "Secret type",
|
||||
"PrivilegedTemplate": "Privileged",
|
||||
"InitialDeploy": "Initial deploy",
|
||||
"Address": "Address",
|
||||
"PrivateKey": "Private key",
|
||||
"Secret": "Secret",
|
||||
"Network": "Network",
|
||||
"All": "All",
|
||||
"CloudPlatform": "Cloud platform",
|
||||
"RecentlyUsed": "Recently used",
|
||||
@ -292,6 +302,7 @@
|
||||
"PasswordOrToken": "Password / Token"
|
||||
},
|
||||
"audits": {
|
||||
"OperateRecord": "Operating record",
|
||||
"Hosts": "Host",
|
||||
"RunUser": "Run user",
|
||||
"User": "User",
|
||||
@ -310,6 +321,22 @@
|
||||
"ReLoginErr": "Login time has exceeded 5 minutes, please login again"
|
||||
},
|
||||
"common": {
|
||||
"Activity": "Activity",
|
||||
"Last30": "Last 30 times",
|
||||
"SpecificInfo": "Specific",
|
||||
"CollectionSucceed": "Collection succeed",
|
||||
"CancelCollection": "Cancel collection",
|
||||
"Version": "Version",
|
||||
"Publish": "Publish",
|
||||
"PublishAllApplets": "Publish all applets",
|
||||
"Icon": "Icon",
|
||||
"DisplayName": "Display name",
|
||||
"Automations": "Automation",
|
||||
"Sync": "Sync",
|
||||
"Deploy": "Deploy",
|
||||
"Detail": "Detail",
|
||||
"Selector": "Selector",
|
||||
"NoContent": "No content",
|
||||
"NeedAddAppsOrSystemUserErrMsg": "Please add apps or system user",
|
||||
"VerificationCodeSent": "The verification code has been sent",
|
||||
"SendVerificationCode": "Send verification code",
|
||||
@ -640,7 +667,7 @@
|
||||
"FileTransferNum": "File transfer num",
|
||||
"LogOfLoginSuccessNum": "Log of login success num",
|
||||
"DangerousCommandNum": "Dangerous command num",
|
||||
"IndexName": "Index name",
|
||||
"SessionsNum": "Sessions num",
|
||||
"BatchCommandNotExecuted": "Batch command not executed",
|
||||
"ExecuteFailedCommand": "Execute failed command",
|
||||
"SessionTrend": "Session trend",
|
||||
@ -654,6 +681,7 @@
|
||||
"Weekly": "Weekly"
|
||||
},
|
||||
"ops": {
|
||||
"ManualInput": "Manual input",
|
||||
"Execute": "Execute",
|
||||
"ID": "ID",
|
||||
"No": "No",
|
||||
@ -947,6 +975,15 @@
|
||||
"Permissions": "Permissions",
|
||||
"NoPermission": "No permission"
|
||||
},
|
||||
"terminal": {
|
||||
"UploadSucceed": "Upload succeed",
|
||||
"UploadFailed": "Upload failed",
|
||||
"Applets": "Remote apps",
|
||||
"AppletHosts": "Remote hosts",
|
||||
"uploadZipTips": "Please upload zip file",
|
||||
"HostDeployment": "Remote host deployment",
|
||||
"TerminalStat": "CPU/MEM/DISK"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "Set to default storage",
|
||||
"SetToDefault": "Set to default",
|
||||
@ -1028,6 +1065,7 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"Applets": "Applets",
|
||||
"EndpointListHelpMessage": "The service endpoint is the address (port) for the user to access the service. When the user connects to the asset, the service endpoint will be selected according to the endpoint rules and asset tags, and the connection will be established as the access entry to realize the distributed connection of assets.",
|
||||
"EndpointRuleListHelpMessage": "For the service endpoint selection strategy, two types are currently supported: <br>1. Specify the endpoint according to the endpoint rule (current page); <br>2. Select the endpoint through the asset tag. The tag name is fixed to endpoint, and the value is the name of the `endpoint`. <br>Two methods preferentially use label matching, because the IP segment may conflict, and the label method exists as a supplement to the rules.",
|
||||
"EnableKoKoSSHHelpText": "Enabled, connect assets to display SSH Client pull-up method",
|
||||
@ -1283,6 +1321,7 @@
|
||||
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
|
||||
},
|
||||
"users": {
|
||||
"Login": "Users login",
|
||||
"InviteSuccess": "Invite success",
|
||||
"FileEncryptionPassword": "File encryption password",
|
||||
"OrgRoles": "Org roles",
|
||||
|
@ -3,7 +3,7 @@
|
||||
"accounts": {
|
||||
"HistoryPassword": "履歴パスワード",
|
||||
"PasswordRecord": "パスワードレコード",
|
||||
"AccountHistableHelpMessage": "現在のアカウントの履歴バージョンを記録する",
|
||||
"AccountHistoryHelpMessage": "現在のアカウントの履歴バージョンを記録する",
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "資産のアカウントのリスト、左側の資産をクリックして表示します",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "アカウントのリストを適用して、左側のアプリケーションをクリックして表示します",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "ユーザーリストを収集し、左側の資産をクリックして表示します。"
|
||||
@ -109,6 +109,11 @@
|
||||
"NoSQLProtocol": "非リレーショナルデータベース"
|
||||
},
|
||||
"assets": {
|
||||
"Secure": "安全である",
|
||||
"LabelInputFormatValidation": "フォーマットが正しくありません:name:value",
|
||||
"IP/Host": "IP/ノア",
|
||||
"OrganizationAsset": "組織資産",
|
||||
"PersonalAsset": "個人資産",
|
||||
"AssetAddress": "IP/ホスト名",
|
||||
"UserSwitchFrom": "ユーザーは",
|
||||
"sshkeyAccount": "SSHキー",
|
||||
@ -298,6 +303,7 @@
|
||||
"PasswordOrToken": "パスワード/トークン"
|
||||
},
|
||||
"audits": {
|
||||
"OperateRecord": "操作記録です",
|
||||
"Hosts": "本体",
|
||||
"RunUser": "実行ユーザー",
|
||||
"User": "ユーザー",
|
||||
@ -318,6 +324,11 @@
|
||||
"ReLoginErr": "ログイン時間が 5 分を超えました。もう一度ログインしてください"
|
||||
},
|
||||
"common": {
|
||||
"Activity": "イベント",
|
||||
"Last30": "最近 30 回です",
|
||||
"SpecificInfo": "特別情報",
|
||||
"CollectionSucceed": "コレクション成功",
|
||||
"CancelCollection": "収集の取消し",
|
||||
"NoContent": "まだ内容がない",
|
||||
"NeedAddAppsOrSystemUserErrMsg": "アプリケーションまたはシステムユーザーを追加してください",
|
||||
"VerificationCodeSent": "検証コードが送信されました",
|
||||
@ -665,7 +676,7 @@
|
||||
"FileTransferNum": "ファイル転送数",
|
||||
"LogOfLoginSuccessNum": "ログイン成功ログ数",
|
||||
"DangerousCommandNum": "危険命令数",
|
||||
"IndexName": "指標名",
|
||||
"SessionsNum": "セッション数",
|
||||
"BatchCommandNotExecuted": "未実行コマンド",
|
||||
"ExecuteFailedCommand": "失敗コマンドの実行",
|
||||
"SessionTrend": "セッショントレンド",
|
||||
@ -679,6 +690,7 @@
|
||||
"Weekly": "週ごと"
|
||||
},
|
||||
"ops": {
|
||||
"ManualInput": "手動入力",
|
||||
"Execute": "実行",
|
||||
"ID": "ID",
|
||||
"No": "いいえ",
|
||||
@ -977,6 +989,15 @@
|
||||
"Permissions": "権限",
|
||||
"NoPermission": "権限なし"
|
||||
},
|
||||
"terminal": {
|
||||
"UploadSucceed": "アップロード成功",
|
||||
"UploadFailed": "アップロードに失敗しました",
|
||||
"Applets": "リモートアプリケーション",
|
||||
"AppletHosts": "アプリケーションパブリッシャ",
|
||||
"uploadZipTips": "zip形式のファイルをアップロードしてください",
|
||||
"HostDeployment": "パブリッシャーの導入",
|
||||
"TerminalStat": "CPU/メモリ/ディスク"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "デフォルトストレージに設定",
|
||||
"SetToDefault": "デフォルトに設定",
|
||||
@ -1349,6 +1370,7 @@
|
||||
"UpdateNodeAssetHardwareInfo": "ノード資産ハードウェア情報の更新"
|
||||
},
|
||||
"users": {
|
||||
"Login": "ユーザー登録",
|
||||
"InviteSuccess": "成功招待",
|
||||
"FileEncryptionPassword": "ファイル暗号化パスワード",
|
||||
"RoleUsers": "承認されたユーザー",
|
||||
|
@ -1,12 +1,100 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"TaskID": "任务 ID",
|
||||
"DynamicUsername": "动态用户名",
|
||||
"HistoryPassword": "历史密码",
|
||||
"PasswordRecord": "密码记录",
|
||||
"AccountHistableHelpMessage": "记录当前账号的历史版本",
|
||||
"AccountHistoryHelpMessage": "记录当前账号的历史版本",
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "资产账号列表,点击左侧资产进行查看",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "应用账号列表,点击左侧应用进行查看",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "收集用户列表,点击左侧资产进行查看"
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "收集用户列表,点击左侧资产进行查看",
|
||||
"AutoCreate": "自动创建",
|
||||
"AccountPush": {
|
||||
"AccountPushList": "账号推送",
|
||||
"AccountPushCreate": "账号推送创建",
|
||||
"AccountPushExecutionList": "执行列表"
|
||||
},
|
||||
"AccountBackup": {
|
||||
"Types": "类型",
|
||||
"Backup": "备份",
|
||||
"AccountBackup": "账号备份",
|
||||
"AccountBackupCreate": "创建账号备份",
|
||||
"AccountBackupUpdate": "更新账号备份",
|
||||
"ExecutionDetail": "执行详情",
|
||||
"ExecutionList": "执行列表",
|
||||
"MailRecipient": "邮件收件人",
|
||||
"IsSuccess": "是否成功",
|
||||
"Reason": "原因"
|
||||
},
|
||||
"AccountChangeSecret": {
|
||||
"ContainAttachment": "含附件",
|
||||
"AddAsset": "添加资产",
|
||||
"MailRecipient": "邮件收件人",
|
||||
"AddNode": "添加节点",
|
||||
"AddSystemUser": "添加系统用户",
|
||||
"Asset": "资产",
|
||||
"Database": "数据库",
|
||||
"DatabaseId": "数据库Id",
|
||||
"AppAmount": "应用数量",
|
||||
"SystemUserAmount": "系统用户数量",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserId": "系统用户Id",
|
||||
"AssetAmount": "资产数量",
|
||||
"AssetAndNode": "资产和节点",
|
||||
"AccountChangeSecret": "账号改密",
|
||||
"AssetChangeSecretCreate": "创建账号改密",
|
||||
"AssetChangeSecretUpdate": "更新账号改密",
|
||||
"SymbolSet": "特殊符号集合",
|
||||
"SymbolSetHelpText": "请输入此类型数据库支持的特殊符号集合,若生成的随机密码中有此类数据库不支持的特殊字符,改密计划将会失败",
|
||||
"CyclePerform": "周期执行",
|
||||
"DateJoined": "创建日期",
|
||||
"DateStart": "开始日期",
|
||||
"DateUpdated": "更新日期",
|
||||
"Detail": "详情",
|
||||
"Execute": "执行",
|
||||
"ExecutionDetail": "执行详情",
|
||||
"ExecutionList": "执行列表",
|
||||
"ExecutionTimes": "执行次数",
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo1": "请确保该值大于或者等于 1"
|
||||
},
|
||||
"HelpText": {
|
||||
"CrontabOfCreateUpdatePage": "例如:每周日 03:05 执行 <5 3 * * 0> <br/> 使用5位 Linux crontab 表达式 <分 时 日 月 星期> (<a href=\"https://tool.lu/crontab/\" target=\"_blank\">在线工具</a>) <br/> 如果同时设置了定期执行和周期执行,优先使用定期执行",
|
||||
"IntervalOfCreateUpdatePage": "单位:时",
|
||||
"UsernameOfCreateUpdatePage": "目标主机上用户的用户名;如果已️存在,修改用户密码;如果不存在,添加用户并设置密码;"
|
||||
},
|
||||
"Log": "日志",
|
||||
"ManualExecutePlan": "手动执行计划",
|
||||
"Name": "名称",
|
||||
"NodeAmount": "节点数量",
|
||||
"PasswordLength": "密码长度",
|
||||
"ChangePassword": "更改密码",
|
||||
"ModifySSHKey": "修改 SSH Key",
|
||||
"Addressee": "收件人",
|
||||
"OnlyMailSend": "当前只支持邮件发送",
|
||||
"PasswordStrategy": "密文生成策略",
|
||||
"SecretKeyStrategy": "密码策略",
|
||||
"RegularlyPerform": "定期执行",
|
||||
"Result": "结果",
|
||||
"Retry": "重试",
|
||||
"Success": "成功",
|
||||
"TaskList": "任务记录",
|
||||
"TimeDelta": "运行时间",
|
||||
"Timer": "定时执行",
|
||||
"TimerPeriod": "定时执行周期",
|
||||
"Username": "用户名"
|
||||
},
|
||||
"AccountGather": {
|
||||
"AccountGather": "账号收集",
|
||||
"ExecutionList": "执行列表",
|
||||
"AccountGatherList": "收集任务",
|
||||
"AccountGatherTaskCreate": "创建任务",
|
||||
"AccountGatherTaskList": "账号收集",
|
||||
"AccountGatherTaskUpdate": "更新任务",
|
||||
"ExecutionDetail": "任务详情",
|
||||
"AccountGatherTaskExecutionList": "任务执行列表"
|
||||
}
|
||||
},
|
||||
"acl": {
|
||||
"IgnoreCase": "忽略大小写",
|
||||
@ -108,6 +196,17 @@
|
||||
"NoSQLProtocol": "非关系数据库"
|
||||
},
|
||||
"assets": {
|
||||
"Secure": "安全",
|
||||
"LabelInputFormatValidation": "标签格式错误,正确格式为:name:value",
|
||||
"GatewayList": "网关列表",
|
||||
"AccessKey": "访问密钥",
|
||||
"Token": "令牌",
|
||||
"SSHKey": "SSH 密钥",
|
||||
"ApiKey": "API Key",
|
||||
"IP/Host": "IP/主机",
|
||||
"AssetTree": "资产树",
|
||||
"CustomTree": "自定义树",
|
||||
"BuiltinTree": "类型树",
|
||||
"UserSwitchFrom": "用户切换自",
|
||||
"sshkeyAccount": "密钥账号",
|
||||
"passwordAccount": "密码账号",
|
||||
@ -220,7 +319,7 @@
|
||||
"Pending": "等待",
|
||||
"Platform": "系统平台",
|
||||
"PlatformDetail": "平台详情",
|
||||
"ProtocolsGroup": "协议组",
|
||||
"ProtocolsGroup": "协议",
|
||||
"DefaultPort": "默认端口",
|
||||
"primary": "主要的",
|
||||
"primaryOnly": "主要的协议, 只能有一个",
|
||||
@ -303,6 +402,7 @@
|
||||
"PasswordOrToken": "密码 / 令牌"
|
||||
},
|
||||
"audits": {
|
||||
"OperateRecord": "操作记录",
|
||||
"Hosts": "主机",
|
||||
"RunUser": "运行用户",
|
||||
"User": "用户",
|
||||
@ -323,8 +423,14 @@
|
||||
"ReLoginErr": "登录时长已超过 5 分钟,请重新登录"
|
||||
},
|
||||
"common": {
|
||||
"SpecificInfo": "特殊信息",
|
||||
"CollectionSucceed": "收藏成功",
|
||||
"CancelCollection": "取消收藏",
|
||||
"Activity": "活动",
|
||||
"Last30": "最近 30 次",
|
||||
"Version": "版本",
|
||||
"Publish": "发布",
|
||||
"PublishAllApplets": "发布所有应用",
|
||||
"Icon": "图标",
|
||||
"DisplayName": "名称",
|
||||
"Automations": "自动化",
|
||||
@ -683,7 +789,7 @@
|
||||
"FileTransferNum": "文件传输数",
|
||||
"LogOfLoginSuccessNum": "登录成功日志数",
|
||||
"DangerousCommandNum": "危险命令数",
|
||||
"IndexName": "指标名称",
|
||||
"SessionsNum": "会话数",
|
||||
"BatchCommandNotExecuted": "未执行批量命令",
|
||||
"ExecuteFailedCommand": "执行失败命令",
|
||||
"SessionTrend": "会话趋势",
|
||||
@ -697,8 +803,42 @@
|
||||
"Weekly": "按周"
|
||||
},
|
||||
"ops": {
|
||||
"UploadPlaybook": "上传 Playbook",
|
||||
"RequiredAssetOrNode": "请至少选择一个资产或节点",
|
||||
"RequiredContent": "请输入命令",
|
||||
"RequiredEntryFile": "此文件作为运行的入口文件,必须存在",
|
||||
"ScrollToTop": "滚动到顶部",
|
||||
"ScrollToBottom": "滚动到底部",
|
||||
"ClearScreen": "清屏",
|
||||
"Skip": "跳过",
|
||||
"PrivilegedFirst": "优先特权账号",
|
||||
"PrivilegedOnly": "仅特权账号",
|
||||
"SaveCommandSuccess": "保存命令成功",
|
||||
"TimeoutHelpText": "当此值为-1时,不指定超时时间",
|
||||
"Timeout": "超时(秒)",
|
||||
"RunCommand": "运行命令",
|
||||
"ManualInput": "手动输入",
|
||||
"DryRun": "测试运行",
|
||||
"Language": "语言",
|
||||
"Job": "作业",
|
||||
"Command": "命令",
|
||||
"Material": "内容",
|
||||
"Type": "类型",
|
||||
"DateCreated": "创建时间",
|
||||
"DateFinished": "完成时间",
|
||||
"Run": "运行",
|
||||
"Reason": "原因",
|
||||
"SuccessAsset": "成功的资产",
|
||||
"ExcludeAsset": "跳过的资产",
|
||||
"FailedAsset": "失败的资产",
|
||||
"ExecutionDetail": "执行详情",
|
||||
"ScriptDetail": "脚本详情",
|
||||
"AssetResultDetail": "资产结果",
|
||||
"VariableHelpText": "您可以在命令中使用 {{ key }} 读取内置变量",
|
||||
"ChdirHelpText": "默认执行目录为执行用户的 home 目录",
|
||||
"OpenCommand": "打开命令",
|
||||
"SaveCommand": "保存命令 ",
|
||||
"Help": "帮助",
|
||||
"Log": "日志",
|
||||
"Execute": "执行",
|
||||
"ID": "ID",
|
||||
@ -728,11 +868,15 @@
|
||||
"ratio": "比例",
|
||||
"run": "执行",
|
||||
"runAs": "运行用户",
|
||||
"RunasPolicy": "账号策略",
|
||||
"runTimes": "执行次数",
|
||||
"selectAssetsMessage": "选择左侧资产, 选择运行的系统用户,批量执行命令",
|
||||
"selectedAssets": "已选择资产:",
|
||||
"stat": "成功/失败/总",
|
||||
"success": "成功",
|
||||
"failed": "失败",
|
||||
"timeout": "超时",
|
||||
"running": "运行中",
|
||||
"total": "总共",
|
||||
"taskDetail": "任务详情",
|
||||
"taskName": "任务名称",
|
||||
@ -753,7 +897,7 @@
|
||||
"privilegeOnly": "仅选择特权账号",
|
||||
"privilegeFirst": "优先选择特权账号",
|
||||
"skip": "忽略当前资产",
|
||||
"QuickJob": "快捷作业",
|
||||
"QuickJob": "快捷命令",
|
||||
"instantAdhoc": "即时命令",
|
||||
"AdhocManage": "命令管理",
|
||||
"PlaybookManage": "Playbook管理",
|
||||
@ -871,7 +1015,7 @@
|
||||
"TicketFlowCreate": "创建审批流",
|
||||
"TicketFlowUpdate": "更新审批流",
|
||||
"Accounts": "账号管理",
|
||||
"AssetAccount": "资产账号",
|
||||
"AssetAccount": "账号列表",
|
||||
"AccountTemplate": "账号模版",
|
||||
"CreateAccountTemplate": "创建账号模版",
|
||||
"UpdateAccountTemplate": "更新账号模版",
|
||||
@ -906,12 +1050,12 @@
|
||||
"SessionList": "会话记录",
|
||||
"BatchCommand": "批量命令",
|
||||
"BatchScript": "批量脚本",
|
||||
"Executions": "执行历史",
|
||||
"ScriptManage": "脚本管理",
|
||||
"Execution": "执行历史",
|
||||
"Template": "模版管理",
|
||||
"TicketsTodo": "待办工单",
|
||||
"TicketsDone": "已办工单",
|
||||
"TicketsNew": "提交工单",
|
||||
"tasksLog": "批量命令",
|
||||
"JobExecutionLog": "作业日志",
|
||||
"CeleryTaskLog": "Celery任务日志",
|
||||
"CommandExecutions": "命令执行",
|
||||
"CommandFilterCreate": "创建命令过滤器",
|
||||
@ -977,7 +1121,7 @@
|
||||
"JobUpdate": "更新作业",
|
||||
"JobDetail": "作业详情",
|
||||
"LabelCreate": "创建标签",
|
||||
"LabelList": "标签管理",
|
||||
"LabelList": "标签列表",
|
||||
"LabelUpdate": "更新标签",
|
||||
"LoginLog": "登录日志",
|
||||
"MyApps": "我的应用",
|
||||
@ -1054,6 +1198,10 @@
|
||||
"NoPermission": "暂无权限"
|
||||
},
|
||||
"terminal": {
|
||||
"BasePort": "监听端口",
|
||||
"DatabasePort": "数据库协议端口",
|
||||
"UploadSucceed": "上传成功",
|
||||
"UploadFailed": "上传失败",
|
||||
"Applets": "远程应用",
|
||||
"AppletHosts": "应用发布机",
|
||||
"uploadZipTips": "请上传zip格式的文件",
|
||||
@ -1335,7 +1483,7 @@
|
||||
"LicenseExpired": "许可证已经过期",
|
||||
"LicenseWillBe": "许可证即将在 ",
|
||||
"LicenseReachedAssetAmountLimit": "资产数量已经超过许可证数量限制",
|
||||
"LicenseForTest": "测试用途许可证, 本许可证仅用于 开发、测试(PoC)和演示",
|
||||
"LicenseForTest": "测试用途许可证, 本许可证仅用于 测试(PoC)和演示",
|
||||
"Expire": " 过期",
|
||||
"WeCom": "企业微信",
|
||||
"DingTalk": "钉钉",
|
||||
@ -1351,8 +1499,7 @@
|
||||
"destinationIP": "目的地址",
|
||||
"testPort": "端口",
|
||||
"testTools": "测试",
|
||||
"testHelpText": "请输入目的地址进行测试",
|
||||
"SMSProvider": "短信服务商 / 协议"
|
||||
"testHelpText": "请输入目的地址进行测试"
|
||||
},
|
||||
"tickets": {
|
||||
"OneAssigneeType": "一级受理人类型",
|
||||
@ -1437,6 +1584,7 @@
|
||||
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
|
||||
},
|
||||
"users": {
|
||||
"Login": "用户登录",
|
||||
"InviteSuccess": "邀请成功",
|
||||
"FileEncryptionPassword": "文件加密密码",
|
||||
"RoleUsers": "授权用户",
|
||||
@ -1589,79 +1737,6 @@
|
||||
"Database": "数据库",
|
||||
"AssetCount": "资产数量",
|
||||
"Auditor": "审计员",
|
||||
"ChangeAuthPlan": {
|
||||
"ContainAttachment": "含附件",
|
||||
"AddAsset": "添加资产",
|
||||
"MailRecipient": "邮件收件人",
|
||||
"AddNode": "添加节点",
|
||||
"AddSystemUser": "添加系统用户",
|
||||
"Asset": "资产",
|
||||
"Database": "数据库",
|
||||
"DatabaseId": "数据库Id",
|
||||
"AppAmount": "应用数量",
|
||||
"SystemUserAmount": "系统用户数量",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserId": "系统用户Id",
|
||||
"AssetAmount": "资产数量",
|
||||
"AssetAndNode": "资产和节点",
|
||||
"ChangeAuthPlan": "改密计划",
|
||||
"AssetChangeAuthPlan": "资产改密计划",
|
||||
"AppChangeAuthPlan": "应用改密计划",
|
||||
"AssetChangeAuthPlanCreate": "创建资产改密计划",
|
||||
"AppChangeAuthPlanCreate": "创建应用改密计划",
|
||||
"AssetChangeAuthPlanUpdate": "更新资产改密计划",
|
||||
"AppChangeAuthPlanUpdate": "更新应用改密计划",
|
||||
"SymbolSet": "特殊符号集合",
|
||||
"SymbolSetHelpText": "请输入此类型数据库支持的特殊符号集合,若生成的随机密码中有此类数据库不支持的特殊字符,改密计划将会失败",
|
||||
"CyclePerform": "周期执行",
|
||||
"DateJoined": "创建日期",
|
||||
"DateStart": "开始日期",
|
||||
"DateUpdated": "更新日期",
|
||||
"Detail": "详情",
|
||||
"Execute": "执行",
|
||||
"ExecutionDetail": "执行详情",
|
||||
"ExecutionList": "执行列表",
|
||||
"ExecutionTimes": "执行次数",
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo1": "请确保该值大于或者等于 1"
|
||||
},
|
||||
"HelpText": {
|
||||
"CrontabOfCreateUpdatePage": "例如:每周日 03:05 执行 <5 3 * * 0> <br/> 使用5位 Linux crontab 表达式 <分 时 日 月 星期> (<a href=\"https://tool.lu/crontab/\" target=\"_blank\">在线工具</a>) <br/> 如果同时设置了定期执行和周期执行,优先使用定期执行",
|
||||
"IntervalOfCreateUpdatePage": "单位:时",
|
||||
"UsernameOfCreateUpdatePage": "目标主机上用户的用户名;如果已️存在,修改用户密码;如果不存在,添加用户并设置密码;"
|
||||
},
|
||||
"Log": "日志",
|
||||
"ManualExecutePlan": "手动执行计划",
|
||||
"Name": "名称",
|
||||
"NodeAmount": "节点数量",
|
||||
"PasswordLength": "密码长度",
|
||||
"ChangePassword": "更改密码",
|
||||
"ModifySSHKey": "修改 SSH Key",
|
||||
"Addressee": "收件人",
|
||||
"OnlyMailSend": "当前只支持邮件发送",
|
||||
"PasswordStrategy": "密码策略",
|
||||
"SecretKeyStrategy": "密钥策略",
|
||||
"RegularlyPerform": "定期执行",
|
||||
"Result": "结果",
|
||||
"Retry": "重试",
|
||||
"Success": "成功",
|
||||
"TaskList": "任务列表",
|
||||
"TimeDelta": "运行时间",
|
||||
"Timer": "定时执行",
|
||||
"TimerPeriod": "定时执行周期",
|
||||
"Username": "用户名"
|
||||
},
|
||||
"AccountBackupPlan": {
|
||||
"Types": "类型",
|
||||
"Backup": "备份",
|
||||
"AccountBackupPlan": "账号备份",
|
||||
"AccountBackupPlanCreate": "创建账号备份",
|
||||
"AccountBackupPlanUpdate": "更新账号备份",
|
||||
"ExecutionDetail": "执行详情",
|
||||
"MailRecipient": "邮件收件人",
|
||||
"IsSuccess": "是否成功",
|
||||
"Reason": "原因"
|
||||
},
|
||||
"Cloud": {
|
||||
"CloudSync": "云同步",
|
||||
"ServerAccountKey": "服务账号密钥",
|
||||
@ -1681,7 +1756,7 @@
|
||||
"BaiduCloud": "百度云",
|
||||
"JDCloud": "京东云",
|
||||
"KingSoftCloud": "金山云",
|
||||
"Azure":"Azure(中国)",
|
||||
"Azure": "Azure(中国)",
|
||||
"Azure_Int": "Azure(国际)",
|
||||
"HostnameStrategy": "用于生成资产主机名。例如:1. 实例名称 (instanceDemo);2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "资产信息保持最新",
|
||||
@ -1734,15 +1809,6 @@
|
||||
"Edition": "版本",
|
||||
"Execute": "执行",
|
||||
"Expired": "过期时间",
|
||||
"GatherUser": {
|
||||
"GatherUser": "收集用户",
|
||||
"GatherUserList": "收集用户",
|
||||
"GatherUserTaskCreate": "创建任务",
|
||||
"GatherUserTaskList": "任务列表",
|
||||
"GatherUserTaskUpdate": "更新任务",
|
||||
"GatherUserTaskDetail": "任务详情",
|
||||
"GatherUserTaskExecutionList": "任务执行列表"
|
||||
},
|
||||
"Import": "导入",
|
||||
"ImportLicense": "导入许可证",
|
||||
"ImportLicenseTip": "请导入许可证",
|
||||
|
1
src/icons/svg/close.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 8L40 40" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 40L40 8" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
After Width: | Height: | Size: 339 B |
1
src/icons/svg/double-left.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M24 36L12 24L24 12" stroke="#333" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M36 36L24 24L36 12" stroke="#333" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
After Width: | Height: | Size: 355 B |
1
src/icons/svg/save.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg t="1671772969164" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5674" width="200" height="200"><path d="M597.333333 85.333333v202.752a10.666667 10.666667 0 0 1-8.234666 10.410667l-2.432 0.256h-256a10.666667 10.666667 0 0 1-10.368-8.192L320 288.085333V85.333333H597.333333z m139.050667 513.706667a10.666667 10.666667 0 0 1 10.666667 10.666667l-0.042667 307.626666H277.290667l0.042666-307.626666a10.666667 10.666667 0 0 1 10.666667-10.666667h448.384zM667.136 85.333333c11.776 0 23.168 3.882667 32.426667 10.965334L923.733333 288c10.538667 10.069333 14.933333 24.234667 14.933334 38.826667v494.506666a96 96 0 0 1-96 96h-31.658667v-307.626666a74.666667 74.666667 0 0 0-74.666667-74.666667H288A74.666667 74.666667 0 0 0 213.333333 609.706667l-0.042666 307.626666H181.333333A96 96 0 0 1 85.333333 821.333333v-640A96 96 0 0 1 181.333333 85.333333H256v202.752a74.666667 74.666667 0 0 0 68.522667 74.453334l6.144 0.213333h256a74.666667 74.666667 0 0 0 74.410666-68.522667l0.256-6.144V85.333333h5.802667z" fill="#888888" p-id="5675"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
1
src/icons/svg/search.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#646A73" d="M16.473 17.887A9.46 9.46 0 0 1 10.5 20a9.5 9.5 0 1 1 9.5-9.5 9.46 9.46 0 0 1-2.113 5.973l4.123 4.123a.5.5 0 0 1 0 .707l-.707.707a.5.5 0 0 1-.707 0l-4.123-4.123ZM18 10.5a7.5 7.5 0 1 0-15 0 7.5 7.5 0 0 0 15 0Z"/><path fill-opacity=".2" fill="#000" d="M16.473 17.887A9.46 9.46 0 0 1 10.5 20a9.5 9.5 0 1 1 9.5-9.5 9.46 9.46 0 0 1-2.113 5.973l4.123 4.123a.5.5 0 0 1 0 .707l-.707.707a.5.5 0 0 1-.707 0l-4.123-4.123ZM18 10.5a7.5 7.5 0 1 0-15 0 7.5 7.5 0 0 0 15 0Z"/></svg>
|
After Width: | Height: | Size: 561 B |
1
src/icons/svg/tools.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><g clip-path="url(#a)" stroke-linejoin="round" stroke-width="2" stroke="#646A73" data-follow-stroke="#646A73"><path stroke-linecap="round" d="m1.5 11.666 3.75 3.75a.758.758 0 0 0 1.071 0L9 12.739a.758.758 0 0 0 0-1.072l-3.75-3.75"/><path d="m8.464 17.56 2.679-2.68a.758.758 0 0 1 1.071 0l3.75 3.75a.758.758 0 0 1 0 1.072l-2.678 2.679a.758.758 0 0 1-1.072 0l-3.75-3.75a.758.758 0 0 1 0-1.072Z"/><path stroke-linecap="round" d="m6.054 12.47 5.357 5.357M22.685 12.75 18.935 9a.758.758 0 0 0-1.072 0l-2.678 2.679a.758.758 0 0 0 0 1.071l3.75 3.75"/><path d="m15.72 6.857-2.678 2.679a.758.758 0 0 1-1.072 0l-3.75-3.75a.758.758 0 0 1 0-1.072L10.9 2.036a.758.758 0 0 1 1.071 0l3.75 3.75a.758.758 0 0 1 0 1.071Z"/><path stroke-linecap="round" d="M18.131 11.946 12.774 6.59"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h24v24H0z" data-follow-fill="#fff"/></clipPath></defs></svg>
|
After Width: | Height: | Size: 950 B |
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<section class="app-main">
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<transition mode="out-in" name="fade-transform">
|
||||
<keep-alive :include="cachedViews">
|
||||
<router-view :key="key" />
|
||||
</keep-alive>
|
||||
@ -25,10 +25,10 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.app-main {
|
||||
background-color: #f3f3f4;
|
||||
min-height: calc(100vh - 0px);
|
||||
height: 100%!important;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
/*padding: 10px 20px 10px;*/
|
||||
}
|
||||
.fixed-header+.app-main {
|
||||
|
@ -153,18 +153,18 @@ export default {
|
||||
}
|
||||
|
||||
.organization {
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background: transparent;
|
||||
color: #FFF;
|
||||
|
||||
&>>> .el-input__prefix {
|
||||
& >>> .el-input__prefix {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&>>> .el-input--prefix .el-input__inner {
|
||||
line-height: 35px !important;
|
||||
height: 35px !important;
|
||||
& >>> .el-input--prefix .el-input__inner {
|
||||
line-height: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
& >>> .el-input__icon {
|
||||
|
@ -216,7 +216,7 @@ export default {
|
||||
padding: 0 25px 20px;
|
||||
}
|
||||
|
||||
> > > .site-msg {
|
||||
>>> .site-msg {
|
||||
.el-drawer__header {
|
||||
border-bottom: solid 1px rgb(231, 234, 239);
|
||||
margin-bottom: 0;
|
||||
@ -314,8 +314,9 @@ export default {
|
||||
.msg-detail-txt {
|
||||
margin-bottom: 20px;
|
||||
line-height: 25px;
|
||||
&>>> a {
|
||||
color: var(--color-success)!important;
|
||||
|
||||
& >>> a {
|
||||
color: var(--color-success) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -325,7 +326,7 @@ export default {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
> > > :focus {
|
||||
>>> :focus {
|
||||
outline: 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -2,22 +2,22 @@
|
||||
<div class="navbar">
|
||||
<ul class="navbar-right">
|
||||
<li class="header-item header-icon">
|
||||
<el-tooltip effect="dark" :content="$tc('route.SiteMessageList')">
|
||||
<el-tooltip :content="$tc('route.SiteMessageList')" effect="dark">
|
||||
<SiteMessages />
|
||||
</el-tooltip>
|
||||
</li>
|
||||
<li v-perms="['rbac.view_webterminal']" class="header-item header-icon">
|
||||
<el-tooltip effect="dark" :content="$tc('route.WebTerminal')">
|
||||
<el-tooltip :content="$tc('route.WebTerminal')" effect="dark">
|
||||
<WebTerminal />
|
||||
</el-tooltip>
|
||||
</li>
|
||||
<li v-perms="'settings.view_setting'" class="header-item header-icon">
|
||||
<el-tooltip effect="dark" :content="$tc('route.SystemSetting')">
|
||||
<el-tooltip :content="$tc('route.SystemSetting')" effect="dark">
|
||||
<SystemSetting />
|
||||
</el-tooltip>
|
||||
</li>
|
||||
<li v-if="ticketsEnabled" class="header-item header-hover">
|
||||
<el-tooltip effect="dark" :content="$tc('route.Ticket')">
|
||||
<el-tooltip :content="$tc('route.Ticket')" effect="dark">
|
||||
<Tickets />
|
||||
</el-tooltip>
|
||||
</li>
|
||||
@ -130,10 +130,9 @@ export default {
|
||||
.navbar-right {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
height: 55px;
|
||||
line-height: 55px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
.header-hover {
|
||||
line-height: 56px!important;
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, .2);
|
||||
}
|
||||
@ -191,7 +190,7 @@ export default {
|
||||
line-height: 30px;
|
||||
border-radius: 4px;
|
||||
border-color: $--color-primary;
|
||||
background-color: $--color-primary;
|
||||
background-color: white;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: .2s;
|
||||
@ -224,5 +223,10 @@ export default {
|
||||
>>> .el-badge__content--primary {
|
||||
background-color: #fff;
|
||||
}
|
||||
.organization {
|
||||
border-radius: 20px;
|
||||
background-color: rgba(255, 255, 255, .15);
|
||||
padding-left: 10px!important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -3,24 +3,22 @@
|
||||
<div class="nav-header">
|
||||
<div class="active-mobile">
|
||||
<Organization v-if="$hasLicense()" class="organization" />
|
||||
<ViewSwitcher mode="vertical" class="mobile-view-switch" />
|
||||
<ViewSwitcher class="mobile-view-switch" mode="vertical" />
|
||||
</div>
|
||||
<div class="nav-title" :class="{'collapsed': isCollapse}">
|
||||
<svg-icon
|
||||
:icon-class="isRouteMeta.icon"
|
||||
style="margin-right: 0;"
|
||||
/>
|
||||
<div :class="{'collapsed': isCollapse}" class="nav-title">
|
||||
<span
|
||||
v-show="!isCollapse"
|
||||
style="margin-left: 10px;"
|
||||
style="margin-left: 5px;"
|
||||
@click="viewShown = !viewShown"
|
||||
>
|
||||
{{ isRouteMeta.title || '' }}
|
||||
</span>
|
||||
<span v-show="!isCollapse" class="switch-view active-switch-view">
|
||||
<span class="switch-view active-switch-view">
|
||||
<el-popover
|
||||
v-model="viewShown"
|
||||
placement="right-start"
|
||||
width="160"
|
||||
trigger="hover"
|
||||
width="160"
|
||||
>
|
||||
<ViewSwitcher :mode="'vertical'" />
|
||||
<svg-icon slot="reference" class="icon" icon-class="switch" />
|
||||
@ -30,23 +28,23 @@
|
||||
</div>
|
||||
<el-scrollbar class="menu-wrap" wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
class="left-menu"
|
||||
:active-text-color="variables['menuActiveText']"
|
||||
:background-color="variables['menuBg']"
|
||||
:collapse="isCollapse"
|
||||
:collapse-transition="false"
|
||||
:default-active="activeMenu"
|
||||
:default-openeds="defaultOpensMenu"
|
||||
:collapse="isCollapse"
|
||||
:background-color="variables['menuBg']"
|
||||
:text-color="variables['menuText']"
|
||||
:text-weigth="variables['menuTextWeight']"
|
||||
:active-text-color="variables['menuActiveText']"
|
||||
:unique-opened="true"
|
||||
:collapse-transition="false"
|
||||
class="left-menu"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="route in currentViewRoute.children"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
:item="route"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
@ -73,6 +71,11 @@ export default {
|
||||
ViewSwitcher,
|
||||
Organization
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewShown: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'currentViewRoute',
|
||||
@ -148,6 +151,7 @@ export default {
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background-color: var(--menu-bg);
|
||||
transition: all 0.3s;
|
||||
|
||||
.switch-view {
|
||||
position: absolute;
|
||||
@ -158,17 +162,20 @@ export default {
|
||||
padding: 3px;
|
||||
line-height: 10px;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: var(--menu-hover)!important;
|
||||
background: var(--menu-hover) !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 0!important;
|
||||
margin-right: 0 !important;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active-switch-view {
|
||||
display: inline-block;
|
||||
}
|
||||
@ -216,7 +223,7 @@ export default {
|
||||
padding-left: 8px;
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid rgba(31,35,41,.15);
|
||||
border-bottom: 1px solid rgba(31, 35, 41, .15);
|
||||
}
|
||||
|
||||
& > > > .menu-main {
|
||||
@ -226,7 +233,8 @@ export default {
|
||||
& > > > .title-label {
|
||||
color: white !important;
|
||||
}
|
||||
.mobile-view-switch >>> .el-menu-item.is-active {
|
||||
|
||||
.mobile-view-switch > > > .el-menu-item.is-active {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
@ -236,7 +244,7 @@ export default {
|
||||
display: block;
|
||||
}
|
||||
.active-switch-view {
|
||||
display: none!important;;
|
||||
display: none !important;;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -56,7 +56,7 @@ export default {
|
||||
.page-heading-left, h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
line-height: 32px;
|
||||
color: #1F2329;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@media print {
|
||||
.disabled-when-print{
|
||||
display: none;
|
||||
|
@ -133,7 +133,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang='scss' scoped>
|
||||
.page-submenu >>> .el-tabs__header {
|
||||
background-color: white;
|
||||
margin-left: -25px;
|
||||
@ -141,6 +141,14 @@ export default {
|
||||
margin-right: -25px;
|
||||
padding-right: 25px;
|
||||
margin-top: -30px;
|
||||
|
||||
.el-tabs__item {
|
||||
|
||||
i {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.page-submenu >>> .el-tabs__nav-wrap {
|
||||
|
@ -62,8 +62,9 @@ export default {
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
&.mobile.openSidebar{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -63,8 +63,8 @@ window._ = require('lodash')
|
||||
import { Message } from '@/utils/Message'
|
||||
Vue.prototype.$message = Message
|
||||
|
||||
import VueDOMPurifyHTML from 'vue-dompurify-html'
|
||||
Vue.use(VueDOMPurifyHTML)
|
||||
import xss from '@/utils/xss'
|
||||
Vue.prototype.$xss = xss
|
||||
|
||||
// 注册全局事件总线
|
||||
Vue.prototype.$eventBus = new Vue()
|
||||
|
@ -29,12 +29,12 @@ export default [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'command-execution-log',
|
||||
name: 'tasksLog',
|
||||
component: () => import('@/views/audits/CommandExecutionList'),
|
||||
path: 'job-execution-log',
|
||||
name: 'JobExecutionLog',
|
||||
component: () => import('@/views/audits/JobExecutionLogList'),
|
||||
meta: {
|
||||
title: i18n.t('route.tasksLog'),
|
||||
permissions: ['ops.view_commandexecution']
|
||||
title: i18n.t('route.JobExecutionLog'),
|
||||
permissions: []
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -7,8 +7,8 @@ export default [
|
||||
component: empty,
|
||||
meta: {
|
||||
title: i18n.t('route.AssetAccount'),
|
||||
app: 'assets',
|
||||
permissions: ['assets.view_account']
|
||||
app: 'accounts',
|
||||
permissions: ['accounts.view_account']
|
||||
},
|
||||
redirect: '',
|
||||
children: [
|
||||
@ -18,8 +18,8 @@ export default [
|
||||
component: () => import('@/views/accounts/AssetAccount/AssetAccountList'),
|
||||
meta: {
|
||||
title: i18n.t('route.AssetAccount'),
|
||||
app: 'assets',
|
||||
permissions: ['assets.view_account']
|
||||
app: 'accounts',
|
||||
permissions: ['accounts.view_account']
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -36,8 +36,8 @@ export default [
|
||||
component: empty,
|
||||
meta: {
|
||||
title: i18n.t('route.AccountTemplate'),
|
||||
app: 'assets',
|
||||
permissions: ['assets.view_accounttemplate']
|
||||
app: 'accounts',
|
||||
permissions: ['accounts.view_accounttemplate']
|
||||
},
|
||||
redirect: '',
|
||||
children: [
|
||||
@ -47,7 +47,7 @@ export default [
|
||||
component: () => import('@/views/accounts/AccountTemplate/AccountTemplateList'),
|
||||
meta: {
|
||||
title: i18n.t('route.AccountTemplate'),
|
||||
permissions: ['assets.view_accounttemplate']
|
||||
permissions: ['accounts.view_accounttemplate']
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -80,169 +80,246 @@ export default [
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'gathered-user',
|
||||
path: 'account-push',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: {
|
||||
title: i18n.t('xpack.GatherUser.GatherUserList'),
|
||||
app: 'assets',
|
||||
app: 'accounts',
|
||||
name: 'AccountPushList',
|
||||
resource: 'pushaccountautomation'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/AccountPush/index.vue'),
|
||||
name: 'AccountPushList',
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountPush.AccountPushList'),
|
||||
permissions: ['accounts.view_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushCreateUpdate.vue'),
|
||||
name: 'AccountPushCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountPush.AccountPushCreate'),
|
||||
permissions: ['accounts.add_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushCreateUpdate.vue'),
|
||||
name: 'AccountPushUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountPush.AccountPushUpdate'),
|
||||
permissions: ['accounts.change_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushDetail/index.vue'),
|
||||
name: 'AccountPushDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountChangeSecret.AccountChangeSecret'),
|
||||
permissions: ['accounts.view_pushaccountautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushExecutionList.vue'),
|
||||
name: 'AccountPushExecutionList',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.AccountPush.ExecutionList'),
|
||||
permissions: ['accounts.view_pushaccountexecution']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions/:id',
|
||||
component: () => import('@/views/accounts/AccountPush/AccountPushExecutionDetail/index.vue'),
|
||||
name: 'AccountPushExecutionDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountChangeSecret.ExecutionDetail'),
|
||||
permissions: ['accounts.view_pushaccountexecution']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'account-gather',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountGather.AccountGatherList'),
|
||||
app: 'accounts',
|
||||
licenseRequired: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/GatheredUser/index'),
|
||||
name: 'GatherUserListIndex',
|
||||
component: () => import('@/views/accounts/AccountGather/index.vue'),
|
||||
name: 'AccountGatherList',
|
||||
meta: {
|
||||
title: i18n.t('xpack.GatherUser.GatherUser'),
|
||||
permissions: ['assets.view_gathereduser|xpack.view_gatherusertask']
|
||||
title: i18n.t('accounts.AccountGather.AccountGatherTaskList'),
|
||||
permissions: ['accounts.view_gatheraccountsautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/GatheredUser/GatheredUserList'),
|
||||
name: 'GatherUserList',
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountGather/TaskCreateUpdate'),
|
||||
name: 'AccountGatherTaskCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.GatherUser.GatherUserList'),
|
||||
activeMenu: '/accounts/gathered-user'
|
||||
title: i18n.t('accounts.AccountGather.AccountGatherTaskCreate'),
|
||||
permissions: ['accounts.add_gatheraccountsautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tasks/create',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskCreateUpdate'),
|
||||
name: 'GatherUserTaskCreate',
|
||||
path: ':id',
|
||||
component: () => import('@/views/accounts/AccountGather/TaskDetail/index'),
|
||||
name: 'AccountGatherTaskDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.GatherUser.GatherUserTaskCreate'),
|
||||
permissions: ['xpack.add_gatherusertask']
|
||||
title: i18n.t('accounts.AccountGather.AccountGatherTaskDetail'),
|
||||
permissions: ['accounts.view_gatheraccountsautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tasks/:id',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskDetail/index'),
|
||||
name: 'GatherUserTaskDetail',
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/accounts/AccountGather/TaskCreateUpdate'),
|
||||
name: 'AccountGatherTaskUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.GatherUser.GatherUserTaskDetail'),
|
||||
permissions: ['xpack.view_gatherusertask']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tasks/:id/update',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskCreateUpdate'),
|
||||
name: 'GatherUserTaskUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.GatherUser.GatherUserTaskUpdate'),
|
||||
title: i18n.t('accounts.AccountGather.AccountGatherTaskUpdate'),
|
||||
action: 'update',
|
||||
permissions: ['xpack.change_gatherusertask'],
|
||||
activeMenu: '/accounts/gathered-user'
|
||||
permissions: ['accounts.change_gatheraccountsautomation'],
|
||||
activeMenu: '/accounts/account-gather'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions',
|
||||
component: () => import('@/views/accounts/AccountGather/TaskDetail/TaskExecutionList.vue'),
|
||||
name: 'AccountGatherTaskExecutionList',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountGather.ExecutionList'),
|
||||
permissions: ['accounts.view_gatheraccountsexecution']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions/:id',
|
||||
component: () => import('@/views/accounts/AccountGather/TaskDetail/AccountGatherExecutionDetail/index.vue'),
|
||||
name: 'AccountGatherExecutionDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountGather.ExecutionDetail'),
|
||||
permissions: ['accounts.view_gatheraccountsexecution']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'change-secret-automation',
|
||||
path: 'account-change-secret',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'),
|
||||
app: 'xpack',
|
||||
title: i18n.t('accounts.AccountChangeSecret.AccountChangeSecret'),
|
||||
app: 'accounts',
|
||||
licenseRequired: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/ChangeSecreAtutomation/index.vue'),
|
||||
name: 'ChangeSecreAtutomationIndex',
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/index.vue'),
|
||||
name: 'AccountChangeSecretList',
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'),
|
||||
permissions: ['assets.view_changesecretautomation']
|
||||
title: i18n.t('accounts.AccountChangeSecret.AccountChangeSecret'),
|
||||
permissions: ['accounts.view_changesecretautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'atutomation',
|
||||
component: () => import('@/views/accounts/ChangeSecreAtutomation/ChangeSecreAtutomationList.vue'),
|
||||
name: 'ChangeSecreAtutomationList',
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.AssetChangeAuthPlan'),
|
||||
permissions: ['assets.view_changesecretautomation']
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'atutomation/create',
|
||||
component: () => import('@/views/accounts/ChangeSecreAtutomation/ChangeSecreAtutomationCreateUpdate.vue'),
|
||||
name: 'ChangeSecreAtutomationCreate',
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/AccountChangeSecretCreateUpdate.vue'),
|
||||
name: 'AccountChangeSecretCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.AssetChangeAuthPlanCreate'),
|
||||
permissions: ['assets.add_changesecretautomation']
|
||||
title: i18n.t('accounts.AccountChangeSecret.AssetChangeSecretCreate'),
|
||||
permissions: ['accounts.add_changesecretautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'atutomation/:id/update',
|
||||
component: () => import('@/views/accounts/ChangeSecreAtutomation/ChangeSecreAtutomationCreateUpdate.vue'),
|
||||
name: 'ChangeSecreAtutomationUpdate',
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/AccountChangeSecretCreateUpdate.vue'),
|
||||
name: 'AccountChangeSecretUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.AssetChangeAuthPlanUpdate'),
|
||||
permissions: ['assets.change_changesecretautomation']
|
||||
title: i18n.t('accounts.AccountChangeSecret.AssetChangeSecretUpdate'),
|
||||
permissions: ['accounts.change_changesecretautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'atutomation/:id',
|
||||
component: () => import('@/views/accounts/ChangeSecreAtutomation/ChangeSecreAtutomationDetail/index.vue'),
|
||||
name: 'ChangeSecreAtutomationDetail',
|
||||
path: ':id',
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/AccountChangeSecretDetail/index.vue'),
|
||||
name: 'AccountChangeSecretDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.AssetChangeAuthPlan'),
|
||||
permissions: ['assets.view_changesecretautomation']
|
||||
title: i18n.t('accounts.AccountChangeSecret.AccountChangeSecret'),
|
||||
permissions: ['accounts.view_changesecretautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'execution/:id',
|
||||
component: () => import('@/views/accounts/ChangeSecreAtutomation/ChangeSecreAtutomationDetail/ChangeSecreAtutomationExecution/ChangeSecreAtutomationExecutionDetail/index.vue'),
|
||||
name: 'ChangeAuthPlanExecutionDetail',
|
||||
path: 'executions',
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/AccountChangeSecretDetail/AccountChangeSecretExecution/AccountChangeSecretExecutionList.vue'),
|
||||
name: 'AccountChangeSecretExecutionList',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.ChangeAuthPlan.ExecutionDetail'),
|
||||
permissions: ['assets.view_automationexecution']
|
||||
title: i18n.t('accounts.AccountChangeSecret.ExecutionList'),
|
||||
permissions: ['accounts.view_changesecretexecution']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions/:id',
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/AccountChangeSecretDetail/AccountChangeSecretExecution/AccountChangeSecretExecutionDetail/index.vue'),
|
||||
name: 'AccountChangeSecretExecutionDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountChangeSecret.ExecutionDetail'),
|
||||
permissions: ['accounts.view_changesecretexecution']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'backup',
|
||||
path: 'account-backup',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: {
|
||||
title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan'),
|
||||
app: 'assets',
|
||||
resource: 'accountbackupplan',
|
||||
title: i18n.t('accounts.AccountBackup.AccountBackup'),
|
||||
app: 'accounts',
|
||||
resource: 'accountbackupautomation',
|
||||
licenseRequired: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanList'),
|
||||
name: 'AccountBackupPlanIndex',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan') }
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanList.vue'),
|
||||
name: 'AccountBackupPlanList',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan') },
|
||||
hidden: true
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/index.vue'),
|
||||
name: 'AccountBackupList',
|
||||
meta: {
|
||||
title: i18n.t('accounts.AccountBackup.AccountBackup'),
|
||||
permissions: ['accounts.view_accountbackupautomation']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanCreateUpdate.vue'),
|
||||
name: 'AccountBackupPlanCreate',
|
||||
meta: {
|
||||
title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlanCreate'),
|
||||
title: i18n.t('accounts.AccountBackup.AccountBackupCreate'),
|
||||
action: 'create'
|
||||
},
|
||||
hidden: true
|
||||
@ -252,7 +329,7 @@ export default [
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanCreateUpdate.vue'),
|
||||
name: 'AccountBackupPlanUpdate',
|
||||
meta: {
|
||||
title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlanUpdate'),
|
||||
title: i18n.t('accounts.AccountBackup.AccountBackupUpdate'),
|
||||
action: 'update'
|
||||
},
|
||||
hidden: true
|
||||
@ -261,14 +338,21 @@ export default [
|
||||
path: ':id',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanDetail/index.vue'),
|
||||
name: 'AccountBackupPlanDetail',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan') },
|
||||
meta: { title: i18n.t('accounts.AccountBackup.AccountBackup') },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'plan-execution/:id',
|
||||
path: 'executions',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanDetail/AccountBackupPlanExecution/AccountBackupPlanExecutionList.vue'),
|
||||
name: 'AccountBackupPlanExecutionList',
|
||||
meta: { title: i18n.t('accounts.AccountBackup.ExecutionDetail') },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'executions/:id',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanDetail/AccountBackupPlanExecution/AccountBackupPlanExecutionDetail/index.vue'),
|
||||
name: 'AccountBackupPlanExecutionDetail',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.ExecutionDetail') },
|
||||
meta: { title: i18n.t('accounts.AccountBackup.ExecutionDetail') },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
@ -7,7 +7,11 @@ export default [
|
||||
path: 'assets',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: { title: i18n.t('route.AssetList'), permissions: ['assets.view_asset'] },
|
||||
meta: {
|
||||
title: i18n.t('route.AssetList'),
|
||||
app: 'assets',
|
||||
resource: 'asset'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@ -36,7 +40,11 @@ export default [
|
||||
component: empty,
|
||||
redirect: '',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.HostList'), permissions: ['assets.view_asset'] },
|
||||
meta: {
|
||||
title: i18n.t('route.HostList'),
|
||||
app: 'assets',
|
||||
resource: 'asset'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@ -66,7 +74,11 @@ export default [
|
||||
component: empty,
|
||||
redirect: '',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.Databases'), permissions: ['assets.view_asset'] },
|
||||
meta: {
|
||||
title: i18n.t('route.Databases'),
|
||||
app: 'assets',
|
||||
resource: 'asset'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@ -96,7 +108,11 @@ export default [
|
||||
component: empty,
|
||||
redirect: '',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.devices'), permissions: ['assets.view_asset'] },
|
||||
meta: {
|
||||
title: i18n.t('route.devices'),
|
||||
app: 'assets',
|
||||
resource: 'asset'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@ -126,7 +142,11 @@ export default [
|
||||
component: empty,
|
||||
redirect: '',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.networking'), permissions: ['assets.view_asset'] },
|
||||
meta: {
|
||||
title: i18n.t('route.networking'),
|
||||
app: 'assets',
|
||||
resource: 'asset'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@ -156,7 +176,11 @@ export default [
|
||||
component: empty,
|
||||
redirect: '',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.networking'), permissions: ['assets.view_asset'] },
|
||||
meta: {
|
||||
title: i18n.t('route.networking'),
|
||||
app: 'assets',
|
||||
resource: 'asset'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
|
@ -12,7 +12,7 @@ export default [
|
||||
path: '',
|
||||
name: 'TaskList',
|
||||
component: () => import('@/views/tasks/TaskList'),
|
||||
meta: { title: i18n.t('route.TaskList'), permissions: [] }
|
||||
meta: { title: i18n.t('route.TaskList'), permissions: ['ops.view_celerytask'] }
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
|
@ -1,10 +1,11 @@
|
||||
import Layout from '@/layout'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import empty from '@/layout/empty'
|
||||
|
||||
const Setting = () => import('@/views/settings/index')
|
||||
|
||||
export default {
|
||||
path: '/settings',
|
||||
component: Layout,
|
||||
component: Setting,
|
||||
redirect: '/settings/basic',
|
||||
name: 'SystemSetting',
|
||||
meta: {
|
||||
@ -367,7 +368,7 @@ export default {
|
||||
component: () => import('@/views/settings/Tools'),
|
||||
meta: {
|
||||
title: i18n.t('setting.SystemTools'),
|
||||
icon: 'wrench',
|
||||
icon: 'tools',
|
||||
permissions: ['settings.view_setting']
|
||||
}
|
||||
},
|
||||
|
@ -57,7 +57,8 @@ export default {
|
||||
component: () => import('@/views/tickets/RequestAssetPerm/CreateUpdate'),
|
||||
meta: {
|
||||
title: i18n.t('tickets.OpenTicket'),
|
||||
permissions: ['tickets.view_ticket']
|
||||
permissions: ['tickets.view_ticket'],
|
||||
activeMenu: '/tickets/my-tickets'
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
@ -130,7 +131,8 @@ export default {
|
||||
component: () => import('@/views/tickets/TicketFlow/TicketFlow'),
|
||||
meta: {
|
||||
title: i18n.t('tickets.FlowSetUp'),
|
||||
permissions: ['tickets.view_ticketflow']
|
||||
permissions: ['tickets.view_ticketflow'],
|
||||
activeMenu: '/tickets/flow'
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -139,7 +141,8 @@ export default {
|
||||
component: () => import('@/views/tickets/TicketFlow/Detail'),
|
||||
meta: {
|
||||
title: i18n.t('route.TicketFlow'),
|
||||
permissions: ['tickets.view_ticketflow']
|
||||
permissions: ['tickets.view_ticketflow'],
|
||||
activeMenu: '/tickets/flow'
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
@ -149,7 +152,8 @@ export default {
|
||||
component: () => import('@/views/tickets/TicketFlow/FlowCreateUpdate'),
|
||||
meta: {
|
||||
title: i18n.t('route.TicketFlowCreate'),
|
||||
permissions: ['tickets.add_ticketflow']
|
||||
permissions: ['tickets.add_ticketflow'],
|
||||
activeMenu: '/tickets/flow'
|
||||
},
|
||||
hidden: true
|
||||
},
|
||||
@ -159,7 +163,8 @@ export default {
|
||||
component: () => import('@/views/tickets/TicketFlow/FlowCreateUpdate'),
|
||||
meta: {
|
||||
title: i18n.t('route.TicketFlowUpdate'),
|
||||
permissions: ['tickets.change_ticketflow']
|
||||
permissions: ['tickets.change_ticketflow'],
|
||||
activeMenu: '/tickets/flow'
|
||||
},
|
||||
hidden: true
|
||||
}
|
||||
|
@ -92,12 +92,10 @@ export default {
|
||||
{
|
||||
path: 'quick-job',
|
||||
name: 'QuickJob',
|
||||
hidden: true,
|
||||
component: () => import('@/views/ops/Job/QuickJob'),
|
||||
meta: {
|
||||
title: i18n.t('ops.QuickJob'),
|
||||
permissions: [],
|
||||
activeMenu: '/workbench/ops/job'
|
||||
permissions: ['ops.view_job', 'ops.add_job']
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -107,7 +105,7 @@ export default {
|
||||
redirect: '',
|
||||
meta: {
|
||||
title: i18n.t('route.JobList'),
|
||||
permissions: []
|
||||
permissions: ['ops.view_job']
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@ -116,7 +114,7 @@ export default {
|
||||
component: () => import('@/views/ops/Job'),
|
||||
meta: {
|
||||
title: i18n.t('route.JobList'),
|
||||
permissions: []
|
||||
permissions: ['ops.view_job']
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -126,7 +124,7 @@ export default {
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.JobCreate'),
|
||||
permissions: [],
|
||||
permissions: ['ops.add_job'],
|
||||
activeMenu: '/workbench/ops/job'
|
||||
}
|
||||
},
|
||||
@ -137,7 +135,7 @@ export default {
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.JobUpdate'),
|
||||
permissions: [],
|
||||
permissions: ['ops.change_job'],
|
||||
activeMenu: '/workbench/ops/job'
|
||||
}
|
||||
},
|
||||
@ -148,84 +146,94 @@ export default {
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.JobDetail'),
|
||||
permissions: [],
|
||||
permissions: ['ops.view_job'],
|
||||
activeMenu: '/workbench/ops/job'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'scripts',
|
||||
name: 'ScriptManage',
|
||||
component: () => import('@/views/ops/ScriptManage'),
|
||||
path: 'templates',
|
||||
name: 'Template',
|
||||
component: () => import('@/views/ops/Template'),
|
||||
meta: {
|
||||
title: i18n.t('route.ScriptManage'),
|
||||
permissions: []
|
||||
title: i18n.t('route.Template'),
|
||||
permissions: ['ops.view_adhoc', 'ops.view_playbook']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions',
|
||||
name: 'Executions',
|
||||
component: () => import('@/views/ops/Executions'),
|
||||
name: 'Execution',
|
||||
component: () => import('@/views/ops/Execution'),
|
||||
meta: {
|
||||
title: i18n.t('route.Executions'),
|
||||
permissions: []
|
||||
title: i18n.t('route.Execution'),
|
||||
permissions: ['ops.view_jobexecution']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'executions/:id',
|
||||
component: () => import('@/views/ops/Execution/ExecutionDetail'),
|
||||
name: 'ExecutionDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ops.ExecutionDetail'),
|
||||
permissions: ['ops.view_jobexecution'],
|
||||
activeMenu: '/workbench/ops/executions'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: 'adhoc/:id/update',
|
||||
name: 'AdhocUpdate',
|
||||
component: () => import('@/views/ops/ScriptManage/Adhoc/AdhocUpdateCreate'),
|
||||
component: () => import('@/views/ops/Template/Adhoc/AdhocUpdateCreate'),
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.updateAdhoc'),
|
||||
permissions: [],
|
||||
activeMenu: '/workbench/ops/scripts'
|
||||
permissions: ['ops.change_adhoc'],
|
||||
activeMenu: '/workbench/ops/templates'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'adhoc/create',
|
||||
name: 'AdhocCreate',
|
||||
hidden: true,
|
||||
component: () => import('@/views/ops/ScriptManage/Adhoc/AdhocUpdateCreate'),
|
||||
component: () => import('@/views/ops/Template/Adhoc/AdhocUpdateCreate'),
|
||||
meta: {
|
||||
title: i18n.t('ops.createAdhoc'),
|
||||
permissions: [],
|
||||
activeMenu: '/workbench/ops/scripts'
|
||||
permissions: ['ops.add_adhoc'],
|
||||
activeMenu: '/workbench/ops/templates'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'adhoc/:id',
|
||||
component: () => import('@/views/ops/ScriptManage/Adhoc/AdhocDetail'),
|
||||
component: () => import('@/views/ops/Template/Adhoc/AdhocDetail'),
|
||||
name: 'AdhocDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.AdhocDetail'),
|
||||
permissions: [],
|
||||
activeMenu: '/workbench/ops/scripts'
|
||||
permissions: ['ops.view_adhoc'],
|
||||
activeMenu: '/workbench/ops/templates'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'playbook/:id/update',
|
||||
name: 'PlaybookUpdate',
|
||||
hidden: true,
|
||||
component: () => import('@/views/ops/ScriptManage/Playbook/PlaybookUpdate'),
|
||||
component: () => import('@/views/ops/Template/Playbook/PlaybookUpdate'),
|
||||
meta: {
|
||||
title: i18n.t('ops.PlaybookUpdate'),
|
||||
permissions: [],
|
||||
activeMenu: '/workbench/ops/scripts'
|
||||
permissions: ['ops.change_playbook'],
|
||||
activeMenu: '/workbench/ops/templates'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'playbook/:id',
|
||||
component: () => import('@/views/ops/ScriptManage/Playbook/PlaybookDetail'),
|
||||
component: () => import('@/views/ops/Template/Playbook/PlaybookDetail'),
|
||||
name: 'PlaybookDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ops.PlaybookDetail'),
|
||||
permissions: [],
|
||||
activeMenu: '/workbench/ops/scripts'
|
||||
permissions: ['ops.view_playbook'],
|
||||
activeMenu: '/workbench/ops/templates'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -1,17 +1,15 @@
|
||||
import { logout, getProfile as apiGetProfile } from '@/api/users'
|
||||
import {
|
||||
getCurrentOrgLocal,
|
||||
getTokenFromCookie,
|
||||
saveCurrentOrgLocal
|
||||
} from '@/utils/auth'
|
||||
import { getProfile as apiGetProfile, logout } from '@/api/users'
|
||||
import { getCurrentOrgLocal, getTokenFromCookie, saveCurrentOrgLocal } from '@/utils/auth'
|
||||
import { resetRouter } from '@/router'
|
||||
import Vue from 'vue'
|
||||
|
||||
const _ = require('lodash')
|
||||
|
||||
const getDefaultState = () => {
|
||||
return {
|
||||
token: getTokenFromCookie(),
|
||||
currentOrg: '',
|
||||
preOrg: '',
|
||||
profile: {},
|
||||
username: '',
|
||||
auditOrgs: [],
|
||||
@ -64,6 +62,10 @@ const mutations = {
|
||||
state.consoleOrgs.push(org)
|
||||
},
|
||||
SET_CURRENT_ORG(state, org) {
|
||||
console.log('set pre org: ', state.currentOrg)
|
||||
if (state.currentOrg?.name !== 'System') {
|
||||
state.preOrg = state.currentOrg
|
||||
}
|
||||
state.currentOrg = org
|
||||
saveCurrentOrgLocal(state.username, org)
|
||||
},
|
||||
@ -120,6 +122,9 @@ const actions = {
|
||||
setCurrentOrg({ commit }, data) {
|
||||
commit('SET_CURRENT_ORG', data)
|
||||
},
|
||||
setPreOrg({ commit }, data) {
|
||||
commit('SET_PRE_ORG', data)
|
||||
},
|
||||
currentUserJoinNewOrg({ state, commit }, users) {
|
||||
const { profile, currentOrg, workbenchOrgs } = state
|
||||
if (users.includes(profile.id)) {
|
||||
|
BIN
src/styles/icons/clickhouse.png
Normal file
After Width: | Height: | Size: 203 B |
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 394 B |
BIN
src/styles/icons/mariadb.png
Normal file
After Width: | Height: | Size: 281 B |
BIN
src/styles/icons/mongodb.png
Normal file
After Width: | Height: | Size: 245 B |
Before Width: | Height: | Size: 596 B After Width: | Height: | Size: 316 B |
Before Width: | Height: | Size: 695 B After Width: | Height: | Size: 342 B |
BIN
src/styles/icons/private.png
Normal file
After Width: | Height: | Size: 278 B |
BIN
src/styles/icons/redis.png
Normal file
After Width: | Height: | Size: 421 B |
BIN
src/styles/icons/sqlserver.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
src/styles/icons/unix.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
src/styles/icons/website.png
Normal file
After Width: | Height: | Size: 303 B |
BIN
src/styles/icons/windows.png
Normal file
After Width: | Height: | Size: 222 B |
@ -5,24 +5,26 @@
|
||||
@import './fonts/font-awesome.min.css';
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width:14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
border-radius:10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
box-shadow: 8px 10px 20px #C6C6C6 inset;
|
||||
border: 3px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
box-shadow: 8px 10px 20px #878787 inset;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
height: calc(100% - 50px);
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: auto;
|
||||
color: #676a6c;
|
||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
@ -38,10 +40,6 @@ html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
@ -173,15 +171,17 @@ input[type=file] {
|
||||
|
||||
.view-switcher {
|
||||
.el-menu--popup-bottom-start {
|
||||
margin-top: 0!important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.el-menu--popup {
|
||||
min-width: 70px!important;
|
||||
min-width: 70px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-col.el-col-sm-24 .ibox {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@ -195,3 +195,7 @@ input[type=file] {
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.el-table .el-table__row .show-full-content > .cell {
|
||||
white-space: normal!important;
|
||||
}
|
||||
|
@ -9,12 +9,24 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-submenu-sidebar.el-submenu.is-opened {
|
||||
//box-shadow: 0 0 8px rgb(0 0 0 / 10%);
|
||||
|
||||
//* {
|
||||
// background-color: #f6f6f6 !important;
|
||||
//}
|
||||
|
||||
border-top: solid 1px #f3f3f4;
|
||||
//border-bottom: solid 1px #f3f3f4;
|
||||
}
|
||||
|
||||
.el-submenu, .el-menu-item.submenu-title-noDropdown {
|
||||
background-color: $menuBg;
|
||||
border-top: solid 1px #f3f3f4;
|
||||
border-top: solid 1px white;
|
||||
|
||||
.el-submenu__title {
|
||||
color: $menuText;
|
||||
font-weight: 400;
|
||||
|
||||
&:hover {
|
||||
color: $menuActiveText;
|
||||
@ -37,18 +49,19 @@
|
||||
|
||||
&.is-active {
|
||||
color: $subMenuActiveText;
|
||||
background-color: $subMenuHover !important;
|
||||
font-weight: 400;
|
||||
//background-color: $subMenuHover !important;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $subMenuHover;
|
||||
background-color: $subMenuHover !important;
|
||||
color: $subMenuActiveText;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
i {
|
||||
color: $menuText;
|
||||
//color: $menuText;
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,8 +82,8 @@
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu__title {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 52px;
|
||||
min-width: 200px;
|
||||
}
|
||||
@ -98,8 +111,12 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
li.is-active {
|
||||
// border-left: 4px solid var(--color-primary);
|
||||
li.is-opened, li.is-active {
|
||||
//* {
|
||||
// background-color: var(--menu-hover) !important;
|
||||
//}
|
||||
|
||||
border-left: 4px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.el-submenu-sidebar .el-menu-item {
|
||||
@ -110,7 +127,6 @@
|
||||
background-color: $subMenuBg;
|
||||
|
||||
.el-submenu__title {
|
||||
background-color: $subMenuBg !important;
|
||||
}
|
||||
|
||||
& .nest-menu {
|
||||
|
@ -1,9 +1,11 @@
|
||||
@import "./variables";
|
||||
|
||||
#app {
|
||||
.main-container {
|
||||
position: relative;
|
||||
top: 50px;
|
||||
min-height: 100%;
|
||||
margin-top: 50px;
|
||||
height: calc(100vh - 50px);
|
||||
transition: margin-left .28s;
|
||||
margin-left: $sideBarWidth;
|
||||
}
|
||||
@ -20,7 +22,7 @@
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
@ -69,23 +71,27 @@
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
font-weight: 450;
|
||||
font-size: 13px;
|
||||
|
||||
i {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
//background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active>.el-submenu__title {
|
||||
.is-active > .el-submenu__title {
|
||||
color: $menuActiveText !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-submenu-sidebar>.el-submenu__title,
|
||||
& .nest-menu .el-submenu-sidebar > .el-submenu__title,
|
||||
& .el-submenu-sidebar .el-menu-item {
|
||||
min-width: $sideBarWidth !important;
|
||||
background-color: $subMenuBg;
|
||||
|
||||
&:hover {
|
||||
background-color: $subMenuHover !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +113,7 @@
|
||||
.el-submenu-sidebar {
|
||||
overflow: hidden;
|
||||
|
||||
&>.el-submenu__title {
|
||||
& > .el-submenu__title {
|
||||
|
||||
.el-submenu__icon-arrow {
|
||||
display: none;
|
||||
@ -117,8 +123,8 @@
|
||||
|
||||
.el-menu--collapse {
|
||||
.el-submenu-sidebar {
|
||||
&>.el-submenu__title {
|
||||
&>span {
|
||||
& > .el-submenu__title {
|
||||
& > span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
@ -164,13 +170,14 @@
|
||||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
&>.el-menu {
|
||||
& > .el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu-sidebar>.el-submenu__title,
|
||||
|
||||
.nest-menu .el-submenu-sidebar > .el-submenu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
@ -178,7 +185,7 @@
|
||||
}
|
||||
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
>.el-menu--popup {
|
||||
> .el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
@ -198,6 +205,7 @@
|
||||
.el-menu-item {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
|
||||
&.is-active {
|
||||
color: var(--color-primary);
|
||||
border-left: 4px solid var(--color-primary);
|
||||
|
@ -30,12 +30,12 @@
|
||||
}
|
||||
|
||||
.ztree li span.button.chk {
|
||||
margin: 0px;
|
||||
margin-right: 1px;
|
||||
cursor: auto;
|
||||
width: 12px;
|
||||
display: inline-block;
|
||||
padding-top: 10px;
|
||||
padding-left: 2px;
|
||||
padding-top: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.ztree li span.button.chk.checkbox_false_full::before {
|
||||
@ -241,11 +241,55 @@
|
||||
}
|
||||
|
||||
.ztree li span.button.k8s_ico_docu {
|
||||
background: url('./icons/k8s.png') no-repeat scroll 0 0 transparent;
|
||||
background: url('./icons/k8s.png') center left no-repeat;
|
||||
}
|
||||
|
||||
.ztree li span.button.mysql_ico_docu {
|
||||
background: url('./icons/mysql.png') no-repeat scroll 0 0 transparent;
|
||||
background: url('./icons/mysql.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.redis_ico_docu {
|
||||
background: url('./icons/redis.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.mariadb_ico_docu {
|
||||
background: url('./icons/mariadb.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.postgresql_ico_docu {
|
||||
background: url('./icons/postgresql.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.mongodb_ico_docu {
|
||||
background: url('./icons/mongodb.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.unix_ico_docu {
|
||||
background: url('./icons/unix.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.private_ico_docu {
|
||||
background: url('./icons/private.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.sqlserver_ico_docu {
|
||||
background: url('./icons/sqlserver.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.website_ico_docu {
|
||||
background: url('./icons/website.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.clickhouse_ico_docu {
|
||||
background: url('./icons/clickhouse.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li span.button.windows_ico_docu {
|
||||
background: url('./icons/windows.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
.ztree li a > span.button {
|
||||
filter: invert(47%) sepia(3%) saturate(412%) hue-rotate(161deg) brightness(83%) contrast(83%);
|
||||
}
|
||||
|
||||
.ztree li span.button.oracle_ico_docu::before {
|
||||
|
@ -333,4 +333,12 @@ export function downloadText(content, filename) {
|
||||
window.URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
export function diffObject(object, base) {
|
||||
return _.transform(object, (result, value, key) => {
|
||||
if (!_.isEqual(value, base[key])) {
|
||||
result[key] = (_.isObject(value) && _.isObject(base[key])) ? diffObject(value, base[key]) : value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export { BASE_URL }
|
||||
|
@ -2,6 +2,7 @@ import store from '@/store'
|
||||
import { hasUUID, replaceUUID } from '@/utils/common'
|
||||
|
||||
export const DEFAULT_ORG_ID = '00000000-0000-0000-0000-000000000002'
|
||||
export const SYSTEM_ORG_ID = '00000000-0000-0000-0000-000000000004'
|
||||
|
||||
function getPropOrg() {
|
||||
const orgs = store.getters.usingOrgs
|
||||
@ -17,7 +18,7 @@ async function change2PropOrg() {
|
||||
await changeOrg(org)
|
||||
}
|
||||
|
||||
async function changeOrg(org) {
|
||||
async function changeOrg(org, reload = true) {
|
||||
await store.dispatch('users/setCurrentOrg', org)
|
||||
await store.dispatch('app/reset')
|
||||
let path = location.href
|
||||
@ -44,6 +45,7 @@ function hasCurrentOrgPermission() {
|
||||
export default {
|
||||
hasCurrentOrgPermission,
|
||||
DEFAULT_ORG_ID,
|
||||
SYSTEM_ORG_ID,
|
||||
change2PropOrg,
|
||||
changeOrg,
|
||||
getPropOrg
|
||||
|
@ -87,6 +87,10 @@ export function changeMenuColor(themeColors) {
|
||||
|
||||
if (key.includes('--color')) {
|
||||
elementStyle.setProperty(key, currentColor)
|
||||
const lightColor = mix(white, currentColor.replace(/#/g, ''), 70)
|
||||
const darkColor = mix(black, currentColor.replace(/#/g, ''), 20)
|
||||
elementStyle.setProperty(key + '-light', lightColor)
|
||||
elementStyle.setProperty(key + '-dark', darkColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|